1999-03-07 Mark Kettenis <kettenis@gnu.org>
[kopensolaris-gnu/glibc.git] / resolv / res_send.c
index eb159be..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>
@@ -98,16 +99,6 @@ 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 circuit? */
 
-#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
-
 /* XXX - this should be done in portability.h */
 #if (defined(BSD) && (BSD >= 199103)) || defined(linux)
 # define CAN_RECONNECT 1
@@ -214,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
@@ -238,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 &&
@@ -267,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) {
@@ -277,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))
@@ -302,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;
@@ -446,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) {
@@ -495,8 +510,8 @@ read_len:
                        /*
                         * Use datagrams.
                         */
-                       struct timeval timeout;
-                       fd_set dsmask;
+                       int timeout;
+                       struct pollfd pfd[1];
                        struct sockaddr_in from;
                        socklen_t fromlen;
 
@@ -595,26 +610,24 @@ 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;
-                       if (s+1 > FD_SETSIZE) {
-                               Perror(stderr, "s+1 > FD_SETSIZE", EMFILE);
+                               timeout /= _res.nscount;
+                       if (timeout <= 0)
+                               timeout = 1000;
+    wait:
+                       if (s < 0 || s >= FD_SETSIZE) {
+                               Perror(stderr, "s out-of-bounds", EMFILE);
                                res_close();
                                goto next_ns;
                        }
-    wait:
-                       FD_ZERO(&dsmask);
-                       FD_SET(s, &dsmask);
-                       n = select(s+1, &dsmask, (fd_set *)NULL,
-                                  (fd_set *)NULL, &timeout);
+                       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);
+                               Perror(stderr, "poll", errno);
                                res_close();
                                goto next_ns;
                        }
@@ -638,6 +651,18 @@ read_len:
                                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.
@@ -757,12 +782,12 @@ read_len:
           } /*foreach ns*/
        } /*foreach retry*/
        res_close();
-       if (!v_circuit)
+       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);
 }