1 2 /* 3 * Copyright (c) 1985 Regents of the University of California. 4 * All rights reserved. The Berkeley software License Agreement 5 * specifies the terms and conditions for redistribution. 6 */ 7 8 #if defined(LIBC_SCCS) && !defined(lint) 9 static char sccsid[] = "@(#)res_send.c 6.17 (Berkeley) 05/12/87"; 10 #endif LIBC_SCCS and not lint 11 12 /* 13 * Send query to name server and wait for reply. 14 */ 15 16 #include <sys/param.h> 17 #include <sys/time.h> 18 #include <sys/socket.h> 19 #include <sys/uio.h> 20 #include <netinet/in.h> 21 #include <stdio.h> 22 #include <errno.h> 23 #include <arpa/nameser.h> 24 #include <resolv.h> 25 26 extern int errno; 27 28 static int s = -1; /* socket used for communications */ 29 static struct sockaddr no_addr; 30 31 32 #ifndef FD_SET 33 #define NFDBITS 32 34 #define FD_SETSIZE 32 35 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) 36 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) 37 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) 38 #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) 39 #endif 40 41 #define KEEPOPEN (RES_USEVC|RES_STAYOPEN) 42 43 res_send(buf, buflen, answer, anslen) 44 char *buf; 45 int buflen; 46 char *answer; 47 int anslen; 48 { 49 register int n; 50 int retry, v_circuit, resplen, ns; 51 int gotsomewhere = 0; 52 u_short id, len; 53 char *cp; 54 fd_set dsmask; 55 struct timeval timeout; 56 HEADER *hp = (HEADER *) buf; 57 HEADER *anhp = (HEADER *) answer; 58 struct iovec iov[2]; 59 int terrno = ETIMEDOUT; 60 61 #ifdef DEBUG 62 if (_res.options & RES_DEBUG) { 63 printf("res_send()\n"); 64 p_query(buf); 65 } 66 #endif DEBUG 67 if (!(_res.options & RES_INIT)) 68 if (res_init() == -1) { 69 return(-1); 70 } 71 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; 72 id = hp->id; 73 /* 74 * Send request, RETRY times, or until successful 75 */ 76 for (retry = _res.retry; retry > 0; retry--) { 77 for (ns = 0; ns < _res.nscount; ns++) { 78 #ifdef DEBUG 79 if (_res.options & RES_DEBUG) 80 printf("Querying server (# %d) address = %s\n", ns+1, 81 inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr)); 82 #endif DEBUG 83 if (v_circuit) { 84 /* 85 * Use virtual circuit. 86 */ 87 if (s < 0) { 88 s = socket(AF_INET, SOCK_STREAM, 0); 89 if (s < 0) { 90 terrno = errno; 91 #ifdef DEBUG 92 if (_res.options & RES_DEBUG) 93 perror("socket failed"); 94 #endif DEBUG 95 continue; 96 } 97 if (connect(s, &(_res.nsaddr_list[ns]), 98 sizeof(struct sockaddr)) < 0) { 99 terrno = errno; 100 #ifdef DEBUG 101 if (_res.options & RES_DEBUG) 102 perror("connect failed"); 103 #endif DEBUG 104 (void) close(s); 105 s = -1; 106 continue; 107 } 108 } 109 /* 110 * Send length & message 111 */ 112 len = htons((u_short)buflen); 113 iov[0].iov_base = (caddr_t)&len; 114 iov[0].iov_len = sizeof(len); 115 iov[1].iov_base = buf; 116 iov[1].iov_len = buflen; 117 if (writev(s, iov, 2) != sizeof(len) + buflen) { 118 terrno = errno; 119 #ifdef DEBUG 120 if (_res.options & RES_DEBUG) 121 perror("write failed"); 122 #endif DEBUG 123 (void) close(s); 124 s = -1; 125 continue; 126 } 127 /* 128 * Receive length & response 129 */ 130 cp = answer; 131 len = sizeof(short); 132 while (len != 0 && 133 (n = read(s, (char *)cp, (int)len)) > 0) { 134 cp += n; 135 len -= n; 136 } 137 if (n <= 0) { 138 terrno = errno; 139 #ifdef DEBUG 140 if (_res.options & RES_DEBUG) 141 perror("read failed"); 142 #endif DEBUG 143 (void) close(s); 144 s = -1; 145 continue; 146 } 147 cp = answer; 148 resplen = len = ntohs(*(u_short *)cp); 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 continue; 163 } 164 } else { 165 /* 166 * Use datagrams. 167 */ 168 if (s < 0) 169 s = socket(AF_INET, SOCK_DGRAM, 0); 170 #if BSD >= 43 171 if (_res.nscount == 1 || retry == _res.retry) { 172 /* 173 * Don't use connect if we might 174 * still receive a response 175 * from another server. 176 */ 177 if (connect(s, &_res.nsaddr_list[ns], 178 sizeof(struct sockaddr)) < 0 || 179 send(s, buf, buflen, 0) != buflen) { 180 #ifdef DEBUG 181 if (_res.options & RES_DEBUG) 182 perror("connect"); 183 #endif DEBUG 184 continue; 185 } 186 } else 187 #endif BSD 188 if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns], 189 sizeof(struct sockaddr)) != buflen) { 190 #ifdef DEBUG 191 if (_res.options & RES_DEBUG) 192 perror("sendto"); 193 #endif DEBUG 194 continue; 195 } 196 197 /* 198 * Wait for reply 199 */ 200 timeout.tv_sec = (_res.retrans << (_res.retry - retry)) 201 / _res.nscount; 202 if (timeout.tv_sec <= 0) 203 timeout.tv_sec = 1; 204 timeout.tv_usec = 0; 205 wait: 206 FD_ZERO(&dsmask); 207 FD_SET(s, &dsmask); 208 n = select(s+1, &dsmask, (fd_set *)NULL, 209 (fd_set *)NULL, &timeout); 210 if (n < 0) { 211 #ifdef DEBUG 212 if (_res.options & RES_DEBUG) 213 perror("select"); 214 #endif DEBUG 215 continue; 216 } 217 if (n == 0) { 218 /* 219 * timeout 220 */ 221 #ifdef DEBUG 222 if (_res.options & RES_DEBUG) 223 printf("timeout\n"); 224 #endif DEBUG 225 /* 226 * Disconnect if we want to listen 227 * for responses from more than one server. 228 */ 229 if (_res.nscount > 1 && retry == _res.retry) 230 (void) connect(s, &no_addr, 231 sizeof(no_addr)); 232 gotsomewhere = 1; 233 continue; 234 } 235 if ((resplen = recv(s, answer, anslen, 0)) <= 0) { 236 #ifdef DEBUG 237 if (_res.options & RES_DEBUG) 238 perror("recvfrom"); 239 #endif DEBUG 240 continue; 241 } 242 gotsomewhere = 1; 243 if (id != anhp->id) { 244 /* 245 * response from old query, ignore it 246 */ 247 #ifdef DEBUG 248 if (_res.options & RES_DEBUG) { 249 printf("old answer:\n"); 250 p_query(answer); 251 } 252 #endif DEBUG 253 goto wait; 254 } 255 if (!(_res.options & RES_IGNTC) && anhp->tc) { 256 /* 257 * get rest of answer 258 */ 259 #ifdef DEBUG 260 if (_res.options & RES_DEBUG) 261 printf("truncated answer\n"); 262 #endif DEBUG 263 (void) close(s); 264 s = -1; 265 /* 266 * retry decremented on continue 267 * to desired starting value 268 */ 269 retry = _res.retry + 1; 270 v_circuit = 1; 271 continue; 272 } 273 } 274 #ifdef DEBUG 275 if (_res.options & RES_DEBUG) { 276 printf("got answer:\n"); 277 p_query(answer); 278 } 279 #endif DEBUG 280 /* 281 * We are going to assume that the first server is preferred 282 * over the rest (i.e. it is on the local machine) and only 283 * keep that one open. 284 */ 285 if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) { 286 return (resplen); 287 } else { 288 (void) close(s); 289 s = -1; 290 return (resplen); 291 } 292 } 293 } 294 if (s >= 0) { 295 (void) close(s); 296 s = -1; 297 } 298 if (v_circuit == 0) 299 if (gotsomewhere == 0) 300 errno = ECONNREFUSED; 301 else 302 errno = ETIMEDOUT; 303 else 304 errno = terrno; 305 return (-1); 306 } 307 308 /* 309 * This routine is for closing the socket if a virtual circuit is used and 310 * the program wants to close it. This provides support for endhostent() 311 * which expects to close the socket. 312 * 313 * This routine is not expected to be user visible. 314 */ 315 _res_close() 316 { 317 if (s != -1) { 318 (void) close(s); 319 s = -1; 320 } 321 } 322