]> 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 6ac33ce64cc00072610d25b9ea86c91e0aaece15..4a9b3b3f27288f1c6926082ba5aadcd0427b142e 100644 (file)
@@ -98,7 +98,7 @@ 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 *resplen2, int *answerp2_malloced);
 
 /*
  * Formulate a normal query, send, and await answer.
@@ -119,11 +119,13 @@ __libc_res_nquery(res_state statp,
                  u_char **answerp,     /* if buffer needs to be enlarged */
                  u_char **answerp2,
                  int *nanswerp2,
-                 int *resplen2)
+                 int *resplen2,
+                 int *answerp2_malloced)
 {
        HEADER *hp = (HEADER *) answer;
+       HEADER *hp2;
        int n, use_malloc = 0;
-        u_int oflags = statp->_flags;
+       u_int oflags = statp->_flags;
 
        size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
        u_char *buf = alloca (bufsize);
@@ -147,7 +149,7 @@ __libc_res_nquery(res_state statp,
            if (n > 0)
              {
                if ((oflags & RES_F_EDNS0ERR) == 0
-                   && (statp->options & RES_USE_EDNS0) != 0)
+                   && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
                  {
                    n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
                    if (n < 0)
@@ -169,7 +171,7 @@ __libc_res_nquery(res_state statp,
                                 NULL, query2, bufsize - nused);
                if (n > 0
                    && (oflags & RES_F_EDNS0ERR) == 0
-                   && (statp->options & RES_USE_EDNS0) != 0)
+                   && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
                  n = __res_nopt(statp, n, query2, bufsize - nused - n,
                                 anslen / 2);
                nquery2 = n;
@@ -184,7 +186,7 @@ __libc_res_nquery(res_state statp,
 
            if (n > 0
                && (oflags & RES_F_EDNS0ERR) == 0
-               && (statp->options & RES_USE_EDNS0) != 0)
+               && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
              n = __res_nopt(statp, n, query1, bufsize, anslen);
 
            nquery1 = n;
@@ -201,16 +203,16 @@ __libc_res_nquery(res_state statp,
                        goto again;
                }
        }
-       if (__builtin_expect (n <= 0, 0)) {
+       if (__glibc_unlikely (n <= 0))       {
                /* If the query choked with EDNS0, retry without EDNS0.  */
-               if ((statp->options & RES_USE_EDNS0) != 0
+               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;
+                       goto again;
                }
 #ifdef DEBUG
                if (statp->options & RES_DEBUG)
@@ -223,7 +225,8 @@ __libc_res_nquery(res_state statp,
        }
        assert (answerp == NULL || (void *) *answerp == (void *) answer);
        n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
-                            anslen, answerp, answerp2, nanswerp2, resplen2);
+                            anslen, answerp, answerp2, nanswerp2, resplen2,
+                            answerp2_malloced);
        if (use_malloc)
                free (buf);
        if (n < 0) {
@@ -239,26 +242,25 @@ __libc_res_nquery(res_state statp,
          /* __libc_res_nsend might have reallocated the buffer.  */
          hp = (HEADER *) *answerp;
 
-       /* We simplify the following tests by assigning HP to HP2.  It
-          is easy to verify that this is the same as ignoring all
-          tests of HP2.  */
-       HEADER *hp2 = answerp2 ? (HEADER *) *answerp2 : hp;
-
-       if (n < (int) sizeof (HEADER) && answerp2 != NULL
-           && *resplen2 > (int) sizeof (HEADER))
+       /* 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))
          {
-           /* Special case of partial answer.  */
-           assert (hp != hp2);
-           hp = hp2;
+           hp2 = hp;
          }
-       else if (answerp2 != NULL && *resplen2 < (int) sizeof (HEADER)
-                && n > (int) sizeof (HEADER))
+       else
          {
-           /* Special case of partial answer.  */
-           assert (hp != hp2);
-           hp2 = hp;
+           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
@@ -289,6 +291,13 @@ __libc_res_nquery(res_state statp,
                        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);
@@ -309,7 +318,7 @@ res_nquery(res_state statp,
           int anslen)          /* size of answer buffer */
 {
        return __libc_res_nquery(statp, name, class, type, answer, anslen,
-                                NULL, NULL, NULL, NULL);
+                                NULL, NULL, NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nquery)
 
@@ -328,7 +337,8 @@ __libc_res_nsearch(res_state statp,
                   u_char **answerp,
                   u_char **answerp2,
                   int *nanswerp2,
-                  int *resplen2)
+                  int *resplen2,
+                  int *answerp2_malloced)
 {
        const char *cp, * const *domain;
        HEADER *hp = (HEADER *) answer;
@@ -337,6 +347,7 @@ __libc_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. */
@@ -352,7 +363,7 @@ __libc_res_nsearch(res_state statp,
        if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
                return (__libc_res_nquery(statp, cp, class, type, answer,
                                          anslen, answerp, answerp2,
-                                         nanswerp2, resplen2));
+                                         nanswerp2, resplen2, answerp2_malloced));
 
 #ifdef DEBUG
        if (statp->options & RES_DEBUG)
@@ -369,8 +380,11 @@ __libc_res_nsearch(res_state statp,
        if (dots >= statp->ndots || trailing_dot) {
                ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
                                              answer, anslen, answerp,
-                                             answerp2, nanswerp2, resplen2);
-               if (ret > 0 || trailing_dot)
+                                             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++;
@@ -378,11 +392,11 @@ __libc_res_nsearch(res_state statp,
                        answer = *answerp;
                        anslen = MAXPACKET;
                }
-               if (answerp2
-                   && (*answerp2 < answer || *answerp2 >= answer + anslen))
+               if (answerp2 && *answerp2_malloced)
                  {
                    free (*answerp2);
                    *answerp2 = NULL;
+                   *answerp2_malloced = 0;
                  }
        }
 
@@ -399,29 +413,41 @@ __libc_res_nsearch(res_state statp,
                for (domain = (const char * const *)statp->dnsrch;
                     *domain && !done;
                     domain++) {
-
-                       if (domain[0][0] == '\0' ||
-                           (domain[0][0] == '.' && domain[0][1] == '\0'))
+                       const char *dname = domain[0];
+                       searched = 1;
+
+                       /* __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 = __libc_res_nquerydomain(statp, name, *domain,
+                       ret = __libc_res_nquerydomain(statp, name, dname,
                                                      class, type,
                                                      answer, anslen, answerp,
                                                      answerp2, nanswerp2,
-                                                     resplen2);
-                       if (ret > 0)
+                                                     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 < answer
-                               || *answerp2 >= answer + anslen))
+                       if (answerp2 && *answerp2_malloced)
                          {
                            free (*answerp2);
                            *answerp2 = NULL;
+                           *answerp2_malloced = 0;
                          }
 
                        /*
@@ -470,15 +496,17 @@ __libc_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 (dots && !(tried_as_is || root_on_list)) {
+       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);
-               if (ret > 0)
+                                             answerp2, nanswerp2, resplen2,
+                                             answerp2_malloced);
+               if (ret > 0 || (ret == 0 && resplen2 != NULL
+                               && *resplen2 > 0))
                        return (ret);
        }
 
@@ -489,10 +517,11 @@ __libc_res_nsearch(res_state statp,
         * else send back meaningless H_ERRNO, that being the one from
         * the last DNSRCH we did.
         */
-       if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen))
+       if (answerp2 && *answerp2_malloced)
          {
            free (*answerp2);
            *answerp2 = NULL;
+           *answerp2_malloced = 0;
          }
        if (saved_herrno != -1)
                RES_SET_H_ERRNO(statp, saved_herrno);
@@ -512,13 +541,12 @@ res_nsearch(res_state statp,
            int anslen)         /* size of answer */
 {
        return __libc_res_nsearch(statp, name, class, type, answer,
-                                 anslen, NULL, NULL, NULL, NULL);
+                                 anslen, NULL, NULL, NULL, NULL, NULL);
 }
 libresolv_hidden_def (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.
+ * Perform a call on res_query on the concatenation of name and domain.
  */
 static int
 __libc_res_nquerydomain(res_state statp,
@@ -530,11 +558,12 @@ __libc_res_nquerydomain(res_state statp,
                        u_char **answerp,
                        u_char **answerp2,
                        int *nanswerp2,
-                       int *resplen2)
+                       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)
@@ -542,21 +571,17 @@ __libc_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);
@@ -568,7 +593,7 @@ __libc_res_nquerydomain(res_state statp,
        }
        return (__libc_res_nquery(statp, longname, class, type, answer,
                                  anslen, answerp, answerp2, nanswerp2,
-                                 resplen2));
+                                 resplen2, answerp2_malloced));
 }
 
 int
@@ -580,7 +605,8 @@ res_nquerydomain(res_state statp,
            int anslen)         /* size of answer */
 {
        return __libc_res_nquerydomain(statp, name, domain, class, type,
-                                      answer, anslen, NULL, NULL, NULL, NULL);
+                                      answer, anslen, NULL, NULL, NULL, NULL,
+                                      NULL);
 }
 libresolv_hidden_def (res_nquerydomain)
 
@@ -593,7 +619,7 @@ res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
        if (statp->options & RES_NOALIASES)
                return (NULL);
        file = getenv("HOSTALIASES");
-       if (file == NULL || (fp = fopen(file, "r")) == NULL)
+       if (file == NULL || (fp = fopen(file, "rce")) == NULL)
                return (NULL);
        setbuf(fp, NULL);
        buf[sizeof(buf) - 1] = '\0';