home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mskermit / msndns.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  16KB  |  603 lines

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