|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.