check return value from socket call
[unix-history] / usr / src / lib / libc / net / res_send.c
index b45595d..4d447fe 100644 (file)
@@ -1,13 +1,23 @@
-
 /*
 /*
- * Copyright (c) 1985 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)res_send.c 6.16 (Berkeley) %G%";
-#endif LIBC_SCCS and not lint
+static char sccsid[] = "@(#)res_send.c 6.24 (Berkeley) %G%";
+#endif /* LIBC_SCCS and not lint */
 
 /*
  * Send query to name server and wait for reply.
 
 /*
  * Send query to name server and wait for reply.
@@ -26,6 +36,7 @@ static char sccsid[] = "@(#)res_send.c        6.16 (Berkeley) %G%";
 extern int errno;
 
 static int s = -1;     /* socket used for communications */
 extern int errno;
 
 static int s = -1;     /* socket used for communications */
+static struct sockaddr no_addr;
   
 
 #ifndef FD_SET
   
 
 #ifndef FD_SET
@@ -37,8 +48,6 @@ static int s = -1;    /* socket used for communications */
 #define FD_ZERO(p)     bzero((char *)(p), sizeof(*(p)))
 #endif
 
 #define FD_ZERO(p)     bzero((char *)(p), sizeof(*(p)))
 #endif
 
-#define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
-
 res_send(buf, buflen, answer, anslen)
        char *buf;
        int buflen;
 res_send(buf, buflen, answer, anslen)
        char *buf;
        int buflen;
@@ -46,8 +55,9 @@ res_send(buf, buflen, answer, anslen)
        int anslen;
 {
        register int n;
        int anslen;
 {
        register int n;
-       int retry, v_circuit, resplen, ns;
-       int gotsomewhere = 0;
+       int try, v_circuit, resplen, ns;
+       int gotsomewhere = 0, connected = 0;
+       int connreset = 0;
        u_short id, len;
        char *cp;
        fd_set dsmask;
        u_short id, len;
        char *cp;
        fd_set dsmask;
@@ -56,6 +66,7 @@ res_send(buf, buflen, answer, anslen)
        HEADER *anhp = (HEADER *) answer;
        struct iovec iov[2];
        int terrno = ETIMEDOUT;
        HEADER *anhp = (HEADER *) answer;
        struct iovec iov[2];
        int terrno = ETIMEDOUT;
+       char junk[512];
 
 #ifdef DEBUG
        if (_res.options & RES_DEBUG) {
 
 #ifdef DEBUG
        if (_res.options & RES_DEBUG) {
@@ -72,24 +83,29 @@ res_send(buf, buflen, answer, anslen)
        /*
         * Send request, RETRY times, or until successful
         */
        /*
         * Send request, RETRY times, or until successful
         */
-       for (retry = _res.retry; retry > 0; retry--) {
+       for (try = 0; try < _res.retry; try++) {
           for (ns = 0; ns < _res.nscount; ns++) {
 #ifdef DEBUG
                if (_res.options & RES_DEBUG)
                        printf("Querying server (# %d) address = %s\n", ns+1,
           for (ns = 0; ns < _res.nscount; ns++) {
 #ifdef DEBUG
                if (_res.options & RES_DEBUG)
                        printf("Querying server (# %d) address = %s\n", ns+1,
-                             inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr));
+                             inet_ntoa(_res.nsaddr_list[ns].sin_addr));
 #endif DEBUG
 #endif DEBUG
+       usevc:
                if (v_circuit) {
                if (v_circuit) {
+                       int truncated = 0;
+
                        /*
                        /*
-                        * Use virtual circuit.
+                        * Use virtual circuit;
+                        * at most one attempt per server.
                         */
                         */
+                       try = _res.retry;
                        if (s < 0) {
                                s = socket(AF_INET, SOCK_STREAM, 0);
                                if (s < 0) {
                                        terrno = errno;
 #ifdef DEBUG
                                        if (_res.options & RES_DEBUG)
                        if (s < 0) {
                                s = socket(AF_INET, SOCK_STREAM, 0);
                                if (s < 0) {
                                        terrno = errno;
 #ifdef DEBUG
                                        if (_res.options & RES_DEBUG)
-                                           perror("socket failed");
+                                           perror("socket (vc) failed");
 #endif DEBUG
                                        continue;
                                }
 #endif DEBUG
                                        continue;
                                }
@@ -141,10 +157,31 @@ res_send(buf, buflen, answer, anslen)
 #endif DEBUG
                                (void) close(s);
                                s = -1;
 #endif DEBUG
                                (void) close(s);
                                s = -1;
+                               /*
+                                * A long running process might get its TCP
+                                * connection reset if the remote server was
+                                * restarted.  Requery the server instead of
+                                * trying a new one.  When there is only one
+                                * server, this means that a query might work
+                                * instead of failing.  We only allow one reset
+                                * per query to prevent looping.
+                                */
+                               if (terrno == ECONNRESET && !connreset) {
+                                       connreset = 1;
+                                       ns--;
+                               }
                                continue;
                        }
                        cp = answer;
                                continue;
                        }
                        cp = answer;
-                       resplen = len = ntohs(*(u_short *)cp);
+                       if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
+#ifdef DEBUG
+                               if (_res.options & RES_DEBUG)
+                                       fprintf(stderr, "response truncated\n");
+#endif DEBUG
+                               len = anslen;
+                               truncated = 1;
+                       } else
+                               len = resplen;
                        while (len != 0 &&
                           (n = read(s, (char *)cp, (int)len)) > 0) {
                                cp += n;
                        while (len != 0 &&
                           (n = read(s, (char *)cp, (int)len)) > 0) {
                                cp += n;
@@ -160,43 +197,108 @@ res_send(buf, buflen, answer, anslen)
                                s = -1;
                                continue;
                        }
                                s = -1;
                                continue;
                        }
+                       if (truncated) {
+                               /*
+                                * Flush rest of answer
+                                * so connection stays in synch.
+                                */
+                               anhp->tc = 1;
+                               len = resplen - anslen;
+                               while (len != 0) {
+                                       n = (len > sizeof(junk) ?
+                                           sizeof(junk) : len);
+                                       if ((n = read(s, junk, n)) > 0)
+                                               len -= n;
+                                       else
+                                               break;
+                               }
+                       }
                } else {
                        /*
                         * Use datagrams.
                         */
                } else {
                        /*
                         * Use datagrams.
                         */
-                       if (s < 0)
+                       if (s < 0) {
                                s = socket(AF_INET, SOCK_DGRAM, 0);
                                s = socket(AF_INET, SOCK_DGRAM, 0);
+                               if (s < 0) {
+                                       terrno = errno;
+#ifdef DEBUG
+                                       if (_res.options & RES_DEBUG)
+                                           perror("socket (dg) failed");
+#endif DEBUG
+                                       continue;
+                               }
+                       }
 #if    BSD >= 43
 #if    BSD >= 43
-                       if (_res.nscount == 1) {
+                       /*
+                        * I'm tired of answering this question, so:
+                        * On a 4.3BSD+ machine (client and server,
+                        * actually), sending to a nameserver datagram
+                        * port with no nameserver will cause an
+                        * ICMP port unreachable message to be returned.
+                        * If our datagram socket is "connected" to the
+                        * server, we get an ECONNREFUSED error on the next
+                        * socket operation, and select returns if the
+                        * error message is received.  We can thus detect
+                        * the absence of a nameserver without timing out.
+                        * If we have sent queries to at least two servers,
+                        * however, we don't want to remain connected,
+                        * as we wish to receive answers from the first
+                        * server to respond.
+                        */
+                       if (_res.nscount == 1 || (try == 0 && ns == 0)) {
                                /*
                                /*
-                                * Connect/Send is detrimental if you
-                                * are going to poll more than one server
+                                * Don't use connect if we might
+                                * still receive a response
+                                * from another server.
                                 */
                                 */
-                               if (connect(s, &_res.nsaddr_list[ns],
-                                   sizeof(struct sockaddr)) < 0 ||
-                                   send(s, buf, buflen, 0) != buflen) {
+                               if (connected == 0) {
+                                       if (connect(s, &_res.nsaddr_list[ns],
+                                           sizeof(struct sockaddr)) < 0) {
+#ifdef DEBUG
+                                               if (_res.options & RES_DEBUG)
+                                                       perror("connect");
+#endif DEBUG
+                                               continue;
+                                       }
+                                       connected = 1;
+                               }
+                               if (send(s, buf, buflen, 0) != buflen) {
 #ifdef DEBUG
                                        if (_res.options & RES_DEBUG)
 #ifdef DEBUG
                                        if (_res.options & RES_DEBUG)
-                                               perror("connect");
+                                               perror("send");
 #endif DEBUG
                                        continue;
                                }
 #endif DEBUG
                                        continue;
                                }
-                       } else
+                       } else {
+                               /*
+                                * Disconnect if we want to listen
+                                * for responses from more than one server.
+                                */
+                               if (connected) {
+                                       (void) connect(s, &no_addr,
+                                           sizeof(no_addr));
+                                       connected = 0;
+                               }
 #endif BSD
 #endif BSD
-                       if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
-                           sizeof(struct sockaddr)) != buflen) {
+                               if (sendto(s, buf, buflen, 0,
+                                   &_res.nsaddr_list[ns],
+                                   sizeof(struct sockaddr)) != buflen) {
 #ifdef DEBUG
 #ifdef DEBUG
-                               if (_res.options & RES_DEBUG)
-                                       perror("sendto");
+                                       if (_res.options & RES_DEBUG)
+                                               perror("sendto");
 #endif DEBUG
 #endif DEBUG
-                               continue;
+                                       continue;
+                               }
+#if    BSD >= 43
                        }
                        }
+#endif
 
                        /*
                         * Wait for reply
                         */
 
                        /*
                         * Wait for reply
                         */
-                       timeout.tv_sec = (_res.retrans << (_res.retry - retry))
-                               / _res.nscount;
+                       timeout.tv_sec = (_res.retrans << try);
+                       if (try > 0)
+                               timeout.tv_sec /= _res.nscount;
                        if (timeout.tv_sec <= 0)
                                timeout.tv_sec = 1;
                        timeout.tv_usec = 0;
                        if (timeout.tv_sec <= 0)
                                timeout.tv_sec = 1;
                        timeout.tv_usec = 0;
@@ -220,7 +322,9 @@ wait:
                                if (_res.options & RES_DEBUG)
                                        printf("timeout\n");
 #endif DEBUG
                                if (_res.options & RES_DEBUG)
                                        printf("timeout\n");
 #endif DEBUG
+#if BSD >= 43
                                gotsomewhere = 1;
                                gotsomewhere = 1;
+#endif
                                continue;
                        }
                        if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
                                continue;
                        }
                        if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
@@ -245,7 +349,8 @@ wait:
                        }
                        if (!(_res.options & RES_IGNTC) && anhp->tc) {
                                /*
                        }
                        if (!(_res.options & RES_IGNTC) && anhp->tc) {
                                /*
-                                * get rest of answer
+                                * get rest of answer;
+                                * use TCP with same server.
                                 */
 #ifdef DEBUG
                                if (_res.options & RES_DEBUG)
                                 */
 #ifdef DEBUG
                                if (_res.options & RES_DEBUG)
@@ -253,13 +358,8 @@ wait:
 #endif DEBUG
                                (void) close(s);
                                s = -1;
 #endif DEBUG
                                (void) close(s);
                                s = -1;
-                               /*
-                                * retry decremented on continue
-                                * to desired starting value
-                                */
-                               retry = _res.retry + 1;
                                v_circuit = 1;
                                v_circuit = 1;
-                               continue;
+                               goto usevc;
                        }
                }
 #ifdef DEBUG
                        }
                }
 #ifdef DEBUG
@@ -269,17 +369,20 @@ wait:
                }
 #endif DEBUG
                /*
                }
 #endif DEBUG
                /*
-                * We are going to assume that the first server is preferred
-                * over the rest (i.e. it is on the local machine) and only
-                * keep that one open.
+                * If using virtual circuits, we assume that the first server
+                * is preferred * over the rest (i.e. it is on the local
+                * machine) and only keep that one open.
+                * If we have temporarily opened a virtual circuit,
+                * or if we haven't been asked to keep a socket open,
+                * close the socket.
                 */
                 */
-               if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
-                       return (resplen);
-               } else {
+               if ((v_circuit &&
+                   ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
+                   (_res.options & RES_STAYOPEN) == 0) {
                        (void) close(s);
                        s = -1;
                        (void) close(s);
                        s = -1;
-                       return (resplen);
                }
                }
+               return (resplen);
           }
        }
        if (s >= 0) {
           }
        }
        if (s >= 0) {
@@ -288,9 +391,9 @@ wait:
        }
        if (v_circuit == 0)
                if (gotsomewhere == 0)
        }
        if (v_circuit == 0)
                if (gotsomewhere == 0)
-                       errno = ECONNREFUSED;
+                       errno = ECONNREFUSED;   /* no nameservers found */
                else
                else
-                       errno = ETIMEDOUT;
+                       errno = ETIMEDOUT;      /* no answer obtained */
        else
                errno = terrno;
        return (-1);
        else
                errno = terrno;
        return (-1);