/*
 * 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 <netdb.h>
#include <arpa/nameser.h>

#ifndef T_TXT
#define T_TXT           16              /* text record */
#endif

#ifndef C_HS
#define C_HS            4               /* for Hesiod type (MIT) */
#endif

#include <netinet/in.h>

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(<how many octets consumed>).
 * 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("    <binary data, see hex dump below>\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 = <no data>\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;
}


syntax highlighted by Code2HTML, v. 0.9.1