/* * TCPVIEW -- detail-domain() function. * * Author: Steve Hubert * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: hubert@cac.washington.edu * * * Copyright 1992 by the University of Washington * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appears in all copies and that both the * above copyright notice and this permission notice appear in supporting * documentation, and that the name of the University of Washington not be * used in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. This software is made * available "as is", and * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "tcpview.h" #include #include #ifndef T_TXT #define T_TXT 16 /* text record */ #endif #ifndef C_HS #define C_HS 4 /* for Hesiod type (MIT) */ #endif #include static char *opcodes[] = { "standard", "inverse", "status", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" }; static char *rcodes[] = { "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" }; /* * ledge and redge are used to keep track of the left and right edges of * the hex window. * clen is the # of octets left to use in the passed-in (to detail_domain()) data. * cp is a pointer to the next octet to use from the passed-in data. * hexarr[] is used to store the hex windows for each line printed in the * detail window, and ind is the current index into that array. */ static int ledge, redge, clen; static char *oldcp, *cp; struct hexholder { int l; /* left edge of window into data */ int r; /* right edge of window into data */ }; struct hexholder hexarr[128]; static int index; /* * The consumer of input should call INCR_PTRS(). * Whenever a newline is printed, one of the MARK()'s should be called. * MARK_WINDOW_NONE() is for the case where there is no data associated with * the line. The INCR_PTRS() calls should be up-to-date before calling * MARK_WINDOW(), since it relies on cp being incremented properly. * There are some cases where it isn't known right away how much data there * is because it is variable sized. For example, the header "Question Section" * is like that. MARK_SAVED_WIN() is used for those cases. * Careful with these macros and if's. They are two statements if you put * a ';' after them, so you either have to leave off the trailing ';' or enclose * it in braces when only one statement is needed. I prefer to enclose it in * braces because it looks funny to me without the trailing ';'. */ #define INCR_PTRS(x) {cp += (x); clen -= (x);} #define MARK_WIN(left,right) {hexarr[index].l=(left);\ hexarr[index].r=(right); index++;} #define MARK_WINDOW() {redge += (cp-oldcp-1); MARK_WIN(ledge,redge);\ ledge = ++redge; oldcp = cp;} #define MARK_WINDOW_NONE() {MARK_WIN(-1,-1);} #define MARK_SAVED_WIN(ind,left,right) {hexarr[ind].l=(left);\ hexarr[ind].r=(right);} #define FAIL 1 #define SUCCEED 0 #define MIN(a,b) (((a)<(b))?(a):(b)) /* * Returns a pointer to the expanded version of the compressed name beginning * at cp (a global). The pointer points to static storage. * Returns NULL on failure (probably not enough data captured). * Modifies globals cp and clen. */ static char * p_cdname(msg, eom) char *msg, *eom; { static char name[MAXDNAME]; register int n; if (clen <= 0) return NULL; if ((n = dn_expand(msg, eom, cp, name, sizeof(name))) <= 0) return NULL; /* root domain */ if (name[0] == '\0') { name[0] = '.'; name[1] = '\0'; } INCR_PTRS(n); return name; } /* * Returns a pointer to a type string. The pointer points to static storage. */ static char * p_type(type) u_short type; { static char type_buf[40]; switch (type) { case T_A: strcpy(type_buf, "A"); break; case T_NS: /* authoritative server */ strcpy(type_buf, "NS"); break; case T_CNAME: /* canonical name */ strcpy(type_buf, "CNAME"); break; case T_SOA: /* start of authority zone */ strcpy(type_buf, "SOA"); break; case T_MB: /* mailbox domain name */ strcpy(type_buf, "MB"); break; case T_MG: /* mail group member */ strcpy(type_buf, "MG"); break; case T_MR: /* mail rename name */ strcpy(type_buf, "MR"); break; case T_NULL: /* null resource record */ strcpy(type_buf, "NULL"); break; case T_WKS: /* well known service */ strcpy(type_buf, "WKS"); break; case T_PTR: /* domain name pointer */ strcpy(type_buf, "PTR"); break; case T_HINFO: /* host information */ strcpy(type_buf, "HINFO"); break; case T_MINFO: /* mailbox information */ strcpy(type_buf, "MINFO"); break; case T_MX: /* mail routing info */ strcpy(type_buf, "MX"); break; case T_TXT: /* text */ strcpy(type_buf, "TXT"); break; case T_AXFR: /* zone transfer */ strcpy(type_buf, "AXFR"); break; case T_MAILB: /* mail box */ strcpy(type_buf, "MAILB"); break; case T_MAILA: /* mail address */ strcpy(type_buf, "MAILA"); break; case T_ANY: /* matches any type */ strcpy(type_buf, "ANY"); break; #ifdef T_UNSPEC case T_UINFO: strcpy(type_buf, "UINFO"); break; case T_UID: strcpy(type_buf, "UID"); break; case T_GID: strcpy(type_buf, "GID"); break; case T_UNSPEC: strcpy(type_buf, "UNSPEC"); break; #endif default: sprintf(type_buf, "%d", type); break; } return type_buf; } /* * Returns a pointer to a class string. The pointer points to static storage. */ static char * p_class(class) u_short class; { static char class_buf[40]; switch (class) { case C_IN: /* internet class */ strcpy(class_buf, "IN"); break; case C_HS: /* hesiod class */ strcpy(class_buf, "HS"); break; case C_ANY: /* matches any class */ strcpy(class_buf, "ANY"); break; default: sprintf(class_buf, "%d", class); break; } return class_buf; } /* * Returns a pointer to a string representing a 32 bit unsigned. The pointer * points to static storage. */ static char * p_ulong(value) u_long value; { static char ulong_buf[40]; sprintf(ulong_buf, "%u", value); return ulong_buf; } /* * Returns a pointer to a string representing a 16 bit unsigned. The pointer * points to static storage. */ static char * p_ushort(value) u_short value; { static char ushort_buf[40]; sprintf(ushort_buf, "%u", value); return ushort_buf; } /* * Returns a pointer to a string representing a time. * The pointer points to static storage. */ static char * p_time(value) u_long value; { static char tbuf[60]; int secs, mins, hours; register u_long v; register char *p; p = tbuf; v = value; (void)sprintf(p, "%u (", v); p += strlen(p); if (v == 0) { (void)strcpy(p, "0 secs)"); return tbuf; } secs = v % 60; v /= 60; mins = v % 60; v /= 60; hours = v % 24; v /= 24; #define PLURALIZE(x) x, (x == 1) ? "" : "s" if (v) { (void)sprintf(p, "%d day%s", PLURALIZE(v)); p += strlen(p); } if (hours) { if (v) *p++ = ' '; (void)sprintf(p, "%d hour%s", PLURALIZE(hours)); p += strlen(p); } if (mins) { if (v || hours) *p++ = ' '; (void)sprintf(p, "%d min%s", PLURALIZE(mins)); p += strlen(p); } if (secs || ! (v || hours || mins)) { if (v || hours || mins) *p++ = ' '; (void)sprintf(p, "%d sec%s", PLURALIZE(secs)); } (void)strcat(p, ")"); return tbuf; } /* * Print a resource record. * Modifies cp, clen, ledge, redge, ind, and hexarr[]. * Actually does the printing, as opposed to passing back a ptr to a string. */ static int p_ans(msg, eom) char *msg, *eom; { register char *q; u_int type, class, rdlength; u_long ul; int n, c, ltmp, pref; struct in_addr inaddr; struct protoent *p; register char *cp1; char *str; static char name[MAXDNAME]; int saveind, saveleftedge; static char buf[1024]; str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return FAIL; } printf("name = %s\n", str); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return FAIL; } type = _getshort(cp); INCR_PTRS(sizeof(u_short)); str = p_type(type); printf("type = %s\n", str); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return FAIL; } class = _getshort(cp); INCR_PTRS(sizeof(u_short)); str = p_class(class); printf("class = %s\n", str); MARK_WINDOW(); if (clen < (int)sizeof(u_long)) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return FAIL; } ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); str = p_time(ul); printf(" ttl = %s\n", str); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return FAIL; } rdlength = _getshort(cp); INCR_PTRS(sizeof(u_short)); str = p_ushort(rdlength); printf(" rdlength = %s\n", str); MARK_WINDOW(); /* now we're at the resource data field */ cp1 = cp; if (clen < (int)rdlength) { printf(" ** Rdata truncated **\n"); MARK_WINDOW_NONE(); return FAIL; } /* * print type specific data */ switch (type) { case T_A: if (class == C_IN || class == C_HS) { memcpy((char *)&inaddr, cp, sizeof(inaddr)); if (rdlength == 4) { printf(" Internet address = %s\n", inet_ntoa(inaddr)); } else if (rdlength == 7) { printf(" Internet address = %s", inet_ntoa(inaddr)); printf(", protocol = %d", cp[4]); printf(", port = %d\n", (cp[5] << 8) + cp[6]); }else { printf(" rdlength = %d, should be 4\n", rdlength); } } INCR_PTRS(rdlength); MARK_WINDOW(); break; case T_CNAME: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" Canonical name = %s\n", str); MARK_WINDOW(); break; case T_MB: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" MB domain name = %s\n", str); MARK_WINDOW(); break; case T_MD: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" MD domain name = %s (obsolete)\n", str); MARK_WINDOW(); break; case T_MF: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" MF domain name = %s (obsolete)\n", str); MARK_WINDOW(); break; case T_MG: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" Mail group member = %s\n", str); MARK_WINDOW(); break; case T_MR: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" Mail rename domain name = %s\n", str); MARK_WINDOW(); break; case T_MX: pref = _getshort(cp); INCR_PTRS(sizeof(u_short)); str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" Mail exchangeer = %s, preference %u\n", str, pref); MARK_WINDOW(); break; case T_NS: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" Authoritative name server = %s\n", str); MARK_WINDOW(); break; case T_PTR: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" Domain name pointer = %s\n", str); MARK_WINDOW(); break; case T_HINFO: printf(" HINFO: "); if (n = *cp) { INCR_PTRS(1); printf("CPU = %.*s;", n, cp); INCR_PTRS(n); }else { INCR_PTRS(1); } if (n = *cp) { INCR_PTRS(1); printf(" OS = %.*s", n, cp); INCR_PTRS(n); }else { INCR_PTRS(1); } putchar('\n'); MARK_WINDOW(); break; case T_SOA: printf(" SOA (Start of Authority):\n"); saveind = index++; saveleftedge = ledge; str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); MARK_SAVED_WIN(saveind, -1, -1); return NULL; } printf(" Origin: %s\n", str); MARK_WINDOW(); str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); MARK_SAVED_WIN(saveind, -1, -1); return NULL; } printf(" Mail Addr: %s\n", str); MARK_WINDOW(); ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); printf(" Serial: %u\n", ul); MARK_WINDOW(); ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); str = p_time(ul); printf(" Refresh: %s\n", str); MARK_WINDOW(); ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); str = p_time(ul); printf(" Retry: %s\n", str); MARK_WINDOW(); ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); str = p_time(ul); printf(" Expire: %s\n", str); MARK_WINDOW(); ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); str = p_time(ul); printf(" Minimum: %s\n", str); MARK_WINDOW(); MARK_SAVED_WIN(saveind, saveleftedge, ledge-1); break; case T_MINFO: str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf(" MINFO: requests = %s", str); str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); return NULL; } printf("; errors = %s\n", str); MARK_WINDOW(); #ifdef T_UNSPEC case T_UINFO: printf(" UINFO: "); printf("%s\n", cp); INCR_PTRS(rdlength); MARK_WINDOW(); break; case T_UID: case T_GID: if (rdlength == 4) { ul = _getlong(cp); INCR_PTRS(sizeof(u_long)); if (type == T_UID) printf(" UID = %u", ul); else printf(" GID = %u", ul); } putchar('\n'); MARK_WINDOW(); break; case T_UNSPEC: printf(" \n"); INCR_PTRS(rdlength); MARK_WINDOW(); break; #endif case T_WKS: if (rdlength < (int)(sizeof(u_long) + 1)) break; memcpy((char *)&inaddr, cp, sizeof(inaddr)); INCR_PTRS(sizeof(u_long)); printf(" Services:\n"); saveind = index++; saveleftedge = ledge; printf(" %s:\n", inet_ntoa(inaddr)); MARK_WINDOW(); p = getprotobynumber(*cp); if (p == NULL) printf(" protocol=%d:\n", *cp); else printf(" %s:\n", p->p_name); INCR_PTRS(1); MARK_WINDOW(); printf(" "); n = 0; while (cp < cp1 + rdlength) { c = *cp; INCR_PTRS(1); do { if (c & 0200) { struct servent *s; s = p != NULL ? getservbyport(htons(n), p->p_name) : NULL; if (s == NULL) { printf(" %d", n); }else { printf(" %s", s->s_name); } } c <<= 1; } while (++n & 07); } putchar('\n'); MARK_WINDOW(); MARK_SAVED_WIN(saveind, saveleftedge, ledge-1); break; case T_TXT: if (rdlength == 0) { printf(" text = \n"); MARK_WINDOW_NONE(); }else { int txtlen; txtlen = MIN(rdlength, clen); while (txtlen > 0) { n = (unsigned char)*cp; INCR_PTRS(1); txtlen--; if (n > txtlen) { printf(" text (length=%d, truncated to %d) = ", n, txtlen); n = txtlen; }else { printf(" text (length=%d) = ", n); } for (c = n; c > 0; c--) { if (*cp == '\n') { putchar('\\'); putchar(*cp); INCR_PTRS(1); txtlen--; }else { putchar(*cp); INCR_PTRS(1); txtlen--; } } putchar('\n'); MARK_WINDOW(); } } break; default: printf(" ???\n"); INCR_PTRS(rdlength); MARK_WINDOW(); } return SUCCEED; } /* * Make all of the calls to hex() to set the pointers into the hex window * correctly. I do it here instead of as I go since I don't know the * correct values all of the time as I go. Instead, I save them up in * an arry. I could use the array that hex() uses but I decided to write the * program to the published interface instead of peeking inside at the data * structures. */ static void finish() { register int i; for (i = 0; i < index; i++) hex(hexarr[i].l, hexarr[i].r); } /* * msg points to header, len is length according to udp, may not be all captured */ void detail_domain(msg, len) char *msg; int len; { register HEADER *hp; register int n; char *str; char *eom = msg+len; int qdcount, ancount, nscount, arcount; int saveind, saveleftedge; int origclen; /* initialize the globals */ ledge = redge = 0; index = 0; clen = Phdr->caplen - Offset; oldcp = cp = msg; /* save this for marking some truncated cases */ origclen = clen; hp = (HEADER *)msg; if (clen < len) { printf("----- Domain (*** truncated ***) -----\n\n"); /* * We use eom when calling dn_expand(). If our captured data is * less than that needed by dn_expand(), that is, the name is truncated, * then this eom will stop it and cause it to return -1. */ eom = msg + clen; }else { printf("----- Domain -----\n\n"); } if (clen > 0) { MARK_WIN(0, clen-1); }else { MARK_WINDOW_NONE(); } MARK_WINDOW_NONE(); if (clen < (int)sizeof(HEADER)) printf("--- Header Section (** truncated **) ---\n\n"); else printf("--- Header Section ---\n\n"); if (clen > 0) { MARK_WIN(0, MIN((int)(sizeof(HEADER)-1), clen-1)); }else { MARK_WINDOW_NONE(); } /* one MARK call per \n */ MARK_WINDOW_NONE(); if (clen < (int)sizeof(u_short)) { finish(); return; } INCR_PTRS(sizeof(u_short)); printf("id = 0x%x\n", ntohs(hp->id)); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { finish(); return; } INCR_PTRS(sizeof(u_short)); if (hp->qr) printf("response: "); else printf("query: "); printf("op=%s", opcodes[hp->opcode]); if (hp->aa) printf(", auth"); if (hp->tc) printf(", truncated"); if (hp->rd) printf(", recurs desired"); if (hp->ra) printf(", recurs avail"); if (hp->qr || hp->rcode) printf(", rcode=%s", rcodes[hp->rcode]); putchar('\n'); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { finish(); return; } INCR_PTRS(sizeof(u_short)); qdcount = ntohs(hp->qdcount); printf("question count = %d\n", qdcount); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { finish(); return; } INCR_PTRS(sizeof(u_short)); ancount = ntohs(hp->ancount); printf("answer count = %d\n", ancount); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { finish(); return; } INCR_PTRS(sizeof(u_short)); nscount = ntohs(hp->nscount); printf("authority count = %d\n", nscount); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { finish(); return; } INCR_PTRS(sizeof(u_short)); arcount = ntohs(hp->arcount); printf("additional count = %d\n", arcount); MARK_WINDOW(); putchar('\n'); MARK_WINDOW_NONE(); if (qdcount) { printf("--- Question Section ---\n\n"); /* save these so I can mark the length later when I know it */ saveind = index++; saveleftedge = ledge; MARK_WINDOW_NONE(); /* once through loop for each question (usually just one) */ while (qdcount-- > 0) { u_short type, class; str = p_cdname(msg, eom); if (str == NULL) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); MARK_SAVED_WIN(saveind, saveleftedge, origclen-1); finish(); return; } printf("qname = %s\n", str); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); MARK_SAVED_WIN(saveind, saveleftedge, origclen-1); finish(); return; } type = _getshort(cp); INCR_PTRS(sizeof(u_short)); str = p_type(type); printf("qtype = %s\n", str); MARK_WINDOW(); if (clen < (int)sizeof(u_short)) { printf(" ** Data truncated **\n"); MARK_WINDOW_NONE(); MARK_SAVED_WIN(saveind, saveleftedge, origclen-1); finish(); return; } class = _getshort(cp); INCR_PTRS(sizeof(u_short)); str = p_class(class); printf("qclass = %s\n", str); MARK_WINDOW(); putchar('\n'); MARK_WINDOW_NONE(); } MARK_SAVED_WIN(saveind, saveleftedge, ledge-1); } if (ancount) { printf("--- Answer Section ---\n\n"); saveind = index++; saveleftedge = ledge; MARK_WINDOW_NONE(); /* once through loop for each answer */ while (ancount-- > 0) { /* print the answer, p_ans() normally handles the MARKs */ if (p_ans(msg, eom) == FAIL) { MARK_SAVED_WIN(saveind, saveleftedge, origclen-1); finish(); return; } putchar('\n'); MARK_WINDOW_NONE(); } MARK_SAVED_WIN(saveind, saveleftedge, ledge-1); } /* format is exactly the same as Answer Section */ if (nscount) { printf("--- Authority Section ---\n\n"); saveind = index++; saveleftedge = ledge; MARK_WINDOW_NONE(); while (nscount-- > 0) { if (p_ans(msg, eom) == FAIL) { MARK_SAVED_WIN(saveind, saveleftedge, origclen-1); finish(); return; } putchar('\n'); MARK_WINDOW_NONE(); } MARK_SAVED_WIN(saveind, saveleftedge, ledge-1); } /* format is exactly the same as Answer Section */ if (arcount) { printf("--- Additional Section ---\n\n"); saveind = index++; saveleftedge = ledge; MARK_WINDOW_NONE(); while (arcount-- > 0) { if (p_ans(msg, eom) == FAIL) { MARK_SAVED_WIN(saveind, saveleftedge, origclen-1); finish(); return; } putchar('\n'); MARK_WINDOW_NONE(); } MARK_SAVED_WIN(saveind, saveleftedge, ledge-1); } finish(); return; }