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