Note: This is not the complete source code--just the main source file.
You can download the full source (with include files) from our sample code archive by clicking on the diskette icons.

AspAnalyzePath.asp

<%@ EnableSessionState=False LANGUAGE="VBSCRIPT" %>
<%
Option explicit
Response.Buffer = false
%>
<!--
AspAnalyzePath sample script
Demonstrates use of HexIcmp and HexLookup

This is an experimental visual traceroute of sorts.
Here's how it works:

1. Discovers the network path to the chosen host using the
   standard traceroute technique - pinging the host while
   incrementing the packet TTL (time-to-live) from 1.

2. Pings each hop separately to collect RTT (round-trip time)
   data. The RTTs from the path discovery aren't used because
   directly pinging the hops yields potentially different and
   better RTT data.

3. Calculates the median of the RTTs for each hop. Taking the
   median yields better results than taking the average
   because it is not influenced as much by the occasional
   extra-long RTT.

4. Calculates the change in RTT from one hop to the next, then
   divides by two to approximate the latency in one direction.
   Does not allow negative deltas

5. Graphs the hops and the latencies between them to give a
   graphical view of the network path. Also does reverse
   lookups for each hop. The graph is implemented in HTML, so
   it is unfortunately rather crude.


Possible future improvements:

1. Calculate packet losses for each hop.

2. Improve graphing. Include packet loss data, total latency
   along path, hop latencies as a percentage of the total.

3. Improve handling of RTT deltas. Sometimes a hop in the 
   middle of the path will consistently return higher RTT
   values than hops beyond it (due to congestion, lower
   priority for ICMP, or whatever). With the current scheme,
   the hops after the anomalous one will get deltas of 0
   (because we can't draw negative deltas), thus skewing the
   picture of the path.

4. Avoid rounding numbers too early, although it doesn't
   matter too much since ICMP.DLL only provides 10ms
   resolution anyway.


Comments? Ideas? Do you have a better implementation? Let us
know at feedback@hexillion.com.


Copyright (C) 1998 Hexillion Technologies. All rights reserved.
-->

<!-- #include file="AuxFuncs.asp" -->

<html>

<head>
<title>Hexillion AspAnalyzePath sample</title>
</head>

<body bgcolor="#FFFFFF" text="#000000" vlink="#808080" link="#0000FF">

<%
dim oLkup, oIcmp, sLocalName, sLocalAddr, sRemoteName

set oLkup = Server.CreateObject( "Hexillion.HexLookup" )
set oIcmp = Server.CreateObject( "Hexillion.HexIcmp" )

sLocalName = request( "SERVER_NAME" )
sLocalAddr = request( "LOCAL_ADDR" )

sRemoteName = Request( "host" )
if "" = sRemoteName then sRemoteName = request( "REMOTE_HOST" )

const maxPoints = 10  '// Max number of data points per hop
dim lNumPoints

if not IsEmpty( request( "points" ) ) then

	lNumPoints = Clng( request( "points" ) )
	if lNumPoints < 0 then
		lNumPoints = 1
	elseif lNumPoints > maxPoints then
		lNumPoints = maxPoints
	end if

else
	lNumPoints = 5
end if

%>

<table cellpadding="5" width="100%"><tr>
<td colspan="2" bgcolor="#E1EFFF"><p><font face="Arial" size="5"><strong>AspAnalyzePath sample</strong></font><br>
An experimental graphical traceroute
</td></tr>
<tr>
<td valign="top" bgcolor="#E1EFFF">
<form name="hostentry" method="POST" action="<%= request( "SCRIPT_NAME" ) %>">
	<table cellpadding="5">
		<tr><td align="right" valign="baseline">from</td>
		<td valign="baseline"><tt><strong><%= Server.HtmlEncode( sLocalName ) %> [<%= sLocalAddr %>]</strong></tt></td></tr>
		<tr><td align="right" valign="baseline">to</td>
		<td valign="baseline"><input type="text" name="host" size="27" value="<%= Server.HtmlEncode( sRemoteName ) %>"></td></tr>
		<tr><td align="right" valign="baseline">data points</td>
		<td valign="baseline"><input type="text" name="points" size="3" value="<%= lNumPoints %>"> (higher = more accurate)</td></tr>
		<tr><td align="right" valign="baseline">&nbsp;</td>
		<td valign="baseline"><input type="submit" value="Go" name="B1"></td></tr>
	</table>
</form>
</td>

<td valign="top" bgcolor="#E1EFFF">
<table border="0" cellspacing="0" cellpadding="7">
<tr>
	<td colspan="2">powered by <b><a href="http://www.hexillion.com/software/" target="_top">HexGadgets</a></b>
	<br><font size="-1">
	<a href="http://www.hexillion.com/samples/view_src.asp?name=AspAnalyzePath.asp" target="_blank">view source</a>
	&nbsp; |&nbsp; <a href="http://www.hexillion.com/samples/" target="_top">download</a>
	</font></td>
</tr>
<%
WriteLicenseRow "HexIcmp", oIcmp
WriteLicenseRow "HexLookup", oLkup
%>
</table>
</td>
</tr></table>

<%
if not IsEmpty( Request( "host" ) ) then

	dim lAddr, lRecvAddr

	lAddr = oLkup.LookUp( sRemoteName )
	if 0 = lAddr then
		Response.Write( "<p>Lookup of " & Server.HtmlEncode( sRemoteName ) & " failed.</p>" )
	else
		'// Display canonical name if available
		if oLkup.Aliases.Count > 0 then sRemoteName = oLkup.Aliases( 1 )

		response.write "<p>Collecting data on path to <tt><strong>" & Server.HtmlEncode( sRemoteName ) & " [" & oLkup.AddrToString( lAddr ) & "]</strong></tt>...</p>"

		'// Discover path
		const maxHops = 30    '// Max TTL to try
		const maxMissing = 4  '// Max missing hops before aborting trace
		const maxWait = 1000  '// Max time to wait for ping
		const maxTries = 3    '// Max number of discovery retries

		dim lNumHops, lMissingCount, lPathErr
		redim lHopAddrs( maxHops )

		Dim i, j, k, lRTT, sRTT, iMax, lErr, bAbort

		i = 0
		oIcmp.Timeout = maxWait
		bAbort = false

		Do While lHopAddrs( i ) <> lAddr And i < maxHops and not bAbort
			i = i + 1
			oIcmp.SendTtl = i

			for j = 1 to maxTries
				lRTT = oIcmp.Ping(lAddr)
				If lRTT < 0 And oIcmp.Error <> hexIcmpErrTtlExpiredTransit Then
					lErr = oIcmp.Error

					Select Case lErr
						Case hexIcmpErrReqTimedOut
							'// Do nothing

						Case Else
							lPathErr = lErr
							bAbort = True
							exit for

					End Select
				Else
					lHopAddrs( i ) = oIcmp.RecvAddr

					'// Reset our count of "missing" hops
					lMissingCount = 0

					exit for
				End If
			next

			if j > maxTries then
				'// We have another "missing" hop
				lMissingCount = lMissingCount + 1

				'// Abort if we've had too many consecutive missing
				if lMissingCount >= maxMissing then bAbort = true
			end if
		loop

		if 0 <> lPathErr then
			lNumHops = i - 1
		else
			lNumHops = i
		end if

		redim lHopRtts( lNumHops, lNumPoints )
		redim lHopMedians( lNumHops )
		redim lHopDeltas( lNumHops )
%>
<table border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td width="80" align="right" bgcolor="#E1EFFF"><tt>hop</tt></td>
<%
		for i = 1 to lNumHops
%>
    <td width="40" align="right" bgcolor="#E1EFFF" valign="bottom"><tt><%= i %></tt></td>
<%
		next
%>
  </tr>
</table>
<%

		'// Collect ping data for each hop
		for j = 1 to lNumPoints
%>
<table border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td width="80" align="right" bgcolor="#E1EFFF"><tt>rtt (ms)</tt></td>
<%
			for i = 1 to lNumHops
				lHopRtts( i, j ) = oIcmp.Ping( lHopAddrs( i ) )
				if lHopRtts( i, j ) < 0 then
					sRTT = "*"
				else
					sRTT = CStr( lHopRtts( i, j ) )
				end if
%>
    <td width="40" align="right" valign="bottom"><tt><%= sRTT %></tt></td>
<%
			next
%>
  </tr>
</table>
<%
		next

		'// Sort RTTs (for finding median)
		dim lMax, lTemp
		for i = 1 to lNumHops
			for j = 1 to lNumPoints
				lMax = j

				for k = j+1 to lNumPoints
					if lHopRtts( i, k ) > lHopRtts( i, lMax ) then lMax = k
				next

				swap lHopRtts( i, j ), lHopRtts( i, lMax )
			next
		next


		'// Calc medians
		for i = 1 to lNumHops
			for j = lNumPoints to 1 step -1
				if lHopRtts( i, j ) >= 0 then exit for
			next

			if j < 0 then
				'// No data for this hop
				lHopMedians( i ) = -1
			else
				dim lMid
				lMid = (j + 1) \ 2

				if CBool( j mod 2 ) then
					lHopMedians( i ) = lHopRtts( i, lMid )
				else
					lHopMedians( i ) = (lHopRtts( i, lMid ) + lHopRtts( i, lMid + 1 )) \ 2
				end if
			end if
		next

%>
<table border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td width="80" align="right" bgcolor="#E1EFFF"><tt>median</tt></td>
<%
			for i = 1 to lNumHops
%>
    <td width="40" align="right" valign="bottom"><tt><%= lHopMedians( i ) %></tt></td>
<%
			next
%>
  </tr>
</table>
<%
		'// Calc deltas
		lMax = 0
		for i = 1 to lNumHops
			lHopDeltas( i ) = max( 0, (lHopMedians( i ) - lMax) \ 2 )
			if lHopMedians( i ) > lMax then lMax = lHopMedians( i )
		next

%>
<table border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td width="80" align="right" bgcolor="#E1EFFF"><tt>delta/2</tt></td>
<%
			for i = 1 to lNumHops
%>
    <td width="40" align="right" valign="bottom"><tt><%= lHopDeltas( i ) %></tt></td>
<%
			next
%>
  </tr>
</table>

<p>Graphing...</p>
<table border="0" cellpadding="0" cellspacing="1">
	<tr>
		<td height="5" width="40" align="right">&nbsp;</td>
		<td height="5" bgcolor="#0000FF"><tt>&nbsp; </tt></td>
		<td height="5" valign="bottom"><tt>&nbsp;[<%= oLkup.AddrToString( oLkup.LookUp( oLkup.HostName ) ) %>] <%= Server.HtmlEncode( oLkup.HostName ) %></tt></td>
	</tr>
</table>
<%
		'// Draw our crude little graph
		const minHeight = 5
		const maxHeight = 400
		dim fRatio, lHeight, lTotalTime, sDelta

		for i = 1 to lNumHops
			lTotalTime = lTotalTime + lHopDeltas( i )
		next

        lTotalTime = max( lTotalTime, 1 )
		fRatio = maxHeight / lTotalTime

		for i = 1 to lNumHops
			lHeight = max( minHeight, CLng( lHopDeltas( i ) * fRatio ) )
			if lHopDeltas( i ) > 0 then
				sDelta = CStr( lHopDeltas( i ) )
			else
				sDelta = ""
			end if
%>
<table border="0" cellpadding="0" cellspacing="1">
	<tr>
		<td height="<%= lHeight %>" width="40" align="right"><tt><%= sDelta %>&nbsp;</tt></td>
		<td height="<%= lHeight %>" bgcolor="#0000ff"><tt>&nbsp; </tt></td>
		<td height="<%= lHeight %>" valign="bottom"><tt>&nbsp;[<%= oLkup.AddrToString( lHopAddrs( i ) ) %>] <%= Server.HtmlEncode( oLkup.ReverseLookup( lHopAddrs( i ) ) ) %></tt></td>
	</tr>
</table>
<%		
		next
	end if
end if


sub swap( a, b )
	dim c
	c = a
	a = b
	b = c
end sub

function max( a, b )
	if b > a then
		max = b
	else
		max = a
	end if
end function


%>
</body>
</html>