00001
00002 import java.io.IOException;
00003 import java.net.Inet4Address;
00004 import java.net.InetAddress;
00005 import java.net.MalformedURLException;
00006 import java.net.URL;
00007 import java.net.UnknownHostException;
00008 import java.util.Arrays;
00009
00010 import jpcap.JpcapCaptor;
00011 import jpcap.JpcapSender;
00012 import jpcap.NetworkInterface;
00013 import jpcap.NetworkInterfaceAddress;
00014 import jpcap.packet.EthernetPacket;
00015 import jpcap.packet.ICMPPacket;
00016 import jpcap.packet.IPPacket;
00017 import jpcap.packet.Packet;
00018 import jpcap.packet.TCPPacket;
00019
00020
00021
00022
00023
00024
00025
00026
00027 public class Traceroute implements Runnable
00028 {
00029
00030
00031
00032 public interface Context
00033 {
00034
00035
00036
00037 public abstract void logNewLine ();
00038
00039
00040
00041
00042
00043
00044
00045 public abstract void logMessage( String str, boolean timestamp );
00046
00047
00048
00049
00050 public abstract void onTracerouteCompleted ();
00051 }
00052
00053
00054
00055
00056
00057
00058 private Thread tracingThread;
00059
00060
00061
00062
00063 private volatile boolean running = false;
00064
00065
00066
00067
00068 private volatile boolean completed;
00069
00070
00071
00072
00073 private int deviceCount = 0;
00074
00075
00076
00077
00078 private JpcapCaptor captor = null;
00079
00080
00081
00082
00083
00084 private NetworkInterface device = null;
00085
00086
00087
00088
00089 private InetAddress localIP = null;
00090
00091
00092
00093
00094
00095 private boolean resolveNames = false;
00096
00097
00098
00099
00100 private String hostName;
00101
00102
00103
00104
00105
00106 private int initialTTL;
00107
00108
00109
00110
00111 private Context context;
00112
00113
00114
00115
00116
00117
00118
00119
00120 public Traceroute( Context context )
00121 {
00122 this.context = context;
00123 this.running = false;
00124 this.completed = true;
00125 this.tracingThread = null;
00126
00127 deviceCount = JpcapCaptor.getDeviceList ().length;
00128 }
00129
00130
00131
00132
00133 private void println( String str )
00134 {
00135 context.logMessage( str, true );
00136 context.logNewLine ();
00137 }
00138
00139
00140
00141
00142 private void println ()
00143 {
00144 context.logNewLine ();
00145 }
00146
00147
00148
00149
00150
00151
00152
00153 private void print( String str, boolean timestamp )
00154 {
00155 context.logMessage( str,timestamp );
00156 }
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168 public void startPinging( int deviceNo, String hostName, int initialTTL )
00169 {
00170 synchronized( this )
00171 {
00172 if ( ! completed ) {
00173 return;
00174 }
00175
00176
00177
00178 openDeviceOnInterface( deviceNo );
00179 this.hostName = hostName;
00180 this.initialTTL = initialTTL;
00181
00182
00183
00184 running = true;
00185 completed = false;
00186
00187
00188
00189 tracingThread = new Thread( this );
00190 tracingThread.start ();
00191 }
00192 }
00193
00194
00195
00196
00197 public void stopTrace ()
00198 {
00199 synchronized( this )
00200 {
00201 running = false;
00202 this.notifyAll ();
00203 }
00204
00205 print( " <interrupt> ", false );
00206 }
00207
00208
00209
00210
00211
00212 public boolean isIdle ()
00213 {
00214 return completed;
00215 }
00216
00217
00218
00219
00220
00221
00222
00223 public void dumpInterfaceInfo( String title, NetworkInterface ni )
00224 {
00225 println( title );
00226 println( " Desc: " + ni.description );
00227 println( " Name: " + ni.name );
00228 for( NetworkInterfaceAddress na : ni.addresses )
00229 {
00230 println( " Addr: " + na.address );
00231 }
00232 }
00233
00234
00235
00236
00237
00238
00239 public String[] getInterfaceList ()
00240 {
00241 this.deviceCount = JpcapCaptor.getDeviceList ().length;
00242 String[] devList = new String[ this.deviceCount ];
00243
00244 int ni_index = 0;
00245 for( NetworkInterface ni : JpcapCaptor.getDeviceList () )
00246 {
00247 String ourDescription = ni.description;
00248 for( NetworkInterfaceAddress addr : ni.addresses )
00249 {
00250 if( addr.address instanceof Inet4Address ) {
00251 ourDescription = addr.address.toString () + " -- " + ni.description;
00252 break;
00253 }
00254 }
00255
00256 devList[ ni_index ] = " iface" + ni_index + " -- " + ourDescription;
00257
00258 dumpInterfaceInfo( "Interface " + (ni_index++), ni );
00259 }
00260
00261 return devList;
00262 }
00263
00264
00265
00266
00267
00268
00269 private void openDeviceOnInterface( int deviceNo )
00270 {
00271
00272
00273 device = JpcapCaptor.getDeviceList()[ deviceNo ];
00274 localIP = null;
00275 captor = null;
00276
00277 try
00278 {
00279 captor = JpcapCaptor.openDevice( device,
00280 2000, false, 1 );
00281
00282
00283
00284
00285 for( NetworkInterfaceAddress addr : device.addresses )
00286 {
00287 if( addr.address instanceof Inet4Address ) {
00288 localIP = addr.address;
00289 break;
00290 }
00291 }
00292 }
00293 catch ( IOException e )
00294 {
00295 device = null;
00296 localIP = null;
00297 captor = null;
00298 }
00299 }
00300
00301
00302
00303
00304
00305
00306 private void interruptibleSleep( int millis )
00307 {
00308 synchronized( this )
00309 {
00310 try {
00311 this.wait( millis );
00312 }
00313 catch( InterruptedException ie ) {
00314 running = false;
00315 }
00316 }
00317 }
00318
00319
00320
00321
00322
00323
00324 private byte[] obtainDefaultGatewayMac( String httpHostToCheck )
00325 {
00326 print( "Obtaining default gateway MAC address... ", true );
00327
00328 byte[] gatewayMAC = null;
00329
00330 if ( captor != null ) try
00331 {
00332 InetAddress hostAddr = InetAddress.getByName( httpHostToCheck );
00333 captor.setFilter( "tcp and dst host " + hostAddr.getHostAddress(), true );
00334
00335 int timeoutTimer = 0;
00336 new URL("http://" + httpHostToCheck ).openStream().close();
00337
00338 while( running )
00339 {
00340 Packet ping = captor.getPacket ();
00341
00342 if( ping == null )
00343 {
00344 if ( timeoutTimer < 20 ) {
00345 interruptibleSleep( 100 );
00346 ++timeoutTimer;
00347 continue;
00348 }
00349
00350
00351 print( "<timeout>", false );
00352 println( "ERROR: Cannot obtain MAC address for default gateway." );
00353 println( "Maybe there is no default gateway on selected interface?" );
00354 return gatewayMAC;
00355 }
00356
00357 byte[] destinationMAC = ((EthernetPacket)ping.datalink).dst_mac;
00358
00359 if( ! Arrays.equals( destinationMAC, device.mac_address ) ) {
00360 gatewayMAC = destinationMAC;
00361 break;
00362 }
00363
00364 timeoutTimer = 0;
00365 new URL("http://" + httpHostToCheck ).openStream().close();
00366 }
00367 }
00368 catch( MalformedURLException e )
00369 {
00370 println( "Invalid URL: " + e.toString () );
00371 }
00372 catch( UnknownHostException e )
00373 {
00374 println( "Unknown host: " + httpHostToCheck );
00375 }
00376 catch( IOException e )
00377 {
00378 println( "ERROR: " + e.toString () );
00379 }
00380
00381 print( " OK.", false );
00382 println ();
00383 return gatewayMAC;
00384 }
00385
00386
00387
00388
00389
00390
00391 public boolean tcpPortScan ( int localPort, String remoteHost, int rp1, int rp2 )
00392 throws UnknownHostException, IOException
00393 {
00394 println( "-------------------------------------------------" );
00395 print( "Looking up " + hostName + "...", true );
00396
00397 InetAddress remoteIP = InetAddress.getByName( remoteHost );
00398
00399 print( " " + remoteIP.getHostAddress (), false );
00400 println ();
00401
00402 byte[] defaultGatewayMAC = obtainDefaultGatewayMac( "dsv.su.se" );
00403 if ( defaultGatewayMAC == null )
00404 {
00405 running = false;
00406 completed = true;
00407 context.onTracerouteCompleted ();
00408 return running;
00409 }
00410
00411 TCPPacket tcp = new TCPPacket(
00412 localPort,
00413 0,
00414 701,
00415 0,
00416 false,
00417 false,
00418 false,
00419 false,
00420 true,
00421 false,
00422 true,
00423 true,
00424 10,
00425 10
00426 );
00427
00428 tcp.setIPv4Parameter(
00429 0,
00430 false,
00431 false,
00432 false,
00433 0,
00434 false,
00435 false,
00436 false,
00437 0,
00438 (int)(Math.random () * 65000),
00439 100,
00440 IPPacket.IPPROTO_TCP,
00441 localIP,
00442 remoteIP
00443 );
00444
00445 tcp.data=("").getBytes();
00446
00447 EthernetPacket ether = new EthernetPacket ();
00448 ether.frametype = EthernetPacket.ETHERTYPE_IP;
00449 ether.src_mac = device.mac_address;
00450 ether.dst_mac = defaultGatewayMAC;
00451 tcp.datalink = ether;
00452
00453
00454
00455 JpcapSender sender = captor.getJpcapSenderInstance ();
00456 captor.setFilter( "tcp and dst port " + localPort
00457 + " and dst host " + localIP.getHostAddress(),
00458 true );
00459
00460 for ( int remotePort = rp1; remotePort <= rp2; ++remotePort ) {
00461 tcp.src_port = localPort;
00462 tcp.dst_port = remotePort;
00463 sender.sendPacket( tcp );
00464
00465 }
00466
00467 while( running )
00468 {
00469 TCPPacket p = (TCPPacket)captor.getPacket ();
00470
00471 if( p == null )
00472 {
00473 interruptibleSleep( 100 );
00474 continue;
00475 }
00476
00477
00478
00479 String hopID = p.src_ip.getHostAddress ();
00480 if ( resolveNames ) {
00481 p.src_ip.getHostName ();
00482 hopID = p.src_ip.toString ();
00483 }
00484
00485
00486
00487
00488 if ( ! p.rst ) {
00489 if ( p.ack_num == 702 ) {
00490 println( "---- " + hopID + " : " + p.src_port );
00491 }
00492 tcp.dst_port = p.src_port;
00493 tcp.fin = p.ack_num == 702 ? true : false;
00494 tcp.ack = true;
00495 tcp.rst = false;
00496 tcp.syn = false;
00497 tcp.sequence = p.ack_num;
00498 tcp.ack_num = p.sequence + 1;
00499 sender.sendPacket( tcp );
00500
00501 }
00502
00503 }
00504
00505 return running;
00506 }
00507
00508
00509
00510
00511
00512
00513 @Override
00514 public void run ()
00515 {
00516
00517
00518 if ( ! running ) {
00519 completed = true;
00520 context.onTracerouteCompleted ();
00521 return;
00522 }
00523
00524
00525
00526 if ( captor == null ) {
00527 println( "Capturing device is not configured..." );
00528 running = false;
00529 completed = true;
00530 context.onTracerouteCompleted ();
00531 return;
00532 }
00533
00534
00535
00536 try
00537 {
00538
00539
00540
00541
00542
00543
00544
00545 println( "-------------------------------------------------" );
00546 print( "Looking up " + hostName + "...", true );
00547
00548 InetAddress remoteIP = InetAddress.getByName( hostName );
00549
00550 print( " " + remoteIP.getHostAddress (), false );
00551 println ();
00552
00553 byte[] defaultGatewayMAC = obtainDefaultGatewayMac( "dsv.su.se" );
00554 if ( defaultGatewayMAC == null )
00555 {
00556 running = false;
00557 completed = true;
00558 context.onTracerouteCompleted ();
00559 return;
00560 }
00561
00562 if ( initialTTL == 0 ) {
00563 println( "Tracing route to " + remoteIP + "..." );
00564 } else {
00565 println( "Pinging host " + remoteIP + "..." );
00566 }
00567
00568
00569
00570 ICMPPacket icmp = new ICMPPacket ();
00571
00572 icmp.type = ICMPPacket.ICMP_ECHO;
00573 icmp.seq = 100;
00574 icmp.id = 0;
00575 icmp.data = "data".getBytes ();
00576
00577 icmp.setIPv4Parameter(
00578 0,
00579 false,
00580 false,
00581 false,
00582 0,
00583 false,
00584 false,
00585 false,
00586 0,
00587 0,
00588 0,
00589 IPPacket.IPPROTO_ICMP,
00590 localIP,
00591 remoteIP
00592 );
00593
00594 EthernetPacket ether = new EthernetPacket ();
00595 ether.frametype = EthernetPacket.ETHERTYPE_IP;
00596 ether.src_mac = device.mac_address;
00597 ether.dst_mac = defaultGatewayMAC;
00598 icmp.datalink = ether;
00599
00600
00601
00602 JpcapSender sender = captor.getJpcapSenderInstance ();
00603 captor.setFilter( "icmp and dst host " + localIP.getHostAddress(), true );
00604
00605 icmp.hop_limit = (short)initialTTL;
00606 print( icmp.hop_limit + ": ", true );
00607 int timeoutTimer = 0;
00608 int timeoutCounter = 0;
00609 long tStart = System.nanoTime ();
00610 sender.sendPacket( icmp );
00611
00612 while( running )
00613 {
00614 ICMPPacket p = (ICMPPacket)captor.getPacket ();
00615 int tDelay = (int)( ( System.nanoTime () - tStart ) / 1000000l );
00616
00617 if( p == null )
00618 {
00619
00620
00621 if ( timeoutTimer < 30 )
00622 {
00623 interruptibleSleep( timeoutTimer < 10 ? 1 : 100 );
00624 ++timeoutTimer;
00625 if ( timeoutTimer >= 10 ) {
00626 print( ".", false );
00627 }
00628 continue;
00629 }
00630
00631
00632
00633 ++timeoutCounter;
00634 print( " Timeout #" + timeoutCounter, false );
00635
00636 if ( timeoutCounter < 3 )
00637 {
00638 print( icmp.hop_limit + ": ", true );
00639 tStart = System.nanoTime ();
00640 timeoutTimer = 0;
00641 sender.sendPacket( icmp );
00642 }
00643 else
00644 {
00645 ++icmp.hop_limit;
00646 print( icmp.hop_limit + ": ", true );
00647 timeoutTimer = 0;
00648 timeoutCounter = 0;
00649 tStart = System.nanoTime ();
00650 sender.sendPacket( icmp );
00651 }
00652 continue;
00653 }
00654
00655
00656
00657 String hopID = p.src_ip.getHostAddress ();
00658 if ( resolveNames ) {
00659 p.src_ip.getHostName ();
00660 hopID = p.src_ip.toString ();
00661 }
00662
00663
00664
00665
00666
00667 if( p.type == ICMPPacket.ICMP_TIMXCEED )
00668 {
00669 print( hopID + ", " + tDelay + " ms", false );
00670 ++icmp.hop_limit;
00671 print( icmp.hop_limit + ": ", true );
00672 timeoutTimer = 0;
00673 timeoutCounter = 0;
00674 tStart = System.nanoTime ();
00675 sender.sendPacket( icmp );
00676 }
00677 else if( p.type == ICMPPacket.ICMP_UNREACH )
00678 {
00679 print( hopID + " unreachable", false );
00680 running = false;
00681 }
00682 else if( p.type == ICMPPacket.ICMP_ECHOREPLY )
00683 {
00684 print( hopID + ", " + tDelay + " ms", false );
00685 if ( initialTTL != 0 ) {
00686 println( hopID + " is alive." );
00687 }
00688 running = false;
00689 }
00690 }
00691 }
00692 catch( UnknownHostException e )
00693 {
00694 println( "Unknown host: " + hostName );
00695 completed = true;
00696 context.onTracerouteCompleted ();
00697 return;
00698 }
00699 catch( IOException e )
00700 {
00701 println( "ERROR: " + e.toString () );
00702 completed = true;
00703 context.onTracerouteCompleted ();
00704 return;
00705 }
00706
00707
00708
00709 println( initialTTL == 0 ? "Traceroute completed." : "Ping completed." );
00710 completed = true;
00711 context.onTracerouteCompleted ();
00712 }
00713 }