package com.netifera.platform.net.tools.portscanning;
import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.netifera.platform.api.tools.ToolException;
import com.netifera.platform.net.internal.tools.portscanning.Activator;
import com.netifera.platform.net.sockets.CompletionHandler;
import com.netifera.platform.net.sockets.UDPChannel;
import com.netifera.platform.tools.RequiredOptionMissingException;
import com.netifera.platform.util.PortSet;
import com.netifera.platform.util.addresses.inet.InternetAddress;
import com.netifera.platform.util.locators.UDPSocketLocator;
public class UDPScanner extends AbstractPortscanner {
Integer timeout;
Integer delay;
UDPChannel channel;
@Override
protected void setupToolOptions() throws ToolException {
context.setTitle("UDP scan");
super.setupToolOptions();
timeout = (Integer) context.getConfiguration().get("timeout");
if (timeout == null)
throw new RequiredOptionMissingException("timeout");
delay = (Integer) context.getConfiguration().get("delay");
if (delay == null)
throw new RequiredOptionMissingException("delay");
}
@Override
protected void scannerRun() throws ToolException {
context.setTitle("UDP scan "+targetNetwork);
context.setTotalWork(targetNetwork.itemCount()*targetPorts.itemCount()+1); //+1 in order to account for waiting responses after sending all requests
try {
channel = Activator.getInstance().getSocketEngine().openUDP();
// channel.setReuseAddress(true);
// channel.bind(new UDPSocketLocator(IPv4Address.any, 53));
readResponses();
scanAllAddresses(channel);
context.setStatus("Waiting responses for "+timeout+" seconds...");
Thread.sleep(timeout*1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
context.warning("Interrupted");
return;
} catch (IOException e) {
context.exception("I/O Error", e);
} finally {
if (channel != null)
try {
channel.close();
} catch (IOException e) {
context.exception("I/O Error", e);
}
}
}
private void scanAllAddresses(UDPChannel channel) throws InterruptedException {
ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
for (Integer port: targetPorts) {
byte[] trigger = Activator.getInstance().getServerDetector().getTrigger("udp",port);
// context.debug("Trigger for port "+port+": "+trigger);
writeBuffer.clear();
writeBuffer.put(trigger);
writeBuffer.flip();
context.setStatus("Scanning "+targetNetwork+":"+port+"/udp");
for (InternetAddress address: targetNetwork) {
if (Thread.currentThread().isInterrupted())
throw new InterruptedException();
try {
writeBuffer.rewind();
channel.send(writeBuffer, new UDPSocketLocator(address, port)).get();
Thread.sleep(delay);
} catch (ExecutionException e) {
if (e.getCause().getClass() == SocketException.class) {
context.warning(e.getCause().getMessage() + " for " +
address + ":" + port + "/udp");
} else {
context.exception("Exception", e);
}
} finally {
context.worked(1);
}
}
}
}
private void readResponses() {
final ByteBuffer responseBuffer = ByteBuffer.allocate(4096);
channel.receive(responseBuffer, 1, TimeUnit.SECONDS, null, new CompletionHandler<UDPSocketLocator,Void>() {
public void cancelled(Void attachment) {
context.debug("Receive operation cancelled");
}
public void completed(UDPSocketLocator peer, Void attachment) {
context.debug("Received response from "+peer);
PortSet ports = new PortSet();
ports.addPort(peer.getPort());
Activator.getInstance().getNetworkEntityFactory().addOpenUDPPorts(realm, context.getSpaceId(), peer.getAddress(), ports);
responseBuffer.flip();
byte[] trigger = Activator.getInstance().getServerDetector().getTrigger("udp",peer.getPort());
Map<String,String> serviceInfo = Activator.getInstance().getServerDetector().detect("udp",peer.getPort(), ByteBuffer.wrap(trigger), responseBuffer);
if (serviceInfo != null) {
Activator.getInstance().getNetworkEntityFactory().createService(realm, context.getSpaceId(), peer, serviceInfo.get("serviceType"), serviceInfo);
context.info(serviceInfo.get("serviceType")+" @ "+peer);
} else {
context.warning("Unknown service @ " + peer);
}
responseBuffer.clear();
channel.receive(responseBuffer, 1, TimeUnit.SECONDS, attachment, this);
}
public void failed(Throwable e, Void attachment) {
if (e instanceof SocketTimeoutException) {
channel.receive(responseBuffer, 1, TimeUnit.SECONDS, attachment, this);
} else {
context.exception("Exception",e);
}
}
});
}
}