/*
* 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