1999-03-07 Mark Kettenis <kettenis@gnu.org>
[kopensolaris-gnu/glibc.git] / resolv / res_send.c
index 3efc6cf..608659b 100644 (file)
@@ -71,6 +71,7 @@ static char rcsid[] = "$Id$";
 
 #include <sys/types.h>
 #include <sys/param.h>
+#include <sys/poll.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
@@ -94,21 +95,9 @@ static char rcsid[] = "$Id$";
 # include <../conf/options.h>
 #endif
 
-void _res_close __P((void));
-
 static int s = -1;     /* socket used for communications */
 static int connected = 0;      /* is the socket connected */
-static int vc = 0;     /* is the socket a virtual ciruit? */
-
-#ifndef FD_SET
-/* XXX - should be in portability.h */
-#define        NFDBITS         32
-#define        FD_SETSIZE      32
-#define        FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
-#define        FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
-#define        FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
-#define FD_ZERO(p)     bzero((char *)(p), sizeof(*(p)))
-#endif
+static int vc = 0;     /* is the socket a virtual circuit? */
 
 /* XXX - this should be done in portability.h */
 #if (defined(BSD) && (BSD >= 199103)) || defined(linux)
@@ -216,6 +205,8 @@ res_isourserver(inp)
 /* int
  * res_nameinquery(name, type, class, buf, eom)
  *     look for (name,type,class) in the query section of packet (buf,eom)
+ * requires:
+ *     buf + HFIXESDZ <= eom
  * returns:
  *     -1 : format error
  *     0  : not found
@@ -240,6 +231,8 @@ res_nameinquery(name, type, class, buf, eom)
                if (n < 0)
                        return (-1);
                cp += n;
+               if (cp + 2 * INT16SZ > eom)
+                       return (-1);
                ttype = _getshort(cp); cp += INT16SZ;
                tclass = _getshort(cp); cp += INT16SZ;
                if (ttype == type &&
@@ -269,6 +262,9 @@ res_queriesmatch(buf1, eom1, buf2, eom2)
        register const u_char *cp = buf1 + HFIXEDSZ;
        int qdcount = ntohs(((HEADER*)buf1)->qdcount);
 
+       if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
+               return (-1);
+
        if (qdcount != ntohs(((HEADER*)buf2)->qdcount))
                return (0);
        while (qdcount-- > 0) {
@@ -279,6 +275,8 @@ res_queriesmatch(buf1, eom1, buf2, eom2)
                if (n < 0)
                        return (-1);
                cp += n;
+               if (cp + 2 * INT16SZ > eom1)
+                       return (-1);
                ttype = _getshort(cp);  cp += INT16SZ;
                tclass = _getshort(cp); cp += INT16SZ;
                if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
@@ -304,6 +302,10 @@ res_send(buf, buflen, ans, anssiz)
                /* errno should have been set by res_init() in this case. */
                return (-1);
        }
+       if (anssiz < HFIXEDSZ) {
+               __set_errno (EINVAL);
+               return (-1);
+       }
        DprintQ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY),
                (stdout, ";; res_send()\n"), buf, buflen);
        v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
@@ -320,7 +322,7 @@ res_send(buf, buflen, ans, anssiz)
                struct sockaddr_in *nsap = &_res.nsaddr_list[ns];
     same_ns:
                if (badns & (1 << ns)) {
-                       _res_close();
+                       res_close();
                        goto next_ns;
                }
 
@@ -337,7 +339,7 @@ res_send(buf, buflen, ans, anssiz)
                                        done = 1;
                                        break;
                                case res_nextns:
-                                       _res_close();
+                                       res_close();
                                        goto next_ns;
                                case res_done:
                                        return (resplen);
@@ -372,7 +374,7 @@ res_send(buf, buflen, ans, anssiz)
                        truncated = 0;
                        if ((s < 0) || (!vc)) {
                                if (s >= 0)
-                                       _res_close();
+                                       res_close();
 
                                s = socket(PF_INET, SOCK_STREAM, 0);
                                if (s < 0) {
@@ -387,7 +389,7 @@ res_send(buf, buflen, ans, anssiz)
                                        Aerror(stderr, "connect/vc",
                                               errno, *nsap);
                                        badns |= (1 << ns);
-                                       _res_close();
+                                       res_close();
                                        goto next_ns;
                                }
                                vc = 1;
@@ -404,7 +406,7 @@ res_send(buf, buflen, ans, anssiz)
                                terrno = errno;
                                Perror(stderr, "write failed", errno);
                                badns |= (1 << ns);
-                               _res_close();
+                               res_close();
                                goto next_ns;
                        }
                        /*
@@ -421,7 +423,7 @@ read_len:
                        if (n <= 0) {
                                terrno = errno;
                                Perror(stderr, "read failed", errno);
-                               _res_close();
+                               res_close();
                                /*
                                 * A long running process might get its TCP
                                 * connection reset if the remote server was
@@ -433,10 +435,10 @@ read_len:
                                 */
                                if (terrno == ECONNRESET && !connreset) {
                                        connreset = 1;
-                                       _res_close();
+                                       res_close();
                                        goto same_ns;
                                }
-                               _res_close();
+                               res_close();
                                goto next_ns;
                        }
                        resplen = _getshort(ans);
@@ -448,6 +450,17 @@ read_len:
                                len = anssiz;
                        } else
                                len = resplen;
+                       if (len < HFIXEDSZ) {
+                               /*
+                                * Undersized message.
+                                */
+                               Dprint(_res.options & RES_DEBUG,
+                                      (stdout, ";; undersized: %d\n", len));
+                               terrno = EMSGSIZE;
+                               badns |= (1 << ns);
+                               res_close();
+                               goto next_ns;
+                       }
                        cp = ans;
                        while (len != 0 &&
                               (n = read(s, (char *)cp, (int)len)) > 0) {
@@ -457,7 +470,7 @@ read_len:
                        if (n <= 0) {
                                terrno = errno;
                                Perror(stderr, "read(vc)", errno);
-                               _res_close();
+                               res_close();
                                goto next_ns;
                        }
                        if (truncated) {
@@ -470,7 +483,7 @@ read_len:
                                while (len != 0) {
                                        char junk[PACKETSZ];
 
-                                       n = (len > sizeof(junk)
+                                       n = ((size_t) len > sizeof(junk)
                                             ? sizeof(junk)
                                             : len);
                                        if ((n = read(s, junk, n)) > 0)
@@ -497,14 +510,14 @@ read_len:
                        /*
                         * Use datagrams.
                         */
-                       struct timeval timeout;
-                       fd_set dsmask;
+                       int timeout;
+                       struct pollfd pfd[1];
                        struct sockaddr_in from;
-                       size_t fromlen;
+                       socklen_t fromlen;
 
                        if ((s < 0) || vc) {
                                if (vc)
-                                       _res_close();
+                                       res_close();
                                s = socket(PF_INET, SOCK_DGRAM, 0);
                                if (s < 0) {
 #if !CAN_RECONNECT
@@ -544,7 +557,7 @@ read_len:
                                                       "connect(dg)",
                                                       errno, *nsap);
                                                badns |= (1 << ns);
-                                               _res_close();
+                                               res_close();
                                                goto next_ns;
                                        }
                                        connected = 1;
@@ -552,7 +565,7 @@ read_len:
                                if (send(s, (char*)buf, buflen, 0) != buflen) {
                                        Perror(stderr, "send", errno);
                                        badns |= (1 << ns);
-                                       _res_close();
+                                       res_close();
                                        goto next_ns;
                                }
                        } else {
@@ -589,7 +602,7 @@ read_len:
                                    != buflen) {
                                        Aerror(stderr, "sendto", errno, *nsap);
                                        badns |= (1 << ns);
-                                       _res_close();
+                                       res_close();
                                        goto next_ns;
                                }
                        }
@@ -597,22 +610,25 @@ read_len:
                        /*
                         * Wait for reply
                         */
-                       timeout.tv_sec = (_res.retrans << try);
+                       timeout = (_res.retrans << try) * 1000;
                        if (try > 0)
-                               timeout.tv_sec /= _res.nscount;
-                       if ((long) timeout.tv_sec <= 0)
-                               timeout.tv_sec = 1;
-                       timeout.tv_usec = 0;
+                               timeout /= _res.nscount;
+                       if (timeout <= 0)
+                               timeout = 1000;
     wait:
-                       FD_ZERO(&dsmask);
-                       FD_SET(s, &dsmask);
-                       n = select(s+1, &dsmask, (fd_set *)NULL,
-                                  (fd_set *)NULL, &timeout);
+                       if (s < 0 || s >= FD_SETSIZE) {
+                               Perror(stderr, "s out-of-bounds", EMFILE);
+                               res_close();
+                               goto next_ns;
+                       }
+                       pfd[0].fd = s;
+                       pfd[0].events = POLLIN;
+                       n = __poll(pfd, 1, timeout);
                        if (n < 0) {
                                if (errno == EINTR)
                                        goto wait;
-                               Perror(stderr, "select", errno);
-                               _res_close();
+                               Perror(stderr, "poll", errno);
+                               res_close();
                                goto next_ns;
                        }
                        if (n == 0) {
@@ -622,7 +638,7 @@ read_len:
                                Dprint(_res.options & RES_DEBUG,
                                       (stdout, ";; timeout\n"));
                                gotsomewhere = 1;
-                               _res_close();
+                               res_close();
                                goto next_ns;
                        }
                        __set_errno (0);
@@ -631,10 +647,22 @@ read_len:
                                           (struct sockaddr *)&from, &fromlen);
                        if (resplen <= 0) {
                                Perror(stderr, "recvfrom", errno);
-                               _res_close();
+                               res_close();
                                goto next_ns;
                        }
                        gotsomewhere = 1;
+                       if (resplen < HFIXEDSZ) {
+                               /*
+                                * Undersized message.
+                                */
+                               Dprint(_res.options & RES_DEBUG,
+                                      (stdout, ";; undersized: %d\n",
+                                       resplen));
+                               terrno = EMSGSIZE;
+                               badns |= (1 << ns);
+                               res_close();
+                               goto next_ns;
+                       }
                        if (hp->id != anhp->id) {
                                /*
                                 * response from old query, ignore it.
@@ -683,7 +711,7 @@ read_len:
                                        (stdout, "server rejected query:\n"),
                                        ans, (resplen>anssiz)?anssiz:resplen);
                                badns |= (1 << ns);
-                               _res_close();
+                               res_close();
                                /* don't retry if called from dig */
                                if (!_res.pfcode)
                                        goto next_ns;
@@ -696,7 +724,7 @@ read_len:
                                Dprint(_res.options & RES_DEBUG,
                                       (stdout, ";; truncated answer\n"));
                                v_circuit = 1;
-                               _res_close();
+                               res_close();
                                goto same_ns;
                        }
                } /*if vc/dg*/
@@ -718,7 +746,7 @@ read_len:
                 */
                if ((v_circuit && (!(_res.options & RES_USEVC) || ns != 0)) ||
                    !(_res.options & RES_STAYOPEN)) {
-                       _res_close();
+                       res_close();
                }
                if (Rhook) {
                        int done = 0, loops = 0;
@@ -734,7 +762,7 @@ read_len:
                                        done = 1;
                                        break;
                                case res_nextns:
-                                       _res_close();
+                                       res_close();
                                        goto next_ns;
                                case res_modified:
                                        /* give the hook another try */
@@ -753,13 +781,13 @@ read_len:
     next_ns: ;
           } /*foreach ns*/
        } /*foreach retry*/
-       _res_close();
-       if (!v_circuit)
+       res_close();
+       if (!v_circuit) {
                if (!gotsomewhere)
                        __set_errno (ECONNREFUSED); /* no nameservers found */
                else
                        __set_errno (ETIMEDOUT);    /* no answer obtained */
-       else
+       else
                __set_errno (terrno);
        return (-1);
 }
@@ -772,7 +800,7 @@ read_len:
  * This routine is not expected to be user visible.
  */
 void
-_res_close()
+res_close()
 {
        if (s >= 0) {
                (void) close(s);
@@ -787,6 +815,12 @@ _res_close()
  * there is more gunk of this kind over in res_debug.c.
  */
 
+void
+_res_close()
+{
+       res_close();
+}
+
 #undef res_send
 int
 res_send(buf, buflen, ans, anssiz)