summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/libc/minires.c
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/libc/minires.c')
-rw-r--r--winsup/cygwin/libc/minires.c916
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;
+}