/* 

Unofficial version of traceroute, derived from the NANOG version, and
modified to produce XML reports, according to RFC 5388.

Maintained by Stephane Bortzmeyer <bortz@users.sourceforge.net>

It requires libxml2 <http://xmlsoft.org/>

To compile, on GNU/Linux-based systems:

cc -DXML -lresolv  $(xml2-config --cflags) $(xml2-config --libs) \
            traceroute-nanog.c   -o traceroute-nanog

On {Free,Net}BSD, same thing without -lresolv.

Then run it with -X to produce the XML output. After some tweaking of
the XSD schema, the output validates.

TODO: broken on UltraSparc? Again an alignment problem?

*/

/*
 *  STILL BROKEN: traceroute -g on linux (LSRR)
 */
#define TRACE_TOS 1
#define SPRAY
#define FIXT
#ifndef lint
static char    *rcsid =
    "@(#)$Header: Etraceroute.c,v 6.4.2 2006/06/06 gavron(gavron@wetwork.net)";
#endif
#ifdef TRACE_TOS
#define TR_VERSION "6.4.2-XML GOLD+emf_prototrace0.2+sl_tostrace0.1"
#else                           /* not TRACE_TOS */
#define TR_VERSION "6.4.2-XML GOLD+emf_prototrace0.2"
#endif                          /* not TRACE_TOS */

/*
 * traceroute host  - trace the route ip packets follow going to "host".
 *
 * Attempt to trace the route an ip packet would follow to some
 * internet host.  We find out intermediate hops by launching probe
 * packets with a small ttl (time to live) then listening for an
 * icmp "time exceeded" reply from a gateway.  We start our probes
 * with a ttl of one and increase by one until we get an icmp "port
 * unreachable" (which means we got to "host") or hit a max (which
 * defaults to 30 hops & can be changed with the -m flag).  Three
 * probes (change with -q flag) are sent at each ttl setting and a
 * line is printed showing the ttl, address of the gateway and
 * round trip time of each probe.  If the probe answers come from
 * different gateways, the address of each responding system will
 * be printed.  If there is no response within a 5 sec. timeout
 * interval (changed with the -w flag), a "*" is printed for that
 * probe.
 *
 * Probe packets are UDP format.  We don't want the destination
 * host to process them so the destination port is set to an
 * unlikely value (if some clod on the destination is using that
 * value, it can be changed with the -p flag).
 *
 * A sample use might be:
 *
 *     [yak 71]% traceroute nis.nsf.net.
 *     traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
 *      1  helios.ee.lbl.gov (128.3.112.1)  19 ms  19 ms  0 ms
 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  39 ms
 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  39 ms  39 ms  39 ms
 *      6  128.32.197.4 (128.32.197.4)  40 ms  59 ms  59 ms
 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  59 ms
 *      8  129.140.70.13 (129.140.70.13)  99 ms  99 ms  80 ms
 *      9  129.140.71.6 (129.140.71.6)  139 ms  239 ms  319 ms
 *     10  129.140.81.7 (129.140.81.7)  220 ms  199 ms  199 ms
 *     11  nic.merit.edu (35.1.1.48)  239 ms  239 ms  239 ms
 *
 * Note that lines 2 & 3 are the same.  This is due to a buggy
 * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
 * packets with a zero ttl.
 *
 * A more interesting example is:
 *
 *     [yak 72]% traceroute allspice.lcs.mit.edu.
 *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  19 ms  19 ms
 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  19 ms
 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  19 ms  39 ms  39 ms
 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  20 ms  39 ms  39 ms
 *      6  128.32.197.4 (128.32.197.4)  59 ms  119 ms  39 ms
 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  39 ms
 *      8  129.140.70.13 (129.140.70.13)  80 ms  79 ms  99 ms
 *      9  129.140.71.6 (129.140.71.6)  139 ms  139 ms  159 ms
 *     10  129.140.81.7 (129.140.81.7)  199 ms  180 ms  300 ms
 *     11  129.140.72.17 (129.140.72.17)  300 ms  239 ms  239 ms
 *     12  * * *
 *     13  128.121.54.72 (128.121.54.72)  259 ms  499 ms  279 ms
 *     14  * * *
 *     15  * * *
 *     16  * * *
 *     17  * * *
 *     18  ALLSPICE.LCS.MIT.EDU (18.26.0.115)  339 ms  279 ms  279 ms
 *
 * (I start to see why I'm having so much trouble with mail to
 * MIT.)  Note that the gateways 12, 14, 15, 16 & 17 hops away
 * either don't send ICMP "time exceeded" messages or send them
 * with a ttl too small to reach us.  14 - 17 are running the
 * MIT C Gateway code that doesn't send "time exceeded"s.  God
 * only knows what's going on with 12.
 *
 * The silent gateway 12 in the above may be the result of a bug in
 * the 4.[23]BSD network code (and its derivatives):  4.x (x <= 3)
 * sends an unreachable message using whatever ttl remains in the
 * original datagram.  Since, for gateways, the remaining ttl is
 * zero, the icmp "time exceeded" is guaranteed to not make it back
 * to us.  The behavior of this bug is slightly more interesting
 * when it appears on the destination system:
 *
 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  39 ms
 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  39 ms  19 ms
 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  19 ms
 *      5  ccn-nerif35.Berkeley.EDU (128.32.168.35)  39 ms  39 ms  39 ms
 *      6  csgw.Berkeley.EDU (128.32.133.254)  39 ms  59 ms  39 ms
 *      7  * * *
 *      8  * * *
 *      9  * * *
 *     10  * * *
 *     11  * * *
 *     12  * * *
 *     13  rip.Berkeley.EDU (128.32.131.22)  59 ms !  39 ms !  39 ms !
 *
 * Notice that there are 12 "gateways" (13 is the final
 * destination) and exactly the last half of them are "missing".
 * What's really happening is that rip (a Sun-3 running Sun OS3.5)
 * is using the ttl from our arriving datagram as the ttl in its
 * icmp reply.  So, the reply will time out on the return path
 * (with no notice sent to anyone since icmp's aren't sent for
 * icmp's) until we probe with a ttl that's at least twice the path
 * length.  I.e., rip is really only 7 hops away.  A reply that
 * returns with a ttl of 1 is a clue this problem exists.
 * Traceroute prints a "!" after the time if the ttl is <= 1.
 * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
 * non-standard (HPUX) software, expect to see this problem
 * frequently and/or take care picking the target host of your
 * probes.
 *
 * Other possible annotations after the time are !H, !N, !P (got a host,
 * network or protocol unreachable, respectively), !S or !F (source
 * route failed or fragmentation needed -- neither of these should
 * ever occur and the associated gateway is busted if you see one).  If
 * almost all the probes result in some kind of unreachable, traceroute
 * will give up and exit.
 *
 * Notes
 * -----
 * This program must be run by root or be setuid.  (I suggest that
 * you *don't* make it setuid -- casual use could result in a lot
 * of unnecessary traffic on our poor, congested nets.)
 *
 * This program requires a kernel mod that does not appear in any
 * system available from Berkeley:  A raw ip socket using proto
 * IPPROTO_RAW must interpret the data sent as an ip datagram (as
 * opposed to data to be wrapped in a ip datagram).  See the README
 * file that came with the source to this program for a description
 * of the mods I made to /sys/netinet/raw_ip.c.  Your mileage may
 * vary.  But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
 * MODIFIED TO RUN THIS PROGRAM.
 *
 * The udp port usage may appear bizarre (well, ok, it is bizarre).
 * The problem is that an icmp message only contains 8 bytes of
 * data from the original datagram.  8 bytes is the size of a udp
 * header so, if we want to associate replies with the original
 * datagram, the necessary information must be encoded into the
 * udp header (the ip id could be used but there's no way to
 * interlock with the kernel's assignment of ip id's and, anyway,
 * it would have taken a lot more kernel hacking to allow this
 * code to set the ip id).  So, to allow two or more users to
 * use traceroute simultaneously, we use this task's pid as the
 * source port (the high bit is set to move the port number out
 * of the "likely" range).  To keep track of which probe is being
 * replied to (so times and/or hop counts don't get confused by a
 * reply that was delayed in transit), we increment the destination
 * port number before each probe.
 *
 * Don't use this as a coding example.  I was trying to find a
 * routing problem and this code sort-of popped out after 48 hours
 * without sleep.  I was amazed it ever compiled, much less ran.
 *
 * I stole the idea for this program from Steve Deering.  Since
 * the first release, I've learned that had I attended the right
 * IETF working group meetings, I also could have stolen it from Guy
 * Almes or Matt Mathis.  I don't know (or care) who came up with
 * the idea first.  I envy the originators' perspicacity and I'm
 * glad they didn't keep the idea a secret.
 *
 * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
 * enhancements to the original distribution.
 *
 * I've hacked up a round-trip-route version of this that works by
 * sending a loose-source-routed udp datagram through the destination
 * back to yourself.  Unfortunately, SO many gateways botch source
 * routing, the thing is almost worthless.  Maybe one day...
 *
 *  -- Van Jacobson (van@helios.ee.lbl.gov)
 *     Tue Dec 20 03:50:13 PST 1988
 *
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * PSC Changes Copyright (c) 1992 Pittsburgh Supercomputing Center.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the modifications to this
 * software were developed by the Pittsburgh Supercomputing Center.
 * The name of the Center may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
 * IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 *
 */

/* DNS lookup portions of this code also subject to the following:  */
/*
 * Use the domain system to resolve a name.
 *
 * Copyright (C) 1988,1990,1992 Dan Nydick, Carnegie-Mellon University
 * Anyone may use this code for non-commercial purposes as long
 * as my name and copyright remain attached.
 */

/*
 *  1/7/92:  Modified to include a new option for MTU discovery.
 *           This option will send out packets with the don't
 *           fragment bit set in order to determine the MTU
 *           of the path being traceroute'd.  Only decreases
 *           in MTU will be detected, and the MTU will initially
 *           be set to the interface MTU which is used for
 *           routing.  In the event of an MTU decrease in the
 *           path, the output will include a message of the
 *           form MTU=#### with the new MTU for the latest
 *           hop.  This option is invoked with "-M".
 *                            Jamshid Mahdavi,   PSC.
 */

/*
 * 4/12/93:  Modified to include new option (-Q) that will report on
 *           percentage packet loss* in addition to the usual
 *           min/avg/max delay.  When -Q is invoked, delay per packet
 *           reporting is turned off.  Also ^C aborts further packets
 *           sent on that ttl and goes on to the next -- two
 *           consecutive ^Cs will terminate the traceroute entirely.
 *           [modified code rom Matt Mathis' uping.c code]
 *                          Jon Boone,  PSC.
 */

/*
 * 4/23/93:  Added support for a "-a" switch which will support
 *           automatic cutoff after a certain number of dropped
 *           packets in a row
 *                          Jon Boone, PSC.
 */

/*
 * 10/21/93: (JM) Fixed SGI version, changed the packet sizing scheme
 *           to a saner system based on total packet size (which
 *           also fixed several bugs in the old code w.r.t.
 *           size of packets).  Added fast timers.  Added network
 *           owner lookup.  Plan to add AS path lookup eventually...
 */

/*
 * 05/20/95: (Ehud Gavron, gavron@aces.com) Added in the following:
 *              1. -A now looks up ASs
 *              2. When nprobes = 1 it doesn't give up after one hop
 *              3. Fixed in-addr searches to try progressively larger blocks
 *                 since MCI's backbone uses one SOA for a 256-class-c block
 *              4. It now works under VMS
 *              5. Miscellaneous casts to make ANSI compilers happy
 *              6. Changes #ifdef'd as MAY95
 */

/*
 * 07/20/95: (Ehud Gavron, gavron@aces.com)
 *           1. Added in a fix to correct for an occasional hang.
 *           2. Fixed minor ANSI compiler things so SunOS likes it
 *
 *           The code, from Steffen Baur (baur@noc.dfn.de) is described by
 *           him as follows.
 *
 *           I've discovered another bug in traceroute. Sometimes traceroute
 *           seem to 'hang' if there's no response for a probe.
 *
 *           The problem is, that all icmp messages (even responses for other
 *           ping or traceroute tasks) are copied to the input socket. After
 *           every receive (even on messages for other tasks), traceroute
 *           waits for another timeout intervall. So, if there is much icmp
 *           traffic on the host, the socket always receives an icmp message
 *           for another task before the timeout occurs.
 *
 *           I introduced a kind of deadline in wait_for_reply().
 */

/*
 * 07/20/95: (Ehud Gavron, gavron@aces.com)
 *              More minor fixes.  Now compiles cleanly on Solaris as well.
 */

/*
 * 09/04/95: (Ehud Gavron, gavron@aces.com)
 *              Now uses VMS command language definition module if available
 *              Cleaned up code a bit...
 */

/*
 * 10/04/95: (Ehud Gavron, gavron@aces.com)
 *              It used to be that everyone only had one RADB entry, so that
 *              finding the AS given the net was easy.
 *              Then it turns out that lots of people now have multiple
 *              entries.
 *
 *              Here's the solution:
 *              1) Use the most specific entries in RADB
 *              2) Where entries are equally specific yet list different
 *                 ASs, list all ASs separated by /.
 *              3) Have a beer
 */

/*
 * 10/15/95: (Ehud Gavron, gavron@aces.com)
 *              1) Joey@teleport.com reports that not terminating reply[]
 *                 is a poor idea ;-)  Corrected.
 *              2) Scott Bradner reports it doesn't work on big-endians.
 *                 Disabled new_packet_ok stuff for now
 *              3) Fixed to use correct offsets.  Removed ntohs() stuff
 *                 since buffer must be in n order (duh).
 *              4) Added in stuff so it will build on Suns without the UCB
 *                 optional compiler /usr/ucb/cc.  (-DSUN_WO_UCB)
 *              5) Added in a diagnostic to check for ip version == 4!
 *              6) Made it work with freebsd by modifying send_probe()
 *                 to fill in ip->ip_v and ip->ip_hl.
 */
/*
 * 11/22/95 Ehud Gavron
 *              bcopy doesn't exist on some systems (Suns without UCB)
 *              but we can't use strncpy since it stops on a 0 byte,
 *              which screws up get_origin since whois.ra.net currently
 *              is blah.blah.0.blah :-)
 */

/*
 * 03/28/96 Ehud Gavron
 *              New Cisco code ignores closely-separated packets that
 *              should generate ICMP errors.  As a result, what used
 *              to be !H !H !H now is !H * !H, and of course delay
 *              stats _to_ a Cisco now show 50% loss.
 *
 *              Modify code to check for unreachable+loss.  This mod
 *              is in under the cisco_icmp ifdef.
 *
 */

/*
 * 12/17/96 Ehud Gavron
 *              If we make the terminator flexible, we can do live HTML
 *              (www.opus1.com/www/traceroute.html) instead of waiting
 *              for the whole thing to be done and then displaying it.
 *              Use -Tterminator in printf() parser format.
 */

/*
 * 01/18/97 Ehud Gavron
 *              MCI administratively prohibits some packets from crossing
 *              routers.  (traceroute www.mci.net).  Check for ICMP type
 *              13 (undefined in include files here :() and display !A
 *
 * 02/11/97     Change random mishmash of printf, Printf, fprintf(stdout,
 *              fprintf(stderr, into consistent usage of err for errors
 *              and verbosity and stdout for everything else.
 */

/*
 * 05/05/98     Ehud Gavron     gavron@aces.com
 *
 *              1) Make SOLARIS a define that automatically sets
 *                 'SUN_WO_UCB' for Sun without UCB compiler addons and
 *                 'POSIX' to fix the bullshit in RESOLV.H (__res_send)
 *
 *              2) Make defines for
 *                 'STRING'  system should use <string.h> not <strings.h>
 *                 'NOINDEX' system should use strchr(), not index()
 *                 'NOBZERO' system should use memset() not bzero, and memcpy()
 *                           not bcopy()
 *
 *              3) Add function prototypes.  They can be removed via
 *                 'NO_PROTOTYPES'
 *
 *              4) Based on Pansiot and Grad's wanting to ignore loss,
 *                 add -U flag.  (/HURRY on VMS).  This one will only
 *                 sit on a hop until it gets a satisfactory response.
 *                 That way hops with loss will get up to -n probes,
 *                 and hops with no loss will get just one.
 *
 *              5) Recoded some passing of parameters to make ansi-compliant
 *                 compilers even happier.
 */

/*
 * 06/30/98     Ehud Gavron     gavron@aces.com
 *
 *              V2.8.1  Added -P  (/PING) to help out sites that are smurf-safe
 *                      by blocking ICMP ECHO reply.
 *                      This will do the same as 1 probe, timeout 3, ttl 64
 *                      and return a status of 1 (success) or 0 (failure)
 */

/*
 * 01/05/1999   Ehud Gavron     gavron@aces.com
 *              V2.8.2  Added in Brian Murphy's linux code.  His comments:
 * 12/31/98 (Brian Murphy, murphy@u.arizona.edu)
 *      Ported to linux.  Tested on Red Hat 5.0 with the 2.1.128 kernel.
 *
 * 01/01/98
 *      Removed trust from getenv() that could lead to a buffer overflow.
 */

/*
 * 01/06/1999   Ehud Gavron     gavron@aces.com
 *              V2.8.3  Added in Craig Watkins <Craig.Watkins@innosoft.com>
 *                      fix to make SolX86 work (missing htons() in ip_len)
 * 01/08/1999   V2.8.4  Added in Richard Irving <rirving@onecall.net> nit-fixes
 *                      to check for null parameters, as well as cast some math
 *                      into proper ints.
 * 02/26/1999   V2.9.0  Craig Watkings suggested sending all probes out at
 *                      once. What a great idea!  -P for parallel probing of
 *                      all TTLs.  Messy on output if we don't want to sit
 *                      and definitely wait it out, but useful!  -$ for the
 *                      old 'ping style' behavior.
 * 02/27/1999   V2.9.1  Allow more than one probe per hop, display last one to
 *                      successfully return.  Fix so all packets returning will
 *                      override the max-wait timer.  Fix so we check the TTL
 *                      and make sure our packets (even out of order) are ok.
 *                      pretty up the output.  Remove magic number "3" for
 *                      ICMP_UNREACH_PORT.
 * 03/01/1999   V2.9.2  Freebsd seems to get packets that generate indices
 *                      which are outside the array (line 1299).  This segfault
 *                      is corrected by a bounds check on line 1298.
 * 03/03/1999   V2.9.3  Make printing the header and time difference standard
 *                      callable routines (print_from, print_time).  Make the
 *                      last address be a struct sockaddr_in instead of u_long
 *                      since that's the right way to do it.  Make the code
 *                      in packet_ok() and send_probe() conditional on spray
 *                      mode so we don't jabber all over the data cells!
 * 09/30/1999   V2.9.4  Added "FORCE_NATURAL_MASK" in lookup_as() so that if
 *                      necessary, old-style lookups would still work.  Now,
 *                      the -A option will use the address as is.
 * 11/15/1999   V6.0    Renumber to Version 6.0 and rename to TrACESroute
 *                      to avoid 'confusion' with vj/lbl traceroute 2.9.x.
 *                      Of course this makes us much more advanced than
 *                      Netscape 4.8, I.E. 5.0, or The New AOL 5.0. ;-)
 * 01/18/2000   V6.1    Dan Cohn <dan@internap.com> provided mods to make
 *                      spray mode as functional as normal mode, both in
 *                      terms of missing packets and in multiple probes.
 * 04/29/2000   V6.1.1  As per Thomas Erskine, add info for -A where the
 *                      route is not in the RR server queried.
 *
 * 06/05/2001   V6.1.2  When using -A, get_origin() returns 0 for ASs not
 *                      found in your particular RA_SERVER.  This, passed
 *                      to printf(%s) ended up as <Null> on Linux, and a
 *                      coredump on Solaris... fixed by adding a nullstring
 *                      <NONE> and checking for zero result from get_origin.
 *                      Thanks to Richard Wright for pointing it out and
 *                      assisting in the debug.
 * 06/16/2001   emf_prototrace 0.2
 *                      Added rudimentary support for generic protocol type
 *                      scan via -I option.  It's really really rough right
 *                      now, but it gives me enough ammunition to tell
 *                      some feeb where my packets are stopping.   I
 *                      generally use this with -I 50 to check for IPSec
 *                      VPN functionality.  (You'd be completely amazed at
 *                      how many people claim their firewall isn't
 *                      interfering with their VPN clients. Sheesh)
 *
 * 10/27/2001   V6.1.3  Disallow max_ttl*nprobes>spraymax. Raise spraymax.
 *
 * 02/05/2002	V6.1.4	Simon Leinen <simon@limmat.switch.ch> provided some
 *			nice code to check TOS changes in the encapsulated
 *			header from its original value ... use -t TOS to set.
 * 04/12/2002           Updated comment above
 *
 * 07/23/2002   V6.2.0  Various sources report that the -T terminator can
 *                      be exploited to run malicious code.  This is due
 *                      to sloppy coding on my part when writing it.
 *                      Fixed. In this version FIXT defines that code, and
 *                      assuming it works everywhere, will be mainline code
 *                      in the next minor release.
 *
 * 10/12/2002   V6.2.1  Daniel Kobras reports a slew of poor coding on
 *   			my part in get_origin(), as well as included Colin
 *    			Phipps code in one nice easy fun patch.  Better
 *			code for better living!
 *
 * 11/28/2002	V6.2.2  Benjamin Zwittnig from ARNES provided more fine
 *                      tuning of the accept logic on incoming ICMP packets
 *                      which reduces false results on machines with high
 *                      received ICMP traffic.  Patch submitted by Simon 
 *  			Leinen at Switch.
 *
 * 02/11/2003	V6.3.0	Three changes.  First, Martin Godisch's patches
 *    			to allow this to run under Debian.  Second, 
 *			Graeme Hewson's patches to fix ICMP traceroute,
 *			and clean up preprocessor inefficiency. Finally,
 *			add Terry Kennedy's fixes for broken raw ip.
 * 
 * 02/24/2003	V6.3.2  Make MTU discovery work the RFC 1191 way.  Also
 *                      make MTU discovery work on little endian machines.
 *                      Also makes -U actually terminate faster.
 *                      Code from and kudos to Graeme Hewson @oracle.com
 *
 * 03/10/2003	V6.3.3	Decode ICMP extension headers as per Jorge Boncompte,
 *			who even supplied the decoding routine!
 *
 * 03/21/2003	V6.3.4	Jorge fixed the decoding for nonzero headers
 *
 * 03/31/2003	V6.3.5	Graeme provided misc bugfixes -- THANKS!!!
 *
 *
 * 05/22/2003	V6.3.6	Marco Steinacher points out some RAs delimit data 
 *			with tabs (0x09) instead of spaces (0x20).  Fixed
 *			get_origin ASN lookup code to also check for tabs.
 *
 * 07/07/2003	V6.3.7	Martin Godisch fixed automatic mode.
 *
 * 07/11/2003	V6.3.8	Martin Godisch provided a fix for Debian bug report
 *			200875 (Integer overflow).
 *
 * 07/15/2003	V6.3.9	Matt Zimmerman <mdz@debian.org> provided bugfixes
 *			for same in Spray mode and other enhancements. 
 *
 * 11/06/2005	V6.4.0	Georg Schwarz provided more bugfixes for sloppy code.
 *
 * 12/16/2005	V6.4.1	Added bounds checking on min_ttl and max_ttl
 *
 * 06/06/2006	V6.4.2	Changed nprobes to not be limited to SPRAYMAX if not 
 *			running in parallel mode, and prevent parallel mode
 *			where nprobes >=SPRAYMAX with a warning to the user.
 *
 * 22/11/2008   unreleased Stephane Bortzmeyer <bortz@users.sourceforge.net> added
 *          support for generation of XML results, following the Internet-Draft 
 *          draft-ietf-ippm-storetraceroutes-12
 */

#include <stdio.h>

#define IP_VERSION 4            /* We can't work with anything else... */
#define CISCO_ICMP 1            /* We check for loss+unreachables = probes */

#ifdef __decc                   /* DEC C wants strings.h to be called string.h */
#define STRING
#define NOINDEX
#define NOBZERO
#endif

#ifdef SOLARIS                  /* Solaris has UCB stuff gone, and POSIX resolver */
#define SUN_WO_UCB
#define POSIX
#endif

#ifdef SUN_WO_UCB
#define STRING
#define NOINDEX
#include <signal.h>
#endif

#ifdef _AIX                     /* Aix has its own set of fd_* macros */
#include <signal.h>
#include <sys/select.h>
#endif

#ifdef STRING
#include <string.h>
#else                           /* ! STRING */
#include <strings.h>
#endif                          /* STRING */

#include <sys/param.h>

#ifdef NOINDEX
#define index(x,y) strchr(x,y)  /* Use ansi strchr() if no index() */
#endif                          /* NOINDEX */

#ifndef bzero
#ifdef NOBZERO
#define bzero(x,y) memset((void *)x,(int)0,(size_t) y)
#define bcopy(x,y,z) memcpy((void *)y, (const void *)x, (size_t) z)
#endif                          /* NOBZERO */
#endif                          /* bzero */

#include <string.h>             /* For strcat(), etc */
#include <stdlib.h>             /* For atof(), etc. */
#include <unistd.h>             /* For getpid(), etc. */

/* The VMS stuff follows */
#ifdef  vms
typedef int     socklen_t;
typedef unsigned short u_int16_t;
/* typedef unsigned long u_int32_t; /* Got added to Multinet */
#include "snprintf.c"           /* VMS doesn't have snprintf */
pid_t           decc$getpid(void);      /* Just don't ask... */
#define getpid decc$getpid      /* Really... don't ask. */
#define perror socket_perror    /* MultiNet wants this */
#ifdef MULTINET_V3
#define errno socket_errno      /* MultiNet wants this */
#include "multinet_root:[multinet.include]errno.h"
#else                           /* MULTINET_V4 */
#define MULTINET_V4
#include <errno.h>
#ifdef errno
#undef errno
#include "multinet_root:[multinet.include]errno.h"
#define errno socket_errno      /* Multinet 4.1 */
#endif                          /* errno defined */
#endif                          /* MULTINET_V3 */
#define write socket_write      /* MultiNet wants this */
#define read socket_read        /* MultiNet wants this */
#define close socket_close      /* MultiNet wants this */
#include <signal.h>
#ifdef __alpha
#define BYTE_ORDER 1234         /* The include files for Alpha are bad. */
#define LITTLE_ENDIAN 1234      /* They incorrectly swap ip_v and ip_hl */
#define BIG_ENDIAN 4321         /* Which makes packet_ok fail.  New diag */
#endif  /* __alpha */                        /* Info says: packet version not 4: 5 */
#ifdef VMS_CLD                  /* use separate qualifers instead of options */
#include "clis.h"
#else                           /* No CLD */
int             fixargs(int *, char **, char **);
#endif                          /* VMS_CLD */
#else                           /* not VMS */
#include <errno.h>
#endif                          /* vms */

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <time.h>
#include <sys/time.h>
#include <sys/utsname.h>

#ifndef vms                     /* Not VMS */
#ifdef POSIX                    /* Posix */
#define BIND_RES_POSIX3
#include <resolv.h>
#ifdef res_mkquery              /* __res_mkquery() */
#undef res_mkquery
#endif                          /* endif */
#ifdef res_send
#undef res_send
#endif
#else   /* POSIX */                        /* else ! Posix */
#include <resolv.h>
#endif  /* POSIX */                        /* Endif Posix */
#else   /* vms */                        /* else VMS */
#ifdef MULTINET_V4              /* they didn't put the prototypes in the file */
#include <resolv.h>
int             res_send(unsigned char *, int, unsigned char *, int);
int             res_mkquery(int op, const char *dname,
                            int class, int type, const char *data, int datalen,
                            void *, u_char * buf, int buflen);
int             gettimeofday(struct timeval *, void *);
#endif                          /* MULTINET_V4 */
#endif  /* __vms */                        /* Endif VMS */

#ifdef __gnu_hurd__
#define __linux__
#endif                          /* 6.3.0 */

#ifdef __linux__                /* Wrapping this may be excessive */
#define __FAVOR_BSD 1           /* 6.3.0 - add value 1 */
#endif

#ifdef __linux__
#define BYTESWAP_IP_FLAGS
#endif

#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifndef __linux__
#include <netinet/ip_var.h>
#else                           /* __linux__ */
#include <sys/time.h>
/* IRD #include <netinet/if_tr.h> */
#endif                          /* __linux__ */
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netdb.h>
#include <ctype.h>
#include <math.h>               /* After resolv.h for gcc2.7/sun __p redef */
#include <signal.h>

#ifdef XML
#include <libxml/parser.h>
/* 
http://xmlsoft.org/html/index.html
http://xmlsoft.org/examples/index.html
http://xmlsoft.org/APIchunk0.html
*/
#endif

#ifdef SOLARIS
typedef uint16_t u_int16_t;
typedef uint32_t u_int32_t;
#endif                          /* SOLARIS */

#ifndef NO_PROTOTYPES           /* By default, have prototypes */
int             send_probe(int, int, int);
int             wait_for_reply(int, struct sockaddr_in *, struct timeval *);
void            decode_icmp_ext(u_char *, int);
int             packet_ok(u_char *, int, struct sockaddr_in *, int, int);
int             tvsub(struct timeval *, struct timeval *);
void            print_time(float *);
void            print_from(struct sockaddr_in *);
int             print(u_char *, int, struct sockaddr_in *);
int             reduce_mtu(int);
int             doqd(unsigned char *, int);
int             dorr(unsigned char *, int, char **);
int             doclass(unsigned char *, int);
int             dordata(unsigned char *, int, int, int, char *, char **);
int             dottl(unsigned char *, int);
int             doname(unsigned char *, int, char *);
int             dotype(unsigned char *, int);
void            AbortIfNull(char *);
void            print_ttl(int);
void            print_packet(struct ip *, int);
char           *get_packet(struct ip *, int);
#ifdef XML
void            print_xml_tree();
#endif
char           *current_date_time();
void            cksum(u_int16_t *, int, volatile u_int16_t *);
#endif                          /* NO_PROTOTYPES */

#define MAX_TTL_REALWORLD 256   /* in the real world you shouldn't traverse more
                                 * than these hops between two points */
#define MAXPACKET       65535   /* max ip packet size */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN  64
#endif
#define MAX_NAME 256
#define MAX_DATETIME_SIZE 64
#define MAX_TIME 32

#define SIZEOFstructip sizeof(struct ip)

#ifndef FD_SET
#define NFDBITS         (8*sizeof(fd_set))
#define FD_SETSIZE      NFDBITS
#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
#endif

#define Fprintf (void)fprintf
#define Sprintf (void)sprintf
#define Printf (void)printf

#define NOERR(val,msg) {if (((int)(val)) < 0) {perror(msg);exit(1);}}

#ifndef NO_SOA_RECORD
#define NO_SOA_RECORD "no SOA record"
#endif

/*  For some reason, IP_HDRINCL and LSRR don't interact well on SGI;
 *  so turn it off:  */
#ifdef sgi
#undef IP_HDRINCL
#endif

#ifndef vms
extern int      errno;
#endif

extern char    *inet_ntoa();
extern u_long   inet_addr();

#ifndef ULTRIX43
char           *index(const char *string, int character);
#endif

#ifndef __linux__
void            halt();         /* signal handler */
#endif                          /* __linux__ */

/*
 * format of a probe packet.
 */
struct opacket {
    struct ip       ip;
    union {
        struct udp_probe {
            struct udphdr   udp;
            u_char          seq;        /* sequence number of this packet */
            u_char          ttl;        /* ttl packet left with */
            struct timeval  tv; /* time packet left */
        } udp_probe;
        struct icmp     icmp_probe;
    } ip_payload;
} __attribute__ ((packed));

#ifdef SPRAY
/*
 * format of a spray data cell.
 */
#define SPRAYMAX 512            /* We'll only do up to 512 packets at once */
struct {
    u_long          dport;      /* check for matching dport */
    u_char          ttl;        /* ttl we sent it to */
    u_char          type;       /* icmp response type */
    struct timeval  out;        /* time packet left */
    struct timeval  rtn;        /* time packet arrived */
    struct sockaddr_in from;    /* whom from */
} spray[SPRAYMAX];
unsigned       *spray_rtn[SPRAYMAX];    /* See which TTLs have responded */
unsigned        spray_target;   /* See which TTL the host responds on */
unsigned        spray_max;      /* See which is the highest TTL we've seen */
unsigned        spray_min;      /* See smallest host-returned TTL */
int             spray_total;    /* total of responses seen */
int             spray_mode = 0; /* By default, turned off */
#endif                          /* SPRAY */

#ifdef TRACE_TOS
static u_char   last_tos;
static u_char   tos_at_this_hop;
#endif                          /* TRACE_TOS */

u_char          packet[512];    /* last inbound (icmp) packet */
struct opacket *outpacket;      /* last output packet */
char           *inetname();
u_char          optlist[MAX_IPOPTLEN];  /* IP options list */
int             _optlen;
struct icmp    *icp;            /* Pointer to ICMP header in packet */

int             s;              /* receive (icmp) socket file descriptor */
int             sndsock;        /* send (udp) socket file descriptor */
#if defined(FREEBSD) || defined(__linux__)
struct timezone tz;
#else
unsigned long   tz;             /* leftover */
#endif
struct sockaddr whereto;        /* Who to try to reach */
struct sockaddr_in addr_last;   /* last printed address */
int             datalen;        /* How much data */

char           *source = 0;
char           *hostname;
char            hnamebuf[MAXHOSTNAMELEN];

unsigned        nprobes = 3;
unsigned        min_ttl = 1;
unsigned        max_ttl = 64;
u_short         ident;
u_short         port = 32768 + 666;     /* start udp dest port # for probe packets */
u_short         sport = 1000;   /* source port ... */
char           *probe_status;
struct utsname  osinfo;

/* Remap a subset of standard socket options so that we're able to OR
 * them together.
 */
enum trt_socket_options {
    TRT_DEBUG = (1 << 0),
    TRT_DONTROUTE = (1 << 1)
};

enum trt_socket_options options;        /* socket options */
int             verbose;
int             mtudisc = 0;    /* do MTU discovery in path */
int             pingmode = 0;   /* replacing ping functionality? */
int             generatexml = 0;
#ifndef vms
float           waittime = 3.0; /* time to wait for response (in seconds) */
#else                           /* vms */
double          waittime = 3.0;
#endif
int             nflag;          /* print addresses numerically */

#define TERM_SIZE 32            /* Size of line terminator... */
char            terminator[TERM_SIZE];  /* Line terminator... */
int             haltf = 0;      /* signal happened */
int             ppdelay = 1;    /* we normally want per-packet delay */
int             pploss = 0;     /* we normally don't want packet loss */
int             lost;           /* how many packets did we not get back */
double          throughput;     /* percentage packets not lost */
int             consecutive = 0;        /* the number of consecutive lost packets */
int             automagic = 0;  /* automatically quit after 10 lost packets? */
int             hurry_mode = 0; /* only do one on successful ttls */
int             utimers = 0;    /* Print timings in microseconds */
int             dns_owner_lookup = 0;   /* Look up owner email in DNS */
int             as_lookup = 0;  /* Look up AS path in routing registries */
int             got_there;
int             unreachable;
int             response_mask = 0;
int             mtu, new_mtu = 0;

/*  The following heuristic taken from RFC1191  */
int             mtuvals[] = { MAXPACKET, 32000, 17914, 8166, 4352, 2002, 1492,
    1006, 508, 296, 68, -1
};

#ifdef XML
xmlNodePtr      xmlroot, xmlnode, tmpnode, formernode, hopnode, probenode,
    measurementnode;
xmlDocPtr       xmldoc;
xmlChar        *xmlbuff;
int             xmlbuffersize;
#endif

char            nullstring[] = "<NONE>";

char            usage[] =
    "%s: TrACESroute\nUsage: traceroute [-adnruvAMOPQU] [-w wait] [-S start_ttl] [-m max_ttl] [-p port#] [-q nqueries] [-g gateway] [-t tos] [-s src_addr] [-g router] [-I proto] host [data size]\n\
      -a: Abort after 10 consecutive hops without answer\n\
      -d: Socket level debugging (root only)\n\
      -g: Use this gateway as an intermediate hop (uses LSRR)\n\
      -S: Set start TTL (default 1)\n\
      -m: Set maximum TTL (default 30)\n\
      -n: Report IP addresses only (not hostnames)\n\
      -p: Use an alternate UDP port\n\
      -q: Set the number of queries at each TTL (default 3)\n\
      -r: Set Dont Route option\n\
      -s: Set your source address\n\
      -t: Set the IP TOS field (default 0)\n\
      -u: Use microsecond timestamps\n\
      -v: Verbose\n\
      -w: Set timeout for replies (default 5 sec)\n\
      -A: Report AS# at each hop (from GRR)\n\
      -I: use this IP protocol instead of UDP\n\
      -M: Do RFC1191 path MTU discovery\n\
      -O: Report owner at each hop (from DNS)\n\
      -P: Parallel probing\n\
      -Q: Report delay statistics at each hop (min/avg+-stddev/max) (ms)\n\
      -T: Terminator (line end terminator)\n\
      -U: Go to next hop on any success\n\
      -X: Generate XML, according to I-D draft-ietf-ippm-storetraceroutes\n";

float           deltaT();

/* cph 2000/10/15 - move raw socket code to separate function */
void
get_sockets(void)
{
    struct protoent *pe;

    if ((pe = getprotobyname("icmp")) == NULL) {
        Fprintf(stderr, "icmp: unknown protocol\n");
        exit(10);
    }
    if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
        perror("traceroute: icmp socket");
        exit(5);
    }
    if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
        perror("traceroute: raw socket");
        exit(5);
    }
}
main(argc, argv)
    int             argc;
    char           *argv[];
{
    struct sockaddr_in from;
    char          **av = argv;
#ifdef VMS_CLD
    char           *ptr;
#endif                          /* VMS_CLD */
#ifdef SPRAY
    float           ddt = 0;    /* Delta delta time... for subtracting packet time */
#endif

    struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
    int             on = 1;
    int             alloc_len;
    struct protoent *pe;
    int             ttl, probe, i;
    int             last_i;
    int             last_ttl;
    int             ttl_diff;
    int             spr_ttl;
    int             idx;        /* index to ttl based on spray sequence */
    int             seq = 0;
    int             tos = 0;
    struct hostent *hp;
    unsigned int    lsrr = 0;
    u_long          gw;
    u_char         *oix;
    u_long          curaddr;
    float           min;
    float           max;
    float           sum;
    float           sumsq;
    int             cc;
    int             probe_protocol = IPPROTO_UDP;       /* what IP protocol to use */
    struct timeval  tv;
    struct timeval  deadline;
    struct ip      *ip;
    int             hdrlen;     /* length of IP and protocol headers */

    char           *result = malloc(MAX_NAME);

    /* cph 2000/10/15 - get raw sockets then drop priviledges ASAP */
    get_sockets();
#ifndef VMS
    if (setuid(getuid())) {
        perror("setuid");
        exit(errno);
    }
#endif                          /* VMS has its own protections for privileged
                                 * programs */

    /* cph - end of additions */

    sprintf(terminator, "\n");  /* Standard line terminator */
    oix = optlist;
    bzero(optlist, sizeof(optlist));

#ifndef VMS_CLD
#ifdef __vms
    if (argc < 3)
        fixargs(&argc, argv, av);
#endif
    argc--, av++;
    while (argc && *av[0] == '-') {
        while (*++av[0])
            switch (*av[0]) {
            case 'a':
                automagic = 1;
                break;
            case 'U':
                hurry_mode = 1;
                break;
            case 'A':
                as_lookup = 1;
                break;
            case 'd':
                options |= TRT_DEBUG;
                break;
            case 'g':
                argc--, AbortIfNull((++av)[0]);
                if ((lsrr + 1) >= ((MAX_IPOPTLEN - IPOPT_MINOFF) / sizeof(u_long))) {
                    Fprintf(stderr, "No more than %d gateway%s",
                            ((MAX_IPOPTLEN -
                              IPOPT_MINOFF) / sizeof(u_long)) - 1, terminator);
                    exit(1);
                }
                if (lsrr == 0) {
                    *oix++ = IPOPT_LSRR;
                    *oix++;     /* Fill in total length later */
                    *oix++ = IPOPT_MINOFF;      /* Pointer to LSRR addresses */
                }
                lsrr++;
                if (*av[0] == 0) {
                    Fprintf(stderr, "Hosts are not blank.%s", terminator);
                    exit(1);
                }
                if (isdigit(*av[0])) {
                    gw = inet_addr(*av);
                    if (gw) {
                        bcopy(&gw, oix, sizeof(u_long));
                    } else {
                        Fprintf(stderr, "Unknown host %s%s", av[0], terminator);
                        exit(1);
                    }
                } else {
                    hp = gethostbyname(av[0]);
                    if (hp) {
                        bcopy(hp->h_addr, oix, sizeof(u_long));
                    } else {
                        Fprintf(stderr, "Unknown host %s%s", av[0], terminator);
                        exit(1);
                    }
                }
                oix += sizeof(u_long);
                goto nextarg;
            case 'I':
                argc--, AbortIfNull((++av)[0]);
                if (isdigit(*av[0])) {
                    probe_protocol = atoi(av[0]);
                } else {
                    pe = getprotobyname(av[0]);
                    if (!pe) {
                        Fprintf(stderr, "unknown protocol %s%s", av[0], terminator);
                        exit(1);
                    }
                    probe_protocol = pe->p_proto;
                }
                if (probe_protocol > 254) {
                    Fprintf(stderr, "protocol must be <=254%s", terminator);
                    exit(1);
                }
                goto nextarg;
            case 'S':
                argc--, AbortIfNull((++av)[0]);
                min_ttl = atoi(av[0]);
                if (min_ttl > max_ttl) {
                    Fprintf(stderr,
                            "min ttl must be <= max_ttl(%u)%s", max_ttl, terminator);
                    exit(1);
                }
                if (min_ttl >= SPRAYMAX) {
                    Fprintf(stderr, "min ttl must be <%d%s", SPRAYMAX, terminator);
                    exit(1);
                }
                if (min_ttl <= 1) {
                    Fprintf(stderr, "min ttl must be at least 1%s", terminator);
                    exit(1);
                }
                goto nextarg;
            case 'm':
                argc--, AbortIfNull((++av)[0]);
                max_ttl = atoi(av[0]);
                if (max_ttl < min_ttl) {
                    Fprintf(stderr,
                            "max ttl must be >= min ttl(%u)%s", min_ttl, terminator);
                    exit(1);
                }
                if (max_ttl >= SPRAYMAX) {
                    Fprintf(stderr, "max ttl must be <%d%s", SPRAYMAX, terminator);
                    exit(1);
                }
                if (max_ttl >= MAX_TTL_REALWORLD) {
                    Fprintf(stderr,
                            "in the real world, max ttl must be <%d%s",
                            MAX_TTL_REALWORLD, terminator);
                    exit(1);
                }
                goto nextarg;
            case 'n':
                nflag++;
                break;
            case 'O':
                dns_owner_lookup = 1;
                break;
            case 'p':
                argc--, AbortIfNull((++av)[0]);
                port = atoi(av[0]);
                if (port < 1) {
                    Fprintf(stderr, "port must be >0%s", terminator);
                    exit(1);
                }
                goto nextarg;
            case 'P':
                if (nprobes >= SPRAYMAX) {
                    Fprintf(stderr,
                            "Warning - unable to use parallel mode with nprobes >=%lu%s",
                            SPRAYMAX, terminator);
                } else {
                    spray_mode = 1;
                }
                break;
            case 'f':
                argc--, AbortIfNull((++av)[0]);
                sport = atoi(av[0]);
                goto nextarg;
            case 'q':
                argc--, AbortIfNull((++av)[0]);
                nprobes = atoi(av[0]);
                if (spray_mode) {
                    if ((nprobes < 1) || (nprobes >= SPRAYMAX)) {
                        Fprintf(stderr,
                                "nprobes must be >0 and <%lu%s",
                                SPRAYMAX, terminator);
                        exit(1);
                    }
                }
                if (nprobes < 1) {
                    Fprintf(stderr, "nprobes must be >0%s", terminator);
                    exit(1);
                }
                goto nextarg;
            case 'r':
                options |= TRT_DONTROUTE;
                break;
            case 's':
                /* 
                 * set the ip source address of the outbound
                 * probe (e.g., on a multi-homed host).
                 */
                argc--, AbortIfNull((++av)[0]);
                source = av[0];
                goto nextarg;
            case 't':
                argc--, AbortIfNull((++av)[0]);
                tos = atoi(av[0]);
                if (tos < 0 || tos > 255) {
                    Fprintf(stderr, "tos must be 0 to 255%s", terminator);
                    exit(1);
                }
                goto nextarg;
            case 'u':
                utimers = 1;
                break;
            case 'v':
                verbose++;
                break;
            case 'w':
                argc--, AbortIfNull((++av)[0]);
                waittime = atof(av[0]);
                if (waittime <= .01) {
                    Fprintf(stderr, "wait must be >10 msec%s", terminator);
                    exit(1);
                }
                goto nextarg;
            case 'M':
                mtudisc++;
                break;
            case '$':
                min_ttl = 64;
                max_ttl = 64;
                nprobes = 1;
                pingmode = 1;
                break;
            case 'Q':
                pploss = 1;
                ppdelay = 0;
                break;
            case 'T':
                av++;
                if (--argc < 1) {
                    Fprintf(stdout, usage, TR_VERSION);
                    exit(1);
                }
                strncpy(terminator, av[0], TERM_SIZE);
                terminator[TERM_SIZE - 1] = 0;
                goto nextarg;
#ifdef XML
            case 'X':
                generatexml = 1;
                break;
#endif
            default:
                Fprintf(stdout, usage, TR_VERSION);
                exit(1);
            }
      nextarg:
        argc--, av++;
    }
#else                           /* VMS_CLD defined */
#include "clis.h"
#endif                          /* VMS_CLD */

    if (argc < 1) {
        Fprintf(stdout, usage, TR_VERSION);
        exit(1);
    }
    if (spray_mode && generatexml) {
        Fprintf(stderr, "Spray mode and XML generation are not yet compatible");
        exit(1);
    }
#ifndef vms
    setlinebuf(stdout);
#endif

    (void) bzero((char *) &whereto, sizeof(struct sockaddr));
    to->sin_family = AF_INET;
#ifdef VMS_CLD
    av[0] = hostname;
#endif
    to->sin_addr.s_addr = inet_addr(av[0]);
    if ((int) to->sin_addr.s_addr != -1) {
        (void) strncpy(hnamebuf, av[0], sizeof(hnamebuf));
        hnamebuf[sizeof(hnamebuf) - 1] = 0;
        hostname = hnamebuf;
    } else {
        hp = gethostbyname(av[0]);
        if (hp) {
            to->sin_family = hp->h_addrtype;
            bcopy(hp->h_addr, (caddr_t) & to->sin_addr, hp->h_length);
            hostname = hp->h_name;
        } else {
            Fprintf(stderr, "%s: unknown host %s%s", argv[0], av[0], terminator);
            exit(1);
        }
    }

#ifndef VMS_CLD
    if (argc >= 2)
        datalen = atoi(av[1]);
    if (datalen < 0 || datalen >= MAXPACKET) {
        Fprintf(stderr, "traceroute: packet size must be 0 <= s < %ld%s",
                (long) MAXPACKET - sizeof(struct opacket), terminator);
        exit(1);
    }
#else                           /* VMS_CLD defined */
#include "clis.h"
#endif                          /* VMS_CLD */
    if (mtudisc)
        /* Ignore data length as set.  Set it to a large value to start things
         * off...  */
#ifndef MAX_DATALEN
        datalen = mtuvals[0];
#else
        datalen = MAX_DATALEN;
#endif
    if (datalen < (int) (sizeof(struct opacket) + MAX_IPOPTLEN)) {
        alloc_len = sizeof(struct opacket) + MAX_IPOPTLEN;
    } else {
        alloc_len = datalen;
    }
    /* Round up to even value for cksum() */
    alloc_len = (alloc_len + 1) & -2;

    if (spray_mode) {
        if (nprobes * max_ttl >= SPRAYMAX) {
            Fprintf(stderr, "Spray mode limited to %d packets.\n", SPRAYMAX);
            Fprintf(stderr, "Max TTL of %d with %d probes = %d\n",
                    max_ttl, nprobes, max_ttl * nprobes);
            Fprintf(stderr, "Disabling spray mode.\n");
            spray_mode = 0;
        }
        if (pploss) {
            Fprintf(stderr, "spray and packet stats are incompatible.\n");
            spray_mode = 0;
        }
        if (mtudisc) {
            Fprintf(stderr, "spray and MTU discovery are incompatible.\n");
            spray_mode = 0;
        }
        if (lsrr > 0) {
            Fprintf(stderr, "spray and loose source are incompatible.\n");
            spray_mode = 0;
        }
        if (probe_protocol != IPPROTO_UDP) {
            Fprintf(stderr, "spray mode requires UDP packets.  Disabled.\n");
            spray_mode = 0;
        }
    }

    outpacket = (struct opacket *) malloc((unsigned) alloc_len);

    if (!outpacket) {
        perror("traceroute: malloc");
        exit(1);
    }
    (void) bzero((char *) outpacket, alloc_len);
    outpacket->ip.ip_dst = to->sin_addr;
    outpacket->ip.ip_tos = tos;

#ifdef TRACE_TOS
    last_tos = tos;
#endif
    ident = (getpid() & 0xffff) | 0x8000;

    /* ^C punts you to the next hop.  Twice will exit.  */

#ifndef __linux__
    NOERR(signal(SIGINT, halt), "signal SIGINT");
#endif                          /* __linux__ */

    if (lsrr > 0) {
        lsrr++;
        optlist[IPOPT_OLEN] = IPOPT_MINOFF - 1 + (lsrr * sizeof(u_long));
        bcopy((caddr_t) & to->sin_addr, oix, sizeof(u_long));
        oix += sizeof(u_long);
        while ((oix - optlist) & 3)
            oix++;              /* Pad to an even boundry */
        _optlen = (oix - optlist);

        if ((pe = getprotobyname("ip")) == NULL) {
            perror("traceroute: unknown protocol ip");
            exit(10);
        }
        if ((setsockopt
             (sndsock, pe->p_proto, IP_OPTIONS, optlist, oix - optlist)) < 0) {
            perror("traceroute: lsrr options");
            exit(5);
        }
    }
    hdrlen = (int) (sizeof(struct ip) + _optlen);
    switch (probe_protocol) {
    case IPPROTO_UDP:
        hdrlen += sizeof(struct udp_probe);
        break;

    case IPPROTO_ICMP:
        hdrlen += ICMP_MINLEN;
        break;

    default:{
        }                       /* Braces good for some compiler versions */
    }

    if (datalen < hdrlen) {
        /* The chosen size is too small to fit everything... make it bigger: */
        datalen = hdrlen;
    }
#ifdef SO_SNDBUF
    if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *) &datalen,
                   sizeof(datalen)) < 0) {
        perror("traceroute: SO_SNDBUF");
        exit(6);
    }
#endif                          /* SO_SNDBUF */
#ifdef IP_HDRINCL
    if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *) &on, sizeof(on)) < 0) {
        perror("traceroute: IP_HDRINCL");
        exit(6);
    }
#endif                          /* IP_HDRINCL */
    if (options & TRT_DEBUG)
        if (setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *) &on, sizeof(on))) {
            perror("traceroute: SO_DEBUG");
            exit(6);
        }
    if (options & TRT_DONTROUTE)
        if (setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *) &on, sizeof(on))) {
            perror("traceroute: SO_DONTROUTE");
            exit(6);
        }

    if (source) {
        (void) bzero((char *) &from, sizeof(struct sockaddr));
        from.sin_family = AF_INET;
        from.sin_addr.s_addr = inet_addr(source);
        if ((int) from.sin_addr.s_addr == -1) {
            Fprintf(stderr, "traceroute: unknown host %s%s", source, terminator);
            exit(1);
        }
        outpacket->ip.ip_src = from.sin_addr;
#ifndef IP_HDRINCL
        if (bind(sndsock, (struct sockaddr *) &from, sizeof(from)) < 0) {
            perror("traceroute: bind:");
            exit(1);
        }
#endif                          /* IP_HDRINCL */
    }
#ifdef XML
    if (generatexml) {
        xmldoc = xmlNewDoc(BAD_CAST "1.0");
        xmlroot = xmlNewNode(NULL, BAD_CAST "traceRoute");
        xmlDocSetRootElement(xmldoc, xmlroot);
        xmlSetProp(xmlroot, "xmlns", "urn:ietf:params:xml:ns:traceroute-1.0");
        xmlnode = xmlNewChild(xmlroot, NULL, BAD_CAST "Measurement", NULL);
        formernode = xmlnode;
        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "MeasurementMetadata", NULL);
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "TestName", NULL);
        snprintf(result, MAX_NAME, "Test to %s", hostname);
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        uname(&osinfo);
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "OSName", NULL);
        xmlNodeSetContent(tmpnode,
                          xmlEncodeSpecialChars(xmldoc, BAD_CAST osinfo.sysname));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "OSVersion", NULL);
        xmlNodeSetContent(tmpnode,
                          xmlEncodeSpecialChars(xmldoc, BAD_CAST osinfo.release));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "ToolVersion", NULL);
        xmlNodeSetContent(tmpnode,
                          xmlEncodeSpecialChars(xmldoc, BAD_CAST TR_VERSION));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "ToolName", NULL);
        xmlNodeSetContent(tmpnode,
                          xmlEncodeSpecialChars(xmldoc,
                                                BAD_CAST "traceroute-nanog+XML"));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "CtlTargetAddress", NULL);
        tmpnode = xmlNewChild(tmpnode, NULL, BAD_CAST "inetAddressDns", NULL);
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST hostname));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "CtlMaxTtl", NULL);
        snprintf(result, MAX_NAME, "%i", max_ttl);
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "CtlInitialTtl", NULL);
        snprintf(result, MAX_NAME, "%i", min_ttl);
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "CtlType", NULL);
        tmpnode = xmlNewChild(tmpnode, NULL, BAD_CAST "UDP", NULL);
        xmlnode = formernode;
        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "MeasurementResult", NULL);
        measurementnode = xmlnode;
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "TestName", NULL);
        snprintf(result, MAX_NAME, "Test to %s", hostname);
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        tmpnode =
            xmlNewChild(xmlnode, NULL, BAD_CAST "ResultsStartDateAndTime", NULL);
        snprintf(result, MAX_NAME, current_date_time());
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        formernode = xmlnode;
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "ResultsIpTgtAddr", NULL);
        tmpnode = xmlNewChild(tmpnode, NULL, BAD_CAST "inetAddressIpv4", NULL); /* This 
                                                                                 * traceroute 
                                                                                 * is 
                                                                                 * IPv4-specific 
                                                                                 */
        snprintf(result, MAX_NAME, "%s", inet_ntoa(to->sin_addr));
        xmlNodeSetContent(tmpnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        xmlnode = formernode;
        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "ProbeResults", NULL);
    }
#endif
    Fprintf(stderr, "traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr));
    if (source)
        Fprintf(stderr, " from %s", source);
    Fprintf(stderr, ", %d hops max", max_ttl);
    if (!mtudisc)
        Fprintf(stderr, ", %d byte packets", datalen);
    Fprintf(stderr, "%s", terminator);
    (void) fflush(stderr);

    if (!spray_mode) {
#ifndef SOLARIS
        /* Solaris is broken: sendto() with a large packet doesn't set the return
         * value and errno as sendto(3n) says it should.  It sends an ICMP
         * unreachable message to itself, though, so we'll pick that up later. */
        if (mtudisc)
            /* Get interface MTU */
            send_probe(seq, -1, probe_protocol);
#endif                          /* SOLARIS */
        /* For all TTL do */
        consecutive = 0;
        for (ttl = min_ttl; ttl <= max_ttl; ++ttl) {
            bzero(&addr_last, sizeof(addr_last));
            got_there = unreachable = mtu = lost = 0;
            min = max = sum = sumsq = 0.0;
            throughput = (double) 0.0;

            if (new_mtu != 0) {
                Fprintf(stdout, "MTU=%d\n", new_mtu);
                new_mtu = 0;
            }
            print_ttl(ttl);
            if (generatexml) {
                hopnode = xmlnode;
                xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "hop", NULL);
            }
            /* For all probes do */
            for (probe = 0; probe < nprobes; ++probe) {
                (void) gettimeofday(&tv, NULL);
                send_probe(++seq, ttl, probe_protocol);
                deadline.tv_sec = tv.tv_sec + (int) waittime;
                deadline.tv_usec =
                    tv.tv_usec + ((int) (waittime * 1000000.0)) % 1000000;
                if (deadline.tv_usec >= 1000000) {
                    deadline.tv_usec -= 1000000;
                    deadline.tv_sec++;
                }
                /* Get an answer */
                while (cc = wait_for_reply(s, &from, &deadline)) {
                    if ((i = packet_ok(packet, cc, &from, seq, probe_protocol))) {
                        float           dt = deltaT(&tv);
                        if (sum == 0) {
                            sum = min = max = dt;
                            sumsq = dt * dt;
                        } else {
                            if (dt < min)
                                min = dt;
                            if (dt > max)
                                max = dt;
                            sum += dt;
                            sumsq += dt * dt;
                        }
                        if (hurry_mode)
                            probe = nprobes;

                        if (generatexml) {
                            probenode = xmlnode;
                            xmlnode =
                                xmlNewChild(xmlnode, NULL, BAD_CAST "probe", NULL);
                        }
                        print_from(&from);

                        if (i == -1) {  /* ICMP_XCEED */
                            decode_icmp_ext(packet, cc);
                        }

                        if (ppdelay) {
                            print_time(&dt);
                        }
                        if (!generatexml) {
                            print_packet((struct ip *)
                                         &packet, i);
                        } else {
                            probe_status = get_packet((struct ip *)
                                                      &packet, i);
                        }
                        consecutive = 0;        /* got a packet back! */
                        if (generatexml) {
                            if (generatexml) {
                                tmpnode =
                                    xmlNewChild
                                    (xmlnode, NULL, BAD_CAST "ResponseStatus", NULL);
                                xmlNodeSetContent
                                    (tmpnode,
                                     xmlEncodeSpecialChars
                                     (xmldoc, BAD_CAST probe_status));
                                tmpnode =
                                    xmlNewChild
                                    (xmlnode, NULL, BAD_CAST "Time", NULL);
                                xmlNodeSetContent
                                    (tmpnode,
                                     xmlEncodeSpecialChars
                                     (xmldoc, BAD_CAST current_date_time()));
                            }
                            xmlnode = probenode;
                        }
                        break;
                    }           /* end if packet ok */
                }               /* end while */

                if (cc == 0) {
                    if (pingmode)
                        exit(23);
                    if (!generatexml) {
                        Fprintf(stdout, " *");
                        (void) fflush(stdout);
                    } else {
                        probenode = xmlnode;
                        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "probe", NULL);
                        formernode = xmlnode;
                        xmlnode =
                            xmlNewChild(xmlnode, NULL, BAD_CAST "HopAddr", NULL);
                        xmlnode =
                            xmlNewChild(xmlnode, NULL,
                                        BAD_CAST "inetAddressUnknown", NULL);
                        xmlnode = formernode;
                        xmlnode =
                            xmlNewChild(xmlnode, NULL,
                                        BAD_CAST "ProbeRoundTripTime", NULL);
                        xmlnode =
                            xmlNewChild(xmlnode, NULL,
                                        BAD_CAST "roundTripTimeNotAvailable", NULL);
                        xmlnode = formernode;
                        xmlnode =
                            xmlNewChild(xmlnode, NULL,
                                        BAD_CAST "ResponseStatus", NULL);
                        xmlNodeSetContent(xmlnode,
                                          xmlEncodeSpecialChars
                                          (xmldoc, BAD_CAST "requestTimedOut"));
                        xmlnode = formernode;
                        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "Time", NULL);
                        xmlNodeSetContent(xmlnode,
                                          xmlEncodeSpecialChars
                                          (xmldoc, BAD_CAST current_date_time()));
                        xmlnode = probenode;
                    }

                    lost++;

#ifndef __linux__
                    /* Reset the ^C action from exit to skip TTL */
                    if (haltf == 0 && lost == 1)

                        NOERR(signal(SIGINT, halt), "signal SIGINT");
                    /* we've missed at least one packet, so let's check for the
                     * signal to go to the next ttl */
                    if (haltf > 0) {
                        haltf = 0;
                        consecutive = 0;
                        break;
                    }
#endif                          /* __linux__ */
                }               /* end if cc = 0 */
            }                   /* end for probe */

            if (pploss) {
                if (lost < probe) {
                    throughput = (100.0 - ((lost * 100.0) / probe));
                    Fprintf(stdout,
                            "  (%1.1f ms/%1.1f ms(+-%1.1f ms)/%1.1f ms)",
                            min, (sum / (probe - lost)),
                            (float) sqrt((double) sumsq) / (probe - lost), max);
                    Fprintf(stdout, " %d/%d (%#3.2f%%)",
                            (probe - lost), probe, throughput);
                    (void) fflush(stdout);
                }
            }
            if (generatexml) {
                xmlnode = hopnode;
            }
            if (!generatexml) {
#ifndef FIXT
                Fprintf(stdout, terminator);
#else                           /* FIXT */
                Fprintf(stdout, "%s", terminator);
#endif                          /* FIXT */
            }
            /* If we're running one probe and we get back one packet, that's no
             * excuse to quit unless we're really done! */
            if (got_there || (hurry_mode && unreachable) ||
#ifndef CISCO_ICMP
                unreachable >= nprobes)
#else                           /* CISCO_ICMP meaning not all our packets will be
                                 * returned. */
                ((unreachable + lost >= nprobes) && unreachable)
                )
#endif                          /* CISCO_ICMP */
                if (!generatexml) {
                exit(0);
            } else {
                break;
            }
            if (new_mtu != 0) {
                ttl--;          /* Redo the same TTL */
                datalen = new_mtu;      /* Set the new data length */
            }
            if (automagic && (consecutive++ > 9))
                break;
        }                       /* end for TTL */
        if (generatexml) {
            tmpnode =
                xmlNewChild(measurementnode, NULL,
                            BAD_CAST "ResultsEndDateAndTime", NULL);
            snprintf(result, MAX_NAME, current_date_time());
            xmlNodeSetContent(tmpnode,
                              xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
            print_xml_tree();
        }
        exit(0);

/* end non-spray mode */

    } else {

/*
 * Enter Spray mode
 */
        spray_target = spray_max = spray_total = 0;
        spray_min = SPRAYMAX + 1;

        /* For all TTL do */
        for (ttl = min_ttl; ttl <= max_ttl; ++ttl) {
            spray_rtn[ttl] = (unsigned int *) malloc(sizeof(int) * nprobes + 1);
            for (probe = 0; probe < nprobes; ++probe) {
                spray_rtn[ttl][probe] = 0;
                send_probe(++seq, ttl, probe_protocol);
            }
        }
        (void) gettimeofday(&tv, NULL);
        deadline.tv_sec = tv.tv_sec + (int) waittime;
        deadline.tv_usec = tv.tv_usec + ((int) (waittime * 1000000.0)) % 1000000;
        if (deadline.tv_usec >= 1000000) {
            deadline.tv_usec -= 1000000;
            deadline.tv_sec++;
        }
        /* Go get responses until either we get them all, or timeout */
        while (((((int) pow(2, spray_min) - 1) & response_mask) !=
                ((int) pow(2, spray_min) - 2))
               && (cc = wait_for_reply(s, &from, &deadline))) {
            (void) packet_ok(packet, cc, &from, seq, probe_protocol);
        }

        last_i = 1;
        last_ttl = 0;
        bzero(&addr_last, sizeof(addr_last));
        for (i = min_ttl; i <= max_ttl; i++) {
            /* First see if it's valid, and if so play with its time */
            idx = spray_rtn[i][0];
            if ((idx > 0) && (idx < SPRAYMAX)
                && (spray[idx].from.sin_addr.s_addr != 0)) {
                spr_ttl = spray[idx].ttl;
                if (spr_ttl != i) {
                    Fprintf(stderr, "Check failure spray(rtn[i]) !=i\n");
                    exit(1);
                }

                /* do not display duplicate entries (responses beyond terminus from
                 * same host) */
                if (0 != memcmp(&spray[idx].from.sin_addr.s_addr,
                                &addr_last.sin_addr.s_addr,
                                sizeof(addr_last.sin_addr.s_addr))) {

                    ttl_diff = (spr_ttl - last_ttl);
                    if (ttl_diff > 1) {
                        for (last_i = last_ttl + 1; last_i < spr_ttl; last_i++) {
                            print_ttl(last_i);
                            for (probe = 1; probe <= nprobes; probe++) {
                                Fprintf(stdout, " *");
                            }   /* end for probes */
#ifndef FIXT
                            Fprintf(stdout, terminator);
#else                           /* FIXT */
                            Fprintf(stdout, "%s", terminator);
#endif                          /* FIXT */
                        }       /* end for last i */
                    }
                    /* endf if spr_ttl ... */
                    if (spr_ttl > last_ttl) {
                        print_ttl(spr_ttl);
                        last_ttl = spr_ttl;
                    }

                    print_from(&spray[idx].from);

                    for (probe = 0; probe < nprobes; ++probe) {
                        if (spray_rtn[i][probe] != 0) {
                            tvsub(&spray[spray_rtn[i]
                                         [probe]].rtn, &spray[spray_rtn[i]
                                                              [probe]].out);
                            ddt = spray[spray_rtn[i]
                                        [probe]].rtn.tv_sec * 1000.0 + ((float)
                                                                        spray
                                                                        [spray_rtn[i]
                                                                         [probe]].
                                                                        rtn.
                                                                        tv_usec) /
                                1000.0;
                            print_time(&ddt);
                        } else {
                            Fprintf(stdout, " *");
                        }
                    }
#ifndef FIXT
                    Fprintf(stdout, terminator);
#else                           /* FIXT */
                    Fprintf(stdout, "%s", terminator);
#endif                          /* FIXT */
                }               /* no duplicate entries */
            }                   /* end if nonzero type */
        }                       /* end for */
#ifdef XML
        print_xml_tree();
#endif
    }                           /* end spray mode */
}

void
print_packet(struct ip *ip, int i)
{
    if (generatexml) {
        Fprintf(stderr,
                "Internal error, print_packet(s) called with generatexml on\n");
        exit(1);
    }
#ifdef TRACE_TOS
    {
        struct icmp    *icp = (struct icmp *) (((u_char *) ip) + (ip->ip_hl << 2));
        struct ip      *inner_ip = (struct ip *) (((u_char *) icp) + 8);
        int             tos = inner_ip->ip_tos;

        if (tos != last_tos) {
            Fprintf(stdout, " (TOS=%d!)", tos);
        }
        last_tos = tos;
    }
#endif                          /* TRACE_TOS */
    switch (i - 1) {
    case 13:
        Fprintf(stdout, " !A"); /* admin prohibited */
        ++unreachable;
        break;
    case ICMP_UNREACH_PORT:
#ifndef ARCHAIC
        ip = (struct ip *) packet;
        if (ip->ip_ttl <= 1)
            Fprintf(stdout, " !");
#endif                          /* ARCHAIC */
        ++got_there;
        break;
    case ICMP_UNREACH_NET:
        ++unreachable;
        Fprintf(stdout, " !N");
        break;
    case ICMP_UNREACH_HOST:
        ++unreachable;
        Fprintf(stdout, " !H");
        break;
    case ICMP_UNREACH_PROTOCOL:
        ++got_there;
        Fprintf(stdout, " !P");
        break;
    case ICMP_UNREACH_NEEDFRAG:
        if (mtudisc) {
            /* Doing MTU discovery */
            mtu = (ntohl(icp->icmp_void) & 0xffff);
            if (mtu >= datalen) {
                /* This should never happen.  There is a serious bug somewhere... */
                Fprintf(stdout, " !M>");
            } else if (mtu == 0) {
                /* Looks like router is not RFC1191 - compliant (using original RFC
                 * 792 spec). */
                new_mtu = reduce_mtu(datalen);
                Fprintf(stdout, " !M?%d", new_mtu);
            } else {
                new_mtu = mtu;
                Fprintf(stdout, " !M=%d", new_mtu);
            }
            break;
        } else {
            /* Not doing MTU discovery */
            ++unreachable;
            Fprintf(stdout, " !F");
            break;
        }
    case ICMP_UNREACH_SRCFAIL:
        ++unreachable;
        Fprintf(stdout, " !S");
        break;
    }                           /* end switch */
}

char           *
get_packet(struct ip *ip, int i)
{
    switch (i - 1) {
    case 13:
        ++unreachable;
        return "noRouteToTarget";       /* adminProhibited */
    case ICMP_UNREACH_PORT:
#ifndef ARCHAIC
        ip = (struct ip *) packet;
        if (ip->ip_ttl <= 1)
            return "unknown";
#endif                          /* ARCHAIC */
        ++got_there;
        return "responseReceived";
    case ICMP_UNREACH_NET:
        ++unreachable;
        return "noRouteToTarget";
    case ICMP_UNREACH_HOST:
        ++unreachable;
        return "noRouteToTarget";
    case ICMP_UNREACH_PROTOCOL:
        ++got_there;
        return "noRouteToTarget";
    case ICMP_UNREACH_NEEDFRAG:
        if (mtudisc) {
            /* Doing MTU discovery */
            mtu = (ntohl(icp->icmp_void) & 0xffff);
            if (mtu >= datalen) {
                /* This should never happen.  There is a serious bug somewhere... */
                /* TODO */
            } else if (mtu == 0) {
                /* Looks like router is not RFC1191 - compliant (using original RFC
                 * 792 spec). */
                new_mtu = reduce_mtu(datalen);
                /* TODO: what status to return ? */
            } else {
                new_mtu = mtu;
                /* TODO: what status to return ? */
            }
            break;
        } else {
            /* Not doing MTU discovery */
            ++unreachable;
            return "noRouteToTarget";
        }
    case ICMP_UNREACH_SRCFAIL:
        ++unreachable;
        return "noRouteToTarget";
    default:
        return "responseReceived";
    }
}

void
print_time(float *dt)
{
    char           *result = malloc(MAX_TIME);
    if (!generatexml) {
        if (utimers) {
            Fprintf(stdout, "  %3.3f ms", *dt);
        } else {
            Fprintf(stdout, "  %d ms", (int) (*(dt) + 0.5));
        }
        (void) fflush(stdout);
    } else {
#ifdef XML
        formernode = xmlnode;
        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "ProbeRoundTripTime", NULL);
        xmlnode = xmlNewChild(xmlnode, NULL, BAD_CAST "roundTripTime", NULL);
        if (utimers) {
            snprintf(result, MAX_TIME, "%d", *dt);      /* Yes, roundTripTime is an
                                                         * integer */
        } else {
            snprintf(result, MAX_TIME, "%d", (int) (*(dt) + 0.5));
        }
        xmlNodeSetContent(xmlnode, xmlEncodeSpecialChars(xmldoc, BAD_CAST result));
        xmlnode = formernode;
        /* TODO: free result */
#else
        Fprintf(stderr,
                "print_time() called with generatexml on while not compiled with XML support");
#endif
    }
}

void
print_ttl(int ttl)
{
    if (!generatexml) {
        Fprintf(stdout, "%2d ", ttl);
        (void) fflush(stdout);
    } else {
        /* Do nothing, the hop count is given by the order of <hop> elements */
    }
}

#ifdef XML
void
print_xml_tree()
{
    xmlDocDumpFormatMemory(xmldoc, &xmlbuff, &xmlbuffersize, 1);
    Fprintf(stdout, (char *) xmlbuff);
    xmlFree(xmlbuff);
    xmlFreeDoc(xmldoc);
}
#endif

wait_for_reply(sock, from, deadline)
    int             sock;
    struct sockaddr_in *from;
    struct timeval *deadline;
{
    fd_set          fds;
    struct timeval  wait;
    struct timeval  now;
    int             cc = 0;
    socklen_t       fromlen = sizeof(*from);

#ifndef __linux__
    gettimeofday(&now, NULL);
#else                           /* __linux__ */
    gettimeofday(&now, &tz);
#endif                          /* __linux__ */
    if ((now.tv_sec > deadline->tv_sec) ||
        ((now.tv_sec == deadline->tv_sec) && (now.tv_usec > deadline->tv_usec)))
        return (int) NULL;

    wait.tv_sec = deadline->tv_sec - now.tv_sec;
    if (deadline->tv_usec >= now.tv_usec) {
        wait.tv_usec = deadline->tv_usec - now.tv_usec;
    } else {
        wait.tv_usec = (1000000 - now.tv_usec) + deadline->tv_usec;
        wait.tv_sec--;
    }

    FD_ZERO(&fds);
    FD_SET(sock, &fds);

    if (select(sock + 1, &fds, (fd_set *) 0, (fd_set *) 0, &wait) > 0)
        cc = recvfrom(s, (char *) packet, sizeof(packet), 0,
                      (struct sockaddr *) from, &fromlen);
    return ((int) cc);
}

char           *
current_date_time()
{
    char           *result = malloc(MAX_DATETIME_SIZE);
    struct timeval  tv;
    struct tm      *time;
    (void) gettimeofday(&tv, NULL);
    time = gmtime(&tv.tv_sec);
    /* Format as ISO 8601 */
    strftime(result, MAX_DATETIME_SIZE, "%Y-%m-%dT%H:%M:%SZ", time);
    return result;
}


send_probe(seq, ttl, proto)
    int             ttl;
    int             seq;
    int             proto;
{
    struct opacket *op = outpacket;
    struct ip      *ip = &op->ip;
    struct udphdr  *up;
    struct icmp    *icmpp;
    int             i;
    int             mtu_low, mtu_high;

#ifdef BROKEN_RAWIP
    register char  *cp, *dp;
    static char    *np = 0;
    int             raw_optlen = _optlen;
    char            raw_optlist[sizeof(optlist)];
#endif                          /* BROKEN_RAWIP */

    if (ttl == -1) {
        /* Getting interface MTU for path MTU discovery */
        mtu_low = mtuvals[(sizeof(mtuvals) / sizeof(mtuvals[0])) - 1 - 1];
        mtu_high = datalen;
        datalen = (mtu_low + mtu_high) >> 1;
    }

  retry:


    /* W. Richard Stevens: UNIX Network Programming, Volume 1, Second Edition, p.
     * 657: "Unfortunately the IP_HDRINCL socket option has never been documented,
     * specifically with regard to the byte ordering of the fields in the IP header. 
     * * * On Berkeley-derived kernels all fields are in network byte order except
     * ip_len and ip_off, which are in host byte order (pp. 233 and 1057 of TCPv2).
     * On Linux, however, all the fields must be in network byte order." See
     * http://www.leto.net/docs/libnet-precis.txt and doc/RAWSOCKET_NON_SEQUITUR in
     * libnet distribution (http://www.packetfactory.net/libnet) for more details.
     * Linux ignores ip_len and fills it in itself from the sendto() length. */


    if (mtudisc) {
#ifndef BYTESWAP_IP_FLAGS
        ip->ip_off = (u_short) IP_DF;
#else                           /* BYTESWAP_IF_FLAGS */
        ip->ip_off = htons((u_short) IP_DF);
#endif
    } else {
        ip->ip_off = 0;
    }

    ip->ip_p = proto;

#ifdef BROKEN_RAWIP
    ip->ip_len = (u_short) datalen;     /* OS ignores setsockopt() */
#else                           /* ! BROKEN_RAWIP */

#ifndef BYTESWAP_IP_LEN
    ip->ip_len = ((u_short) datalen - _optlen); /* The OS inserts options */
#else                           /* BYTESWAP_IP_LEN */
    ip->ip_len = htons((u_short) datalen - _optlen);    /* The OS inserts options */
#endif                          /* BYTESWAP_IP_LEN */
#endif                          /* ! BROKEN_RAWIP */

    if (ttl == -1)
        ip->ip_ttl = 0;
    else
        ip->ip_ttl = ttl;
    ip->ip_v = IP_VERSION;
    ip->ip_hl = sizeof(*ip) >> 2;

    if (proto == IPPROTO_UDP) {
        up = &op->ip_payload.udp_probe.udp;
        up->uh_sport = htons(ident);
        up->uh_dport = htons(port + seq);
        up->uh_ulen = htons((u_short) (datalen - sizeof(struct ip) - _optlen));
        op->ip_payload.udp_probe.seq = seq;
        op->ip_payload.udp_probe.ttl = ttl;

#ifndef __linux__
        (void) gettimeofday(&op->ip_payload.udp_probe.tv, NULL);
#else                           /* __linux__ */
        (void) gettimeofday(&op->ip_payload.udp_probe.tv, &tz);
#endif                          /* __linux__ */
    } else if (proto == IPPROTO_ICMP) {
        icmpp = &op->ip_payload.icmp_probe;
        icmpp->icmp_type = ICMP_ECHO;
        icmpp->icmp_hun.ih_idseq.icd_id = ident;
        icmpp->icmp_hun.ih_idseq.icd_seq = seq;
        /* Unlike UDP, we need a correct checksum. */
        cksum((u_int16_t *) icmpp,
              (datalen - sizeof(struct ip) - _optlen + 1) >> 1, &icmpp->icmp_cksum);
    }
#ifdef BROKEN_RAWIP
    if (!np)
        np = (char *) malloc(datalen);
    bzero(np, datalen);
    bzero(raw_optlist, sizeof raw_optlist);

    dp = np;
    if (_optlen) {
        raw_optlen = _optlen - sizeof(struct in_addr);
        raw_optlist[IPOPT_OPTVAL] = optlist[IPOPT_OPTVAL];
        raw_optlist[IPOPT_OLEN] = optlist[IPOPT_OLEN] - sizeof(struct in_addr);
        raw_optlist[IPOPT_OFFSET] = optlist[IPOPT_OFFSET];
        bcopy(&optlist[IPOPT_OFFSET + 1], &ip->ip_dst, sizeof(struct in_addr));
        bcopy(&optlist[IPOPT_OFFSET + 1] + sizeof(struct in_addr),
              &raw_optlist[IPOPT_OFFSET + 1], raw_optlen - sizeof(struct in_addr));
        ip->ip_hl = (sizeof(*ip) + raw_optlen) >> 2;
        for (cp = (char *) ip, i = sizeof(*ip); i; i--)
            *dp++ = *cp++;
        for (cp = (char *) raw_optlist, i = raw_optlen; i; i--)
            *dp++ = *cp++;
    } else
        for (cp = (char *) ip, i = sizeof(*ip); i; i--)
            *dp++ = *cp++;
    for (cp = (char *) up, i = sizeof(*up); i; i--)
        *dp++ = *cp++;
    *dp++ = seq;
    *dp++ = ttl;
    bcopy(&op->ip_payload.udp_probe.tv, dp, sizeof(op->ip_payload.udp_probe.tv));

#ifdef SPRAY
    if (spray_mode) {
        if (proto == IPPROTO_UDP)
            spray[seq].dport = up->uh_dport;
        spray[seq].ttl = ttl;
        bcopy(&op->ip_payload.udp_probe.tv, &spray[seq].out, sizeof(struct timeval));
    }
#endif

    i = sendto(sndsock, (char *) np, datalen, 0, &whereto, sizeof(struct sockaddr));

    if (i < 0 || i != datalen) {
#else


#ifdef SPRAY
    if (spray_mode) {
        /* Must be IPPROTO_UDP */
        spray[seq].dport = up->uh_dport;
        spray[seq].ttl = ttl;

        bcopy(&op->ip_payload.udp_probe.tv, &spray[seq].out, sizeof(struct timeval));
    }
#endif                          /* SPRAY */

    i = sendto(sndsock, (char *) outpacket, datalen - _optlen, 0, &whereto,
               sizeof(struct sockaddr));

    if (i < 0 || i != datalen - _optlen) {
#endif                          /* ! BROKEN_RAWIP */

        if (i < 0) {
            if (errno == EMSGSIZE) {
                if (ttl == -1)
                    mtu_high = datalen - 1;
                else {
                    datalen = reduce_mtu(datalen);
                    Fprintf(stdout, " MTU=%d", datalen);
                    goto retry;

                }
            } else
                perror("sendto");
        } else {
            Fprintf(stderr, "traceroute: wrote %s %d chars, ret=%d%s",
                    hostname, datalen, i, terminator);
            (void) fflush(stdout);

        }
    } else if (ttl == -1)
        mtu_low = datalen;
    if (ttl == -1) {
        if (mtu_high > mtu_low) {
            if (mtu_high == mtu_low + 1)
                datalen = mtu_high;
            else
                datalen = (mtu_high + mtu_low) >> 1;
            goto retry;
        } else if (mtu_high < mtu_low) {
            /* Should never happen */
            Fprintf(stderr, "traceroute: unable to determine interface MTU");
            exit(1);
        }
        /* else mtu_high == mtu_low and we have interface MTU */
        new_mtu = datalen = mtu_high;

    }
}

void
cksum(buf, nwords, ckaddr)
    u_int16_t      *buf;
    int             nwords;
    volatile u_int16_t *ckaddr;
{
    /* Based on cksum in Douglas E. Comer and David L. Stevens: Internetworking with 
     * TCP/IP Volume II.  We store the checksum ourselves instead of returning its
     * value to prevent the compiler possibly optimising away its initialisation. If 
     * the size in bytes of the buffer is odd, nwords (the size in 16-bit words)
     * should be rounded up and the byte beyond the end of the buffer should be
     * zero. */

    u_int32_t       sum;

    *ckaddr = 0;

    for (sum = 0; nwords > 0; nwords--)
        sum += *buf++;
    sum = (sum >> 16) + (sum & 0xffff); /* add in carry */
    sum += (sum >> 16);         /* maybe one more */
    *ckaddr = ~sum;
}


float
deltaT(tp)
    struct timeval *tp;
{
    struct timeval  tv;

#ifndef __linux__
    (void) gettimeofday(&tv, NULL);
#else                           /* __linux__ */
    (void) gettimeofday(&tv, &tz);
#endif                          /* __linux__ */
    tvsub(&tv, tp);
    return (tv.tv_sec * 1000.0 + ((float) tv.tv_usec) / 1000.0);
}

/*
 * Convert an ICMP "type" field to a printable string.
 */
char           *
pr_type(t)
    u_char          t;
{
    static char    *ttab[] = {
        "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
        "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
        "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
        "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
        "Info Reply"
    };

    if (t > 16)
        return ("OUT-OF-RANGE");

    return (ttab[t]);
}

/*
 *  Decodes ICMP extension header.
 *  draft-ietf-mpls-icmp-02.txt
 *  Jorge Boncompte - DTI2
 */
void
decode_icmp_ext(buf, cc)
    u_char         *buf;
    int             cc;
{
    static unsigned long last_mpls_label;
    static unsigned int last_mpls_exp;
    unsigned long   mpls_label;
    char           *result;
    unsigned int    ext_ver, ext_res, ext_chk, obj_hdr_len, mpls_exp;
    u_char          obj_hdr_class, obj_hdr_type;

    /* IP hdr + ICMP hdr + 128b original packet + 4 bytes ICMP ext hdr */
    if (cc > 160) {
        ext_ver = buf[156] >> 4;
        ext_res = (buf[156] & 15) + buf[157];
        ext_chk = ((unsigned int) buf[158] << 8) + buf[159];

        if (ext_ver == 2 && ext_res == 0 && ext_chk != 0) {
            /* Ok. This is an ICMP ext header. But we haven't checked that the
             * checksum is correct */
            obj_hdr_len = ((int) buf[160] << 8) + buf[161];
            obj_hdr_class = buf[162];
            obj_hdr_type = buf[163];

            if (obj_hdr_len >= 8 && obj_hdr_class == 1 && obj_hdr_type == 1) {
                mpls_label =
                    ((unsigned long) buf[164] << 12) +
                    ((unsigned int) buf[165] << 4) + ((buf[166] >> 4) & 0xff);
                mpls_exp = (buf[166] >> 1) & 0x7;

                /* Print the label, if it is different that the one received with
                 * the last probe, print again. Maybe in between the 2 probes
                 * happened a reroute? */
                if (mpls_label != last_mpls_label || mpls_exp != last_mpls_exp) {
                    last_mpls_label = mpls_label;
                    last_mpls_exp = mpls_exp;
                    if (!generatexml) {
                        Fprintf(stdout, " [MPLS: Label %lu Exp %u]",
                                mpls_label, mpls_exp);
                    } else {
                        result = malloc(MAX_NAME);
                        tmpnode =
                            xmlNewChild(xmlnode, NULL,
                                        BAD_CAST "MPLSLabelStackEntry", NULL);
                        snprintf(result, MAX_NAME, "%i", mpls_label);
                        xmlNodeSetContent(tmpnode,
                                          xmlEncodeSpecialChars(xmldoc,
                                                                BAD_CAST result));
                    }
                }
            }

            /* FIXME: We only print the first label */
            if (obj_hdr_len >= 12)
                Fprintf(stdout, " More labels");
        }
    }
}

/*
 * packet_ok - Make sure it's a real ICMP return of a real packet
 */
packet_ok(buf, cc, from, seq, proto)
    u_char         *buf;
    int             cc;
    struct sockaddr_in *from;
    int             seq;
    int             proto;
{
    u_char          type, code;
    int             hlen, mtu;
    u_short         temp;
    int             tmp, tmp2;
    int             spr_seq;
    int             spr_ttl;
    int             probecnt;

#ifndef ARCHAIC
    struct ip      *ip;

    ip = (struct ip *) buf;
    /* get header length and convert from longwords to bytes */
    if ((ip->ip_v) != IP_VERSION) {
        Fprintf(stderr, "packet version not 4: %d%s", ip->ip_v, terminator);
        return (0);
    }
    hlen = ip->ip_hl << 2;
    if (cc < hlen + ICMP_MINLEN) {
        if (verbose)
            Fprintf(stderr, "packet too short (%d bytes) from %s%s", cc,
                    inet_ntoa(from->sin_addr), terminator);
        return (0);
    }
    /* go from returned length of packet to cc=data portion */
    cc -= hlen;
    /* make icp point to supposed ICMP portion */
    icp = (struct icmp *) ((u_char *) buf + hlen);
#else
    icp = (struct icmp *) buf;
#endif                          /* ARCHAIC */
    type = icp->icmp_type;
    code = icp->icmp_code;
    if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
        type == ICMP_UNREACH) {
        struct ip      *hip;
        struct udphdr  *up = NULL;
        struct icmp    *icp2 = NULL;

        hip = &icp->icmp_ip;
        hlen = hip->ip_hl << 2;
        if (proto == IPPROTO_UDP)
            up = (struct udphdr *) ((u_char *) hip + hlen);
        else if (proto == IPPROTO_ICMP)
            icp2 = (struct icmp *) ((u_char *) hip + hlen);
#ifdef SPRAY
/*
 * First we make sure we got a legal response back, and if so we
 * get the sequence number and ttl out of it
 */
        if (spray_mode) {
            if (up == NULL) {
                Fprintf(stderr, "invalid NULL UDP header%s", terminator);
                return (0);
            }
            spr_seq = ntohs(up->uh_dport) - port;
            if ((spr_seq >= 0) && (spr_seq < max_ttl * nprobes + 1)) {
                spr_ttl = spray[spr_seq].ttl;
                response_mask |= (1 << spr_ttl);
/*
 * Now we increment the response count for this ttl, and then if it's the
 * first, increment the total of ttl's seen
 */
                if (spray_rtn[spr_ttl][0] == 0) {
                    spray_total++;
                }

                for (probecnt = 0; probecnt < nprobes; ++probecnt) {
                    if (spray_rtn[spr_ttl][probecnt] == 0) {
                        spray_rtn[spr_ttl][probecnt] = spr_seq;
                        break;
                    }
                }
/*
 * We want to do some heuristics on the smallest TTL received from the target
 * host, but we need the type for that...
 */
                spray[spr_seq].type = type;
                if (type == ICMP_UNREACH_PORT) {
                    if (spr_ttl < spray_min) {
                        spray_min = spr_ttl;
                    }
                    spray_target = spr_ttl;
                }
/*
 * We also want the largest TTL we've seen...
 */
                if (spr_ttl > spray_max)
                    spray_max = spr_ttl;
/*
 * And finally, fill the data structure with the other things we'll need
 * to spit out later, namely the packet transit type and the source IP.
 */
                gettimeofday(&(spray[spr_seq].rtn), 0);
                bcopy(&from->sin_addr,
                      &spray[spr_seq].from.sin_addr, sizeof(from->sin_addr));

            }                   /* end if sequence number valid */
        }                       /* end if spray mode */
#endif
        if (hlen + 12 <= cc && hip->ip_p == proto && hip->ip_ttl > 0) {
            if (proto == IPPROTO_UDP) {
                if (up->uh_sport == htons(ident) &&
                    up->uh_dport == htons(port + seq))
                    return (type == ICMP_TIMXCEED ? -1 : code + 1);
            } else if (proto == IPPROTO_ICMP) {
                if (icp2->icmp_hun.ih_idseq.icd_id == ident &&
                    icp2->icmp_hun.ih_idseq.icd_seq == seq)
                    return (type == ICMP_TIMXCEED ? -1 : code + 1);
            } else {
                /* XXX More than one trace at a time will be confusing */
                return (type == ICMP_TIMXCEED ? -1 : code + 1);
            }
        }
    } else if (type == ICMP_ECHOREPLY && code == 0) {
        if (proto == IPPROTO_ICMP &&
            icp->icmp_hun.ih_idseq.icd_id == ident &&
            icp->icmp_hun.ih_idseq.icd_seq == seq)
            return (ICMP_UNREACH_PORT + 1);
    }
    /* end if valid ICMP type */
#ifndef ARCHAIC
    if (verbose) {
        int             i;
        u_long         *lp = (u_long *) & icp->icmp_ip;

        Fprintf(stderr, "\n%d bytes from %s to %s", cc,
                inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst));
        Fprintf(stderr, ": icmp type %d (%s) code %d%s", type, pr_type(type),
                icp->icmp_code, terminator);
        for (i = 4; i < cc; i += sizeof(long))
            Fprintf(stderr, "%2d: x%8.8lx\n", i, *lp++);
    }
#endif                          /* ARCHAIC */
    return (0);
}

int
reduce_mtu(value)
    int             value;
{
    int             i = 0;

    while (value <= mtuvals[i])
        i++;
    if (mtuvals[i] > 0) {
        value = mtuvals[i];
    } else {
        Fprintf(stderr, " No valid MTU!!!%s", terminator);
        exit(1);
    }
    return (value);
}

char           *lookup_owner();
char           *doresolve();
char           *lookup_as();

void
print_from(struct sockaddr_in *from)
{
    if (!generatexml) {
        if (0 != memcmp(&from->sin_addr.s_addr,
                        &addr_last.sin_addr.s_addr,
                        sizeof(addr_last.sin_addr.s_addr))) {
            if (nflag)
                Fprintf(stdout, " %s", inet_ntoa(from->sin_addr));
            else
                Fprintf(stdout, " %s (%s)", inetname(from->sin_addr),
                        inet_ntoa(from->sin_addr));
        }
    } else {
        formernode = xmlnode;
        tmpnode = xmlNewChild(xmlnode, NULL, BAD_CAST "HopAddr", NULL);
        tmpnode = xmlNewChild(tmpnode, NULL, BAD_CAST "inetAddressIpv4", NULL);
        xmlNodeSetContent(tmpnode,
                          xmlEncodeSpecialChars(xmldoc,
                                                BAD_CAST inet_ntoa(from->sin_addr)));
        xmlnode = formernode;
        if (!nflag) {
            xmlnode = xmlNewChild(formernode, NULL, BAD_CAST "HopName", NULL);
            xmlNodeSetContent(xmlnode,
                              xmlEncodeSpecialChars(xmldoc,
                                                    BAD_CAST
                                                    inetname(from->sin_addr)));
        }
        xmlnode = formernode;
    }
    if (as_lookup)
        Fprintf(stdout, " [%s]", lookup_as(from->sin_addr));    /* TODO: XML */

    if (dns_owner_lookup)
        Fprintf(stdout, " %s", lookup_owner(from->sin_addr));   /* TODO: XML */

    memcpy(&addr_last.sin_addr.s_addr,
           &from->sin_addr.s_addr, sizeof(addr_last.sin_addr.s_addr));
}

print(buf, cc, from)
    u_char         *buf;
    int             cc;
    struct sockaddr_in *from;
{
    struct ip      *ip;
    int             hlen;

    ip = (struct ip *) buf;
    hlen = ip->ip_hl << 2;
    cc -= hlen;

    if (nflag)
        Fprintf(stdout, " %s", inet_ntoa(from->sin_addr));
    else
        Fprintf(stdout, " %s (%s)", inetname(from->sin_addr),
                inet_ntoa(from->sin_addr));

    if (as_lookup)
        Fprintf(stdout, " [%s]", lookup_as(from->sin_addr));

    if (dns_owner_lookup)
        Fprintf(stdout, " %s", lookup_owner(from->sin_addr));

    if (verbose)
        Fprintf(stderr, " %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
}

/*
 * Subtract 2 timeval structs:  out = out - in.
 * Out is assumed to be >= in.
 */
tvsub(out, in)
    register struct timeval *out, *in;
{
    if ((out->tv_usec -= in->tv_usec) < 0) {
        out->tv_sec--;
        out->tv_usec += 1000000;
    }
    out->tv_sec -= in->tv_sec;
}

/*
 * Construct an Internet address representation.
 * If the nflag has been supplied, give
 * numeric value, otherwise try for symbolic name.
 */
char           *
inetname(in)
    struct in_addr  in;
{
    register char  *cp;
    static char     line[50];
    struct hostent *hp;
    static char     domain[MAXHOSTNAMELEN + 1];
    static int      first = 1;

    if (first && !nflag) {
        first = 0;
        /* Under certain conditions, gethostname returns a string that is not
         * NUL-terminated. Make sure there's a guard in place. */
        domain[sizeof(domain) - 1] = 0;
        if (gethostname(domain, MAXHOSTNAMELEN) == 0 && (cp = index(domain, '.')))
            (void) strcpy(domain, cp + 1);
        else
            domain[0] = 0;
    }
    cp = 0;
    if (!nflag && in.s_addr != INADDR_ANY) {
        hp = gethostbyaddr((char *) &in, sizeof(in), AF_INET);
        if (hp) {
            if ((cp = index(hp->h_name, '.')) && !strcmp(cp + 1, domain))
                *cp = 0;
            cp = hp->h_name;
        }
    }
    if (cp) {
        (void) strncpy(line, cp, sizeof(line));
        line[sizeof(line) - 1] = 0;
    } else {

        in.s_addr = ntohl(in.s_addr);
#define C(x)    ((x) & 0xff)
#ifndef __linux__
        sprintf(line, "%lu.%lu.%lu.%lu", C(in.s_addr >> 24),
#else                           /* __linux__ */
        sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),        /* Why no lu??? */
#endif                          /* __linux__ */
                C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
    }
    return (line);
}

#ifndef __linux__
void
halt()
{
    haltf++;

    NOERR(signal(SIGINT, 0), "signal SIGINT,0");

}
#endif                          /* __linux__ */

/*
 *  Lookup owner of the net in DNS.
 */

char           *
lookup_owner(in)
    struct in_addr  in;
{
    char            dns_query[100];
    char           *owner, *dot_ptr;
    unsigned char  *addr_ptr;

    addr_ptr = (unsigned char *) (&in.s_addr);

    /* Try /24 */
    sprintf(dns_query, "%d.%d.%d.in-addr.arpa", addr_ptr[2], addr_ptr[1],
            addr_ptr[0]);
    if (!(owner = doresolve(dns_query))) {
        /* Failed, try /16 */
        sprintf(dns_query, "%d.%d.in-addr.arpa", addr_ptr[1], addr_ptr[0]);
        if (!(owner = doresolve(dns_query))) {
            /* Failed.  If eligible try /8 */
            if (addr_ptr[0] < 128) {
                sprintf(dns_query, "%d.in-addr.arpa", addr_ptr[0]);
                owner = doresolve(dns_query);
            }                   /* tried /8 for A's */
        }                       /* tried /16 */
    }

    /* tried /24 */
    /* reformat slightly */
    if (owner == NULL) {
        owner = NO_SOA_RECORD;
    } else {
        dot_ptr = (char *) strchr(owner, (int) '.');
        if (dot_ptr != NULL)
            *dot_ptr = '@';

        if (strlen(owner) > 0) {
            dot_ptr = owner + strlen(owner) - 1;
            while (*dot_ptr == ' ' || *dot_ptr == '.') {
                *dot_ptr = 0;
                dot_ptr--;
            }
        }
    }

    return (owner);

}

/*
 *  Lookup origin of the net in radb.
 */

char           *
lookup_as(in)
    struct in_addr  in;
{
    static char     query[100];
    static unsigned char *addr_ptr;
    static char    *sp;
    char           *get_origin();

    addr_ptr = (unsigned char *) (&in.s_addr);

#ifdef FORCE_NATURAL_MASK
    if (addr_ptr[0] >= 192) {
        sprintf(query, "%d.%d.%d.0", addr_ptr[0], addr_ptr[1], addr_ptr[2]);
    } else if (addr_ptr[0] >= 128) {
        sprintf(query, "%d.%d.0.0", addr_ptr[0], addr_ptr[1]);
    } else {
        sprintf(query, "%d.0.0.0", addr_ptr[0]);
    }
#else
    sprintf(query, "%d.%d.%d.%d", addr_ptr[0], addr_ptr[1], addr_ptr[2],
            addr_ptr[3]);
#endif                          /* FORCE_NATURAL_MASK */

    sp = get_origin(query);
/*  printf("as_lookup: get_origin returned %d\n",sp); */
    if (0 == sp) {
        return ((char *) &nullstring);
    } else {
        return (sp);
    }

}

/*
 * get_origin   - Return origin (ASnnnn) given a network designation
 *
 * char *get_origin(char *net_designation)
 *
 * Returns:     0 - Error occurred, unable to get origin
 *              !0  Pointer to origin string
 *
 * Define STANDALONE to use this as a client.  Also define EXAMPLE_NET for
 * an example for the truly clueless...
 *
 *      20-May-1995     Ehud Gavron     gavron@aces.com
 *      28-Apr-2000                     Return error if no string
 */

/* The following are used to determine which service at which host to
   connect to.  A getenv() of the following elements occurs at run-time,
   which may override these values. */

#ifndef RA_SERVER
#define RA_SERVER "whois.ra.net"
#endif

#ifndef RA_SERVICE
#define RA_SERVICE "whois"
#endif

/* The following determines what fields will be returned for the -A value
   (/AS_LOOKUP for VMS).  This is the "origin" of the route entry in the
   RADB. */

#define DATA_DELIMITER "origin:"

/* Since now the RADB has multiple route objects, we will list only the
   origin of the most specific one.  To do so we actually have to parse
   the route lines and look for the most specific route.  To do so we
   parse:
                net.net.net.net/prefix

   and use the most specific (largest) prefix.  The following determine
   how we get this.  */

#define ROUTE_DELIMITER "route:"
#define PREFIX_DELIMITER "/"

#ifdef STANDALONE
#ifdef __vms
#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include <stdio.h>
#include "multinet_root:[multinet.include]netdb.h"
#define perror socket_perror
#define write socket_write
#define read socket_read
#define close socket_close
#else                           /* not VMS */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#endif                          /* VMS */
#endif                          /* STANDALONE */

#ifndef boolean
#define boolean int
#endif

#ifndef TRUE
#define TRUE (1==1)
#endif

#ifndef FALSE
#define FALSE (!(TRUE))
#endif
#define MAXREPLYLEN 8192

#ifdef STANDALONE
main(argc, argv)
    int             argc;
    char          **argv;
{
    char            buffer[100];
    char           *p;
    char           *get_origin();
#ifdef EXAMPLE_NET
    strcpy(buffer, "192.195.240.0/24");
#else                           /* 6.3.0 null terminate buffer in standalone: */
    strncpy(buffer, argv[1], sizeof(buffer));
    buffer[sizeof(buffer) - 1] = '\0';
#endif                          /* EXAMPLE_NET */
    p = get_origin(buffer);
    if (p) {
        strncpy(buffer, p, sizeof(buffer));
        buffer[sizeof(buffer) - 1] = '\0';
        Fprintf(stdout, "origin is: %s\n", buffer);
    } else {
        Fprintf(stderr, "unable to get origin.\n");
    }
}
#endif                          /* STANDALONE */

char           *
get_origin(net)
    char           *net;
{
    char           *i, *j, *k;
    char            tmp[100], tmp2[100], tmp3[100];     /* store string delimiters */
    char            tmp4[100];  /* here's where we store the AS */
    static char     origin[100];        /* the returned route origin */
    char           *rp;         /* pointer to route: line */
    char           *pp;         /* pointer to /prefix part of route */
    int             prefix;     /* prefix off this line (decimal) */
    int             best_prefix;        /* best prefix thus far */
    int             s, n, count;
    char            buf[256];
    boolean         done;
    static char     reply[MAXREPLYLEN];
    struct sockaddr_in sin;
    struct hostent *hp;
    struct servent *sp;
    char           *getenv();

    /* 
     * Get the IP address of the host which serves the routing arbiter
     * database.  We use RA_SERVER.  On the offchance that someone wants
     * to query another database, we check for the environment variable
     * RA_SERVER to have been set.
     */
    if ((i = getenv("RA_SERVER")) == 0) {
        strcpy(tmp, RA_SERVER);
    } else {
        strncpy(tmp, i, sizeof(tmp));
        tmp[(sizeof(tmp)) - 1] = '\0';  /* strncpy may not null term */
    }

    hp = gethostbyname(tmp);
    if (hp == NULL) {
        Fprintf(stderr, "get_origin: localhost unknown%s", terminator);
        return (0);
    }

    /* 
     *  Create an IP-family socket on which to make the connection
     */

    s = socket(hp->h_addrtype, SOCK_STREAM, 0);
    if (s < 0) {
        perror("get_origin: socket");
        return (0);
    }

    /* 
     *  Get the TCP port number of the "whois" server.
     *  Again if this needs to be updated, the environment variable
     *  RA_SERVICE should be set.
     */
    if ((i = getenv("RA_SERVICE")) == 0) {
        strcpy(tmp, RA_SERVICE);
    } else {
        strncpy(tmp, i, sizeof(tmp));
        tmp[(sizeof(tmp)) - 1] = '\0';  /* strncpy may not null term */
    }

    sp = getservbyname(tmp, "tcp");
    if (sp == NULL) {
        Fprintf(stderr, "get_origin: getservbyname: unknown service%s", terminator);
        return (0);
    }

    /* 
     *  Create a "sockaddr_in" structure which describes the remote
     *  IP address we want to connect to (from gethostbyname()) and
     *  the remote TCP port number (from getservbyname()).
     */

    sin.sin_family = hp->h_addrtype;
    bcopy(hp->h_addr, (caddr_t) & sin.sin_addr, hp->h_length);
    sin.sin_port = sp->s_port;

    /* 
     *  Connect to that address...
     */

    if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
        perror("get_origin: connect");
        return (0);
    }

    /* 
     * Now send the request out to the server...
     */

    done = FALSE;
    snprintf(buf, sizeof(buf), "%s\r\n", net);
    write(s, buf, strlen(buf));

    /* 
     * Now get the entire answer in one long buffer...
     */
    count = 0;
    while ((n = read(s, buf, sizeof(buf))) > 0) {
        if (count + n >= MAXREPLYLEN)
            break;
        strcpy((char *) &reply[count], (char *) buf);
        count += n;
    }

    if (n < 0) {
        perror("get_origin: read");
        return (0);
    }

    reply[count] = '\0';        /* Terminate it - thanks Joey! */

    /* 
     * sometimes there's no answer
     */
    if (strncmp(reply, "%%  No entries found for the selected source(s).",
                strlen("%%  No entries found for the selected source(s).")) == 0) {
        return "NONE";
    }

    /* 
     * So now we have a large string, somewhere in which we can
     * find  origin:*AS%%%%%%<lf>.  We parse this into AS%%%%%.
     */

    if ((i = getenv("DATA_DELIMITER")) == 0) {
        strcpy(tmp, DATA_DELIMITER);
    } else {
        strncpy(tmp, i, sizeof(tmp));
        tmp[(sizeof(tmp)) - 1] = '\0';  /* strncpy may not null term */
    }

    /* TMP2 will have the route delimiter... */

    if ((i = getenv("ROUTE_DELIMITER")) == 0) {
        strcpy(tmp2, ROUTE_DELIMITER);
    } else {
        strncpy(tmp2, i, sizeof(tmp2));
        tmp2[(sizeof(tmp2)) - 1] = '\0';        /* strncpy may not null term */
    }

    if ((i = getenv("PREFIX_DELIMITER")) == 0) {
        strcpy(tmp3, PREFIX_DELIMITER);
    } else {
        strncpy(tmp3, i, sizeof(tmp3));
        tmp3[(sizeof(tmp3)) - 1] = '\0';        /* strncpy may not null term */
    }

/*
 * The next while statement was put in because of SPRINTLINK's ingeneous
 * Reasonable Default announcement project.  They registered nets in the
 * RADB of the ilk of 0.0.0.0/1, 128.0.0.0/1, 192..../2, etc... just so
 * that ANS wouldn't be such a pain in the butt.
 *
 * For us this means instead of taking the first origin...we take the best...
 */

/*
 * Initialize it so far as we've seen no prefixes, and are still looking
 * for route entries...
 */
    best_prefix = 0;            /* 0 bits is not very specific */
    done = FALSE;               /* not done finding route: entries */

    rp = (char *) reply;        /* initialize main pointer to buffer */
    origin[0] = '\0';           /* initialize returned string */
    reply[MAXREPLYLEN - 1] = '\0';

    rp = (char *) strstr(rp, tmp2);     /* Find route: in the string */
    while (rp != 0) {           /* If there is such a thing... */
        /* find it again later */
        pp = (char *) strstr(rp, tmp3); /* Find / in the route entry */
        if (pp == 0) {          /* No prefix... */
            prefix = 0;         /* So we bias it out of here */
        } else {
            prefix = atoi(pp + 1);      /* convert to decimal */
        }

        if (prefix >= best_prefix) {    /* it's equal to or better */
            i = (char *) strstr(pp, tmp);       /* find origin: delimiter */
            if (i != 0) {       /* it's nice if there is one */
                i += strlen(DATA_DELIMITER);    /* skip delimiter... */
                i++;            /* and the colon... */
                while ((*i == ' ' || *i == 9)
                       && (i - reply) < MAXREPLYLEN)
                    i++;        /* skip spaces */
                /* i now points to start of origin AS string */
                j = i;          /* terminate... */
                while (*j >= '0' && (j - reply) < MAXREPLYLEN)
                    j++;
                if (prefix > best_prefix) {
                    strcpy(origin, "/");        /* put a slash in */
                    best_prefix = prefix;       /* update best */
                } else {
                    strcat(origin, "/");        /* put a mutiple as separator */
                }
                if (j >= i + sizeof(tmp4))
                    j = i + sizeof(tmp4) - 1;
                strncpy(tmp4, i, (j - i));      /* copy new origin */
                tmp4[j - i] = '\0';     /* null terminate it */
                if (!(strstr(origin, tmp4))) {  /* if it's not a dup */
                    strncat(origin, i, (j - i));        /* stick it in */
                } else {
                    if (prefix == best_prefix)  /* Otherwise * * remove * slash */
                        origin[strlen(origin) - 1] = '\0';
                }               /* end if not a dup */
            }                   /* end if origin found */
        }                       /* endif prefix > best_prefix */
        rp = (char *) strstr(rp + 1, tmp2);     /* Find route: in the string */
    }                           /* end while */
    /* 
     * Go home...
     */
    close(s);
    if (best_prefix != 0) {     /* did we get anything? */
        return ((char *) &origin[1]);   /* strip off leading slash */
    } else {
        return (0);
    }
}

short
getshort(ptr)
    char           *ptr;
{
    union {
        short           retval;
        char            ch[2];
    } foo;

    foo.ch[0] = (*ptr & 0xff);
    foo.ch[1] = (*(ptr + 1) & 0xff);

    return (foo.retval);
}

char           *
doresolve(name)
    char           *name;
{
    int             query = QUERY;
    int             qtype = T_SOA;
    int             qclass = C_IN;
    unsigned char   buf[256];
    char           *ans;
    int             blen, alen, got;
    int             anssiz, i;
    short           shrt;
    HEADER         *h;
    char           *contact_ptr;
    int             ptr;

    anssiz = 512;
    ans = (char *) malloc(anssiz);
    if (!ans) {
        return (0);
    }

    blen =
        res_mkquery(query, name, qclass, qtype, NULL, 0, NULL, (u_char *) buf,
                    sizeof(buf));
    if (blen < 0) {
        return (0);
    }

    alen = res_send((unsigned char *) buf, blen, (unsigned char *) ans, anssiz);
    if (alen == -1) {
        return (0);
    }

    if (alen < 12) {
        return (0);
    }

    h = (HEADER *) ans;

    h->id = ntohs(h->id);
    h->qdcount = ntohs(h->qdcount);
    h->ancount = ntohs(h->ancount);
    h->nscount = ntohs(h->nscount);
    h->arcount = ntohs(h->arcount);

    if (h->ancount == 0)
        return (0);

    ptr = 12;                   /* point at first question field */
    for (i = 0; i < (int) h->qdcount && ptr < alen; i++) {
        ptr = doqd((unsigned char *) ans, ptr);
    }

    for (i = 0; i < (int) h->ancount && ptr < alen; i++) {
        ptr = dorr((unsigned char *) ans, ptr, &contact_ptr);
    }

    return (contact_ptr);
}

doqd(ans, off)
    unsigned char  *ans;
    int             off;
{
    char            name[256];

    name[0] = 0;
    off = doname(ans, off, name);
    off = dotype(ans, off);
    off = doclass(ans, off);
    return (off);
}

dorr(ans, off, contact_ptr)
    unsigned char  *ans;
    int             off;
    char          **contact_ptr;
{
    int             class, typ;
    char            name[256];

    name[0] = 0;
    off = doname(ans, off, name);
    typ = ntohs(getshort((char *) ans + off));
    off = dotype(ans, off);
    class = ntohs(getshort((char *) ans + off));
    off = doclass(ans, off);
    off = dottl(ans, off);
    off = dordata(ans, off, class, typ, name, contact_ptr);
    return (off);
}

doname(ans, off, name)
    int             off;
    unsigned char  *ans;
    char           *name;
{
    int             newoff, i;
    char            tmp[50];

    /* redirect? */
    if ((*(ans + off) & 0xc0) == 0xc0) {
        newoff = getshort((char *) ans + off);
        newoff = 0x3fff & ntohs(newoff);
        doname(ans, newoff, name);
        return (off + 2);
    }
    /* end of string */
    if (*(ans + off) == 0) {
        strcat(name, " ");
        return (off + 1);
    }
    /* token */
    for (i = 1; i <= (int) *(ans + off); i++)
        tmp[i - 1] = ans[off + i];
    tmp[i - 1] = '.';
    tmp[i] = 0;
    strcat(name, tmp);
    return (doname(ans, off + 1 + (*(ans + off)), name));
}

dotype(ans, off)
    int             off;
    unsigned char  *ans;
{
    return (off + 2);
}

doclass(ans, off)
    int             off;
    unsigned char  *ans;
{
    return (off + 2);
}

dottl(ans, off)
    int             off;
    unsigned char  *ans;
{
    return (off + 4);
}

dordata(ans, off, class, typ, fname, contact_ptr)
    unsigned char  *ans;
    int             off, class, typ;
    char           *fname;
    char          **contact_ptr;
{
    int             len = ntohs(getshort((char *) ans + off));
    int             retval = off + len + 2;
    int             i, j;
    char            name[256];

    off += 2;
    switch (typ) {
    case T_SOA:
        name[0] = 0;
        off = doname(ans, off, name);
        name[0] = 0;
        off = doname(ans, off, name);
        *contact_ptr = name;
        return (0);
    default:
        return (0);
    }
}

/*
        The VMS command line interface, DCL, uppercases all unquoted input.
        By default this program can be executed via
                $ mc location:traceroute options

        However, since this program needs case sensitivity for the various
        options to work, it is defined as an alias ``symbol'' with open
        quotes:
                $ traceroute == "location:traceroute """

        Unfortunately, this has the side effects of making argc=2, where
        argv[0] is the image location and name, and argv[1] is the entire
        option line.  Thus for VMS we need to break the options up...

        Note that if the symbol VMS_CLD is defined, then this is not used
        at all, but rather the VMS Command Language Definition facility
        is used.

*/

void
AbortIfNull(ThePointer)
    char           *ThePointer;
{
    if (ThePointer == NULL) {
        Fprintf(stderr, "bad flags on that switch!%s", terminator);
        exit(666);
    };
}

#ifdef __vms


fixargs(a, b, c)
    int            *a;          /* argc */
    char          **b;          /* argv */
    char          **c;          /* av */
{
    char           *ptr, *space;
    /* Set the image name */
    c[0] = b[0];

    /* Initialize pointers */
    *a = 1;
    ptr = b[1];
    if (*ptr == ' ')
        ptr++;                  /* eliminate first space */

    /* Delineate all strings ending with space */
    while ((space = strchr(ptr, ' ')) != 0) {
        *space = '\0';
        c[(*a)++] = ptr;
        ptr = space + 1;
    }

    /* Transfer last one - ending with null */
    c[*a] = ptr;

    /* Update argc */
    (*a)++;
}

#endif                          /* vms */

