home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msr313src.zip / msndns.c < prev    next >
C/C++ Source or Header  |  1993-07-12  |  16KB  |  585 lines

  1. /* File MSNDNS.C
  2.  * Domain name server requester
  3.  *
  4.  * Copyright (C) 1991, University of Waterloo.
  5.  * Copyright (C) 1985, 1993, Trustees of Columbia University in the 
  6.  * City of New York.  Permission is granted to any individual or institution
  7.  * to use this software as long as it is not sold for profit.  This copyright
  8.  * notice must be retained.  This software may not be included in commercial
  9.  * products without written permission of Columbia University.
  10.  *
  11.  * Original version created by Erick Engelke of the University of
  12.  *  Waterloo, Waterloo, Ontario, Canada.
  13.  * Adapted and modified for MS-DOS Kermit by Joe R. Doupnik, 
  14.  *  Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet.
  15.  *
  16.  * Last edit
  17.  * 17 June 1993 v3.13
  18.  *
  19.  * Originally based on NCSA Telnet
  20.  *
  21.  */
  22.  
  23. #include "msntcp.h"
  24. #include "msnlib.h"
  25.  
  26. byte *def_domain;
  27.  
  28. longword def_nameservers[ MAX_NAMESERVERS ];
  29. int last_nameserver;
  30. extern int icmp_unreach;        /* catch ICMP unreachable msgs */
  31.  
  32. static udp_Socket *dom_sock;
  33.  
  34. #define DNS_PORT 53        /* Domain Name Service lookup port */
  35. #define DOMSIZE 512         /* maximum domain message size to mess with */
  36.  
  37. /*
  38.  *  Header for the DOMAIN queries
  39.  */
  40. struct dhead {
  41.     word    ident,        /* unique identifier */
  42.         flags,
  43.         qdcount,    /* question section, # of entries */
  44.         ancount,    /* answers, how many */
  45.         nscount,    /* count of name server Respsonse Records */
  46.         arcount;    /* number of "additional" records */
  47. };
  48.  
  49. /*
  50.  *  flag masks for the flags field of the DOMAIN header
  51.  */
  52. #define DQR        0x8000    /* query = 0, response = 1 */
  53. #define DOPCODE        0x7100    /* opcode, see below */
  54. #define DAA        0x0400    /* Authoritative answer */
  55. #define DTC        0x0200    /* Truncation, response was cut off at 512 */
  56. #define DRD        0x0100    /* Recursion desired */
  57. #define DRA        0x0080    /* Recursion available */
  58. #define DRCODE        0x000F    /* response code, see below */
  59.  
  60. /* opcode possible values: */
  61. #define DOPQUERY    0    /* a standard query */
  62. #define DOPIQ        1    /* an inverse query */
  63. #define DOPCQM        2    /* a completion query, multiple reply */
  64. #define DOPCQU        3         /* a completion query, single reply */
  65. /* the rest reserved for future */
  66.  
  67. /* legal response codes: */
  68. #define DROK    0        /* okay response */
  69. #define DRFORM    1        /* format error */
  70. #define DRFAIL    2        /* their problem, server failed */
  71. #define DRNAME    3        /* name error, we know name doesn't exist */
  72. #define DRNOPE    4        /* no can do request */
  73. #define DRNOWAY    5        /* name server refusing to do request */
  74.  
  75. #define DTYPEA        1    /* host address resource record (RR) */
  76. #define DTYPEPTR    12    /* a domain name ptr */
  77.  
  78. #define DIN        1    /* ARPA Internet class */
  79. #define DWILD        255    /* wildcard for several classifications */
  80.  
  81. /*
  82.  *  a resource record is made up of a compressed domain name followed by
  83.  *  this structure.  All of these ints need to be byteswapped before use.
  84.  */
  85. struct rrpart {
  86.     word       rtype,        /* resource record type = DTYPEA */
  87.         rclass;        /* RR class = DIN */
  88.     longword    ttl;        /* time-to-live, changed to 32 bits */
  89.     word    rdlength;    /* length of next field */
  90.     byte     rdata[DOMSIZE];    /* data field */
  91. };
  92.  
  93. static int packdom(byte *, byte *);
  94. static int unpackdom(byte *, byte *, byte *);
  95. static word sendom(byte *, longword, word);
  96. static longword Sdomain(byte *, int, longword, byte *);
  97. static int countpaths(byte *);
  98. static int ddextract(struct useek *, longword *);
  99. static byte * getpath(byte *, int);
  100. static byte * nextdomain(byte *, int);
  101.  
  102. /*
  103.  *  data for domain name lookup
  104.  */
  105. static struct useek {
  106.     struct    dhead h;
  107.     byte    x[DOMSIZE];
  108. } *question;
  109.  
  110. static void
  111. qinit() {
  112.     question->h.flags = htons(DRD);
  113.     question->h.qdcount = htons(1);
  114.     question->h.ancount = 0;
  115.     question->h.nscount = 0;
  116.     question->h.arcount = 0;
  117. }
  118.  
  119. int 
  120. add_server(int *counter, int max, longword *array, longword value)
  121. {
  122.     if (array == NULL) return (0);            /* failure */
  123.     if (value && (*counter < max))
  124.         array[ (*counter)++ ] = value;
  125.     return (1);
  126. }
  127.  
  128. /*********************************************************************/
  129. /*  packdom
  130.  *   pack a regular text string into a packed domain name, suitable
  131.  *   for the name server.
  132.  *
  133.  *   returns length
  134. */
  135. static int
  136. packdom(byte *dst, byte *src)
  137. {
  138.     register byte *p, *q;
  139.     byte *savedst;
  140.     int i, dotflag, defflag;
  141.  
  142.     if (dst == NULL || src == NULL) return (0);        /* failure */
  143.     dotflag = defflag = 0;
  144.     p = src;
  145.     savedst = dst;
  146.  
  147.     do {            /* copy whole string */
  148.     *dst = 0;
  149.     q = dst + 1;
  150.     while (*p && (*p != '.'))
  151.         *q++ = *p++;
  152.  
  153.     i = p - src;
  154.     if (i > 0x3f)            /* if string is too long */
  155.         return (-1);
  156.     *dst = (byte)i;            /* leading length byte */
  157.     *q = 0;
  158.  
  159.     if (*p) {            /* if a next field */
  160.         dotflag = 1;        /* say dot seen in string */
  161.         src = ++p;
  162.         dst = q;
  163.     }
  164.     else if ((dotflag == 0) && (defflag == 0) && (def_domain != NULL)) {
  165.         p = def_domain;        /* continue packing with default */
  166.         defflag = 1;        /* say using default domain ext */
  167.         src = p;
  168.         dst = q;
  169.     }
  170.     }
  171.     while (*p);
  172.     q++;
  173.     return (q - savedst);        /* length of packed string */
  174. }
  175.  
  176. /*********************************************************************/
  177. /*  unpackdom
  178.  *  Unpack a compressed domain name that we have received from another
  179.  *  host.  Handles pointers to continuation domain names -- buf is used
  180.  *  as the base for the offset of any pointer which is present.
  181.  *  returns the number of bytes at src which should be skipped over.
  182.  *  Includes the NULL terminator in its length count.
  183.  */
  184. static int
  185. unpackdom(byte *dst, byte *src, byte *buf)
  186. {
  187.     register word i, j;
  188.     int retval;
  189.     byte *savesrc;
  190.  
  191.     if (src == NULL || dst == NULL || buf == NULL)
  192.         return (-1);    /* failure */
  193.     savesrc = src;
  194.     retval = 0;
  195.  
  196.     while (*src) {
  197.     j = *src & 0xff;            /* length byte */
  198.     while ((j & 0xC0) == 0xC0) {         /* while 14-bit pointer */
  199.         if (retval == 0)
  200.         retval = src - savesrc + 2;
  201.         src++;
  202.         src = &buf[(j & 0x3f)*256+(*src & 0xff)]; /* 14-bit pointer deref */
  203.         j = *src & 0xff;            /* new length byte */
  204.     }
  205.  
  206.     src++;                    /* assumes 6-bit count */
  207.     for (i = 0; i < (j & 0x3f); i++)
  208.         *dst++ = *src++;            /* copy counted string */
  209.     *dst++ = '.';                /* append a dot */
  210.     }
  211.     *(--dst) = 0;            /* add terminator */
  212.     src++;                /* account for terminator on src */
  213.  
  214.     if (retval == 0)
  215.     retval = src - savesrc;
  216.     return (retval);
  217. }
  218.  
  219. /*********************************************************************/
  220. /*  sendom
  221.  *   put together a domain lookup packet and send it
  222.  *   uses UDP port 53
  223.  *    num is used as identifier
  224.  */
  225. static word
  226. sendom(byte *s, longword towho, word num)
  227. {
  228.     word i, ulen;
  229.     register byte *psave;
  230.     register byte *p;
  231.  
  232.     psave = question->x;
  233.     i = packdom(question->x, s);    /* i = length of packed string */
  234.  
  235.     p = &(question->x[i]);
  236.     *p++ = 0;                /* high byte of qtype */
  237.     *p++ = DTYPEA;        /* number is < 256, so we know high byte=0 */
  238.     *p++ = 0;                /* high byte of qclass */
  239.     *p++ = DIN;                /* qtype is < 256 */
  240.  
  241.     question->h.ident = htons(num);
  242.     ulen = sizeof(struct dhead) + (p - psave);
  243.     if (udp_open(dom_sock, 0, towho, DNS_PORT) == 0)        /* failure */
  244.         return (0);                         /* fail*/
  245.  
  246.     if ((word)sock_write(dom_sock, (byte *)question, ulen) != ulen)
  247.         return (0);                        /* fail */
  248.  
  249.     return (ulen);
  250. }
  251.  
  252. static int 
  253. countpaths(byte * pathstring)
  254. {
  255.     register int count = 0;
  256.     register byte *p;
  257.  
  258.     for (p = pathstring; (*p != 0) || (*(p+1) != 0); p++)
  259.     if (*p == '.')
  260.         count++;
  261.  
  262.     return (++count);
  263. }
  264.  
  265. static byte *
  266. getpath(byte * pathstring, int whichone)
  267. /* pathstring    the path list to search */
  268. /* whichone    which path to get, starts at 1 */
  269. {
  270.     register byte *retval;
  271.  
  272.     if (pathstring == NULL) return (NULL);    /* failure */
  273.  
  274.     if (whichone > countpaths(pathstring))
  275.         return (NULL);
  276.     whichone--;
  277.     for (retval = pathstring; whichone > 0; retval++)
  278.         if (*retval == '.')
  279.             whichone--;
  280.     return (retval);
  281. }
  282.  
  283. /*********************************************************************/
  284. /*  ddextract
  285.  *   Extract the ip number from a response message.
  286.  *   Returns the appropriate status code and if the ip number is available,
  287.  *   and copies it into mip.
  288.  */
  289. static int
  290. ddextract(struct useek *qp, longword *mip)
  291. {
  292.     register int i;
  293.     int nans, rcode;
  294.     struct rrpart *rrp;
  295.     byte *p;
  296.     byte space[260];
  297.  
  298.     if (qp == NULL || mip == NULL) return (0);    /* failure */
  299.     memset(space, 0, sizeof(space));
  300.     nans = ntohs(qp->h.ancount);        /* number of answers */
  301.     rcode = DRCODE & ntohs(qp->h.flags);    /* return code for message*/
  302.  
  303.     if (rcode != 0)
  304.         return (rcode);
  305.  
  306.     if (nans == 0 || (ntohs(qp->h.flags) & DQR) == 0)
  307.         return (-1);         /* if no answer or no response flag */
  308.  
  309.     p = qp->x;                /* where question starts */
  310.     if ((i = unpackdom(space, p, (byte *)qp)) == -1)
  311.                         /* unpack question name */
  312.         return (-1);            /* failure to unpack */
  313.  
  314.     /*  spec defines name then  QTYPE + QCLASS = 4 bytes */
  315.     p += i + 4;
  316. /*
  317.  *  At this point, there may be several answers.  We will take the first
  318.  *  one which has an IP number.  There may be other types of answers that
  319.  *  we want to support later.
  320.  */
  321.     while (nans-- > 0) {            /* look at each answer */
  322.         if ((i = unpackdom(space, p, (byte *)qp)) == -1)
  323.                 /* answer name to unpack */
  324.             return (-1);            /* failure to unpack */
  325.         p += i;                /* account for string */
  326.         rrp = (struct rrpart *)p;        /* resource record here */
  327.         if (*p == 0 && *(p+1) == DTYPEA &&     /* correct type and class */
  328.         *(p+2) == 0 && *(p+3) == DIN) {
  329.         bcopy(&rrp->rdata, mip, 4);    /* save binary IP # */
  330.         return (0);            /* successful return */
  331.         }
  332.         p += 10 + ntohs(rrp->rdlength);    /* length of rest of RR */
  333.     }
  334.     return (-1);                /* generic failed to parse */
  335. }
  336.  
  337. /*********************************************************************/
  338. /*  getdomain
  339.  *   Look at the results to see if our DOMAIN request is ready.
  340.  *   It may be a timeout, which requires another query.
  341.  */
  342.  
  343. static longword 
  344. udpdom() {
  345.     register int i, uret;
  346.     longword desired;
  347.  
  348.     uret = sock_fastread(dom_sock, (byte *)question, sizeof(struct useek));
  349.  
  350.     if (uret == 0) return (0L);        /* fastread failed to read */
  351.  
  352.     /* check if the necessary information was in the UDP response */
  353.     i = ddextract(question, &desired);
  354.     switch (i)
  355.         {
  356.             case 0: return (ntohl(desired)); /* we found the IP number */
  357.             case 3:                /* name does not exist */
  358.             case -1:        /* strange ret code from ddextract */
  359.             default: return (0);        /* dunno */
  360.         }
  361. }
  362.  
  363.  
  364. /**************************************************************************/
  365. /*  Sdomain
  366.  *   DOMAIN based name lookup
  367.  *   query a domain name server to get an IP number
  368.  *    Returns the machine number of the machine record for future reference.
  369.  *   Events generated will have this number tagged with them.
  370.  *   Returns various negative numbers on error conditions.
  371.  *
  372.  *   if adddom is nonzero, add default domain
  373.  */
  374. static longword
  375. Sdomain(byte *mname, int adddom, longword nameserver, byte *timedout)
  376. /* *timedout    set to 1 on timeout */
  377. {
  378. #define NAMBUFSIZ 256
  379.     byte namebuff[NAMBUFSIZ];
  380.     int namlen;
  381.     register int i;
  382.     register byte *p;
  383.     byte *nextdomain(byte *, int);
  384.     longword response;
  385.  
  386.     response = 0;
  387.     *timedout = 1;            /* presume a timeout */
  388.  
  389.     if (nameserver == 0L)
  390.         {            /* no nameserver, give up now */
  391.         outs("\r\n No nameserver defined!");
  392.         return (0);
  393.         }
  394.  
  395.     while (*mname == ' ' || *mname == '\t') mname++;
  396.                         /* kill leading spaces */
  397.     if (*mname == '\0')        /* no host name, fail */
  398.         return (0L);
  399.  
  400.     qinit();            /* initialize some flag fields */
  401.  
  402.     namlen = strlen(mname);        /* Get length of name */
  403.     if (namlen >= NAMBUFSIZ || namlen == 0)    /* Check it before copying */
  404.         return (0L);
  405.  
  406.     strcpy(namebuff, mname);            /* OK to copy */
  407.     if(namebuff[strlen(namebuff) - 1] == '.')
  408.         namebuff[strlen(namebuff) - 1] = '\0';
  409.  
  410.     if (adddom > 0 && adddom <= countpaths(def_domain))
  411.         {                 /* there is a search list */
  412.         p = getpath(def_domain, adddom); /* get end of def_domain */
  413.         if (p != NULL)            /* if got something */
  414.             {
  415.             if (strlen(p) > (NAMBUFSIZ - namlen - 1))
  416.                 return (0L);        /* if too big */
  417.             if (*p != '.')            /* one dot please */
  418.                 strcat(namebuff, ".");
  419.             strcat(namebuff, p);    /* new name to try */
  420.             }
  421.             }
  422.  
  423.     outs("\r\n  trying name "); outs(namebuff); /* hand holder */
  424.  
  425.     /*
  426.      * This is not terribly good, but it attempts to use a binary
  427.      * exponentially increasing delays.
  428.      */
  429.     for (i = 2; i < 17; i *= 2)
  430.         {
  431.         if (sendom(namebuff, nameserver, 0xf001) == 0)    /* try UDP */
  432.             goto sock_err;            /* sendom() failed */
  433.         ip_timer_init(dom_sock, i);
  434.         do
  435.             {
  436.             if (icmp_unreach != 0)        /* unreachable? */
  437.                  goto sock_err;
  438.             if (tcp_tick(dom_sock) == 0)     /* read packets */
  439.                 goto sock_err;        /* socket is closed */
  440.             if (ip_timer_expired(dom_sock))
  441.                 break;            /* timeout */
  442.             if (sock_dataready(dom_sock))    /* have response */
  443.                 *timedout = 0;        /* say no timeout */
  444.             } while (*timedout);
  445.  
  446.         if (*timedout == 0) break;    /* got an answer */
  447.         }
  448.  
  449.     if (*timedout == 0)            /* if answer, else fall thru*/
  450.         {
  451.         response = udpdom();        /* process the received data*/
  452.         sock_close(dom_sock);
  453.         return (response);
  454.         }
  455.  
  456. sock_err:
  457.     outs("\r\n  Cannot reach name server ");
  458.     ntoa(namebuff, nameserver);    /* nameserver IP to dotted decimal */
  459.     outs(namebuff);            /* display nameserver's IP */
  460.     *timedout = 1;            /* say timeout */
  461.     sock_close(dom_sock);
  462.     return (0);
  463. }
  464.  
  465. /*
  466.  * nextdomain - given domain and count = 0,1,2,..., return next larger
  467.  *        domain or NULL when no more are available
  468.  */
  469. static byte *
  470. nextdomain(byte *domain, int count)
  471. {
  472.     register byte *p;
  473.     register int i;
  474.  
  475.     if ((p = domain) == NULL) return (NULL);    /* failure */
  476.     if (count < 0) return (NULL);
  477.  
  478.     for (i = 0; i < count; i++)
  479.         {
  480.         p = strchr(p, '.');
  481.         if (p == NULL) return (NULL);
  482.         p++;
  483.         }
  484.     return (p);
  485. }
  486.  
  487. static longword
  488. resolve2(byte *name)
  489. {            /* detailed worker for resolve() */
  490.     longword ip_address;
  491.     register int count;
  492.     register int i;
  493.     byte timeout;
  494.     struct useek qp;            /* temp buffer */
  495.     udp_Socket ds;                  /* working socket (big!) */
  496.  
  497.     question = &qp;
  498.     dom_sock = &ds;
  499.  
  500.     for (i = 0; i < last_nameserver; i++)    /* for each nameserver */
  501.         {
  502.         icmp_unreach = 0;    /* clear ICMP unreachable msg */
  503.         count = 0;        /* number domain extensions to add */
  504.         do            /* try name then name.extensions */
  505.             {
  506.             if (ip_address = Sdomain(name, count,
  507.                 def_nameservers[i], &timeout))
  508.                     return (ip_address);
  509.             if (timeout != 0)        /* no nameserver */
  510.                 break;             /* exit do-while */
  511.             } 
  512.         while (nextdomain(def_domain, count++) != NULL); /* are ext */
  513.         }
  514.     return (0L);            /* return failure, IP of 0L */
  515. }
  516.  
  517.  
  518. /*
  519.  * resolve()
  520.  *     convert domain name -> address resolution.
  521.  *     returns 0 if name is unresolvable right now
  522.  */
  523.  
  524. longword 
  525. resolve(byte *name)
  526. {
  527.     if (name == NULL) return (0L);
  528.  
  529.     if (isaddr(name) != 0)
  530.         return (aton(name));    /* IP numerical address */
  531.     return (resolve2(name));    /* call upon the worker */
  532. }
  533.  
  534. /*
  535.  * aton()
  536.  *    - converts [a.b.c.d] or a.b.c.d to 32 bit long
  537.  *    - returns 0 on error (safer than -1)
  538.  */
  539.  
  540. longword 
  541. aton(byte *text)
  542. {
  543.     register int i;
  544.     longword ip, j;
  545.  
  546.     ip = 0;
  547.     if (text == NULL) return (0L);        /* failure */
  548.  
  549.     if (*text == '[')
  550.         text++;
  551.     for (i = 24; i >= 0; i -= 8)
  552.         {
  553.         j = atoi(text) & 0xff;
  554.         ip |= (j << i);
  555.         while (*text != '\0' && *text != '.') text++;
  556.         if (*text == '\0')
  557.             break;
  558.         text++;
  559.         }
  560.     return (ip);
  561. }
  562.  
  563. /*
  564.  * isaddr
  565.  *    - returns nonzero if text is simply ip address
  566.  * such as 123.456.789.012 or [same] or 123 456 789 012 or [same]
  567.  */
  568. int 
  569. isaddr(byte *text)
  570. {
  571.     register byte ch;
  572.  
  573.     if (text == NULL) return (0);        /* failure */
  574.     while (ch = *text++)
  575.         {
  576.         if (('0' <= ch) && (ch <= '9'))
  577.             continue;    /* in digits */
  578.         if ((ch == '.') || (ch == ' ') || (ch == '[') || (ch == ']'))
  579.                 continue;            /* and in punct */
  580.         return (0);                /* failure */
  581.         }
  582.     return (1);
  583. }
  584.  
  585.