/* File MSNDNS.C * Domain name server requester * * Copyright (C) 1991, University of Waterloo. * Copyright (C) 1982, 1997, Trustees of Columbia University in the * City of New York. The MS-DOS Kermit software may not be, in whole * or in part, licensed or sold for profit as a software product itself, * nor may it be included in or distributed with commercial products * or otherwise distributed by commercial concerns to their clients * or customers without written permission of the Office of Kermit * Development and Distribution, Columbia University. This copyright * notice must not be removed, altered, or obscured. * * Original version created by Erick Engelke of the University of * Waterloo, Waterloo, Ontario, Canada. * Adapted and modified for MS-DOS Kermit by Joe R. Doupnik, * Utah State University, jrd@cc.usu.edu * * Last edit * 12 Jan 1995 v3.14 * * Originally based on NCSA Telnet * */ #include "msntcp.h" #include "msnlib.h" byte *def_domain; longword def_nameservers[ MAX_NAMESERVERS ]; int last_nameserver; extern int icmp_unreach; /* catch ICMP unreachable msgs */ static udp_Socket *dom_sock; #define DNS_PORT 53 /* Domain Name Service lookup port */ #define DOMSIZE 512 /* maximum domain message size to mess with */ /* * Header for the DOMAIN queries */ struct dhead { word ident, /* unique identifier */ flags, qdcount, /* question section, # of entries */ ancount, /* answers, how many */ nscount, /* count of name server Response Records */ arcount; /* number of "additional" records */ }; /* * flag masks for the flags field of the DOMAIN header */ #define DQR 0x8000 /* query = 0, response = 1 */ #define DOPCODE 0x7100 /* opcode, see below */ #define DAA 0x0400 /* Authoritative answer */ #define DTC 0x0200 /* Truncation, response was cut off at 512 */ #define DRD 0x0100 /* Recursion desired */ #define DRA 0x0080 /* Recursion available */ #define DRCODE 0x000F /* response code, see below */ /* opcode possible values: */ #define DOPQUERY 0 /* a standard query */ #define DOPIQ 1 /* an inverse query */ #define DOPCQM 2 /* a completion query, multiple reply */ #define DOPCQU 3 /* a completion query, single reply */ /* the rest reserved for future */ /* legal response codes: */ #define DROK 0 /* okay response */ #define DRFORM 1 /* format error */ #define DRFAIL 2 /* their problem, server failed */ #define DRNAME 3 /* name error, we know name doesn't exist */ #define DRNOPE 4 /* no can do request */ #define DRNOWAY 5 /* name server refusing to do request */ #define DTYPEA 1 /* host address resource record (RR) */ #define DTYPEPTR 12 /* a domain name ptr */ #define DIN 1 /* ARPA Internet class */ #define DWILD 255 /* wildcard for several classifications */ /* * a resource record is made up of a compressed domain name followed by * this structure. All of these ints need to be byteswapped before use. */ struct rrpart { word rtype, /* resource record type = DTYPEA */ rclass; /* RR class = DIN */ longword ttl; /* time-to-live, changed to 32 bits */ word rdlength; /* length of next field */ byte rdata[DOMSIZE]; /* data field */ }; static int packdom(byte *, byte *); static int unpackdom(byte *, byte *, byte *); static word sendom(byte *, longword, word); static longword Sdomain(byte *, int, longword, byte *); static int countpaths(byte *); static int ddextract(struct useek *, longword *); static byte * getpath(byte *, int); static byte * nextdomain(byte *, int); static int isaddr(byte *); /* * data for domain name lookup */ static struct useek { struct dhead h; byte x[DOMSIZE]; } *question; static void qinit() { question->h.flags = htons(DRD); question->h.qdcount = htons(1); question->h.ancount = 0; question->h.nscount = 0; question->h.arcount = 0; } int add_server(int *counter, int max, longword *array, longword value) { if (array == NULL) return (0); /* failure */ if (value && (*counter < max)) array[ (*counter)++ ] = value; return (1); } /*********************************************************************/ /* packdom * pack a regular text string into a packed domain name, suitable * for the name server. * * returns length */ static int packdom(byte *dst, byte *src) { register byte *p, *q; byte *savedst; int i, dotflag, defflag; if (dst == NULL || src == NULL) return (0); /* failure */ dotflag = defflag = 0; p = src; savedst = dst; do { /* copy whole string */ *dst = 0; q = dst + 1; while (*p && (*p != '.')) *q++ = *p++; i = p - src; if (i > 0x3f) /* if string is too long */ return (-1); *dst = (byte)i; /* leading length byte */ *q = 0; if (*p) { /* if a next field */ dotflag = 1; /* say dot seen in string */ src = ++p; dst = q; } else if ((dotflag == 0) && (defflag == 0) && (def_domain != NULL)) { p = def_domain; /* continue packing with default */ defflag = 1; /* say using default domain ext */ src = p; dst = q; } } while (*p); q++; return (q - savedst); /* length of packed string */ } /*********************************************************************/ /* unpackdom * Unpack a compressed domain name that we have received from another * host. Handles pointers to continuation domain names -- buf is used * as the base for the offset of any pointer which is present. * returns the number of bytes at src which should be skipped over. * Includes the NULL terminator in its length count. */ static int unpackdom(byte *dst, byte *src, byte *buf) { register word i, j; int retval; byte *savesrc; if (src == NULL || dst == NULL || buf == NULL) return (-1); /* failure */ savesrc = src; retval = 0; while (*src) { j = *src & 0xff; /* length byte */ while ((j & 0xC0) == 0xC0) { /* while 14-bit pointer */ if (retval == 0) retval = src - savesrc + 2; src++; src = &buf[(j & 0x3f)*256+(*src & 0xff)]; /* 14-bit pointer deref */ j = *src & 0xff; /* new length byte */ } src++; /* assumes 6-bit count */ for (i = 0; i < (j & 0x3f); i++) *dst++ = *src++; /* copy counted string */ *dst++ = '.'; /* append a dot */ } *(--dst) = 0; /* add terminator */ src++; /* account for terminator on src */ if (retval == 0) retval = src - savesrc; return (retval); } /*********************************************************************/ /* sendom * put together a domain lookup packet and send it * uses UDP port 53 * num is used as identifier */ static word sendom(byte *s, longword towho, word num) { word i, ulen; register byte *psave; register byte *p; psave = question->x; i = packdom(question->x, s); /* i = length of packed string */ p = &(question->x[i]); *p++ = 0; /* high byte of qtype */ *p++ = DTYPEA; /* number is < 256, so we know high byte=0 */ *p++ = 0; /* high byte of qclass */ *p++ = DIN; /* qtype is < 256 */ question->h.ident = htons(num); ulen = sizeof(struct dhead) + (p - psave); if (dom_sock->sisopen != SOCKET_OPEN) if (udp_open(dom_sock, 0, towho, DNS_PORT) == 0) /* failure */ return (0); /* fail*/ if ((word)sock_write(dom_sock, (byte *)question, ulen) != ulen) return (0); /* fail */ return (ulen); } static int countpaths(byte * pathstring) { register int count = 0; register byte *p; for (p = pathstring; (*p != 0) || (*(p+1) != 0); p++) if (*p == '.') count++; return (++count); } static byte * getpath(byte * pathstring, int whichone) /* pathstring the path list to search */ /* whichone which path to get, starts at 1 */ { register byte *retval; if (pathstring == NULL) return (NULL); /* failure */ if (whichone > countpaths(pathstring)) return (NULL); whichone--; for (retval = pathstring; whichone > 0; retval++) if (*retval == '.') whichone--; return (retval); } /*********************************************************************/ /* ddextract * Extract the ip number from a response message. * Returns the appropriate status code and if the ip number is available, * and copies it into mip. */ static int ddextract(struct useek *qp, longword *mip) { register int i; int nans, rcode; struct rrpart *rrp; byte *p; byte space[260]; if (qp == NULL || mip == NULL) return (0); /* failure */ memset(space, 0, sizeof(space)); nans = ntohs(qp->h.ancount); /* number of answers */ rcode = DRCODE & ntohs(qp->h.flags); /* return code for message*/ if (rcode != 0) return (rcode); if (nans == 0 || (ntohs(qp->h.flags) & DQR) == 0) return (-1); /* if no answer or no response flag */ p = qp->x; /* where question starts */ if ((i = unpackdom(space, p, (byte *)qp)) == -1) /* unpack question name */ return (-1); /* failure to unpack */ /* spec defines name then QTYPE + QCLASS = 4 bytes */ p += i + 4; /* * At this point, there may be several answers. We will take the first * one which has an IP number. There may be other types of answers that * we want to support later. */ while (nans-- > 0) { /* look at each answer */ if ((i = unpackdom(space, p, (byte *)qp)) == -1) /* answer name to unpack */ return (-1); /* failure to unpack */ p += i; /* account for string */ rrp = (struct rrpart *)p; /* resource record here */ if (*p == 0 && *(p+1) == DTYPEA && /* correct type and class */ *(p+2) == 0 && *(p+3) == DIN) { bcopy(&rrp->rdata, mip, 4); /* save binary IP # */ return (0); /* successful return */ } p += 10 + ntohs(rrp->rdlength); /* length of rest of RR */ } return (-1); /* generic failed to parse */ } /*********************************************************************/ /* getdomain * Look at the results to see if our DOMAIN request is ready. * It may be a timeout, which requires another query. */ static longword udpdom() { register int i, uret; longword desired; uret = sock_fastread(dom_sock, (byte *)question, sizeof(struct useek)); if (uret == 0) return (0L); /* fastread failed to read */ /* check if the necessary information was in the UDP response */ i = ddextract(question, &desired); switch (i) { case 0: return (ntohl(desired)); /* we found the IP number */ case 3: /* name does not exist */ case -1: /* strange ret code from ddextract */ default: return (0); /* dunno */ } } /**************************************************************************/ /* Sdomain * DOMAIN based name lookup * query a domain name server to get an IP number * Returns the machine number of the machine record for future reference. * Events generated will have this number tagged with them. * Returns various negative numbers on error conditions. * * if adddom is nonzero, add default domain */ static longword Sdomain(byte *mname, int adddom, longword nameserver, byte *timedout) /* *timedout set to 1 on timeout */ { #define NAMBUFSIZ 256 byte namebuff[NAMBUFSIZ]; int namlen; register int i; register byte *p; byte *nextdomain(byte *, int); longword response, timeout; response = 0; *timedout = 1; /* presume a timeout */ if (nameserver == 0L) { /* no nameserver, give up now */ outs("\r\n No nameserver defined!"); return (0); } while (*mname == ' ' || *mname == '\t') mname++; /* kill leading spaces */ if (*mname == '\0') /* no host name, fail */ return (0L); qinit(); /* initialize some flag fields */ namlen = strlen(mname); /* Get length of name */ if (namlen >= NAMBUFSIZ || namlen == 0) /* Check it before copying */ return (0L); strcpy(namebuff, mname); /* OK to copy */ if(namebuff[strlen(namebuff) - 1] == '.') namebuff[strlen(namebuff) - 1] = '\0'; if (adddom > 0 && adddom <= countpaths(def_domain)) { /* there is a search list */ p = getpath(def_domain, adddom); /* get end of def_domain */ if (p != NULL) /* if got something */ { if (strlen(p) > (NAMBUFSIZ - namlen - 1)) return (0L); /* if too big */ if (*p != '.') /* one dot please */ strcat(namebuff, "."); strcat(namebuff, p); /* new name to try */ } } outs("\r\n trying name "); outs(namebuff); /* hand holder */ /* * This is not terribly good, but it attempts to use a binary * exponentially increasing delays. */ for (i = 2; i < 17; i *= 2) { if (sendom(namebuff, nameserver, 0xf001) == 0) /* try UDP */ goto sock_err; /* sendom() failed */ timeout = set_timeout(i); do { if (icmp_unreach != 0) /* unreachable? */ goto sock_err; if (tcp_tick(dom_sock) == 0) /* read packets */ goto sock_err; /* socket is closed */ if (chk_timeout(timeout) == TIMED_OUT) break; /* timeout */ if (sock_dataready(dom_sock)) /* have response */ *timedout = 0; /* say no timeout */ if (chkcon() != 0) /* Control-Break */ goto sock_err; /* bail out */ } while (*timedout); if (*timedout == 0) break; /* got an answer */ } if (*timedout == 0) /* if answer, else fall thru*/ { response = udpdom(); /* process the received data*/ sock_close(dom_sock); return (response); } sock_err: outs("\r\n Cannot reach name server "); ntoa(namebuff, nameserver); /* nameserver IP to dotted decimal */ outs(namebuff); /* display nameserver's IP */ *timedout = 1; /* say timeout */ sock_close(dom_sock); while (chkcon()) ; /* consume extra ^Cs */ return (0); } /* * nextdomain - given domain and count = 0,1,2,..., return next larger * domain or NULL when no more are available */ static byte * nextdomain(byte *domain, int count) { register byte *p; register int i; if ((p = domain) == NULL) return (NULL); /* failure */ if (count < 0) return (NULL); for (i = 0; i < count; i++) { p = strchr(p, '.'); if (p == NULL) return (NULL); p++; } return (p); } static longword resolve2(byte *name) { /* detailed worker for resolve() */ longword ip_address; register int count; register int i; byte timeout; struct useek qp; /* temp buffer */ udp_Socket ds; /* working socket (big!) */ question = &qp; memset(&qp, 0, sizeof(struct useek)); dom_sock = &ds; memset(&ds, 0, sizeof(udp_Socket)); chkcon(); /* clear Control-Break flag */ for (i = 0; i < last_nameserver; i++) /* for each nameserver */ { icmp_unreach = 0; /* clear ICMP unreachable msg */ count = 0; /* number domain extensions to add */ do /* try name then name.extensions */ { if (ip_address = Sdomain(name, count, def_nameservers[i], &timeout)) return (ip_address); if (chkcon() != 0) /* Control-Break */ break; /* quit */ if (timeout != 0) /* no nameserver */ break; /* exit do-while */ } while (nextdomain(def_domain, count++) != NULL); /* are ext */ } while (chkcon()) ; /* soak up ^C's */ return (0L); /* return failure, IP of 0L */ } /* * resolve() * convert domain name -> address resolution. * returns 0 if name is unresolvable right now */ longword resolve(byte *name) { if (name == NULL) return (0L); if (isaddr(name) != 0) return (aton(name)); /* IP numerical address */ return (resolve2(name)); /* call upon the worker */ } /* * aton() * - converts [a.b.c.d] or a.b.c.d to 32 bit long * - returns 0 on error (safer than -1) */ longword aton(byte *text) { register int i; longword ip, j; ip = 0; if (text == NULL) return (0L); /* failure */ if (*text == '[') text++; for (i = 24; i >= 0; i -= 8) { j = atoi(text) & 0xff; ip |= (j << i); while (*text != '\0' && *text != '.') text++; if (*text == '\0') break; text++; } return (ip); } /* * isaddr * - returns nonzero if text is simply ip address * such as 123.456.789.012 or [same] or 123 456 789 012 or [same] */ int isaddr(byte *text) { register byte ch; if (text == NULL) return (0); /* failure */ while (ch = *text++) { if (('0' <= ch) && (ch <= '9')) continue; /* in digits */ if ((ch == '.') || (ch == ' ') || (ch == '[') || (ch == ']')) continue; /* and in punct */ return (0); /* failure */ } return (1); }