diff options
Diffstat (limited to 'winsup/cygwin/libc/minires.c')
-rw-r--r-- | winsup/cygwin/libc/minires.c | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/winsup/cygwin/libc/minires.c b/winsup/cygwin/libc/minires.c new file mode 100644 index 000000000..4c9495831 --- /dev/null +++ b/winsup/cygwin/libc/minires.c @@ -0,0 +1,916 @@ +/* minires.c. Stub synchronous resolver for Cygwin. + + Copyright 2006 Red Hat, Inc. + + Written by Pierre A. Humblet <Pierre.Humblet@ieee.org> + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "minires.h" + +/*********************************************************************** + +Utilities + +***********************************************************************/ +/*********************************************************************** + +dprintf +***********************************************************************/ +void minires_dprintf(const char * format, ...) +{ + va_list args; + + va_start(args, format); + fprintf(stderr, "Minires: "); + vfprintf(stderr, format, args); + va_end(args); +} + +/*********************************************************************** + +scanline +Put pointers in list[] to the beginning of each space or comma delimited +word in "in", and put the lengths in sizes[] (counting the final 0). +Return the number of words found +***********************************************************************/ +static int scanline(char * in, char **list, int * sizes, int maxnum) +{ + int i; + char * startp; + for (i = 0; i < maxnum; i++) { + while((*in) && (isspace(*in) || *in == ',')) in++; + if (*in == 0) + break; + startp = in++; + while((*in) && !isspace(*in) && *in != ',') in++; + list[i] = startp; + sizes[i] = in - startp + 1; + if (*in) + *in++ = 0; + } + return i; +} + +/*********************************************************************** + +Read the search string. + +***********************************************************************/ +void minires_get_search(char * string, res_state statp) +{ + char * words[MAXDNSRCH+1], * ptr; + int sizes[MAXDNSRCH+1]; + int i, j, debug = statp->options & RES_DEBUG; + + i = scanline(string, words, sizes, MAXDNSRCH+1); + ptr = statp->defdname; + for (j = 0; j < i; j++) { + if (j < MAXDNSRCH + && ptr + sizes[j] < &statp->defdname[DIM(statp->defdname)]) { + statp->dnsrch[j] = strcpy(ptr, words[j]); + statp->dnsrch[j+1] = NULL; + ptr += sizes[j]; + DPRINTF(debug, "search \"%s\"\n", words[j]); + } + else + DPRINTF(debug, "no space for \"%s\"\n", words[j]); + } +} + +/*********************************************************************** + +Read options + + +***********************************************************************/ +static void get_options(res_state statp, int i, char **words) +{ + char *ptr; + int value; + + while (i-- > 0) { + if (!strcasecmp("debug", words[i])) { + statp->options |= RES_DEBUG; + DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]); + continue; + } + if (!strcasecmp("osquery", words[i])) { + statp->use_os = 1; + DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]); + continue; + } + + if ((ptr = strchr(words[i], ':'))) { + *ptr++ = 0; + value = atoi(ptr); + /* Not supported + if (!strcasecmp("ndots", words[i])) { + statp->ndots = value; + continue; + } + */ + if (!strcasecmp("retry", words[i])) { + if (value < 1) + value = 1; + statp->retry = value; + DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value); + continue; + } + if (!strcasecmp("retrans", words[i])) { + if (value < 1) + value = 1; + statp->retrans = value; + DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value); + continue; + } + } + DPRINTF(statp->options & RES_DEBUG, "unknown option: \"%s\"\n", words[i]); + } +} + +/*********************************************************************** + +Read the resolv.conf file. +We only look for nameserver, domain, search and options + +***********************************************************************/ +#if MAXNS > MAXDNSRCH + 1 +#define MAXSIZE MAXNS +#else +#define MAXSIZE MAXDNSRCH + 1 /* Make unused one visible */ +#endif +static void get_resolv(res_state statp) +{ + FILE * fd; + char *words[MAXSIZE + 1], line[4096], *ptr; + int sizes[DIM(words)]; + int i, j, ns = 0, have_search, have_address, debug = statp->options & RES_DEBUG; + + fd = fopen(_PATH_RESCONF, "r"); + DPRINTF(debug, _PATH_RESCONF ": %s\n", fd?"OK":strerror(errno)); + if (fd == NULL) + return; + + statp->use_os = 0; /* Do not use os_query, except if allowed by "options" */ + have_search = (statp->dnsrch[0] != NULL); + have_address = (statp->nscount != 0); + + while ( fgets(line, sizeof(line), fd) != 0) { + DPRINTF(debug, "resolv.conf %s", line); + if ((i = scanline(line, words, sizes, DIM(words))) > 0) { + if (!have_address + && !strncasecmp("nameserver", words[0], sizes[0])) { + for ( j = 1; j < i ; j++) { + unsigned int address; + address = cygwin_inet_addr(words[j]); + if (address == -1) { + DPRINTF(debug, "invalid server \"%s\"\n", words[j]); + } + else if (ns >= MAXNS) { + DPRINTF(debug, "no space for server \"%s\"\n", words[j]); + } + else { + statp->nsaddr_list[ns++].sin_addr.s_addr = address; + statp->nscount++; + DPRINTF(debug, "server \"%s\"\n", words[j]); + } + } + } + else if (!have_search + && (!strncasecmp("search", words[0], sizes[0]) + || !strncasecmp("domain", words[0], sizes[0]))) { + ptr = statp->defdname; + for (j = 0; j + 1 < i; j++) { + if (j < MAXDNSRCH + && ptr + sizes[j + 1] < &statp->defdname[DIM(statp->defdname)]) { + statp->dnsrch[j] = strcpy(ptr, words[j+1]); + statp->dnsrch[j+1] = 0; + ptr += sizes[j+1]; + DPRINTF(debug, "domain|search \"%s\"\n", statp->dnsrch[j]); + } + else { + DPRINTF(debug, "no space for \"%s\"\n", words[j+1]); + } + } + } + /* Options line */ + else if (!strncasecmp("options", words[0], sizes[0])) + get_options(statp, i - 1, &words[1]); + } + } + fclose(fd); + return; +} + +/****************************************************************************/ +/* + open_sock() + Create a datagram socket and call bind. + +****************************************************************************/ + +static int open_sock(struct sockaddr_in *CliAddr, int debug) +{ + int fd; + + DPRINTF(debug, "opening UDP socket\n"); + + /* Create a datagram socket */ + if ((fd = cygwin_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + DPRINTF(debug, "socket(UDP): %s\n", strerror(errno)); + return -1; + } + CliAddr->sin_family = AF_INET; + CliAddr->sin_addr.s_addr = htonl(INADDR_ANY); + CliAddr->sin_port = htons(0); + bzero(CliAddr->sin_zero, sizeof(CliAddr->sin_zero)); + /* Get a port */ + if (cygwin_bind(fd, (struct sockaddr *) CliAddr, sizeof(*CliAddr)) < 0) { + DPRINTF(debug, "bind: %s\n", strerror(errno)); + return -1; + } + return fd; +} + +/***************************************************************** + * + __res_state() + Undocumented but public. Accessed through _res + + *****************************************************************/ +static struct __res_state res; +struct __res_state *__res_state(void) +{ + return & res; +} + +/***************************************************************** + * + res_init() + + *****************************************************************/ +int res_ninit(res_state statp) +{ + int i; + char * ptr; + + statp->res_h_errno = NETDB_SUCCESS; + statp->nscount = 0; + statp->os_query = NULL; + statp->retrans = RES_TIMEOUT; /* timeout in seconds */ + statp->retry = RES_MAXRETRY; /* max number of retries */ + statp->use_os = 1; /* use os_query if available and allowed by get_resolv */ + statp->mypid = -1; + statp->sockfd = -1; + + for (i = 0; i < DIM(statp->dnsrch); i++) statp->dnsrch[i] = 0; + + /* Get search list from LOCALDOMAIN */ + if ((ptr = getenv("LOCALDOMAIN")) != 0 ) { + DPRINTF(statp->options & RES_DEBUG, "LOCALDOMAIN \"%s\"\n", ptr); + minires_get_search(ptr, statp); /* domain or dnsrch */ + } + /* resolv.conf (dns servers & search list)*/ + get_resolv(statp); + /* Get dns servers and search list from an os-specific routine, set os_query */ + get_dns_info(statp); + + if (statp->nscount == 0 && !statp->os_query) { + errno = ENONET; + statp->res_h_errno = NETDB_INTERNAL; + DPRINTF(statp->options & RES_DEBUG, "no dns server found\n"); + return -1; + } + for (i = 0; i < statp->nscount; i++) { + statp->nsaddr_list[i].sin_family = AF_INET; + statp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT); + bzero(statp->nsaddr_list[i].sin_zero, sizeof(statp->nsaddr_list[i].sin_zero)); + } + /* Only debug may be set before calling init */ + statp->options &= RES_DEBUG; + statp->options |= RES_INIT | RES_DEFAULT; + return 0; +} + +int res_init() +{ + int r = res_ninit(& res); + h_errno = res.res_h_errno; + return r; +} + +/***************************************************************** + * + res_close() + + *****************************************************************/ +void res_nclose(res_state statp) +{ + int res; + if (statp->sockfd != -1) { + res = close(statp->sockfd); + DPRINTF(statp->options & RES_DEBUG, "close sockfd %d: %s\n", + statp->sockfd, (res == 0)?"OK":strerror(errno)); + statp->sockfd = -1; + } +} + +void res_close() +{ + res_nclose(& res); +} + +/***************************************************************** + * + get_tcp_buf() + + *****************************************************************/ +static int get_tcp_buf(int fd, unsigned char *buf, int size, int debug) +{ + int res; + while (size > 0) { + if ((res = read(fd, buf, size)) < 0) { + DPRINTF(debug, "read: %s\n", strerror(errno)); + return -1; + } + DPRINTF(debug, "read %d out of %d\n", res, size); + size -= res; + buf += res; + } + return 0; +} + +/***************************************************************** + * + get_tcp() + + *****************************************************************/ +static int get_tcp(struct sockaddr_in *CliAddr, + const unsigned char * MsgPtr, int MsgLength, + unsigned char * AnsPtr, int AnsLength, int debug) +{ + int fd, res = -1; + unsigned short ans_length; + union {short len; u_char buf[sizeof(short)];} len_buf; + + DPRINTF(debug, "retrying with TCP\n"); + + /* Create a tcp socket */ + if ((fd = cygwin_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + DPRINTF(debug, "socket(TCP): %s\n", strerror(errno)); + return -1; + } + + if (cygwin_connect(fd, (struct sockaddr *) CliAddr, sizeof(* CliAddr)) < 0) { + DPRINTF(debug, "connect: %s\n", strerror(errno)); + goto done; + } + + /* Send the length then the message */ + len_buf.len = htons(MsgLength); + if (write(fd, len_buf.buf, sizeof(len_buf)) != sizeof(len_buf) + || write(fd, MsgPtr, MsgLength) != MsgLength) { + DPRINTF(debug, "write: %s\n", strerror(errno)); + goto done; + } + + /* Read the answer length */ + if (get_tcp_buf(fd, len_buf.buf, sizeof(len_buf), debug)) + goto done; + ans_length = ntohs(len_buf.len); + + /* Read the answer */ + if (get_tcp_buf(fd, AnsPtr, MIN(ans_length, AnsLength), debug)) + goto done; + res = ans_length; + + done: + close (fd); + return res; +} + +/***************************************************************** + ** + res_send + Assumes that the message is a query starting with a short id. + Handles retransmissions until that id is received. + +*****************************************************************/ +int res_nsend( res_state statp, const unsigned char * MsgPtr, + int MsgLength, unsigned char * AnsPtr, int AnsLength) +{ + /* Current server, shared by all tasks */ + volatile static unsigned int SServ = 0XFFFFFFFF; + int tcp; + const int debug = statp->options & RES_DEBUG; + + fd_set fdset_read; + int rslt, addrLen, transNum, wServ; + struct sockaddr_in mySockAddr, dnsSockAddr; + struct timeval timeOut; + + statp->res_h_errno = NETDB_SUCCESS; + if (((statp->options & RES_INIT) == 0) && (res_ninit(statp) != 0)) + return -1; + + /* Close the socket if it had been opened before a fork. + Reuse of pid's cannot hurt */ + if ((statp->sockfd != -1) && (statp->mypid != getpid())) { + res_nclose(statp); + } + + /* Open a socket for this process */ + if (statp->sockfd == -1) { + /* Create a socket and bind it (to any port) */ + statp->sockfd = open_sock(& mySockAddr, debug); + if (statp->sockfd < 0 ) { + statp->res_h_errno = NETDB_INTERNAL; + return -1; + } + /* Set close on exec flag */ + if (fcntl(statp->sockfd, F_SETFD, 1) == -1) { + DPRINTF(debug, "fcntl: %s\n", + strerror(errno)); + statp->res_h_errno = NETDB_INTERNAL; + return -1; + } + statp->mypid = getpid(); + if (SServ == 0XFFFFFFFF) /* Pseudo random */ + SServ = statp->mypid % statp->nscount; + } + + transNum = 0; + while ( transNum++ < statp->retry) { + if ((wServ = SServ + 1) >= statp->nscount) + wServ = 0; + SServ = wServ; + /* Send the message */ + rslt = cygwin_sendto(statp->sockfd, MsgPtr, MsgLength, 0, + (struct sockaddr *) &statp->nsaddr_list[wServ], + sizeof(struct sockaddr_in)); + DPRINTF(debug, "sendto: server %08x sockfd %d %s\n", + statp->nsaddr_list[wServ].sin_addr.s_addr, + statp->sockfd, (rslt == MsgLength)?"OK":strerror(errno)); + if (rslt != MsgLength) { + statp->res_h_errno = NETDB_INTERNAL; + return -1; + }; + /* + Wait for a reply with select() + */ + FD_ZERO(&fdset_read); + FD_SET (statp->sockfd, &fdset_read ); + timeOut.tv_sec = statp->retrans; + timeOut.tv_usec = 0; + rslt = cygwin_select(statp->sockfd + 1, &fdset_read, NULL, NULL, &timeOut); + if ( rslt == 0 ) { /* Timeout */ + DPRINTF(statp->options & RES_DEBUG, "timeout for server %08x\n", + statp->nsaddr_list[wServ].sin_addr.s_addr); + continue; + } + else if ((rslt != 1) || (FD_ISSET(statp->sockfd, &fdset_read) == 0)) { + DPRINTF(debug, "select: %s\n", strerror(errno)); + statp->res_h_errno = NETDB_INTERNAL; + return -1; + } + + addrLen = sizeof(dnsSockAddr); + rslt = cygwin_recvfrom(statp->sockfd, AnsPtr, AnsLength, 0, + (struct sockaddr *) & dnsSockAddr, & addrLen); + if (rslt <= 0) { + DPRINTF(debug, "recvfrom: %s\n", strerror(errno)); + statp->res_h_errno = NETDB_INTERNAL; + return -1; + } + /* + Prepare to retry with tcp + */ + for (tcp = 0; tcp < 2; tcp++) { + /* Check if this is the message we expected */ + if ((*MsgPtr == *AnsPtr) /* Ids match */ + && (*(MsgPtr + 1) == *(AnsPtr + 1)) +/* We have stopped checking this because the question may not be present on error, + in particular when the name in the question is not a valid name. + Simply check that the header is present. */ + && (rslt >= HFIXEDSZ) +/* && (rslt >= MsgLength ) + && (memcmp(MsgPtr + HFIXEDSZ, AnsPtr + HFIXEDSZ, MsgLength - HFIXEDSZ) == 0) */ + && ((AnsPtr[2] & QR) != 0)) { + + DPRINTF(debug, "answer %u from %08x. Error %d. Count %d.\n", + rslt, dnsSockAddr.sin_addr.s_addr, + AnsPtr[3] & ERR_MASK, AnsPtr[6]*256 + AnsPtr[7]); +#if 0 + NETDB_INTERNAL -1 /* see errno */ + NETDB_SUCCESS 0 /* no problem */ + HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */ + TRY_AGAIN 2 /* Non-Authoritive Host not found, or SERVERFAIL */ + Also seen returned by some servers when the name is too long + NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ + NO_DATA 4 /* Valid name, no data record of requested type */ +#endif + if ((AnsPtr[3] & ERR_MASK) == NOERROR) { + if ((AnsPtr[2] & TC) && !(statp->options & RES_IGNTC)) { /* Truncated. Try TCP */ + rslt = get_tcp(&statp->nsaddr_list[wServ], MsgPtr, MsgLength, + AnsPtr, AnsLength, statp->options & RES_DEBUG); + continue; + } + else if ((AnsPtr[6] | AnsPtr[7])!= 0) + return rslt; + else + statp->res_h_errno = NO_DATA; + } + else { + /* return HOST_NOT_FOUND even for non-authoritative answers */ + if ((AnsPtr[3] & ERR_MASK) == NXDOMAIN) + statp->res_h_errno = HOST_NOT_FOUND; + else if ((AnsPtr[3] & ERR_MASK) == SERVFAIL) + statp->res_h_errno = TRY_AGAIN; + else + statp->res_h_errno = NO_RECOVERY; + } + return -1; + } + else { + DPRINTF(debug, "unexpected answer %u from %x to query to %x\n", + rslt, dnsSockAddr.sin_addr.s_addr, + statp->nsaddr_list[wServ].sin_addr.s_addr); + break; + } + } /* TCP */ + } + DPRINTF(debug, "too many retries\n"); + statp->res_h_errno = TRY_AGAIN; + return -1; +} + +int res_send( const unsigned char * MsgPtr, int MsgLength, + unsigned char * AnsPtr, int AnsLength) +{ + int r = res_nsend(& res, MsgPtr, MsgLength, AnsPtr, AnsLength); + h_errno = res.res_h_errno; + return r; +} + +/***************************************************************** + * + res_mkquery + + Return: packet size + -1 name format is incorrect +*****************************************************************/ +int res_nmkquery (res_state statp, + int op, const char * dnameptr, int qclass, int qtype, + const unsigned char * dataptr, int datalen, + const unsigned char * newrr, unsigned char * buf, int buflen) +{ + int i, len; + short id; + + if (op == QUERY) { + /* Write the name and verify buffer length */ + len = dn_comp(dnameptr, buf + HFIXEDSZ, buflen - HFIXEDSZ - QFIXEDSZ, NULL, NULL); + if (len < 0) { + DPRINTF(statp->options & RES_DEBUG, + "\"%s\" invalid or buffer too short\n", dnameptr); + statp->res_h_errno = NETDB_INTERNAL; + return -1; + } + /* Fill the header */ + id = statp->id; + PUTSHORT(id, buf); + PUTSHORT(RD, buf); + PUTSHORT(1, buf); /* Number of questions */ + for (i = 0; i < 3; i++) + PUTSHORT(0, buf); /* Number of answers */ + + /* Write qtype and qclass */ + buf += len; + PUTSHORT(qtype, buf); + PUTSHORT(qclass, buf); + return len + 16; /* packet size */ + } + else { /* Not implemented */ + errno = ENOSYS; + statp->res_h_errno = NETDB_INTERNAL; + return -1; + } +} + +int res_mkquery (int op, const char * dnameptr, int qclass, int qtype, + const unsigned char * dataptr, int datalen, + const unsigned char * newrr, unsigned char * buf, int buflen) +{ + int r = res_nmkquery (& res, op, dnameptr, qclass, qtype, + dataptr, datalen, newrr, buf, buflen); + h_errno = res.res_h_errno; + return r; + +} + +/***************************************************************** + * + res_query() + + *****************************************************************/ + +int res_nquery( res_state statp, const char * DomName, int Class, int Type, + unsigned char * AnsPtr, int AnsLength) +{ + u_char packet[PACKETSZ]; + int len; + + DPRINTF(statp->options & RES_DEBUG, "query \"%s\" type %d\n", DomName, Type); + statp->res_h_errno = NETDB_SUCCESS; + + /* If a hook exists to a native implementation, use it */ + if (statp->os_query) + return ((os_query_t *) statp->os_query)(statp, DomName, Class, Type, AnsPtr, AnsLength); + + if ((len = res_nmkquery (statp, QUERY, DomName, Class, Type, + 0, 0, 0, packet, PACKETSZ)) < 0) + return -1; + return res_nsend( statp, packet, len, AnsPtr, AnsLength); +} + +int res_query( const char * DomName, int Class, int Type, unsigned char * AnsPtr, int AnsLength) +{ + int r = res_nquery(& res, DomName, Class, Type, AnsPtr, AnsLength); + h_errno = res.res_h_errno; + return r; +} + +/***************************************************************** + * + res_querydomain() + + *****************************************************************/ +int res_nquerydomain( res_state statp, const char * Name, const char * DomName, + int Class, int Type, unsigned char * AnsPtr, int AnsLength) +{ + char fqdn[MAXDNAME], *ptr; + int nlen; + + if (!DomName) + ptr = (char *) Name; + else if ((nlen = strlen(Name)) >= sizeof(fqdn) - 1) + goto error; + else { + strcpy(fqdn, Name); + ptr = &fqdn[nlen]; + if (nlen && *(ptr - 1) != '.') + *(ptr++ - 1) = '.'; + fqdn[sizeof(fqdn) - 1] = 0; + strncpy(ptr, DomName, sizeof(fqdn) - (ptr - fqdn)); + if (fqdn[sizeof(fqdn) - 1]) + goto error; + ptr = fqdn; + } + return res_nquery(statp, ptr, Class, Type, AnsPtr, AnsLength); + + error: + DPRINTF(statp->options & RES_DEBUG, "querydomain: name too long\n"); + errno = EINVAL; + statp->res_h_errno = NETDB_INTERNAL;; + return -1; +} + +int res_querydomain( const char * Name, const char * DomName, int Class, + int Type, unsigned char * AnsPtr, int AnsLength) +{ + int r = res_nquerydomain(& res, Name, DomName, Class, Type, AnsPtr, + AnsLength); + h_errno = res.res_h_errno; + return r; +} + +/***************************************************************** + * + res_search() + + *****************************************************************/ + +int res_nsearch( res_state statp, const char * DomName, int Class, int Type, + unsigned char * AnsPtr, int AnsLength) +{ + int len, stat, i; + char fullDomName[MAXDNAME], *ptr, *sptr; + + DPRINTF(statp->options & RES_DEBUG, "search \"%s\" type %d\n", DomName, Type); + + if (((statp->options & RES_INIT) == 0) && (res_ninit(statp) != 0)) + return -1; + + stat = res_nquery( statp, DomName, Class, Type, AnsPtr, AnsLength); + + /* Check if will skip search */ + if (statp->res_h_errno != HOST_NOT_FOUND /* Success or hard failure */ + || ((ptr = strrchr(DomName, '.')) && (!*(ptr+1))) /* Final dot */ + || (((statp->options & RES_DNSRCH) == 0) /* Or no search */ + && ((ptr != NULL) /* And some dot */ + || ((statp->options & RES_DEFNAMES) == 0)))/* or no def domain */ + || (!(sptr = statp->dnsrch[0]))) + return stat; + + len = strlen(DomName); + if (len >= MAXDNAME - 1) /* Space for next dot */ + goto error; + strcpy(fullDomName, DomName); + fullDomName[len++] = '.'; + fullDomName[MAXDNAME - 1] = 0; /* Overflow indicator */ + i = 0; + do { + strncpy(fullDomName + len, sptr, MAXDNAME - len); + if (fullDomName[MAXDNAME - 1]) + goto error; + stat = res_nquery(statp, fullDomName, Class, Type, AnsPtr, AnsLength); + } while ((sptr = statp->dnsrch[++i]) != NULL + && statp->res_h_errno == HOST_NOT_FOUND + && (statp->options & RES_DNSRCH) != 0); + + /* Return last stat */ + return stat; + + error: + DPRINTF(statp->options & RES_DEBUG, "name too long during search\n"); + errno = EINVAL; + statp->res_h_errno = NETDB_INTERNAL; + return -1; +} + +int res_search( const char * DomName, int Class, int Type, + unsigned char * AnsPtr, int AnsLength) +{ + int r = res_nsearch(& res, DomName, Class, Type, AnsPtr, AnsLength); + h_errno = res.res_h_errno; + return r; +} + +/***************************************************************** + * + dn_expand + + *****************************************************************/ + +int dn_expand(const unsigned char *msg, const unsigned char *eomorig, + const unsigned char *comp_dn, char *exp_dn, int length) +{ + unsigned int len, complen = 0; + const unsigned char *comp_dn_orig = comp_dn; +/* char * exp_start = exp_dn; */ + + errno = EINVAL; + if (comp_dn >= eomorig) + goto expand_fail; + if ((len = *comp_dn++) == 0) /* Weird case */ + exp_dn++; + else do { + if (len <= MAXLABEL) { + if ((length -= (len + 1)) > 0 /* Need space for final . */ + && comp_dn + len <= eomorig) { + do { *exp_dn++ = *comp_dn++; } while (--len != 0); + *exp_dn++ = '.'; + } + else + goto expand_fail; + } + else if (len >= (128+64)) { + if (!complen) /* Still in the original field? */ + complen = (comp_dn - comp_dn_orig) + 1; + comp_dn = msg + (((len & ~(128+64)) << 8) + *comp_dn); + if (comp_dn >= eomorig) + goto expand_fail; + } + else + goto expand_fail; + } while ((len = *comp_dn++) != 0); + /* Replace last . with a 0 */ + *(--exp_dn) = 0; + if (!complen) + complen = comp_dn - comp_dn_orig; +/* fprintf(stderr, "dn_expand %s\n", exp_start); */ + return complen; + +expand_fail: +/* fprintf(stderr, "dn_expand fails\n"); */ + return -1; +} + + +/***************************************************************** + * + dn_comp + + Return -1 in case of overflow, but still fill buffer correctly. + We do not check the alphabet of the host names + nor the length of the compressed name and we + preserve the letter cases. + + *****************************************************************/ +int dn_comp(const char * exp_dn, u_char * comp_dn, int length, + u_char ** dnptrs, u_char ** lastdnptr) +{ + u_char *cptr = comp_dn, *dptr, *lptr, *rptr; + unsigned int i, len; + u_char * const eptr = comp_dn + length - 1; /* Last valid */ + + errno = EINVAL; + + if (*exp_dn == '.' && !*(exp_dn + 1)) + exp_dn++; + while (1) { + if (*exp_dn == '.' || cptr > eptr) + return -1; + if (*exp_dn == 0) { + *cptr++ = 0; + break; + } + /* Try to compress */ + if (dnptrs) { + for (i = 1; dnptrs[i]; i++) { + dptr = dnptrs[i]; + if (dptr >= comp_dn) /* Handle name.name */ + continue; + rptr = (u_char *) exp_dn; + len = *dptr++; + while (1) { + do { + if (*dptr++ != *rptr++) + goto next_dn; + } while (--len); + len = *dptr++; + if (len == 0) { /* last label */ + if (!*rptr || (*rptr == '.' && !*(rptr + 1))) { /* Full match */ + len = (dnptrs[i] - dnptrs[0]) | 0xC000; + /* Write pointer */ + *cptr++ = len >> 8; + if (cptr > eptr) + return -1; + *cptr++ = len; + goto done; + } + goto next_dn; + } + if (*rptr++ != '.') + goto next_dn; + if (len >= 128 + 64) { + dptr = dnptrs[0] + ((len - 128 - 64) << 8) + *dptr; + len = *dptr++; + } + } + next_dn: ; + } + /* Record label if asked and if space is available and if not too far off */ + if (lastdnptr && (lastdnptr != &dnptrs[i]) && (cptr - dnptrs[0]) < 0xC000) { + dnptrs[i] = cptr; + dnptrs[i+1] = NULL; + } + } + /* Write label */ + lptr = cptr++; /* Length byte */ + rptr = (u_char *) exp_dn; + do { + if (cptr <= eptr) + *cptr++ = *rptr; + } while ((*++rptr != '.') && (*rptr != 0)); + len = rptr - (u_char *) exp_dn; + if (len > MAXLABEL) + return -1; + *lptr = len; + exp_dn = (char *) rptr; + if (*exp_dn != 0) + exp_dn++; /* Skip over . */ + } + done: + return cptr - comp_dn; +} + +/***************************************************************** + * + dn_skipname + + Measures the compressed domain name length and returns it. + *****************************************************************/ +int dn_skipname(const unsigned char *comp_dn, const unsigned char *eom) +{ + int len; + const unsigned char *comp_dn_orig = comp_dn; + + do { + len = *comp_dn++; + if (len >= (128 + 64)) { + comp_dn++; + break; + } + if (len > MAXLABEL || + (comp_dn += len) > eom) + return -1; + } while (len != 0); + + return comp_dn - comp_dn_orig; +} |