|
Is Anybody Out There?
Listing 2. With raw sockets you can send ICMP echo requests and implement a Java ping command. package example;
import java.io.IOException;
import java.net.InetAddress;
import java.util.concurrent.*;
import org.savarese.vserv.tcpip.*;
import org.savarese.rocksaw.net.RawSocket;
public class Ping {
public static interface EchoReplyListener {
public void notifyEchoReply(
ICMPEchoPacket packet, byte[] data,
int dataOffset);
}
private RawSocket socket;
private ICMPEchoPacket packet;
private int offset, length, dataOffset;
private byte[] data;
private int sequence, identifier;
private EchoReplyListener listener;
public Ping(int id) throws IOException {
sequence = 0;
identifier = id;
setEchoReplyListener(null);
packet = new ICMPEchoPacket(1);
data = new byte[84];
packet.setData(data);
packet.setIPHeaderLength(5);
packet.setICMPDataByteLength(56);
offset = packet.getIPHeaderByteLength();
dataOffset =
packet.getIPHeaderByteLength() +
packet.getICMPHeaderByteLength();
length = packet.getICMPPacketByteLength();
socket = new RawSocket();
socket.open(RawSocket.PF_INET,
RawSocket.getProtocolByName("icmp"));
}
public void setEchoReplyListener(
EchoReplyListener l) {
listener = l;
}
public void close() throws IOException {
socket.close();
}
public void sendEchoRequest(InetAddress host)
throws IOException
{
packet.setType(ICMPPacket.TYPE_ECHO_REQUEST);
packet.setCode(0);
packet.setIdentifier(identifier);
packet.setSequenceNumber(sequence++);
OctetConverter.longToOctets(
System.nanoTime(), data, dataOffset);
packet.computeICMPChecksum();
socket.write(host, data, offset, length);
}
public void receive(InetAddress host)
throws IOException
{
socket.read(host, data);
}
public void receiveEchoReply(InetAddress host)
throws IOException
{
do {
receive(host);
} while(packet.getType() !=
ICMPPacket.TYPE_ECHO_REPLY ||
packet.getIdentifier() != identifier);
if(listener != null)
listener.notifyEchoReply(
packet, data, dataOffset);
}
public long ping(InetAddress host)
throws IOException
{
sendEchoRequest(host);
receiveEchoReply(host);
long end = System.nanoTime();
long start =
OctetConverter.octetsToLong(data, dataOffset);
return (end - start);
}
public int getDataLength() {
return packet.getICMPDataByteLength();
}
public int getPacketLength() {
return packet.getIPPacketLength();
}
public static final void main(String[] args)
throws Exception
{
if(args.length < 1 || args.length > 2) {
System.err.println("usage: Ping host [count]");
System.exit(1);
}
try{
final InetAddress address =
InetAddress.getByName(args[0]);
final String hostname =
address.getCanonicalHostName();
final String hostaddr =
address.getHostAddress();
final int count;
if(args.length == 2)
count = Integer.parseInt(args[1]);
else
count = 5;
// Ping programs usually use the process ID for
// the identifier, but this is only a demo.
final Ping ping = new Ping(65535);
ping.setEchoReplyListener(
new EchoReplyListener() {
public void notifyEchoReply(
ICMPEchoPacket packet, byte[] data,
int dataOffset) {
long end = System.nanoTime();
long start =
OctetConverter.octetsToLong(
data, dataOffset);
double rtt = (double)(end - start) / 1e6;
System.out.println(
packet.getICMPPacketByteLength() +
" bytes from " + hostname + " (" +
hostaddr + "): icmp_seq=" +
packet.getSequenceNumber() +" ttl=" +
packet.getTTL() +" time=" + rtt +
" ms");
}
});
System.out.println("PING " + hostname + " (" +
hostaddr + ") " + ping.getDataLength() + "(" +
ping.getPacketLength() + ") bytes of data).");
final ScheduledThreadPoolExecutor executor =
new ScheduledThreadPoolExecutor(2);
final CountDownLatch latch =
new CountDownLatch(1);
executor.scheduleAtFixedRate(new Runnable() {
int counter = count;
public void run() {
try {
if(counter > 0) {
ping.sendEchoRequest(address);
if(counter == count)
latch.countDown();
--counter;
} else
executor.shutdown();
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
}, 0, 1, TimeUnit.SECONDS);
// We wait for first ping to be sent because
// Windows times out with WSAETIMIEDOUT if
// echo request hasn't been sent first.
// POSIX does the right thing and just blocks.
latch.await();
for(int i = 0; i < count; ++i)
ping.receiveEchoReply(address);
ping.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
|