Make traceroute(8) faster by sending probes and doing DNS async.
authorflorian <florian@openbsd.org>
Fri, 3 Sep 2021 09:13:00 +0000 (09:13 +0000)
committerflorian <florian@openbsd.org>
Fri, 3 Sep 2021 09:13:00 +0000 (09:13 +0000)
Traditional traceroute would send one probe and then wait for up to 5
seconds for a reply and then send the next probe. On a lossy link that
eventually ends in a black hole this would take about 15 minutes and
people would hit control-c in anger.

This rewrites the traceroute engine to use libevent and asr's async
DNS interface. Probes are now send every 30ms or as soon as we get an
answer back. With that we got the 15 minute worse case down to about
10 seconds.

A minor adjustment that is possible with this is to delay printing a
line until we get to a line with answers. This has two effects:

1) If there are intermediate hops that don't answer, output pauses for
a bit so we keep the visual cue of "something might be wrong here".
2) If there is a black hole at the end, we don't print out many "* * *"
lines and thus scrolling the interesting bits out of the terminal.
We collapse those lines and just print
    64 * * *
at the end.

Unfortunately the -c option to send udp probes to a fixed port had to
go for now. But we should be able to add it back.

"Once you have seen the new one you can't go back to the old one" &
enthusiastic OK deraadt@
OK sthen@
"I am very distressed that florian went to bed without committing it"
beck@

usr.sbin/traceroute/Makefile
usr.sbin/traceroute/traceroute.8
usr.sbin/traceroute/traceroute.c
usr.sbin/traceroute/traceroute.h
usr.sbin/traceroute/worker.c

index c9797ab..bc6fbb8 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.10 2016/09/03 22:00:06 benno Exp $
+#      $OpenBSD: Makefile,v 1.11 2021/09/03 09:13:00 florian Exp $
 
 PROG=  traceroute
 
@@ -8,6 +8,8 @@ CFLAGS+= -Wall -I${.CURDIR}
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+LDADD= -levent
+DPADD= ${LIBEVENT}
 
 MAN=   traceroute.8
 
index cc211c1..d3f5afa 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: traceroute.8,v 1.71 2021/08/29 06:50:29 jmc Exp $
+.\"    $OpenBSD: traceroute.8,v 1.72 2021/09/03 09:13:00 florian Exp $
 .\"    $NetBSD: traceroute.8,v 1.6 1995/10/12 03:05:50 mycroft Exp $
 .\"
 .\" Copyright (c) 1990, 1991, 1993
@@ -33,7 +33,7 @@
 .\"
 .\"    @(#)traceroute.8        8.1 (Berkeley) 6/6/93
 .\"
-.Dd $Mdocdate: August 29 2021 $
+.Dd $Mdocdate: September 3 2021 $
 .Dt TRACEROUTE 8
 .Os
 .Sh NAME
@@ -42,7 +42,7 @@
 .Nd print the route packets take to network host
 .Sh SYNOPSIS
 .Nm traceroute\ \&
-.Op Fl AcDdIlnSvx
+.Op Fl ADdIlnSvx
 .Op Fl f Ar first_ttl
 .Op Fl g Ar gateway_addr
 .Op Fl m Ar max_ttl
@@ -56,7 +56,7 @@
 .Ar host
 .Op Ar datalen
 .Nm traceroute6
-.Op Fl AcDdIlnSv
+.Op Fl ADdIlnSv
 .Op Fl f Ar first_hop
 .Op Fl m Ar max_hop
 .Op Fl p Ar port
@@ -91,11 +91,6 @@ The options are as follows:
 Look up the AS number for each hop address.
 Uses the DNS service described at
 .Lk https://www.team-cymru.com/IP-ASN-mapping.html#dns
-.It Fl c
-Do not increment the destination port number in successive UDP packets.
-Rather, all UDP packets will have the same destination port, as set via the
-.Fl p
-flag (or 33434 if none is specified).
 .It Fl D
 Dump the packet data to standard error before transmitting it.
 .It Fl d
index 21ee76d..e3bd866 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: traceroute.c,v 1.167 2021/08/31 18:12:47 florian Exp $        */
+/*     $OpenBSD: traceroute.c,v 1.168 2021/09/03 09:13:00 florian Exp $        */
 /*     $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $  */
 
 /*
 
 #include <err.h>
 #include <errno.h>
+#include <event.h>
 #include <limits.h>
 #include <netdb.h>
 #include <pwd.h>
@@ -271,33 +272,44 @@ int       sndsock;        /* send (udp) socket file descriptor */
 int    rcvhlim;
 struct in6_pktinfo *rcvpktinfo;
 
-       int     datalen;        /* How much data */
+int    datalen;        /* How much data */
 
 char   *hostname;
 
 u_int16_t      srcport;
 
-void   usage(int);
+void   usage(void);
 
 #define        TRACEROUTE_USER "_traceroute"
 
+void   sock_read(int, short, void *);
+void   send_timer(int, short, void *);
+
+struct tr_conf         *conf;  /* configuration defaults */
+struct tr_result       *tr_results;
+struct sockaddr_in      from4, to4;
+struct sockaddr_in6     from6, to6;
+struct sockaddr                *from, *to;
+struct msghdr           rcvmhdr;
+struct event            timer_ev;
+int                     v6flag;
+int                    *waiting_ttls;
+int                     last_tos = 0;
+
 int
 main(int argc, char *argv[])
 {
        int     mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL };
        char    hbuf[NI_MAXHOST];
 
-       struct tr_conf          *conf;  /* configuration defaults */
-       struct sockaddr_in       from4, to4;
-       struct sockaddr_in6      from6, to6;
-       struct sockaddr         *from, *to;
        struct addrinfo          hints, *res;
        struct hostent          *hp;
        struct ip               *ip = NULL;
        struct iovec             rcviov[2];
-       struct msghdr            rcvmhdr;
        static u_char           *rcvcmsgbuf;
        struct passwd           *pw;
+       struct event             sock_ev;
+       struct timeval          tv = {0, 0};
 
        long             l;
        socklen_t        len;
@@ -305,25 +317,18 @@ main(int argc, char *argv[])
 
        int              ch;
        int              on = 1;
-       int              seq = 0;
        int              error;
-       int              curwaittime;
        int              headerlen;     /* How long packet's header is */
        int              i;
-       int              last_tos = 0;
        int              packetlen;
-       int              probe;
        int              rcvcmsglen;
        int              rcvsock4, rcvsock6;
        int              sndsock4, sndsock6;
        u_int32_t        tmprnd;
        int              v4sock_errno, v6sock_errno;
-       int              v6flag = 0;
-       int              xflag = 0;     /* show ICMP extension header */
 
        char            *dest;
        const char      *errstr;
-       u_int8_t         ttl;
 
        uid_t            ouid, uid;
        gid_t            gid;
@@ -337,11 +342,11 @@ main(int argc, char *argv[])
        if ((conf = calloc(1, sizeof(*conf))) == NULL)
                err(1,NULL);
 
-       conf->incflag = 1;
        conf->first_ttl = 1;
        conf->proto = IPPROTO_UDP;
        conf->max_ttl = IPDEFTTL;
        conf->nprobes = 3;
+       conf->expected_responses = 2; /* icmp + DNS */
 
        /* start udp dest port # for probe packets */
        conf->port = 32768+666;
@@ -421,14 +426,12 @@ main(int argc, char *argv[])
                err(1, "sysctl");
        conf->max_ttl = i;
 
-       while ((ch = getopt(argc, argv, v6flag ? "AcDdf:Ilm:np:q:Ss:t:w:vV:" :
-           "AcDdf:g:Ilm:nP:p:q:Ss:t:V:vw:x")) != -1)
+       while ((ch = getopt(argc, argv, v6flag ? "ADdf:Ilm:np:q:Ss:t:w:vV:" :
+           "ADdf:g:Ilm:nP:p:q:Ss:t:V:vw:x")) != -1)
                switch (ch) {
                case 'A':
                        conf->Aflag = 1;
-                       break;
-               case 'c':
-                       conf->incflag = 0;
+                       conf->expected_responses++;
                        break;
                case 'd':
                        conf->dflag = 1;
@@ -476,6 +479,7 @@ main(int argc, char *argv[])
                        break;
                case 'n':
                        conf->nflag = 1;
+                       conf->expected_responses--;
                        break;
                case 'p':
                        conf->port = strtonum(optarg, 1, 65535, &errstr);
@@ -500,7 +504,7 @@ main(int argc, char *argv[])
                        }
                        break;
                case 'q':
-                       conf->nprobes = strtonum(optarg, 1, INT_MAX, &errstr);
+                       conf->nprobes = strtonum(optarg, 1, 1024, &errstr);
                        if (errstr)
                                errx(1, "nprobes must be >0.");
                        break;
@@ -561,10 +565,10 @@ main(int argc, char *argv[])
                        conf->waittime *= 1000;
                        break;
                case 'x':
-                       xflag = 1;
+                       conf->xflag = 1;
                        break;
                default:
-                       usage(v6flag);
+                       usage();
                }
 
        if (ouid == 0 && (setgroups(1, &gid) ||
@@ -576,7 +580,16 @@ main(int argc, char *argv[])
        argv += optind;
 
        if (argc < 1 || argc > 2)
-               usage(v6flag);
+               usage();
+
+       tr_results = calloc(sizeof(struct tr_result), conf->max_ttl *
+           conf->nprobes);
+       if (tr_results == NULL)
+               err(1, NULL);
+
+       waiting_ttls = calloc(sizeof(int), conf->max_ttl);
+       for (i = 0; i < conf->max_ttl; i++)
+               waiting_ttls[i] = conf->nprobes * conf->expected_responses;
 
        setvbuf(stdout, NULL, _IOLBF, 0);
 
@@ -862,112 +875,26 @@ main(int argc, char *argv[])
        if (conf->first_ttl > 1)
                printf("Skipping %u intermediate hops\n", conf->first_ttl - 1);
 
-       for (ttl = conf->first_ttl; ttl && ttl <= conf->max_ttl; ++ttl) {
-               int got_there = 0, unreachable = 0, timeout = 0, loss;
-               in_addr_t lastaddr = 0;
-               struct in6_addr lastaddr6;
-
-               printf("%2u ", ttl);
-               memset(&lastaddr6, 0, sizeof(lastaddr6));
-               for (probe = 0, loss = 0; probe < conf->nprobes; ++probe) {
-                       int cc;
-                       struct timeval t1, t2;
-
-                       gettime(&t1);
-                       send_probe(conf, ++seq, ttl, conf->incflag, to);
-                       curwaittime = conf->waittime;
-                       while ((cc = wait_for_reply(rcvsock, &rcvmhdr,
-                           curwaittime))) {
-                               gettime(&t2);
-                               i = packet_ok(conf, to->sa_family, &rcvmhdr,
-                                   cc, seq, conf->incflag);
-                               /* Skip wrong packet */
-                               if (i == 0) {
-                                       curwaittime = conf->waittime -
-                                           ((t2.tv_sec - t1.tv_sec) * 1000 +
-                                           (t2.tv_usec - t1.tv_usec) / 1000);
-                                       if (curwaittime < 0)
-                                               curwaittime = 0;
-                                       continue;
-                               }
-                               if (to->sa_family == AF_INET) {
-                                       ip = (struct ip *)packet;
-                                       if (from4.sin_addr.s_addr != lastaddr) {
-                                               print(conf, from,
-                                                   cc - (ip->ip_hl << 2),
-                                                   inet_ntop(AF_INET,
-                                                   &ip->ip_dst, hbuf,
-                                                   sizeof(hbuf)));
-                                               lastaddr =
-                                                   from4.sin_addr.s_addr;
-                                       }
-                               } else if (to->sa_family == AF_INET6) {
-                                       if (!IN6_ARE_ADDR_EQUAL(
-                                           &from6.sin6_addr, &lastaddr6)) {
-                                               print(conf, from, cc,
-                                                   rcvpktinfo ?
-                                                   inet_ntop( AF_INET6,
-                                                   &rcvpktinfo->ipi6_addr,
-                                                   hbuf, sizeof(hbuf)) : "?");
-                                               lastaddr6 = from6.sin6_addr;
-                                       }
-                               } else
-                                       errx(1, "unsupported AF: %d",
-                                           to->sa_family);
-
-                               printf("  %g ms", deltaT(&t1, &t2));
-                               if (conf->ttl_flag)
-                                       printf(" (%u)", v6flag ? rcvhlim :
-                                           ip->ip_ttl);
-                               if (to->sa_family == AF_INET) {
-                                       if (i == -2) {
-                                               if (ip->ip_ttl <= 1)
-                                                       printf(" !");
-                                               ++got_there;
-                                               break;
-                                       }
-
-                                       if (conf->tflag)
-                                               check_tos(ip, &last_tos);
-                               }
+       event_init();
 
-                               /* time exceeded in transit */
-                               if (i == -1)
-                                       break;
-                               icmp_code(to->sa_family, i - 1, &got_there,
-                                   &unreachable);
-                               break;
-                       }
-                       if (cc == 0) {
-                               printf(" *");
-                               timeout++;
-                               loss++;
-                       } else if (cc && probe == conf->nprobes - 1 &&
-                           (xflag || conf->verbose))
-                               print_exthdr(packet, cc);
-                       (void) fflush(stdout);
-               }
-               if (conf->sump)
-                       printf(" (%d%% loss)", (loss * 100) / conf->nprobes);
-               putchar('\n');
-               if (got_there ||
-                   (unreachable && (unreachable + timeout) >= conf->nprobes))
-                       break;
-       }
-       exit(0);
+       event_set(&sock_ev, rcvsock, EV_READ | EV_PERSIST, sock_read, NULL);
+       event_add(&sock_ev, NULL);
+       evtimer_set(&timer_ev, send_timer, &timer_ev);
+       evtimer_add(&timer_ev, &tv);
+       event_dispatch();
 }
 
 void
-usage(int v6flag)
+usage(void)
 {
        if (v6flag) {
                fprintf(stderr, "usage: %s "
-                   "[-AcDdIlnSv] [-f first_hop] [-m max_hop] [-p port]\n"
+                   "[-ADdIlnSv] [-f first_hop] [-m max_hop] [-p port]\n"
                    "\t[-q nqueries] [-s sourceaddr] [-t toskeyword] [-V rtable] "
                    "[-w waittime]\n\thost [datalen]\n", __progname);
        } else {
                fprintf(stderr,
-                   "usage: %s [-AcDdIlnSvx] [-f first_ttl] [-g gateway_addr] "
+                   "usage: %s [-ADdIlnSvx] [-f first_ttl] [-g gateway_addr] "
                    "[-m max_ttl]\n"
                    "\t[-P proto] [-p port] [-q nqueries] [-s sourceaddr]\n"
                    "\t[-t toskeyword] "
@@ -976,3 +903,106 @@ usage(int v6flag)
        }
        exit(1);
 }
+
+void
+sock_read(int fd, short events, void *arg)
+{
+       struct ip       *ip;
+       struct timeval   t2, tv = {0, 0};
+       int              pkg_ok, cc, recv_seq, recv_seq_row;
+       char             hbuf[NI_MAXHOST];
+
+       cc = recvmsg(rcvsock, &rcvmhdr, 0);
+
+       if (cc == 0)
+               return;
+
+       evtimer_add(&timer_ev, &tv);
+
+       gettime(&t2);
+
+       pkg_ok = packet_ok(conf, to->sa_family, &rcvmhdr, cc, &recv_seq);
+
+       /* Skip wrong packet */
+       if (pkg_ok == 0)
+               goto out;
+
+       /* skip corrupt sequence number */
+       if (recv_seq < 0 || recv_seq >= conf->max_ttl * conf->nprobes)
+               goto out;
+
+       recv_seq_row = recv_seq / conf->nprobes;
+
+       /* skipping dup */
+       if (tr_results[recv_seq].dup++)
+               goto out;
+
+       switch (to->sa_family) {
+       case AF_INET:
+               ip = (struct ip *)packet;
+
+               print(conf, from, cc - (ip->ip_hl << 2), inet_ntop(AF_INET,
+                   &ip->ip_dst, hbuf, sizeof(hbuf)), &tr_results[recv_seq]);
+               break;
+       case AF_INET6:
+               print(conf, from, cc, rcvpktinfo ? inet_ntop(AF_INET6,
+                   &rcvpktinfo->ipi6_addr, hbuf, sizeof(hbuf)) : "?",
+                   &tr_results[recv_seq]);
+               break;
+       default:
+               errx(1, "unsupported AF: %d", to->sa_family);
+       }
+
+       tr_results[recv_seq].t2 = t2;
+       tr_results[recv_seq].resp_ttl = v6flag ? rcvhlim : ip->ip_ttl;
+
+       waiting_ttls[recv_seq_row]--;
+
+       if (pkg_ok == -2) {
+               if ((v6flag && rcvhlim <= 1) ||
+                   (!v6flag && ip->ip_ttl <=1))
+                       snprintf(tr_results[recv_seq].icmp_code,
+                           sizeof(tr_results[recv_seq].icmp_code), "%s", " !");
+               tr_results[recv_seq].got_there++;
+       } else {
+               if (to->sa_family == AF_INET && conf->tflag)
+                       check_tos(ip, &last_tos, &tr_results[recv_seq]);
+               if (pkg_ok != -1) {
+                       icmp_code(to->sa_family, pkg_ok - 1,
+                           &tr_results[recv_seq].got_there,
+                           &tr_results[recv_seq].unreachable,
+                           &tr_results[recv_seq]);
+               }
+       }
+
+       if (cc && ((recv_seq + 1) % conf->nprobes) == 0 &&
+           (conf->xflag || conf->verbose))
+               print_exthdr(packet, cc, &tr_results[recv_seq]);
+ out:
+       catchup_result_rows(tr_results, conf);
+}
+
+void
+send_timer(int fd, short events, void *arg)
+{
+       static int       seq;
+       struct timeval   tv = {0, 30000}, t1;
+       struct event    *ev = arg;
+       int              ttl;
+
+       evtimer_add(ev, &tv);
+
+       ttl = conf->first_ttl + seq / conf->nprobes;
+       if (ttl <= conf->max_ttl) {
+               gettime(&t1);
+               tr_results[seq].seq = seq;
+               tr_results[seq].row = seq / conf->nprobes;
+               tr_results[seq].ttl = ttl;
+               tr_results[seq].t1 = t1;
+               send_probe(conf, seq, ttl, to);
+               seq++;
+       }
+
+       catchup_result_rows(tr_results, conf);
+
+}
index 1704068..c148a6c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: traceroute.h,v 1.6 2021/08/31 18:12:47 florian Exp $  */
+/*     $OpenBSD: traceroute.h,v 1.7 2021/09/03 09:13:00 florian Exp $  */
 /*     $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $  */
 
 /*
@@ -67,6 +67,8 @@
 #include <netinet/ip_var.h>
 #include <netmpls/mpls.h>
 
+#define ICMP_CODE 0;
+
 #define DUMMY_PORT 10010
 
 #define MAX_LSRR               ((MAX_IPOPTLEN - 4) / 4)
@@ -86,7 +88,6 @@ struct packetdata {
 } __packed;
 
 struct tr_conf {
-       int              incflag;       /* Do not inc the dest. port num */
        int              first_ttl;     /* Set the first TTL or hop limit */
        u_char           proto;         /* IP payload protocol to use */
        u_int8_t         max_ttl;       /* Set the maximum TTL / hop limit */
@@ -106,46 +107,55 @@ struct tr_conf {
        int              sump;
        int              tos;
        int              tflag;         /* tos value was set */
+       int              xflag;         /* show ICMP extension header */
        int              verbose;
        u_int            rtableid;      /* Set the routing table */
        u_short          ident;
+       int              expected_responses;
+};
+
+struct tr_result {
+       int              seq;
+       int              row;
+       int              dup;
+       int              timeout;
+       uint8_t          ttl;
+       uint8_t          resp_ttl;
+       char             hbuf[NI_MAXHOST];
+       char             inetname[NI_MAXHOST];
+       char            *asn;
+       char            *exthdr;
+       char             to[NI_MAXHOST];
+       int              cc;
+       struct timeval   t1;
+       struct timeval   t2;
+       char             icmp_code[sizeof("!<255>")];
+       char             tos[sizeof(" (TOS=255!)")];
+       int              got_there;
+       int              unreachable;
+       int              inetname_done;
+       int              asn_done;
 };
 
+extern int             *waiting_ttls;
 extern int32_t          sec_perturb;
 extern int32_t          usec_perturb;
 
 extern u_char           packet[512];
 extern u_char          *outpacket;     /* last inbound (icmp) packet */
 
-int             wait_for_reply(int, struct msghdr *, int);
-void            dump_packet(void);
-void            build_probe4(struct tr_conf *, int, u_int8_t, int);
-void            build_probe6(struct tr_conf *, int, u_int8_t, int,
-                       struct sockaddr *);
-void            send_probe(struct tr_conf *, int, u_int8_t, int,
-                       struct sockaddr *);
-struct udphdr  *get_udphdr(struct tr_conf *, struct ip6_hdr *, u_char *);
-int             packet_ok(struct tr_conf *, int, struct msghdr *, int, int,
-                       int);
-int             packet_ok4(struct tr_conf *, struct msghdr *, int, int, int);
-int             packet_ok6(struct tr_conf *, struct msghdr *, int, int, int);
-void            icmp_code(int, int, int *, int *);
-void            icmp4_code(int, int *, int *);
-void            icmp6_code(int, int *, int *);
-void            dump_packet(void);
-void            print_exthdr(u_char *, int);
-void            check_tos(struct ip*, int *);
-void            print(struct tr_conf *, struct sockaddr *, int, const char *);
-const char     *inetname(struct sockaddr*);
-void            print_asn(struct sockaddr_storage *);
-u_short                 in_cksum(u_short *, int);
-char           *pr_type(u_int8_t);
+void            send_probe(struct tr_conf *, int, u_int8_t, struct sockaddr *);
+int             packet_ok(struct tr_conf *, int, struct msghdr *, int, int *);
+void            icmp_code(int, int, int *, int *, struct tr_result *);
+void            check_tos(struct ip*, int *, struct tr_result *);
 int             map_tos(char *, int *);
-double          deltaT(struct timeval *, struct timeval *);
-
+void            print(struct tr_conf *, struct sockaddr *, int, const char *,
+                    struct tr_result *);
+void            print_exthdr(u_char *, int, struct tr_result *);
 void            gettime(struct timeval *);
 
-extern int              rcvsock;  /* receive (icmp) socket file descriptor */
+void            catchup_result_rows(struct tr_result *, struct tr_conf *);
+
 extern int              sndsock;  /* send (udp) socket file descriptor */
 
 extern int              rcvhlim;
@@ -157,8 +167,6 @@ extern char         *hostname;
 
 extern u_int16_t        srcport;
 
-#define ICMP_CODE 0;
-
 extern int verbose;
 extern int dump;
 
index 7a6aef0..d53d0a3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: worker.c,v 1.7 2021/08/31 18:12:47 florian Exp $      */
+/*     $OpenBSD: worker.c,v 1.8 2021/09/03 09:13:00 florian Exp $      */
 /*     $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $  */
 
 /*
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
 
+#include <asr.h>
 #include <err.h>
+#include <event.h>
 #include <limits.h>
 #include <netdb.h>
-#include <poll.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "traceroute.h"
 
-static u_int8_t        icmp_type = ICMP_ECHO; /* default ICMP code/type */
+void            build_probe4(struct tr_conf *, int, u_int8_t);
+void            build_probe6(struct tr_conf *, int, u_int8_t,
+                    struct sockaddr *);
+int             packet_ok4(struct tr_conf *, struct msghdr *, int, int *);
+int             packet_ok6(struct tr_conf *, struct msghdr *, int, int *);
+void            icmp4_code(int, int *, int *, struct tr_result *);
+void            icmp6_code(int, int *, int *, struct tr_result *);
+struct udphdr  *get_udphdr(struct tr_conf *, struct ip6_hdr *, u_char *);
+void            dump_packet(void);
+void            print_asn(struct sockaddr_storage *, struct tr_result *);
+u_short                 in_cksum(u_short *, int);
+char           *pr_type(u_int8_t);
+double          deltaT(struct timeval *, struct timeval *);
+void            check_timeout(struct tr_result *, struct tr_conf *);
+void            print_result_row(struct tr_result *, struct tr_conf *);
+void            getnameinfo_async_done(struct asr_result *, void *);
+void            getrrsetbyname_async_done(struct asr_result *, void *);
 
 void
-print_exthdr(u_char *buf, int cc)
+print_exthdr(u_char *buf, int cc, struct tr_result *tr_res)
 {
        struct icmp_ext_hdr exthdr;
        struct icmp_ext_obj_hdr objhdr;
        struct ip *ip;
        struct icmp *icp;
+       size_t exthdr_size, len;
        int hlen, first;
        u_int32_t label;
        u_int16_t off, olen;
        u_int8_t type;
+       char *exthdr_str;
 
        ip = (struct ip *)buf;
        hlen = ip->ip_hl << 2;
@@ -148,6 +168,13 @@ print_exthdr(u_char *buf, int cc)
        buf += sizeof(exthdr);
        cc -= sizeof(exthdr);
 
+       /* rough estimate of needed space */
+       exthdr_size = sizeof("[MPLS Label 1048576 (Exp 3)]") *
+           (cc / sizeof(u_int32_t));
+       if ((tr_res->exthdr = calloc(1, exthdr_size)) == NULL)
+               err(1, NULL);
+       exthdr_str = tr_res->exthdr;
+
        while (cc > sizeof(objhdr)) {
                memcpy(&objhdr, buf, sizeof(objhdr));
                olen = ntohs(objhdr.ieo_length);
@@ -175,21 +202,62 @@ print_exthdr(u_char *buf, int cc)
                                        olen -= sizeof(u_int32_t);
 
                                        if (first == 0) {
-                                               printf(" [MPLS Label ");
+                                               len = snprintf(exthdr_str,
+                                                   exthdr_size, "%s",
+                                                   " [MPLS Label ");
+                                               if (len != -1 && len <
+                                                   exthdr_size) {
+                                                       exthdr_str += len;
+                                                       exthdr_size -= len;
+                                               }
                                                first++;
-                                       } else
-                                               printf(", ");
-                                       printf("%d", MPLS_LABEL(label));
-                                       if (MPLS_EXP(label))
-                                               printf(" (Exp %x)",
+                                       } else {
+                                               len = snprintf(exthdr_str,
+                                                   exthdr_size, "%s",
+                                                   ", ");
+                                               if (len != -1 && len <
+                                                   exthdr_size) {
+                                                       exthdr_str += len;
+                                                       exthdr_size -= len;
+                                               }
+                                       }
+                                       len = snprintf(exthdr_str,
+                                           exthdr_size,
+                                           "%d", MPLS_LABEL(label));
+                                       if (len != -1 && len <  exthdr_size) {
+                                               exthdr_str += len;
+                                               exthdr_size -= len;
+                                       }
+                                       if (MPLS_EXP(label)) {
+                                               len = snprintf(exthdr_str,
+                                                   exthdr_size, " (Exp %x)",
                                                    MPLS_EXP(label));
+                                               if (len != -1 && len <
+                                                   exthdr_size) {
+                                                       exthdr_str += len;
+                                                       exthdr_size -= len;
+                                               }
+                                       }
                                }
                                if (olen > 0) {
-                                       printf("|]");
+                                       len = snprintf(exthdr_str,
+                                           exthdr_size, "%s", "|]");
+                                       if (len != -1 && len <
+                                           exthdr_size) {
+                                               exthdr_str += len;
+                                               exthdr_size -= len;
+                                       }
                                        return;
                                }
-                               if (first != 0)
-                                       printf("]");
+                               if (first != 0) {
+                                       len = snprintf(exthdr_str,
+                                           exthdr_size, "%s", "]");
+                                       if (len != -1 && len <
+                                           exthdr_size) {
+                                               exthdr_str += len;
+                                               exthdr_size -= len;
+                                       }
+                               }
                                break;
                        default:
                                buf += olen;
@@ -205,7 +273,7 @@ print_exthdr(u_char *buf, int cc)
 }
 
 void
-check_tos(struct ip *ip, int *last_tos)
+check_tos(struct ip *ip, int *last_tos, struct tr_result *tr_res)
 {
        struct icmp *icp;
        struct ip *inner_ip;
@@ -214,27 +282,12 @@ check_tos(struct ip *ip, int *last_tos)
        inner_ip = (struct ip *) (((u_char *)icp)+8);
 
        if (inner_ip->ip_tos != *last_tos)
-               printf (" (TOS=%d!)", inner_ip->ip_tos);
+               snprintf(tr_res->tos, sizeof(tr_res->tos),
+                   " (TOS=%d!)", inner_ip->ip_tos);
 
        *last_tos = inner_ip->ip_tos;
 }
 
-int
-wait_for_reply(int sock, struct msghdr *mhdr, int curwaittime)
-{
-       struct pollfd pfd[1];
-       int cc = 0;
-
-       pfd[0].fd = sock;
-       pfd[0].events = POLLIN;
-       pfd[0].revents = 0;
-
-       if (poll(pfd, 1, curwaittime) > 0)
-               cc = recvmsg(rcvsock, mhdr, 0);
-
-       return (cc);
-}
-
 void
 dump_packet(void)
 {
@@ -251,7 +304,7 @@ dump_packet(void)
 }
 
 void
-build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag)
+build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl)
 {
        struct ip *ip = (struct ip *)outpacket;
        u_char *p = (u_char *)(ip + 1);
@@ -266,7 +319,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag)
 
        switch (conf->proto) {
        case IPPROTO_ICMP:
-               icmpp->icmp_type = icmp_type;
+               icmpp->icmp_type = ICMP_ECHO;
                icmpp->icmp_code = ICMP_CODE;
                icmpp->icmp_seq = htons(seq);
                icmpp->icmp_id = htons(conf->ident);
@@ -274,10 +327,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag)
                break;
        case IPPROTO_UDP:
                up->uh_sport = htons(conf->ident);
-               if (iflag)
-                       up->uh_dport = htons(conf->port+seq);
-               else
-                       up->uh_dport = htons(conf->port);
+               up->uh_dport = htons(conf->port+seq);
                up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip) -
                    conf->lsrrlen));
                up->uh_sum = 0;
@@ -307,7 +357,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag)
        op->sec = htonl(tv.tv_sec + sec_perturb);
        op->usec = htonl((tv.tv_usec + usec_perturb) % 1000000);
 
-       if (conf->proto == IPPROTO_ICMP && icmp_type == ICMP_ECHO) {
+       if (conf->proto == IPPROTO_ICMP) {
                icmpp->icmp_cksum = 0;
                icmpp->icmp_cksum = in_cksum((u_short *)icmpp,
                    datalen - sizeof(struct ip) - conf->lsrrlen);
@@ -317,7 +367,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag)
 }
 
 void
-build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, int iflag,
+build_probe6(struct tr_conf *conf, int seq, u_int8_t hops,
     struct sockaddr *to)
 {
        struct timeval tv;
@@ -329,10 +379,9 @@ build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, int iflag,
            (char *)&i, sizeof(i)) == -1)
                warn("setsockopt IPV6_UNICAST_HOPS");
 
-       if (iflag)
-               ((struct sockaddr_in6*)to)->sin6_port = htons(conf->port + seq);
-       else
-               ((struct sockaddr_in6*)to)->sin6_port = htons(conf->port);
+
+       ((struct sockaddr_in6*)to)->sin6_port = htons(conf->port + seq);
+
        gettime(&tv);
 
        if (conf->proto == IPPROTO_ICMP) {
@@ -354,17 +403,16 @@ build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, int iflag,
 }
 
 void
-send_probe(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag,
-       struct sockaddr *to)
+send_probe(struct tr_conf *conf, int seq, u_int8_t ttl, struct sockaddr *to)
 {
        int i;
 
        switch (to->sa_family) {
        case AF_INET:
-               build_probe4(conf, seq, ttl, iflag);
+               build_probe4(conf, seq, ttl);
                break;
        case AF_INET6:
-               build_probe6(conf, seq, ttl, iflag, to);
+               build_probe6(conf, seq, ttl, to);
                break;
        default:
                errx(1, "unsupported AF: %d", to->sa_family);
@@ -428,15 +476,14 @@ pr_type(u_int8_t t)
 }
 
 int
-packet_ok(struct tr_conf *conf, int af, struct msghdr *mhdr, int cc, int seq,
-    int iflag)
+packet_ok(struct tr_conf *conf, int af, struct msghdr *mhdr, int cc, int *seq)
 {
        switch (af) {
        case AF_INET:
-               return packet_ok4(conf, mhdr, cc, seq, iflag);
+               return packet_ok4(conf, mhdr, cc, seq);
                break;
        case AF_INET6:
-               return packet_ok6(conf, mhdr, cc, seq, iflag);
+               return packet_ok6(conf, mhdr, cc, seq);
                break;
        default:
                errx(1, "unsupported AF: %d", af);
@@ -445,7 +492,7 @@ packet_ok(struct tr_conf *conf, int af, struct msghdr *mhdr, int cc, int seq,
 }
 
 int
-packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc,int seq, int iflag)
+packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc, int *seq)
 {
        struct sockaddr_in *from = (struct sockaddr_in *)mhdr->msg_name;
        struct icmp *icp;
@@ -478,27 +525,26 @@ packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc,int seq, int iflag)
 
                switch (conf->proto) {
                case IPPROTO_ICMP:
-                       if (icmp_type == ICMP_ECHO &&
-                           type == ICMP_ECHOREPLY &&
-                           icp->icmp_id == htons(conf->ident) &&
-                           icp->icmp_seq == htons(seq))
+                       if (type == ICMP_ECHOREPLY &&
+                           icp->icmp_id == htons(conf->ident)) {
+                               *seq = ntohs(icp->icmp_seq);
                                return (-2); /* we got there */
-
+                       }
                        icmpp = (struct icmp *)((u_char *)hip + hlen);
                        if (hlen + 8 <= cc && hip->ip_p == IPPROTO_ICMP &&
-                           icmpp->icmp_id == htons(conf->ident) &&
-                           icmpp->icmp_seq == htons(seq))
+                           icmpp->icmp_id == htons(conf->ident)) {
+                               *seq = ntohs(icmpp->icmp_seq);
                                return (type == ICMP_TIMXCEED? -1 : code + 1);
+                       }
                        break;
 
                case IPPROTO_UDP:
                        up = (struct udphdr *)((u_char *)hip + hlen);
                        if (hlen + 12 <= cc && hip->ip_p == conf->proto &&
-                           up->uh_sport == htons(conf->ident) &&
-                           ((iflag && up->uh_dport == htons(conf->port +
-                           seq)) ||
-                           (!iflag && up->uh_dport == htons(conf->port))))
+                           up->uh_sport == htons(conf->ident)) {
+                               *seq = ntohs(up->uh_dport) - conf->port;
                                return (type == ICMP_TIMXCEED? -1 : code + 1);
+                       }
                        break;
                default:
                        /* this is some odd, user specified proto,
@@ -523,8 +569,7 @@ packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc,int seq, int iflag)
 }
 
 int
-packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq,
-    int iflag)
+packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int *seq)
 {
        struct icmp6_hdr *icp;
        struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
@@ -540,7 +585,8 @@ packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq,
                        if (getnameinfo((struct sockaddr *)from, from->sin6_len,
                            hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
                                strlcpy(hbuf, "invalid", sizeof(hbuf));
-                       printf("data too short (%d bytes) from %s\n", cc, hbuf);
+                       printf("packet too short (%d bytes) from %s\n", cc,
+                           hbuf);
                }
                return(0);
        }
@@ -582,18 +628,19 @@ packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq,
                        return(0);
                }
                if (useicmp &&
-                   ((struct icmp6_hdr *)up)->icmp6_id == conf->ident &&
-                   ((struct icmp6_hdr *)up)->icmp6_seq == htons(seq))
+                   ((struct icmp6_hdr *)up)->icmp6_id == conf->ident) {
+                       *seq = ntohs(((struct icmp6_hdr *)up)->icmp6_seq);
                        return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
-               else if (!useicmp &&
-                   up->uh_sport == htons(srcport) &&
-                   ((iflag && up->uh_dport == htons(conf->port + seq)) ||
-                   (!iflag && up->uh_dport == htons(conf->port))))
+               } else if (!useicmp &&
+                   up->uh_sport == htons(srcport)) {
+                       *seq = ntohs(up->uh_dport) - conf->port;
                        return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
+               }
        } else if (useicmp && type == ICMP6_ECHO_REPLY) {
-               if (icp->icmp6_id == conf->ident &&
-                   icp->icmp6_seq == htons(seq))
-                       return (ICMP6_DST_UNREACH_NOPORT + 1);
+               if (icp->icmp6_id == conf->ident) {
+                       *seq = ntohs(icp->icmp6_seq);
+                       return (-2);
+               }
        }
        if (conf->verbose) {
                char sbuf[NI_MAXHOST], dbuf[INET6_ADDRSTRLEN];
@@ -626,22 +673,32 @@ packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq,
 }
 
 void
-print(struct tr_conf *conf, struct sockaddr *from, int cc, const char *to)
+print(struct tr_conf *conf, struct sockaddr *from, int cc, const char *to,
+    struct tr_result *tr_res)
 {
-       char hbuf[NI_MAXHOST];
+       struct asr_query        *aq;
+       char                     hbuf[NI_MAXHOST];
+
        if (getnameinfo(from, from->sa_len,
-           hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
-               strlcpy(hbuf, "invalid", sizeof(hbuf));
-       if (conf->nflag)
-               printf(" %s", hbuf);
-       else
-               printf(" %s (%s)", inetname(from), hbuf);
+           tr_res->hbuf, sizeof(tr_res->hbuf), NULL, 0, NI_NUMERICHOST) != 0)
+               strlcpy(tr_res->hbuf, "invalid", sizeof(hbuf));
+
+       if (!conf->nflag) {
+               aq = getnameinfo_async(from, from->sa_len, tr_res->inetname,
+                   sizeof(tr_res->inetname), NULL, 0, NI_NAMEREQD, NULL);
+               if (aq != NULL)
+                       event_asr_run(aq, getnameinfo_async_done, tr_res);
+               else {
+                       waiting_ttls[tr_res->row]--;
+                       tr_res->inetname_done = 1; /* use hbuf */
+               }
+       }
 
        if (conf->Aflag)
-               print_asn((struct sockaddr_storage *)from);
+               print_asn((struct sockaddr_storage *)from, tr_res);
 
-       if (conf->verbose)
-               printf(" %d bytes to %s", cc, to);
+       strlcpy(tr_res->to, to, sizeof(tr_res->to));
+       tr_res->cc = cc;
 }
 
 /*
@@ -690,14 +747,15 @@ get_udphdr(struct tr_conf *conf, struct ip6_hdr *ip6, u_char *lim)
 }
 
 void
-icmp_code(int af, int code, int *got_there, int *unreachable)
+icmp_code(int af, int code, int *got_there, int *unreachable,
+    struct tr_result *tr_res)
 {
        switch (af) {
        case AF_INET:
-               icmp4_code(code, got_there, unreachable);
+               icmp4_code(code, got_there, unreachable, tr_res);
                break;
        case AF_INET6:
-               icmp6_code(code, got_there, unreachable);
+               icmp6_code(code, got_there, unreachable, tr_res);
                break;
        default:
                errx(1, "unsupported AF: %d", af);
@@ -706,97 +764,116 @@ icmp_code(int af, int code, int *got_there, int *unreachable)
 }
 
 void
-icmp4_code(int code, int *got_there, int *unreachable)
+icmp4_code(int code, int *got_there, int *unreachable, struct tr_result *tr_res)
 {
        struct ip *ip = (struct ip *)packet;
 
        switch (code) {
        case ICMP_UNREACH_PORT:
                if (ip->ip_ttl <= 1)
-                       printf(" !");
+                       snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code),
+                           "%s", " !");
                ++(*got_there);
                break;
        case ICMP_UNREACH_NET:
                ++(*unreachable);
-               printf(" !N");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !N");
                break;
        case ICMP_UNREACH_HOST:
                ++(*unreachable);
-               printf(" !H");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !H");
                break;
        case ICMP_UNREACH_PROTOCOL:
                ++(*got_there);
-               printf(" !P");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !P");
                break;
        case ICMP_UNREACH_NEEDFRAG:
                ++(*unreachable);
-               printf(" !F");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !F");
                break;
        case ICMP_UNREACH_SRCFAIL:
                ++(*unreachable);
-               printf(" !S");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !S");
                break;
        case ICMP_UNREACH_FILTER_PROHIB:
                ++(*unreachable);
-               printf(" !X");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !X");
                break;
        case ICMP_UNREACH_NET_PROHIB: /*misuse*/
                ++(*unreachable);
-               printf(" !A");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !A");
                break;
        case ICMP_UNREACH_HOST_PROHIB:
                ++(*unreachable);
-               printf(" !C");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !C");
                break;
        case ICMP_UNREACH_NET_UNKNOWN:
        case ICMP_UNREACH_HOST_UNKNOWN:
                ++(*unreachable);
-               printf(" !U");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !U");
                break;
        case ICMP_UNREACH_ISOLATED:
                ++(*unreachable);
-               printf(" !I");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !I");
                break;
        case ICMP_UNREACH_TOSNET:
        case ICMP_UNREACH_TOSHOST:
                ++(*unreachable);
-               printf(" !T");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !T");
                break;
        default:
                ++(*unreachable);
-               printf(" !<%d>", code);
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), " !<%d>",
+                   code & 0xff);
                break;
        }
 }
 
 void
-icmp6_code(int code, int *got_there, int *unreachable)
+icmp6_code(int code, int *got_there, int *unreachable, struct tr_result *tr_res)
 {
        switch (code) {
        case ICMP6_DST_UNREACH_NOROUTE:
                ++(*unreachable);
-               printf(" !N");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !N");
                break;
        case ICMP6_DST_UNREACH_ADMIN:
                ++(*unreachable);
-               printf(" !P");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !P");
                break;
        case ICMP6_DST_UNREACH_BEYONDSCOPE:
                ++(*unreachable);
-               printf(" !S");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !S");
                break;
        case ICMP6_DST_UNREACH_ADDR:
                ++(*unreachable);
-               printf(" !A");
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s",
+                   " !A");
                break;
        case ICMP6_DST_UNREACH_NOPORT:
-               if (rcvhlim >= 0 && rcvhlim <= 1)
-                       printf(" !");
+               if (rcvhlim <= 1)
+                       snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code),
+                           "%s", " !");
                ++(*got_there);
                break;
        default:
                ++(*unreachable);
-               printf(" !<%d>", code);
+               snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), " !<%d>",
+                   code & 0xff);
                break;
        }
 }
@@ -834,45 +911,12 @@ in_cksum(u_short *addr, int len)
        return (answer);
 }
 
-/*
- * Construct an Internet address representation.
- */
-const char *
-inetname(struct sockaddr *sa)
-{
-       static char line[NI_MAXHOST], domain[HOST_NAME_MAX + 1];
-       static int first = 1;
-       char *cp;
-
-       if (first) {
-               first = 0;
-               if (gethostname(domain, sizeof(domain)) == 0 &&
-                   (cp = strchr(domain, '.')) != NULL)
-                       memmove(domain, cp + 1, strlen(cp + 1) + 1);
-               else
-                       domain[0] = 0;
-       }
-       if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
-           NI_NAMEREQD) == 0) {
-               if ((cp = strchr(line, '.')) != NULL && strcmp(cp + 1,
-                   domain) == 0)
-                       *cp = '\0';
-               return (line);
-       }
-
-       if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
-           NI_NUMERICHOST) != 0)
-               return ("invalid");
-       return (line);
-}
-
 void
-print_asn(struct sockaddr_storage *ss)
+print_asn(struct sockaddr_storage *ss, struct tr_result *tr_res)
 {
-       struct rrsetinfo *answers = NULL;
-       int counter;
-       const u_char *uaddr;
-       char qbuf[MAXDNAME];
+       struct asr_query        *aq;
+       const u_char            *uaddr;
+       char                     qbuf[MAXDNAME];
 
        switch (ss->ss_family) {
        case AF_INET:
@@ -911,21 +955,12 @@ print_asn(struct sockaddr_storage *ss)
                return;
        }
 
-       if (getrrsetbyname(qbuf, C_IN, T_TXT, 0, &answers) != 0)
-               return;
-       for (counter = 0; counter < answers->rri_nrdatas; counter++) {
-               char *p, *as = answers->rri_rdatas[counter].rdi_data;
-               as++; /* skip first byte, it contains length */
-               if ((p = strchr(as,'|'))) {
-                       printf(counter ? ", " : " [");
-                       p[-1] = 0;
-                       printf("AS%s", as);
-               }
+       if ((aq = getrrsetbyname_async(qbuf, C_IN, T_TXT, 0, NULL)) != NULL)
+               event_asr_run(aq, getrrsetbyname_async_done, tr_res);
+       else {
+               waiting_ttls[tr_res->row]--;
+               tr_res->asn_done = 1;
        }
-       if (counter)
-               printf("]");
-
-       freerrset(answers);
 }
 
 int
@@ -986,3 +1021,201 @@ gettime(struct timeval *tv)
 
        TIMESPEC_TO_TIMEVAL(tv, &ts);
 }
+
+void
+check_timeout(struct tr_result *tr_row, struct tr_conf *conf)
+{
+       struct timeval   t2;
+       int              i;
+
+       gettime(&t2);
+
+       for (i = 0; i < conf->nprobes; i++) {
+               /* we didn't send the probe yet */
+               if (tr_row[i].ttl == 0)
+                       return;
+               /* we got a result, it can no longer timeout */
+               if (tr_row[i].dup)
+                       continue;
+
+               if (deltaT(&tr_row[i].t1, &t2) > conf->waittime) {
+                       tr_row[i].timeout = 1;
+                       tr_row[i].dup++; /* we "saw" the result */
+                       waiting_ttls[tr_row[i].row] -=
+                           conf->expected_responses;
+               }
+       }
+}
+
+void
+catchup_result_rows(struct tr_result *tr_results, struct tr_conf *conf)
+{
+       static int       timeout_row = 0;
+       static int       print_row = 0;
+       int              i, j, all_timeout = 1;
+
+       for (; timeout_row < conf->max_ttl; timeout_row++) {
+               struct tr_result *tr_row = tr_results +
+                   timeout_row * conf->nprobes;
+               check_timeout(tr_row, conf);
+               if (waiting_ttls[timeout_row] > 0)
+                       break;
+       }
+
+       for (i = print_row; i < timeout_row; i++) {
+               struct tr_result *tr_row = tr_results + i * conf->nprobes;
+
+               if (waiting_ttls[i] > 0)
+                       break;
+
+               for (j = 0; j < conf->nprobes; j++) {
+                       if (!tr_row[j].timeout) {
+                               all_timeout = 0;
+                               break;
+                       }
+               }
+               if (!all_timeout)
+                       break;
+       }
+
+       if (all_timeout && i != conf->max_ttl)
+               return;
+
+       if (i == conf->max_ttl)
+               print_row = i - 1; /* jump ahead, skip long trail of * * * */
+
+       for (; print_row <= i; print_row++) {
+               struct tr_result *tr_row = tr_results +
+                   print_row * conf->nprobes;
+               if (waiting_ttls[print_row] > 0)
+                       break;
+               print_result_row(tr_row, conf);
+       }
+}
+
+void
+print_result_row(struct tr_result *tr_results, struct tr_conf *conf)
+{
+       int      i, loss = 0, got_there = 0, unreachable = 0;
+       char    *lastaddr = NULL;
+
+       printf("%2u ", tr_results[0].ttl);
+       for (i = 0; i < conf->nprobes; i++) {
+               got_there += tr_results[i].got_there;
+               unreachable += tr_results[i].unreachable;
+
+               if (tr_results[i].timeout) {
+                       printf(" %s%s", "*", tr_results[i].icmp_code);
+                       loss++;
+                       continue;
+               }
+
+               if (lastaddr == NULL || strcmp(lastaddr, tr_results[i].hbuf)
+                   != 0) {
+                       if (*tr_results[i].hbuf != '\0') {
+                               if (conf->nflag)
+                                       printf(" %s", tr_results[i].hbuf);
+                               else
+                                       printf(" %s (%s)",
+                                           tr_results[i].inetname[0] == '\0' ?
+                                           tr_results[i].hbuf :
+                                           tr_results[i].inetname,
+                                           tr_results[i].hbuf);
+                               if (conf->Aflag && tr_results[i].asn != NULL)
+                                       printf(" %s", tr_results[i].asn);
+                               if (conf->verbose)
+                                       printf(" %d bytes to %s",
+                                           tr_results[i].cc,
+                                           tr_results[i].to);
+                       }
+               }
+               lastaddr = tr_results[i].hbuf;
+               printf("  %g ms%s%s",
+                   deltaT(&tr_results[i].t1,
+                   &tr_results[i].t2),
+                   tr_results[i].tos,
+                   tr_results[i].icmp_code);
+               if (conf->ttl_flag)
+                       printf(" (%u)", tr_results[i].resp_ttl);
+
+               if (tr_results[i].exthdr)
+                       printf("%s", tr_results[i].exthdr);
+       }
+       if (conf->sump)
+               printf(" (%d%% loss)", (loss * 100) / conf->nprobes);
+       putchar('\n');
+       fflush(stdout);
+       if (got_there || unreachable || tr_results[0].ttl == conf->max_ttl)
+               exit(0);
+}
+
+void
+getnameinfo_async_done(struct asr_result *ar, void *arg)
+{
+       static char              domain[HOST_NAME_MAX + 1];
+       static int               first = 1;
+       struct tr_result        *tr_res = arg;
+       char                    *cp;
+
+       if (first) {
+               first = 0;
+               if (gethostname(domain, sizeof(domain)) == 0 &&
+                   (cp = strchr(domain, '.')) != NULL)
+                       memmove(domain, cp + 1, strlen(cp + 1) + 1);
+               else
+                       domain[0] = 0;
+       }
+
+       tr_res->inetname_done = 1;
+       waiting_ttls[tr_res->row]--;
+
+       if (ar->ar_gai_errno == 0) {
+               if ((cp = strchr(tr_res->inetname, '.')) != NULL &&
+                   strcmp(cp + 1, domain) == 0)
+                       *cp = '\0';
+       } else
+               tr_res->inetname[0]='\0';
+}
+
+void
+getrrsetbyname_async_done(struct asr_result *ar, void *arg)
+{
+       struct tr_result        *tr_res = arg;
+       struct rrsetinfo        *answers;
+       size_t                   asn_size = 0, len;
+       int                      counter;
+       char                    *asn;
+
+       tr_res->asn_done = 1;
+       waiting_ttls[tr_res->row]--;
+       if (ar->ar_rrset_errno != 0)
+               return;
+
+       answers = ar->ar_rrsetinfo;
+
+       if (answers->rri_nrdatas > 0) {
+               asn_size = answers->rri_nrdatas * sizeof("AS2147483647, ") + 3;
+               if ((tr_res->asn = calloc(1, asn_size)) == NULL)
+                       err(1, NULL);
+               asn = tr_res->asn;
+       }
+
+       for (counter = 0; counter < answers->rri_nrdatas; counter++) {
+               char *p, *as = answers->rri_rdatas[counter].rdi_data;
+               as++; /* skip first byte, it contains length */
+               if ((p = strchr(as,'|'))) {
+                       p[-1] = 0;
+                       len = snprintf(asn, asn_size, "%sAS%s",
+                           counter ? ", " : "[", as);
+                       if (len != -1 && len < asn_size) {
+                               asn += len;
+                               asn_size -= len;
+                       } else
+                               asn_size = 0;
+               }
+       }
+       if (counter && asn_size > 0)
+               *asn=']';
+
+       freerrset(answers);
+}