home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / internet / tcpipsrc / DNS / c / dns next >
Encoding:
Text File  |  1994-12-21  |  14.1 KB  |  694 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <ctype.h>
  5.  
  6. #include "vterm.h"
  7. #include "dnsfn.h"
  8.  
  9. #include "bbc.h"
  10.  
  11. static char *type2s[] =
  12.   { "", "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG",
  13.   "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT" };
  14.  
  15.  
  16. static char *rcode2s[] =
  17.   { "OK", "BADFORM", "SERVFAIL", "NAMEERR", "NOTIMP", "REFUSED" };
  18.  
  19.  
  20. static char *strdup(char *s)
  21. {
  22.   char *p;
  23.   if (p = (char *)malloc(strlen(s)+1), p!=NULL)
  24.     strcpy(p, s);
  25.  
  26.   return p;
  27. }
  28.  
  29.  
  30. static void strfree(char *s)
  31. {
  32.   if (s)
  33.     free(s);
  34. }
  35.  
  36.  
  37. static char *skip_name(char *p)
  38. {
  39.   for (; *p != 0x00 && *p != 0xc0; p++);
  40.   if (*p == 0xC0) p++;
  41.   return ++p;
  42. }
  43.  
  44.  
  45. static void put_name(char *buf, char *name)
  46. {
  47.   int a, b, p, q;
  48.  
  49.   a = 0; p = 0; q = 0;
  50.  
  51.   while (name[p]>=' ')
  52.   {
  53.     b = a+1;
  54.     while (name[p]!='.' && name[p]>=' ')
  55.     {
  56.       buf[b++] = name[p++];
  57.     }
  58.     buf[a] = p-q;
  59.     p += (name[p]=='.');
  60.     q = p;
  61.     a = b;
  62.   }
  63.   buf[a] = 0;
  64. }
  65.  
  66. static void put_uint8(char *p, uint8 n)
  67. {
  68.   p[0] = n;
  69. }
  70.  
  71. static void put_uint16(char *p, uint16 n)
  72. {
  73.   p[0] = (n >> 8) & 0xff;
  74.   p[1] = n & 0xff;
  75. }
  76.  
  77. static void put_uint32(char *p, uint32 n)
  78. {
  79.   p[0] = (n >> 24) & 0xff;
  80.   p[1] = (n >> 16) & 0xff;
  81.   p[2] = (n >> 8) & 0xff;
  82.   p[3] = n & 0xff;
  83. }
  84.  
  85.  
  86. static uint8 get_uint8(char *p)
  87. {
  88.   return p[0];
  89. }
  90.  
  91.  
  92. static uint16 get_uint16(char *p)
  93. {
  94.   return (p[0]<<8) | p[1] ;
  95. }
  96.  
  97.  
  98. static uint32 get_uint32(char *p)
  99. {
  100.   return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3] ;
  101. }
  102.  
  103. static void get_name(char *dnsbuf, char *ptr, char *name)
  104. {
  105.   int n;
  106.   int offset;
  107.  
  108.   while (*ptr != 0)
  109.   {
  110.     if ((*ptr & 0xc0) == 0xc0)
  111.     {
  112.       offset = (int)get_uint16(ptr) & 0x3fff;
  113.       ptr = dnsbuf + offset;
  114.     }
  115.     else
  116.     {
  117.       n = *ptr++;
  118.       while (n-- > 0) *name++ = *ptr++;
  119.       *name++ = '.';
  120.     }
  121.   }
  122.   *name++ = '\0';
  123. }
  124.  
  125. static uint32 aton(char *s)
  126. {
  127.   int i;
  128.   uint32 n;
  129.  
  130.   n = 0;
  131.   for (i=24; i>=0; i -= 8 )
  132.   {
  133.     n |= (uint32)atoi(s) << i;
  134.     if ( (s = strchr(s,'.')) == NULL)
  135.       break;
  136.     s++;
  137.   }
  138.   return n;
  139. }
  140.  
  141. /* Address class */
  142. int ntoclass(uint32 n)
  143. {
  144.   int c;
  145.  
  146.   while (n & 0x80000000)
  147.   {
  148.     n <<= 1;
  149.     ++ c;
  150.   }
  151.   return c;
  152. }
  153.  
  154.  
  155. static char *ntoinarpa(uint32 n, char *buf)
  156. {
  157.   sprintf(buf, "%d.%d.%d.%d.in-addr.arpa",
  158.     n & 0xff,
  159.     (n >> 8) & 0xff,
  160.     (n >> 16) & 0xff,
  161.     (n >> 24) & 0xff );
  162.  
  163.   return buf;
  164. }
  165.  
  166. static char *gntoinarpa(uint32 n, char *buf)
  167. {
  168.   int c = ntoclass(n);
  169.  
  170.   if (c==0)
  171.     sprintf(buf, "%d.in-addr.arpa",
  172.       (n >> 24) & 0xff );
  173.  
  174.   else if (c==1)
  175.     sprintf(buf, "%d.%d.in-addr.arpa",
  176.       (n >> 16) & 0xff,
  177.       (n >> 24) & 0xff );
  178.  
  179.   else if (c==2)
  180.     sprintf(buf, "%d.%d.%d.in-addr.arpa",
  181.       (n >> 8) & 0xff,
  182.       (n >> 16) & 0xff,
  183.       (n >> 24) & 0xff );
  184.  
  185.   else
  186.     sprintf(buf, "%d.%d.%d.%d.in-addr.arpa",
  187.       n & 0xff,
  188.       (n >> 8) & 0xff,
  189.       (n >> 16) & 0xff,
  190.       (n >> 24) & 0xff );
  191.  
  192.   return buf;
  193. }
  194.  
  195. static char *make_query(char *buf, char *name, uint16 qclass, uint16 qtype)
  196. {
  197.   char *p;
  198.  
  199.   p = buf;
  200.  
  201.   put_name(p, name);  p = skip_name(p);
  202.  
  203.   put_uint16(p, qtype);  p+=2;
  204.   put_uint16(p, qclass);  p+=2;
  205.  
  206.   return p;
  207. }
  208.  
  209.  
  210. static char *make_header(char *buf, int rd, uint8 opcode)
  211. {
  212.   char *p;
  213.   static uint16 id = 1;
  214.  
  215.   p = buf;
  216.  
  217.   rd = (rd!=0);
  218.  
  219.   put_uint16(p, id++);  p+=2;
  220.   put_uint16(p, (rd << 8) | (opcode << 11));  p+=2;
  221.   put_uint16(p, 1);  p+=2;
  222.   put_uint16(p, 0);  p+=2;
  223.   put_uint16(p, 0);  p+=2;
  224.   put_uint16(p, 0);  p+=2;
  225.   return p;
  226. }
  227.  
  228.  
  229. static char *skip_rr(char *p)
  230. {
  231.   p = skip_name(p)+8;
  232.   return p+get_uint16(p)+2;
  233. }
  234.  
  235.  
  236. static rr_str *extract_rr(char *dnsbuf, char *p)
  237. {
  238.   int i;
  239.   uint16 rdlen;
  240.   char name[256];
  241.   rr_str *rr;
  242.  
  243.   if (rr = (rr_str *)malloc(sizeof(rr_str)), rr==NULL)
  244.     return NULL;
  245.  
  246.   memset(rr, '\0', sizeof(rr_str));
  247.  
  248.   get_name(dnsbuf, p, name);  p = skip_name(p);
  249.  
  250.   if (rr->name = strdup(name), rr->name==NULL)
  251.     goto err1;
  252.  
  253.   rr->type = get_uint16(p);  p+=2;
  254.   rr->class = get_uint16(p);  p+=2;
  255.   rr->ttl = get_uint32(p);  p+=4;
  256.   rdlen = get_uint16(p);  p+=2;
  257.  
  258.   switch (rr->type)
  259.   {
  260.     case TYPE_NS:
  261.     case TYPE_MD:
  262.     case TYPE_MF:
  263.     case TYPE_CNAME:
  264.     case TYPE_MB:
  265.     case TYPE_MG:
  266.     case TYPE_MR:
  267.     case TYPE_PTR:
  268.     case TYPE_TXT:
  269.       get_name(dnsbuf, p, name);  p = skip_name(p);
  270.       rr->rdata.txt.text = strdup(name);
  271.       break;
  272.  
  273.     case TYPE_A:
  274.       rr->rdata.a.addr = get_uint32(p);  p+=4;
  275.       break;
  276.  
  277.     case TYPE_SOA:
  278.       get_name(dnsbuf, p, name);  p = skip_name(p);
  279.       rr->rdata.soa.mname = strdup(name);
  280.       get_name(dnsbuf, p, name);  p = skip_name(p);
  281.       /* Replace 1st '.' in name with '@' to make an email address */
  282.       for (i=0; name[i]!='.' && name[i]!='\0'; i++);
  283.       if (name[i]=='.')
  284.         name[i] = '@';
  285.       rr->rdata.soa.rname = strdup(name);
  286.       rr->rdata.soa.serial = get_uint32(p);  p+=4;
  287.       rr->rdata.soa.refresh = get_uint32(p);  p+=4;
  288.       rr->rdata.soa.retry = get_uint32(p);  p+=4;
  289.       rr->rdata.soa.expire = get_uint32(p);  p+=4;
  290.       rr->rdata.soa.minttl = get_uint32(p);  p+=4;
  291.       break;
  292.  
  293.     case TYPE_NULL:
  294.       if (rr->rdata.null.data = (char *)malloc(rdlen), rr->rdata.null.data!=NULL)
  295.       {
  296.         rr->rdata.null.len = rdlen;
  297.         memcpy(rr->rdata.null.data, p, rdlen);
  298.       }
  299.       break;
  300.  
  301.     case TYPE_HINFO:
  302.       get_name(dnsbuf, p, name);  p = skip_name(p);
  303.       rr->rdata.hinfo.cpu = strdup(name);
  304.       get_name(dnsbuf, p, name);  p = skip_name(p);
  305.       rr->rdata.hinfo.os = strdup(name);
  306.       break;
  307.  
  308.     case TYPE_MINFO:
  309.       get_name(dnsbuf, p, name);  p = skip_name(p);
  310.       rr->rdata.minfo.rmbx = strdup(name);
  311.       get_name(dnsbuf, p, name);  p = skip_name(p);
  312.       rr->rdata.minfo.embx = strdup(name);
  313.       break;
  314.  
  315.     case TYPE_MX:
  316.       rr->rdata.mx.pref = get_uint16(p);  p+=2;
  317.       get_name(dnsbuf, p, name);  p = skip_name(p);
  318.       rr->rdata.mx.name = strdup(name);
  319.       break;
  320.  
  321.     case TYPE_WKS:
  322.       rr->rdata.wks.addr = get_uint32(p);  p+=4;
  323.       rr->rdata.wks.protocol = get_uint8(p); p+=1;
  324.       if (rr->rdata.null.data = (char *)malloc(rdlen-5), rr->rdata.null.data!=NULL)
  325.       {
  326.         rr->rdata.wks.len = rdlen-5;
  327.         memcpy(rr->rdata.null.data, p, rdlen-5);
  328.       }
  329.       break;
  330.   }
  331.  
  332.   return rr;
  333.  
  334. err2:
  335.   free(rr->name);
  336. err1:
  337.   free(rr);
  338.   return NULL;
  339. }
  340.  
  341.  
  342. static void show_rr(vterm vt, rr_str *rr)
  343. {
  344.   int i, j, n;
  345.  
  346.   if (!rr)
  347.     return;
  348.  
  349.   if (rr->type<=TYPE_TXT)
  350.     vterm_printf(vt, -1, "%-24s %-5s ", rr->name, type2s[rr->type]);
  351.   else
  352.     vterm_printf(vt, -1, "%-24s %5d ", rr->name, rr->type);
  353.   vterm_printf(vt, -1, "%-8d ", rr->ttl);
  354.  
  355.   switch (rr->type)
  356.   {
  357.     case TYPE_NS:
  358.     case TYPE_MD:
  359.     case TYPE_MF:
  360.     case TYPE_CNAME:
  361.     case TYPE_MB:
  362.     case TYPE_MG:
  363.     case TYPE_MR:
  364.     case TYPE_PTR:
  365.     case TYPE_TXT:
  366.       vterm_printf(vt, -1, "%s\n",rr->rdata.txt.text);
  367.       break;
  368.  
  369.     case TYPE_A:
  370.       vterm_printf(vt, -1, "     %d.%d.%d.%d\n",
  371.         (rr->rdata.a.addr >> 24) & 0xff,
  372.         (rr->rdata.a.addr >> 16) & 0xff,
  373.         (rr->rdata.a.addr >> 8) & 0xff,
  374.         rr->rdata.a.addr & 0xff );
  375.       break;
  376.  
  377.     case TYPE_SOA:
  378.       vterm_printf(vt, -1, "    %s\n",rr->rdata.soa.mname);
  379.       vterm_printf(vt, -1, "    %s\n",rr->rdata.soa.rname);
  380.       vterm_printf(vt, -1, "    %10d %10d %10d %10d %10d\n",
  381.         rr->rdata.soa.serial, rr->rdata.soa.refresh, rr->rdata.soa.retry,
  382.         rr->rdata.soa.expire, rr->rdata.soa.minttl );
  383.       break;
  384.  
  385.     case TYPE_NULL:
  386.       for (i = 0; i<rr->rdata.null.len; i+=8)
  387.       {
  388.         vterm_printf(vt, -1, "    %.4x: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x ", i,
  389.           rr->rdata.null.data[i+0], rr->rdata.null.data[i+1], rr->rdata.null.data[i+2], rr->rdata.null.data[i+3],
  390.           rr->rdata.null.data[i+4], rr->rdata.null.data[i+5], rr->rdata.null.data[i+6], rr->rdata.null.data[i+7] );
  391.  
  392.         vterm_printf(vt, -1, "%c%c%c%c%c%c%c%c",
  393.           PRASC(rr->rdata.null.data[i+0]), PRASC(rr->rdata.null.data[i+1]),
  394.           PRASC(rr->rdata.null.data[i+2]), PRASC(rr->rdata.null.data[i+3]),
  395.           PRASC(rr->rdata.null.data[i+4]), PRASC(rr->rdata.null.data[i+5]),
  396.           PRASC(rr->rdata.null.data[i+6]), PRASC(rr->rdata.null.data[i+7]) );
  397.       }
  398.       vterm_printf(vt, -1, "\n");
  399.       break;
  400.  
  401.     case TYPE_HINFO:
  402.       vterm_printf(vt, -1, "%s %s\n", rr->rdata.hinfo.cpu, rr->rdata.hinfo.os);
  403.       break;
  404.  
  405.     case TYPE_MINFO:
  406.       vterm_printf(vt, -1, "    %s\n", rr->rdata.minfo.rmbx);
  407.       vterm_printf(vt, -1, "    %s\n", rr->rdata.minfo.embx);
  408.       break;
  409.  
  410.     case TYPE_MX:
  411.       vterm_printf(vt, -1, "%6d %s\n", rr->rdata.mx.pref, rr->rdata.mx.name);
  412.       break;
  413.  
  414.     case TYPE_WKS:
  415.       vterm_printf(vt, -1, "%d.%d.%d.%d\n",
  416.         (rr->rdata.wks.addr >> 24) & 0xff,
  417.         (rr->rdata.wks.addr >> 16) & 0xff,
  418.         (rr->rdata.wks.addr >> 8) & 0xff,
  419.         rr->rdata.wks.addr & 0xff );
  420.       vterm_printf(vt, -1, "    Services available on %d follow:\n", rr->rdata.wks.protocol);
  421.       n = 4;
  422.       for (i = 0; i<rr->rdata.wks.len; i++)
  423.       {
  424.         for (j = 0; j<8; j++)
  425.         {
  426.           if (rr->rdata.wks.data[i] & (2<<j))
  427.           {
  428.             vterm_printf(vt, -1, "%d", i*8+j);
  429.             n += 8;
  430.           }
  431.           if (n>68)
  432.           {
  433.             n = 4;
  434.             vterm_printf(vt, -1, "\n    ");
  435.           }
  436.         }
  437.       }
  438.       vterm_printf(vt, -1, "\n");
  439.       break;
  440.  
  441.     default:
  442.       vterm_printf(vt, -1, "\n");
  443.       break;
  444.   }
  445. }
  446.  
  447. static void free_rr(rr_str *rr)
  448. {
  449.   if (!rr)
  450.     return;
  451.  
  452.   switch (rr->type)
  453.   {
  454.     case TYPE_NS:
  455.     case TYPE_MD:
  456.     case TYPE_MF:
  457.     case TYPE_CNAME:
  458.     case TYPE_MB:
  459.     case TYPE_MG:
  460.     case TYPE_MR:
  461.     case TYPE_PTR:
  462.     case TYPE_TXT:
  463.       strfree(rr->rdata.txt.text);
  464.       break;
  465.  
  466.     case TYPE_A:
  467.       break;
  468.  
  469.     case TYPE_SOA:
  470.       strfree(rr->rdata.soa.mname);
  471.       strfree(rr->rdata.soa.rname);
  472.       break;
  473.  
  474.     case TYPE_NULL:
  475.       if (rr->rdata.null.data)
  476.         free(rr->rdata.null.data);
  477.       break;
  478.  
  479.     case TYPE_HINFO:
  480.       strfree(rr->rdata.hinfo.cpu);
  481.       strfree(rr->rdata.hinfo.os);
  482.       break;
  483.  
  484.     case TYPE_MINFO:
  485.       strfree(rr->rdata.minfo.rmbx);
  486.       strfree(rr->rdata.minfo.embx);
  487.       break;
  488.  
  489.     case TYPE_MX:
  490.       strfree(rr->rdata.mx.name);
  491.       break;
  492.  
  493.     case TYPE_WKS:
  494.       if (rr->rdata.null.data)
  495.         free(rr->rdata.null.data);
  496.       break;
  497.   }
  498.  
  499.   free(rr);
  500. }
  501.  
  502. static char *skip_query(char *p)
  503. {
  504.   return skip_name(p) + 4;
  505. }
  506.  
  507. char *dns_encode_query(vterm vt, char *host, char *type)
  508. {
  509.   int n, t;
  510.   FILE *fp;
  511.   char name[256];
  512.   char *p, *query;
  513.  
  514.   n = strlen(type);
  515.  
  516.   if (isdigit(*type))
  517.     t = atoi(type);
  518.   else if (!strncmp(type, "a", n))
  519.     t = TYPE_A;
  520.   else if (!strncmp(type, "ms", n))
  521.     t = TYPE_MX;
  522.   else if (!strncmp(type, "ns", n))
  523.     t = TYPE_NS;
  524.   else if (!strncmp(type, "cname", n))
  525.     t = TYPE_CNAME;
  526.   else if (!strncmp(type, "ptr", n))
  527.     t = TYPE_PTR;
  528.   else if (!strncmp(type, "wks", n))
  529.     t = TYPE_WKS;
  530.   else if (!strncmp(type, "soa", n))
  531.     t = TYPE_SOA;
  532.   else if (!strncmp(type, "txt", n))
  533.     t = TYPE_TXT;
  534.   else
  535.     t = TYPE_ALL;
  536.  
  537.   if (isdigit(*host))
  538.     ntoinarpa(aton(host), name);
  539.   else
  540.     strcpy(name, host);
  541.  
  542.   if (query = malloc(512), query==NULL)
  543.     return NULL;
  544.  
  545.   p = make_header(query+2, 1, OPCODE_STD);
  546.   p = make_query(p, name, CLASS_IN, t);
  547.  
  548.   put_uint16(query, p-(query+2));
  549.  
  550.   return query;
  551. }
  552.  
  553. int dns_decode_answer(vterm vt, char *answer, int length)
  554. {
  555.   int n;
  556.   uint16 id, flags, qdcount, ancount, nscount, arcount;
  557.   int rcode, opcode, qr, aa, tc, rd, ra, z;
  558.   char *p;
  559.   rr_str *rr;
  560.  
  561.   FILE *fp;
  562.  
  563.   char *opcode2s[] = {
  564.     "Standard query", "Inverse Query", "Server status req", "",
  565.     "", "", "", "",  "", "", "", "",  "", "", "", ""};
  566.  
  567.   char *rcode2s[] = {
  568.     "OK", "Syntax error", "Server failure", "Name unknown",
  569.     "Not implemeted", "Refused", "", "",
  570.     "", "", "", "",  "", "", "", ""};
  571.  
  572.   p = answer;
  573.  
  574.   id      = get_uint16(p);  p+=2;
  575.   flags   = get_uint16(p);  p+=2;
  576.   qdcount = get_uint16(p);  p+=2;
  577.   ancount = get_uint16(p);  p+=2;
  578.   nscount = get_uint16(p);  p+=2;
  579.   arcount = get_uint16(p);  p+=2;
  580.  
  581.   rcode  = flags & 0x0f;
  582.   z      = (flags >> 4) & 0x07;
  583.   ra     = (flags >> 7) & 0x01;
  584.   rd     = (flags >> 8) & 0x01;
  585.   tc     = (flags >> 9) & 0x01;
  586.   aa     = (flags >> 10) & 0x01;
  587.   opcode = (flags >> 11) & 0x0f;
  588.   qr     = (flags >> 15) & 0x01;
  589.  
  590.   vterm_printf(vt, -1, "%s", (qr)?((aa)?"Authoritive Answer":"Answer"):"Query");
  591.   if (tc)
  592.     vterm_printf(vt, -1, " (truncated)");
  593.   if (rd)
  594.     vterm_printf(vt, -1, " (recursion wanted)");
  595.   if (rd)
  596.     vterm_printf(vt, -1, " (recursion available)");
  597.  
  598.   vterm_printf(vt, -1, "\n");
  599.  
  600.   vterm_printf(vt, -1, "Op code:       %2d (%s)\n", opcode, opcode2s[opcode]);
  601.   vterm_printf(vt, -1, "Response code: %2d (%s)\n", rcode, rcode2s[rcode]);
  602.  
  603.   vterm_printf(vt, -1, "\nReceived:");
  604.   vterm_printf(vt, -1, "%d queries, %d answers, %d name server records\n", qdcount, ancount, nscount);
  605.   vterm_printf(vt, -1, "  and %d additional records\n", arcount);
  606.  
  607.   vterm_printf(vt, -1, "\n");
  608.  
  609.   if (qdcount)
  610.     p = skip_query(p);
  611.  
  612.   if (ancount)
  613.     vterm_printf(vt, -1, "Answers:\n");
  614.  
  615.   for (n = 0; n<ancount; n++)
  616.   {
  617.     rr = extract_rr(answer, p);
  618.     p = skip_rr(p);
  619.     show_rr(vt, rr);
  620.     free_rr(rr);
  621.   }
  622.   vterm_printf(vt, -1, "\n");
  623.  
  624.   if (nscount)
  625.     vterm_printf(vt, -1, "Name Servers:\n");
  626.  
  627.   for (n = 0; n<nscount; n++)
  628.   {
  629.     rr = extract_rr(answer, p);
  630.     p = skip_rr(p);
  631.     show_rr(vt, rr);
  632.     free_rr(rr);
  633.   }
  634.   vterm_printf(vt, -1, "\n");
  635.  
  636.   if (arcount)
  637.     vterm_printf(vt, -1, "Additional Records:\n");
  638.  
  639.   for (n = 0; n<arcount; n++)
  640.   {
  641.     rr = extract_rr(answer, p);
  642.     p = skip_rr(p);
  643.     show_rr(vt, rr);
  644.     free_rr(rr);
  645.   }
  646.   vterm_printf(vt, -1, "\n");
  647.  
  648.   return 1; /* Answer received and decoded */
  649. }
  650.  
  651. static int vt_handler(void *handle, char *buf, int len)
  652. {
  653.   vterm vt = (vterm)handle;
  654.  
  655.   if (buf==NULL && len==-1)
  656.     vterm_destroy(vt);
  657.  
  658.   return 1;
  659. }
  660.  
  661. void dns_test(void)
  662. {
  663.   int s;
  664.   FILE *fp;
  665.   vterm vt;
  666.   char *answer;
  667.  
  668.   if (fp = fopen("<TCPIP$Dir>.Answer", "rb"), fp == NULL)
  669.     return;
  670.  
  671.   fseek(fp, 0, SEEK_END);
  672.   s = (int)ftell(fp);
  673.   fseek(fp, 0, SEEK_SET);
  674.  
  675.   if (answer = (char *)malloc(s), answer==NULL)
  676.   {
  677.     fclose(fp);
  678.     return;
  679.   }
  680.  
  681.   fread(answer, 1, s, fp);
  682.   fclose(fp);
  683.  
  684.   vt = vterm_create(VTSW_VIEW|VTSW_NEWLINE);
  685.   vterm_title(vt, "DNS Query Test");
  686.   vterm_resize(vt, 80, 200);
  687.   vterm_visible(vt, 80, 24);
  688.   vterm_register_handler(vt, vt_handler, vt);
  689.  
  690.   dns_decode_answer(vt, answer+2, s-2);
  691.  
  692.   free(answer);
  693. }
  694.