xref: /original-bsd/lib/libc/net/res_send.c (revision 43bfbc1c)
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.12 (Berkeley) 04/30/86";
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 
30 #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
31 
32 res_send(buf, buflen, answer, anslen)
33 	char *buf;
34 	int buflen;
35 	char *answer;
36 	int anslen;
37 {
38 	register int n;
39 	int retry, v_circuit, resplen, ns;
40 	int gotsomewhere = 0;
41 	u_short id, len;
42 	char *cp;
43 	int dsmask;
44 	struct timeval timeout;
45 	HEADER *hp = (HEADER *) buf;
46 	HEADER *anhp = (HEADER *) answer;
47 	struct iovec iov[2];
48 
49 #ifdef DEBUG
50 	if (_res.options & RES_DEBUG) {
51 		printf("res_send()\n");
52 		p_query(buf);
53 	}
54 #endif DEBUG
55 	if (!(_res.options & RES_INIT))
56 		if (res_init() == -1) {
57 			return(-1);
58 		}
59 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
60 	id = hp->id;
61 	/*
62 	 * Send request, RETRY times, or until successful
63 	 */
64 	for (retry = _res.retry; retry > 0; retry--) {
65 	   for (ns = 0; ns < _res.nscount; ns++) {
66 #ifdef DEBUG
67 		if (_res.options & RES_DEBUG)
68 			printf("Querying server (# %d) address = %s\n", ns+1,
69 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr));
70 #endif DEBUG
71 		if (v_circuit) {
72 			/*
73 			 * Use virtual circuit.
74 			 */
75 			if (s < 0) {
76 				s = socket(AF_INET, SOCK_STREAM, 0);
77 				if (s < 0) {
78 #ifdef DEBUG
79 					if (_res.options & RES_DEBUG)
80 					    perror("socket failed");
81 #endif DEBUG
82 					continue;
83 				}
84 				if (connect(s, &(_res.nsaddr_list[ns]),
85 				   sizeof(struct sockaddr)) < 0) {
86 #ifdef DEBUG
87 					if (_res.options & RES_DEBUG)
88 					    perror("connect failed");
89 #endif DEBUG
90 					(void) close(s);
91 					s = -1;
92 					continue;
93 				}
94 			}
95 			/*
96 			 * Send length & message
97 			 */
98 			len = htons((u_short)buflen);
99 			iov[0].iov_base = (caddr_t)&len;
100 			iov[0].iov_len = sizeof(len);
101 			iov[1].iov_base = buf;
102 			iov[1].iov_len = buflen;
103 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
104 #ifdef DEBUG
105 				if (_res.options & RES_DEBUG)
106 					perror("write failed");
107 #endif DEBUG
108 				(void) close(s);
109 				s = -1;
110 				continue;
111 			}
112 			/*
113 			 * Receive length & response
114 			 */
115 			cp = answer;
116 			len = sizeof(short);
117 			while (len != 0 &&
118 			    (n = read(s, (char *)cp, (int)len)) > 0) {
119 				cp += n;
120 				len -= n;
121 			}
122 			if (n <= 0) {
123 #ifdef DEBUG
124 				if (_res.options & RES_DEBUG)
125 					perror("read failed");
126 #endif DEBUG
127 				(void) close(s);
128 				s = -1;
129 				continue;
130 			}
131 			cp = answer;
132 			resplen = len = ntohs(*(u_short *)cp);
133 			while (len != 0 &&
134 			   (n = read(s, (char *)cp, (int)len)) > 0) {
135 				cp += n;
136 				len -= n;
137 			}
138 			if (n <= 0) {
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 		} else {
148 			/*
149 			 * Use datagrams.
150 			 */
151 			if (s < 0)
152 				s = socket(AF_INET, SOCK_DGRAM, 0);
153 #if	BSD >= 43
154 			if (connect(s, &_res.nsaddr_list[ns],
155 			    sizeof(struct sockaddr)) < 0 ||
156 			    send(s, buf, buflen, 0) != buflen) {
157 #ifdef DEBUG
158 				if (_res.options & RES_DEBUG)
159 					perror("connect");
160 #endif DEBUG
161 				continue;
162 			}
163 #else BSD
164 			if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
165 			    sizeof(struct sockaddr)) != buflen) {
166 #ifdef DEBUG
167 				if (_res.options & RES_DEBUG)
168 					perror("sendto");
169 #endif DEBUG
170 				continue;
171 			}
172 #endif BSD
173 			/*
174 			 * Wait for reply
175 			 */
176 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
177 				/ _res.nscount;
178 			if (timeout.tv_sec <= 0)
179 				timeout.tv_sec = 1;
180 			timeout.tv_usec = 0;
181 wait:
182 			dsmask = 1 << s;
183 			n = select(s+1, &dsmask, (fd_set *)NULL,
184 				(fd_set *)NULL, &timeout);
185 			if (n < 0) {
186 #ifdef DEBUG
187 				if (_res.options & RES_DEBUG)
188 					perror("select");
189 #endif DEBUG
190 				continue;
191 			}
192 			if (n == 0) {
193 				/*
194 				 * timeout
195 				 */
196 #ifdef DEBUG
197 				if (_res.options & RES_DEBUG)
198 					printf("timeout\n");
199 #endif DEBUG
200 				gotsomewhere = 1;
201 				continue;
202 			}
203 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
204 #ifdef DEBUG
205 				if (_res.options & RES_DEBUG)
206 					perror("recvfrom");
207 #endif DEBUG
208 				continue;
209 			}
210 			gotsomewhere = 1;
211 			if (id != anhp->id) {
212 				/*
213 				 * response from old query, ignore it
214 				 */
215 #ifdef DEBUG
216 				if (_res.options & RES_DEBUG) {
217 					printf("old answer:\n");
218 					p_query(answer);
219 				}
220 #endif DEBUG
221 				goto wait;
222 			}
223 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
224 				/*
225 				 * get rest of answer
226 				 */
227 #ifdef DEBUG
228 				if (_res.options & RES_DEBUG)
229 					printf("truncated answer\n");
230 #endif DEBUG
231 				(void) close(s);
232 				s = -1;
233 				/*
234 				 * retry decremented on continue
235 				 * to desired starting value
236 				 */
237 				retry = _res.retry + 1;
238 				v_circuit = 1;
239 				continue;
240 			}
241 		}
242 #ifdef DEBUG
243 		if (_res.options & RES_DEBUG) {
244 			printf("got answer:\n");
245 			p_query(answer);
246 		}
247 #endif DEBUG
248 		/*
249 		 * We are going to assume that the first server is preferred
250 		 * over the rest (i.e. it is on the local machine) and only
251 		 * keep that one open.
252 		 */
253 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
254 			return (resplen);
255 		} else {
256 			(void) close(s);
257 			s = -1;
258 			return (resplen);
259 		}
260 	   }
261 	}
262 	(void) close(s);
263 	s = -1;
264 	if (v_circuit == 0 && gotsomewhere == 0)
265 		errno = ECONNREFUSED;
266 	else
267 		errno = ETIMEDOUT;
268 	return (-1);
269 }
270 
271 /*
272  * This routine is for closing the socket if a virtual circuit is used and
273  * the program wants to close it.  This provides support for endhostent()
274  * which expects to close the socket.
275  *
276  * This routine is not expected to be user visible.
277  */
278 _res_close()
279 {
280 	if (s != -1) {
281 		(void) close(s);
282 		s = -1;
283 	}
284 }
285