fix rejection of dns responses with pointers past 512 byte offset
[musl] / src / network / res_msend.c
index 1e76886..86c2fcf 100644 (file)
@@ -25,7 +25,8 @@ static void cleanup(void *p)
 static unsigned long mtime()
 {
        struct timespec ts;
-       clock_gettime(CLOCK_REALTIME, &ts);
+       if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0 && errno == ENOSYS)
+               clock_gettime(CLOCK_REALTIME, &ts);
        return (unsigned long)ts.tv_sec * 1000
                + ts.tv_nsec / 1000000;
 }
@@ -132,6 +133,22 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
                family = AF_INET;
                sl = sizeof sa.sin;
        }
+
+       /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
+       if (fd >= 0 && family == AF_INET6) {
+               setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
+               for (i=0; i<nns; i++) {
+                       if (ns[i].sin.sin_family != AF_INET) continue;
+                       memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
+                               &ns[i].sin.sin_addr, 4);
+                       memcpy(ns[i].sin6.sin6_addr.s6_addr,
+                               "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+                       ns[i].sin6.sin6_family = AF_INET6;
+                       ns[i].sin6.sin6_flowinfo = 0;
+                       ns[i].sin6.sin6_scope_id = 0;
+               }
+       }
+
        sa.sin.sin_family = family;
        if (fd < 0 || bind(fd, (void *)&sa, sl) < 0) {
                if (fd >= 0) close(fd);
@@ -151,21 +168,6 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
        pthread_cleanup_push(cleanup, pfd);
        pthread_setcancelstate(cs, 0);
 
-       /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
-       if (family == AF_INET6) {
-               setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
-               for (i=0; i<nns; i++) {
-                       if (ns[i].sin.sin_family != AF_INET) continue;
-                       memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
-                               &ns[i].sin.sin_addr, 4);
-                       memcpy(ns[i].sin6.sin6_addr.s6_addr,
-                               "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
-                       ns[i].sin6.sin6_family = AF_INET6;
-                       ns[i].sin6.sin6_flowinfo = 0;
-                       ns[i].sin6.sin6_scope_id = 0;
-               }
-       }
-
        memset(alens, 0, sizeof *alens * nqueries);
 
        retry_interval = timeout / attempts;
@@ -194,9 +196,18 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
                /* Wait for a response, or until time to retry */
                if (poll(pfd, nqueries+1, t1+retry_interval-t2) <= 0) continue;
 
-               while (next < nqueries &&
-                 (rlen = recvfrom(fd, answers[next], asize, 0,
-                 (void *)&sa, (socklen_t[1]){sl})) >= 0) {
+               while (next < nqueries) {
+                       struct msghdr mh = {
+                               .msg_name = (void *)&sa,
+                               .msg_namelen = sl,
+                               .msg_iovlen = 1,
+                               .msg_iov = (struct iovec []){
+                                       { .iov_base = (void *)answers[next],
+                                         .iov_len = asize }
+                               }
+                       };
+                       rlen = recvmsg(fd, &mh, 0);
+                       if (rlen < 0) break;
 
                        /* Ignore non-identifiable packets */
                        if (rlen < 4) continue;
@@ -240,7 +251,7 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
                        if (next == nqueries) pfd[nqueries].events = 0;
 
                        /* If answer is truncated (TC bit), fallback to TCP */
-                       if (answers[i][2] & 2) {
+                       if ((answers[i][2] & 2) || (mh.msg_flags & MSG_TRUNC)) {
                                alens[i] = -1;
                                pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
                                r = start_tcp(pfd+i, family, ns+j, sl, queries[i], qlens[i]);
@@ -277,7 +288,7 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
                        };
                        step_mh(&mh, apos[i]);
                        r = recvmsg(pfd[i].fd, &mh, 0);
-                       if (r < 0) goto out;
+                       if (r <= 0) goto out;
                        apos[i] += r;
                        if (apos[i] < 2) continue;
                        int alen = alen_buf[i][0]*256 + alen_buf[i][1];