Traceroute.V Explained

Introduction


This started off as an experiment for me. I wanted to play with a bunch of interesting and new (or neglected) technologies like the Google Maps API, asynchronous communication between JavaScript and a HTTP server, asynchronous communication between a Java applet and JavaScript, IP address to location mapping, dynamic page updates, regular expressions, etc.

TRACEROUTE.V has four major components: an HTLM page, a JavaScript state machine, a Java applet and a simple server back-end.

The HTML Page


This is arguably the simplest component of the four. It's really just some (hopefully not entirely unaesthetic) cobweb that holds together the rest. A form, a few tables, an OnLoad() handler to check for Java support, etc. Very simple and quite efficient.

JavaScript


The JavaScript application is probably the most complex of the four components. The idea is that once the user clicks the "Traceroute" button, we discover the route to the destination using a series of TTL-limited pings. Once we arrive at the destination we resolve the IP addresses to host names in a separate pass. Finally, we enter an infinite loop and start pinging the IP address alongside the route, displaying the results. To make things look a bit more interesting we display a Google Map with markers for known hops.

The map itself was a piece of cake to implement (the Google Maps API is a joy to work with):
map = new GMap2(document.getElementById("map"));
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
map.setCenter(new GLatLng(42.487535, -71.115706), 13);
To get at the latitude/longitude coordinates that belong to a specific IP address we use a database back on the server. The JavaScript component talks to the server using asynchronous HTTP calls.

Low-level stuff like the actual pings or reverse DNS lookups have to be punted to a Java applet.
document.applets[0].resolveHost("www.codefromthe70s.org")
The application, of course, cannot run for extended periods of time: it can't just send off a ping and wait for a response that might never come. Scripts in the running state effectively block the browser therefore everything has to be asynchronous. The simplest way to do this is to build a state machine that has a single entry point (the nextstep() function in this code), and keeps track of what it's supposed to do via a bunch of global variables.

Feel free to check out the source code of the page - the JavaScript is all inline and not obfuscated. I even left some comments in there.

The Java Applet


The applet itself is quite simple. Unfortunately it had to be signed code (therefore the user must explicitly allow it to run with elevated privileges) for two main reasons:

1. Using the InetAddress class in any interesting manner (such as reverse DNS lookups) is only available to signed applets.

2. ICMP support is nonexistent in Java, save for a single castrated and utterly useless isReachable call. The only way you can really do a ping from Java code is to call the native operating system's ping executable and parse its output. (In theory you can also do JNI but that's downright impossible from an applet - and requires a lot of work for every supported platform.)

Once the applet was built in a simple (synchronous) manner for testing purposes, my JavaScript code calling its public methods that performed things like DNS lookups fell on its face due to a bunch of security exceptions. Even though the applet is signed, the script calling it isn't - and in this scenario Java becomes paranoid and executes bytecode as if it weren't signed. I can't say I disagree with this design decision but if your goal is to create a signed applet that wants to expose a few elevated-privilege methods to unsigned scripts it's very easy to engineer around any limitations you might face. For example, have the applet's start() function spawn a thread that can watch for and act upon events received by public methods called by scripts. The spawned thread will have the correct security context so it will be free to do whatever it needs to.

In any case - the applet receives "events" via its public methods that return immediately. Then it acts upon these events (let's say it performs a reverse DNS lookup) and it invokes a JavaScript call within the page itself when it has results to report back to the script. Calling JavaScript from Java is not a very widely used technique but it's certainly very easy to do:
import netscape.javascript.*;

...

((JSObject) JSObject.getWindow(this)).eval("alert('Hello from Java!');");
The tricky part is running and parsing the output of the ping executable. Yes, you can use runtime.exec() but you need to eat the input from the two InputStreams you get from the resulting Process object instead of just calling Process.wait(). If you don't do this you run the risk of deadlocking yourself. An excellent article on runtime.exec() is here: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html

Parsing the output from the ping executable is very easy using the Java regular expression classes. It goes something like:
import java.util.regex.*;

...

Pattern patternReply = Pattern.compile(sReplyPattern);
Matcher matcherReply = patternReply.matcher(current_output_line);

if (matcherReply.matches()) {
	sHost = matcherReply.group(1);
	fTime = Float.parseFloat(matcherReply.group(2));
}
Once you build the correct regular expression it just works. The result is some really clean code - and it's very easy to support ping executables on different platforms. It took me a few hours to build this for Windows, but then only 30 minutes or so to add support for the ping executables found in Mac OS X and Linux.

Server Back-End


The server is running IIS so I have an ISAPI extension DLL that takes requests containing an IP address, looks up the IP address in its database then returns information such as latitude & longitude. The database I use is from IP2Location.com and it came with a very useful library that exposes a few neat lookup functions. As a result, the whole server-side program is maybe 50 ELOC, and most of that is paranoid error handling.

My only gripe is that the quality of the database is not that great. IP addresses I *know* to be in British Columbia are shown in Florida. IPs that are in the UK are shown in the Ukraine. It's a vast 40-megabyte file so there's a lot of stuff in it - I'm just not sure how accurate it is. If you know of a better one drop me a line at marton at logmein dot com.

Wrapping up



Well, that's about it. If you have further questions or ideas about where to take this project from here, email me. It's marton at logmein dot com.