home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / ip / dns / resolv+2.1.1 / res_send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-28  |  9.8 KB  |  418 lines

  1. /*
  2.  * Copyright (c) 1985, 1989 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that: (1) source distributions retain this entire copyright
  7.  * notice and comment, and (2) distributions including binaries display
  8.  * the following acknowledgement:  ``This product includes software
  9.  * developed by the University of California, Berkeley and its contributors''
  10.  * in the documentation or other materials provided with the distribution
  11.  * and in all advertising materials mentioning features or use of this
  12.  * software. Neither the name of the University nor the names of its
  13.  * contributors may be used to endorse or promote products derived
  14.  * from this software without specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  16.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  17.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19.  
  20. #if defined(LIBC_SCCS) && !defined(lint)
  21. static char sccsid[] = "@(#)res_send.c    6.25 (Berkeley) 6/1/90";
  22. #endif /* LIBC_SCCS and not lint */
  23.  
  24. /*
  25.  * Send query to name server and wait for reply.
  26.  */
  27.  
  28. #include <sys/param.h>
  29. #include <sys/time.h>
  30. #include <sys/socket.h>
  31. #include <sys/uio.h>
  32. #include <netinet/in.h>
  33. #include <stdio.h>
  34. #include <errno.h>
  35. #include <arpa/nameser.h>
  36. #include <resolv.h>
  37.  
  38. extern int errno;
  39.  
  40. static int s = -1;    /* socket used for communications */
  41. static struct sockaddr no_addr;
  42.   
  43.  
  44. #ifndef FD_SET
  45. #define    NFDBITS        32
  46. #define    FD_SETSIZE    32
  47. #define    FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
  48. #define    FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
  49. #define    FD_ISSET(n, p)    ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
  50. #define FD_ZERO(p)    bzero((char *)(p), sizeof(*(p)))
  51. #endif
  52.  
  53. res_send(buf, buflen, answer, anslen)
  54.     char *buf;
  55.     int buflen;
  56.     char *answer;
  57.     int anslen;
  58. {
  59.     register int n;
  60.     int try, v_circuit, resplen, ns;
  61.     int gotsomewhere = 0, connected = 0;
  62.     int connreset = 0;
  63.     u_short id, len;
  64.     char *cp;
  65.     fd_set dsmask;
  66.     struct timeval timeout;
  67.     HEADER *hp = (HEADER *) buf;
  68.     HEADER *anhp = (HEADER *) answer;
  69.     struct iovec iov[2];
  70.     int terrno = ETIMEDOUT;
  71.     char junk[512];
  72.  
  73. #ifdef DEBUG
  74.     if (_res.options & RES_DEBUG) {
  75.         printf("res_send()\n");
  76.         p_query(buf);
  77.     }
  78. #endif DEBUG
  79.     if (!(_res.options & RES_INIT))
  80.         if (res_init() == -1) {
  81.             return(-1);
  82.         }
  83.     v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
  84.     id = hp->id;
  85.     /*
  86.      * Send request, RETRY times, or until successful
  87.      */
  88.     for (try = 0; try < _res.retry; try++) {
  89.        for (ns = 0; ns < _res.nscount; ns++) {
  90. #ifdef DEBUG
  91.         if (_res.options & RES_DEBUG)
  92.             printf("Querying server (# %d) address = %s\n", ns+1,
  93.                   inet_ntoa(_res.nsaddr_list[ns].sin_addr));
  94. #endif DEBUG
  95.     usevc:
  96.         if (v_circuit) {
  97.             int truncated = 0;
  98.  
  99.             /*
  100.              * Use virtual circuit;
  101.              * at most one attempt per server.
  102.              */
  103.             try = _res.retry;
  104.             if (s < 0) {
  105.                 s = socket(AF_INET, SOCK_STREAM, 0);
  106.                 if (s < 0) {
  107.                     terrno = errno;
  108. #ifdef DEBUG
  109.                     if (_res.options & RES_DEBUG)
  110.                         perror("socket (vc) failed");
  111. #endif DEBUG
  112.                     continue;
  113.                 }
  114.                 if (connect(s, &(_res.nsaddr_list[ns]),
  115.                    sizeof(struct sockaddr)) < 0) {
  116.                     terrno = errno;
  117. #ifdef DEBUG
  118.                     if (_res.options & RES_DEBUG)
  119.                         perror("connect failed");
  120. #endif DEBUG
  121.                     (void) close(s);
  122.                     s = -1;
  123.                     continue;
  124.                 }
  125.             }
  126.             /*
  127.              * Send length & message
  128.              */
  129.             len = htons((u_short)buflen);
  130.             iov[0].iov_base = (caddr_t)&len;
  131.             iov[0].iov_len = sizeof(len);
  132.             iov[1].iov_base = buf;
  133.             iov[1].iov_len = buflen;
  134.             if (writev(s, iov, 2) != sizeof(len) + buflen) {
  135.                 terrno = errno;
  136. #ifdef DEBUG
  137.                 if (_res.options & RES_DEBUG)
  138.                     perror("write failed");
  139. #endif DEBUG
  140.                 (void) close(s);
  141.                 s = -1;
  142.                 continue;
  143.             }
  144.             /*
  145.              * Receive length & response
  146.              */
  147.             cp = answer;
  148.             len = sizeof(short);
  149.             while (len != 0 &&
  150.                 (n = read(s, (char *)cp, (int)len)) > 0) {
  151.                 cp += n;
  152.                 len -= n;
  153.             }
  154.             if (n <= 0) {
  155.                 terrno = errno;
  156. #ifdef DEBUG
  157.                 if (_res.options & RES_DEBUG)
  158.                     perror("read failed");
  159. #endif DEBUG
  160.                 (void) close(s);
  161.                 s = -1;
  162.                 /*
  163.                  * A long running process might get its TCP
  164.                  * connection reset if the remote server was
  165.                  * restarted.  Requery the server instead of
  166.                  * trying a new one.  When there is only one
  167.                  * server, this means that a query might work
  168.                  * instead of failing.  We only allow one reset
  169.                  * per query to prevent looping.
  170.                  */
  171.                 if (terrno == ECONNRESET && !connreset) {
  172.                     connreset = 1;
  173.                     ns--;
  174.                 }
  175.                 continue;
  176.             }
  177.             cp = answer;
  178.             if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
  179. #ifdef DEBUG
  180.                 if (_res.options & RES_DEBUG)
  181.                     fprintf(stderr, "response truncated\n");
  182. #endif DEBUG
  183.                 len = anslen;
  184.                 truncated = 1;
  185.             } else
  186.                 len = resplen;
  187.             while (len != 0 &&
  188.                (n = read(s, (char *)cp, (int)len)) > 0) {
  189.                 cp += n;
  190.                 len -= n;
  191.             }
  192.             if (n <= 0) {
  193.                 terrno = errno;
  194. #ifdef DEBUG
  195.                 if (_res.options & RES_DEBUG)
  196.                     perror("read failed");
  197. #endif DEBUG
  198.                 (void) close(s);
  199.                 s = -1;
  200.                 continue;
  201.             }
  202.             if (truncated) {
  203.                 /*
  204.                  * Flush rest of answer
  205.                  * so connection stays in synch.
  206.                  */
  207.                 anhp->tc = 1;
  208.                 len = resplen - anslen;
  209.                 while (len != 0) {
  210.                     n = (len > sizeof(junk) ?
  211.                         sizeof(junk) : len);
  212.                     if ((n = read(s, junk, n)) > 0)
  213.                         len -= n;
  214.                     else
  215.                         break;
  216.                 }
  217.             }
  218.         } else {
  219.             /*
  220.              * Use datagrams.
  221.              */
  222.             if (s < 0) {
  223.                 s = socket(AF_INET, SOCK_DGRAM, 0);
  224.                 if (s < 0) {
  225.                     terrno = errno;
  226. #ifdef DEBUG
  227.                     if (_res.options & RES_DEBUG)
  228.                         perror("socket (dg) failed");
  229. #endif DEBUG
  230.                     continue;
  231.                 }
  232.             }
  233. #if    BSD >= 43
  234.             /*
  235.              * I'm tired of answering this question, so:
  236.              * On a 4.3BSD+ machine (client and server,
  237.              * actually), sending to a nameserver datagram
  238.              * port with no nameserver will cause an
  239.              * ICMP port unreachable message to be returned.
  240.              * If our datagram socket is "connected" to the
  241.              * server, we get an ECONNREFUSED error on the next
  242.              * socket operation, and select returns if the
  243.              * error message is received.  We can thus detect
  244.              * the absence of a nameserver without timing out.
  245.              * If we have sent queries to at least two servers,
  246.              * however, we don't want to remain connected,
  247.              * as we wish to receive answers from the first
  248.              * server to respond.
  249.              */
  250.             if (_res.nscount == 1 || (try == 0 && ns == 0)) {
  251.                 /*
  252.                  * Don't use connect if we might
  253.                  * still receive a response
  254.                  * from another server.
  255.                  */
  256.                 if (connected == 0) {
  257.                     if (connect(s, &_res.nsaddr_list[ns],
  258.                         sizeof(struct sockaddr)) < 0) {
  259. #ifdef DEBUG
  260.                         if (_res.options & RES_DEBUG)
  261.                             perror("connect");
  262. #endif DEBUG
  263.                         continue;
  264.                     }
  265.                     connected = 1;
  266.                 }
  267.                 if (send(s, buf, buflen, 0) != buflen) {
  268. #ifdef DEBUG
  269.                     if (_res.options & RES_DEBUG)
  270.                         perror("send");
  271. #endif DEBUG
  272.                     continue;
  273.                 }
  274.             } else {
  275.                 /*
  276.                  * Disconnect if we want to listen
  277.                  * for responses from more than one server.
  278.                  */
  279.                 if (connected) {
  280.                     (void) connect(s, &no_addr,
  281.                         sizeof(no_addr));
  282.                     connected = 0;
  283.                 }
  284. #endif BSD
  285.                 if (sendto(s, buf, buflen, 0,
  286.                     &_res.nsaddr_list[ns],
  287.                     sizeof(struct sockaddr)) != buflen) {
  288. #ifdef DEBUG
  289.                     if (_res.options & RES_DEBUG)
  290.                         perror("sendto");
  291. #endif DEBUG
  292.                     continue;
  293.                 }
  294. #if    BSD >= 43
  295.             }
  296. #endif
  297.  
  298.             /*
  299.              * Wait for reply
  300.              */
  301.             timeout.tv_sec = (_res.retrans << try);
  302.             if (try > 0)
  303.                 timeout.tv_sec /= _res.nscount;
  304.             if (timeout.tv_sec <= 0)
  305.                 timeout.tv_sec = 1;
  306.             timeout.tv_usec = 0;
  307. wait:
  308.             FD_ZERO(&dsmask);
  309.             FD_SET(s, &dsmask);
  310.             n = select(s+1, &dsmask, (fd_set *)NULL,
  311.                 (fd_set *)NULL, &timeout);
  312.             if (n < 0) {
  313. #ifdef DEBUG
  314.                 if (_res.options & RES_DEBUG)
  315.                     perror("select");
  316. #endif DEBUG
  317.                 continue;
  318.             }
  319.             if (n == 0) {
  320.                 /*
  321.                  * timeout
  322.                  */
  323. #ifdef DEBUG
  324.                 if (_res.options & RES_DEBUG)
  325.                     printf("timeout\n");
  326. #endif DEBUG
  327. #if BSD >= 43
  328.                 gotsomewhere = 1;
  329. #endif
  330.                 continue;
  331.             }
  332.             if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
  333. #ifdef DEBUG
  334.                 if (_res.options & RES_DEBUG)
  335.                     perror("recvfrom");
  336. #endif DEBUG
  337.                 continue;
  338.             }
  339.             gotsomewhere = 1;
  340.             if (id != anhp->id) {
  341.                 /*
  342.                  * response from old query, ignore it
  343.                  */
  344. #ifdef DEBUG
  345.                 if (_res.options & RES_DEBUG) {
  346.                     printf("old answer:\n");
  347.                     p_query(answer);
  348.                 }
  349. #endif DEBUG
  350.                 goto wait;
  351.             }
  352.             if (!(_res.options & RES_IGNTC) && anhp->tc) {
  353.                 /*
  354.                  * get rest of answer;
  355.                  * use TCP with same server.
  356.                  */
  357. #ifdef DEBUG
  358.                 if (_res.options & RES_DEBUG)
  359.                     printf("truncated answer\n");
  360. #endif DEBUG
  361.                 (void) close(s);
  362.                 s = -1;
  363.                 v_circuit = 1;
  364.                 goto usevc;
  365.             }
  366.         }
  367. #ifdef DEBUG
  368.         if (_res.options & RES_DEBUG) {
  369.             printf("got answer:\n");
  370.             p_query(answer);
  371.         }
  372. #endif DEBUG
  373.         /*
  374.          * If using virtual circuits, we assume that the first server
  375.          * is preferred * over the rest (i.e. it is on the local
  376.          * machine) and only keep that one open.
  377.          * If we have temporarily opened a virtual circuit,
  378.          * or if we haven't been asked to keep a socket open,
  379.          * close the socket.
  380.          */
  381.         if ((v_circuit &&
  382.             ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
  383.             (_res.options & RES_STAYOPEN) == 0) {
  384.             (void) close(s);
  385.             s = -1;
  386.         }
  387.         return (resplen);
  388.        }
  389.     }
  390.     if (s >= 0) {
  391.         (void) close(s);
  392.         s = -1;
  393.     }
  394.     if (v_circuit == 0)
  395.         if (gotsomewhere == 0)
  396.             errno = ECONNREFUSED;    /* no nameservers found */
  397.         else
  398.             errno = ETIMEDOUT;    /* no answer obtained */
  399.     else
  400.         errno = terrno;
  401.     return (-1);
  402. }
  403.  
  404. /*
  405.  * This routine is for closing the socket if a virtual circuit is used and
  406.  * the program wants to close it.  This provides support for endhostent()
  407.  * which expects to close the socket.
  408.  *
  409.  * This routine is not expected to be user visible.
  410.  */
  411. _res_close()
  412. {
  413.     if (s != -1) {
  414.         (void) close(s);
  415.         s = -1;
  416.     }
  417. }
  418.