]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - resolv/res_query.c
Consistency about byte vs character in string.texi
[thirdparty/glibc.git] / resolv / res_query.c
index 00a5e3eec35944966acbb527488de96c11a13727..4a9b3b3f27288f1c6926082ba5aadcd0427b142e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 1988, 1993
  *    The Regents of the University of California.  All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -13,7 +13,7 @@
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
 /*
  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
- * 
+ *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies, and that
  * the name of Digital Equipment Corporation not be used in advertising or
  * publicity pertaining to distribution of the document or software without
  * specific, written prior permission.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
@@ -69,6 +69,7 @@ static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93";
 static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vixie Exp $";
 #endif /* LIBC_SCCS and not lint */
 
+#include <assert.h>
 #include <sys/types.h>
 #include <sys/param.h>
 #include <netinet/in.h>
@@ -85,12 +86,20 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
 /* Options.  Leave them on. */
 /* #undef DEBUG */
 
-#if PACKETSZ > 1024
+#if PACKETSZ > 65536
 #define MAXPACKET      PACKETSZ
 #else
-#define MAXPACKET      1024
+#define MAXPACKET      65536
 #endif
 
+#define QUERYSIZE      (HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1)
+
+static int
+__libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
+                       int class, int type, u_char *answer, int anslen,
+                       u_char **answerp, u_char **answerp2, int *nanswerp2,
+                       int *resplen2, int *answerp2_malloced);
+
 /*
  * Formulate a normal query, send, and await answer.
  * Returned answer is placed in supplied buffer "answer".
@@ -102,16 +111,30 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
  * Caller must parse answer and determine whether it answers the question.
  */
 int
-res_nquery(res_state statp,
-          const char *name,    /* domain name */
-          int class, int type, /* class and type of query */
-          u_char *answer,      /* buffer to put answer */
-          int anslen)          /* size of answer buffer */
+__libc_res_nquery(res_state statp,
+                 const char *name,     /* domain name */
+                 int class, int type,  /* class and type of query */
+                 u_char *answer,       /* buffer to put answer */
+                 int anslen,           /* size of answer buffer */
+                 u_char **answerp,     /* if buffer needs to be enlarged */
+                 u_char **answerp2,
+                 int *nanswerp2,
+                 int *resplen2,
+                 int *answerp2_malloced)
 {
-       u_char buf[MAXPACKET];
        HEADER *hp = (HEADER *) answer;
-       int n;
+       HEADER *hp2;
+       int n, use_malloc = 0;
+       u_int oflags = statp->_flags;
+
+       size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
+       u_char *buf = alloca (bufsize);
+       u_char *query1 = buf;
+       int nquery1 = -1;
+       u_char *query2 = NULL;
+       int nquery2 = 0;
 
+ again:
        hp->rcode = NOERROR;    /* default */
 
 #ifdef DEBUG
@@ -119,17 +142,93 @@ res_nquery(res_state statp,
                printf(";; res_query(%s, %d, %d)\n", name, class, type);
 #endif
 
-       n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
-                        buf, sizeof(buf));
-       if (n <= 0) {
+       if (type == T_UNSPEC)
+         {
+           n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
+                            query1, bufsize);
+           if (n > 0)
+             {
+               if ((oflags & RES_F_EDNS0ERR) == 0
+                   && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
+                 {
+                   n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
+                   if (n < 0)
+                     goto unspec_nomem;
+                 }
+
+               nquery1 = n;
+               /* Align the buffer.  */
+               int npad = ((nquery1 + __alignof__ (HEADER) - 1)
+                           & ~(__alignof__ (HEADER) - 1)) - nquery1;
+               if (n > bufsize - npad)
+                 {
+                   n = -1;
+                   goto unspec_nomem;
+                 }
+               int nused = n + npad;
+               query2 = buf + nused;
+               n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
+                                NULL, query2, bufsize - nused);
+               if (n > 0
+                   && (oflags & RES_F_EDNS0ERR) == 0
+                   && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
+                 n = __res_nopt(statp, n, query2, bufsize - nused - n,
+                                anslen / 2);
+               nquery2 = n;
+             }
+
+         unspec_nomem:;
+         }
+       else
+         {
+           n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
+                            query1, bufsize);
+
+           if (n > 0
+               && (oflags & RES_F_EDNS0ERR) == 0
+               && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
+             n = __res_nopt(statp, n, query1, bufsize, anslen);
+
+           nquery1 = n;
+         }
+
+       if (__builtin_expect (n <= 0, 0) && !use_malloc) {
+               /* Retry just in case res_nmkquery failed because of too
+                  short buffer.  Shouldn't happen.  */
+               bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET;
+               buf = malloc (bufsize);
+               if (buf != NULL) {
+                       query1 = buf;
+                       use_malloc = 1;
+                       goto again;
+               }
+       }
+       if (__glibc_unlikely (n <= 0))       {
+               /* If the query choked with EDNS0, retry without EDNS0.  */
+               if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0
+                   && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) {
+                       statp->_flags |= RES_F_EDNS0ERR;
+#ifdef DEBUG
+                       if (statp->options & RES_DEBUG)
+                               printf(";; res_nquery: retry without EDNS0\n");
+#endif
+                       goto again;
+               }
 #ifdef DEBUG
                if (statp->options & RES_DEBUG)
                        printf(";; res_query: mkquery failed\n");
 #endif
                RES_SET_H_ERRNO(statp, NO_RECOVERY);
+               if (use_malloc)
+                       free (buf);
                return (n);
        }
-       n = res_nsend(statp, buf, n, answer, anslen);
+       assert (answerp == NULL || (void *) *answerp == (void *) answer);
+       n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
+                            anslen, answerp, answerp2, nanswerp2, resplen2,
+                            answerp2_malloced);
+       if (use_malloc)
+               free (buf);
        if (n < 0) {
 #ifdef DEBUG
                if (statp->options & RES_DEBUG)
@@ -139,24 +238,66 @@ res_nquery(res_state statp,
                return (n);
        }
 
-       if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+       if (answerp != NULL)
+         /* __libc_res_nsend might have reallocated the buffer.  */
+         hp = (HEADER *) *answerp;
+
+       /* We simplify the following tests by assigning HP to HP2 or
+          vice versa.  It is easy to verify that this is the same as
+          ignoring all tests of HP or HP2.  */
+       if (answerp2 == NULL || *resplen2 < (int) sizeof (HEADER))
+         {
+           hp2 = hp;
+         }
+       else
+         {
+           hp2 = (HEADER *) *answerp2;
+           if (n < (int) sizeof (HEADER))
+             {
+               hp = hp2;
+             }
+         }
+
+       /* Make sure both hp and hp2 are defined */
+       assert((hp != NULL) && (hp2 != NULL));
+
+       if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
+           && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
 #ifdef DEBUG
-               if (statp->options & RES_DEBUG)
+               if (statp->options & RES_DEBUG) {
                        printf(";; rcode = %d, ancount=%d\n", hp->rcode,
                            ntohs(hp->ancount));
+                       if (hp != hp2)
+                         printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode,
+                                ntohs(hp2->ancount));
+               }
 #endif
-               switch (hp->rcode) {
+               switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
                case NXDOMAIN:
+                       if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
+                           || (hp2->rcode == NOERROR
+                               && ntohs (hp2->ancount) != 0))
+                               goto success;
                        RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
                        break;
                case SERVFAIL:
                        RES_SET_H_ERRNO(statp, TRY_AGAIN);
                        break;
                case NOERROR:
+                       if (ntohs (hp->ancount) != 0
+                           || ntohs (hp2->ancount) != 0)
+                               goto success;
                        RES_SET_H_ERRNO(statp, NO_DATA);
                        break;
                case FORMERR:
                case NOTIMP:
+                       /* Servers must not reply to AAAA queries with
+                          NOTIMP etc but some of them do.  */
+                       if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
+                           || (hp2->rcode == NOERROR
+                               && ntohs (hp2->ancount) != 0))
+                               goto success;
+                       /* FALLTHROUGH */
                case REFUSED:
                default:
                        RES_SET_H_ERRNO(statp, NO_RECOVERY);
@@ -164,8 +305,22 @@ res_nquery(res_state statp,
                }
                return (-1);
        }
+ success:
        return (n);
 }
+libresolv_hidden_def (__libc_res_nquery)
+
+int
+res_nquery(res_state statp,
+          const char *name,    /* domain name */
+          int class, int type, /* class and type of query */
+          u_char *answer,      /* buffer to put answer */
+          int anslen)          /* size of answer buffer */
+{
+       return __libc_res_nquery(statp, name, class, type, answer, anslen,
+                                NULL, NULL, NULL, NULL, NULL);
+}
+libresolv_hidden_def (res_nquery)
 
 /*
  * Formulate a normal query, send, and retrieve answer in supplied buffer.
@@ -174,11 +329,16 @@ res_nquery(res_state statp,
  * is detected.  Error code, if any, is left in H_ERRNO.
  */
 int
-res_nsearch(res_state statp,
-           const char *name,   /* domain name */
-           int class, int type,        /* class and type of query */
-           u_char *answer,     /* buffer to put answer */
-           int anslen)         /* size of answer */
+__libc_res_nsearch(res_state statp,
+                  const char *name,    /* domain name */
+                  int class, int type, /* class and type of query */
+                  u_char *answer,      /* buffer to put answer */
+                  int anslen,          /* size of answer */
+                  u_char **answerp,
+                  u_char **answerp2,
+                  int *nanswerp2,
+                  int *resplen2,
+                  int *answerp2_malloced)
 {
        const char *cp, * const *domain;
        HEADER *hp = (HEADER *) answer;
@@ -187,6 +347,7 @@ res_nsearch(res_state statp,
        int trailing_dot, ret, saved_herrno;
        int got_nodata = 0, got_servfail = 0, root_on_list = 0;
        int tried_as_is = 0;
+       int searched = 0;
 
        __set_errno (0);
        RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);  /* True if we never query. */
@@ -200,7 +361,15 @@ res_nsearch(res_state statp,
 
        /* If there aren't any dots, it could be a user-level alias. */
        if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
-               return (res_nquery(statp, cp, class, type, answer, anslen));
+               return (__libc_res_nquery(statp, cp, class, type, answer,
+                                         anslen, answerp, answerp2,
+                                         nanswerp2, resplen2, answerp2_malloced));
+
+#ifdef DEBUG
+       if (statp->options & RES_DEBUG)
+               printf("dots=%d, statp->ndots=%d, trailing_dot=%d, name=%s\n",
+                      (int)dots,(int)statp->ndots,(int)trailing_dot,name);
+#endif
 
        /*
         * If there are enough dots in the name, let's just give it a
@@ -209,12 +378,26 @@ res_nsearch(res_state statp,
         */
        saved_herrno = -1;
        if (dots >= statp->ndots || trailing_dot) {
-               ret = res_nquerydomain(statp, name, NULL, class, type,
-                                        answer, anslen);
-               if (ret > 0 || trailing_dot)
+               ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
+                                             answer, anslen, answerp,
+                                             answerp2, nanswerp2, resplen2,
+                                             answerp2_malloced);
+               if (ret > 0 || trailing_dot
+                   /* If the second response is valid then we use that.  */
+                   || (ret == 0 && resplen2 != NULL && *resplen2 > 0))
                        return (ret);
                saved_herrno = h_errno;
                tried_as_is++;
+               if (answerp && *answerp != answer) {
+                       answer = *answerp;
+                       anslen = MAXPACKET;
+               }
+               if (answerp2 && *answerp2_malloced)
+                 {
+                   free (*answerp2);
+                   *answerp2 = NULL;
+                   *answerp2_malloced = 0;
+                 }
        }
 
        /*
@@ -230,17 +413,43 @@ res_nsearch(res_state statp,
                for (domain = (const char * const *)statp->dnsrch;
                     *domain && !done;
                     domain++) {
+                       const char *dname = domain[0];
+                       searched = 1;
 
-                       if (domain[0][0] == '\0' ||
-                           (domain[0][0] == '.' && domain[0][1] == '\0'))
+                       /* __libc_res_nquerydoman concatenates name
+                          with dname with a "." in between.  If we
+                          pass it in dname the "." we got from the
+                          configured default search path, we'll end
+                          up with "name..", which won't resolve.
+                          OTOH, passing it "" will result in "name.",
+                          which has the intended effect for both
+                          possible representations of the root
+                          domain.  */
+                       if (dname[0] == '.')
+                               dname++;
+                       if (dname[0] == '\0')
                                root_on_list++;
 
-                       ret = res_nquerydomain(statp, name, *domain,
-                                              class, type,
-                                              answer, anslen);
-                       if (ret > 0)
+                       ret = __libc_res_nquerydomain(statp, name, dname,
+                                                     class, type,
+                                                     answer, anslen, answerp,
+                                                     answerp2, nanswerp2,
+                                                     resplen2, answerp2_malloced);
+                       if (ret > 0 || (ret == 0 && resplen2 != NULL
+                                       && *resplen2 > 0))
                                return (ret);
 
+                       if (answerp && *answerp != answer) {
+                               answer = *answerp;
+                               anslen = MAXPACKET;
+                       }
+                       if (answerp2 && *answerp2_malloced)
+                         {
+                           free (*answerp2);
+                           *answerp2 = NULL;
+                           *answerp2_malloced = 0;
+                         }
+
                        /*
                         * If no server present, give up.
                         * If name isn't found in this domain,
@@ -287,14 +496,17 @@ res_nsearch(res_state statp,
        }
 
        /*
-        * If the name has any dots at all, and no earlier 'as-is' query 
-        * for the name, and "." is not on the search list, then try an as-is
-        * query now.
+        * If the query has not already been tried as is then try it
+        * unless RES_NOTLDQUERY is set and there were no dots.
         */
-       if (statp->ndots && !(tried_as_is || root_on_list)) {
-               ret = res_nquerydomain(statp, name, NULL, class, type,
-                                      answer, anslen);
-               if (ret > 0)
+       if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0)
+           && !(tried_as_is || root_on_list)) {
+               ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
+                                             answer, anslen, answerp,
+                                             answerp2, nanswerp2, resplen2,
+                                             answerp2_malloced);
+               if (ret > 0 || (ret == 0 && resplen2 != NULL
+                               && *resplen2 > 0))
                        return (ret);
        }
 
@@ -305,6 +517,12 @@ res_nsearch(res_state statp,
         * else send back meaningless H_ERRNO, that being the one from
         * the last DNSRCH we did.
         */
+       if (answerp2 && *answerp2_malloced)
+         {
+           free (*answerp2);
+           *answerp2 = NULL;
+           *answerp2_malloced = 0;
+         }
        if (saved_herrno != -1)
                RES_SET_H_ERRNO(statp, saved_herrno);
        else if (got_nodata)
@@ -313,22 +531,39 @@ res_nsearch(res_state statp,
                RES_SET_H_ERRNO(statp, TRY_AGAIN);
        return (-1);
 }
+libresolv_hidden_def (__libc_res_nsearch)
 
-/*
- * Perform a call on res_query on the concatenation of name and domain,
- * removing a trailing dot from name if domain is NULL.
- */
 int
-res_nquerydomain(res_state statp,
-           const char *name,
-           const char *domain,
+res_nsearch(res_state statp,
+           const char *name,   /* domain name */
            int class, int type,        /* class and type of query */
-           u_char *answer,             /* buffer to put answer */
+           u_char *answer,     /* buffer to put answer */
            int anslen)         /* size of answer */
+{
+       return __libc_res_nsearch(statp, name, class, type, answer,
+                                 anslen, NULL, NULL, NULL, NULL, NULL);
+}
+libresolv_hidden_def (res_nsearch)
+
+/*
+ * Perform a call on res_query on the concatenation of name and domain.
+ */
+static int
+__libc_res_nquerydomain(res_state statp,
+                       const char *name,
+                       const char *domain,
+                       int class, int type,    /* class and type of query */
+                       u_char *answer,         /* buffer to put answer */
+                       int anslen,                     /* size of answer */
+                       u_char **answerp,
+                       u_char **answerp2,
+                       int *nanswerp2,
+                       int *resplen2,
+                       int *answerp2_malloced)
 {
        char nbuf[MAXDNAME];
        const char *longname = nbuf;
-       int n, d;
+       size_t n, d;
 
 #ifdef DEBUG
        if (statp->options & RES_DEBUG)
@@ -336,21 +571,17 @@ res_nquerydomain(res_state statp,
                       name, domain?domain:"<Nil>", class, type);
 #endif
        if (domain == NULL) {
-               /*
-                * Check for trailing '.';
-                * copy without '.' if present.
-                */
                n = strlen(name);
-               if (n >= MAXDNAME) {
+
+               /* Decrement N prior to checking it against MAXDNAME
+                  so that we detect a wrap to SIZE_MAX and return
+                  a reasonable error.  */
+               n--;
+               if (n >= MAXDNAME - 1) {
                        RES_SET_H_ERRNO(statp, NO_RECOVERY);
                        return (-1);
                }
-               n--;
-               if (n >= 0 && name[n] == '.') {
-                       strncpy(nbuf, name, n);
-                       nbuf[n] = '\0';
-               } else
-                       longname = name;
+               longname = name;
        } else {
                n = strlen(name);
                d = strlen(domain);
@@ -360,8 +591,24 @@ res_nquerydomain(res_state statp,
                }
                sprintf(nbuf, "%s.%s", name, domain);
        }
-       return (res_nquery(statp, longname, class, type, answer, anslen));
+       return (__libc_res_nquery(statp, longname, class, type, answer,
+                                 anslen, answerp, answerp2, nanswerp2,
+                                 resplen2, answerp2_malloced));
+}
+
+int
+res_nquerydomain(res_state statp,
+           const char *name,
+           const char *domain,
+           int class, int type,        /* class and type of query */
+           u_char *answer,             /* buffer to put answer */
+           int anslen)         /* size of answer */
+{
+       return __libc_res_nquerydomain(statp, name, domain, class, type,
+                                      answer, anslen, NULL, NULL, NULL, NULL,
+                                      NULL);
 }
+libresolv_hidden_def (res_nquerydomain)
 
 const char *
 res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
@@ -371,8 +618,8 @@ res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
 
        if (statp->options & RES_NOALIASES)
                return (NULL);
-       file = __secure_getenv("HOSTALIASES");
-       if (file == NULL || (fp = fopen(file, "r")) == NULL)
+       file = getenv("HOSTALIASES");
+       if (file == NULL || (fp = fopen(file, "rce")) == NULL)
                return (NULL);
        setbuf(fp, NULL);
        buf[sizeof(buf) - 1] = '\0';
@@ -399,3 +646,4 @@ res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
        fclose(fp);
        return (NULL);
 }
+libresolv_hidden_def (res_hostalias)