xref: /original-bsd/lib/libc/net/res_send.c (revision bbf9881c)
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.10 (Berkeley) 04/10/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 <netinet/in.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <arpa/nameser.h>
23 #include <resolv.h>
24 
25 extern int errno;
26 
27 static int s = -1;	/* socket used for communications */
28 
29 #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
30 
31 res_send(buf, buflen, answer, anslen)
32 	char *buf;
33 	int buflen;
34 	char *answer;
35 	int anslen;
36 {
37 	register int n;
38 	int retry, v_circuit, resplen, ns;
39 	int gotsomewhere = 0;
40 	u_short id, len;
41 	char *cp;
42 	int dsmask;
43 	struct timeval timeout;
44 	HEADER *hp = (HEADER *) buf;
45 	HEADER *anhp = (HEADER *) answer;
46 
47 	extern u_short htons(), ntohs();
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; ) {
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 			if (write(s, (char *)&len, sizeof(len)) != sizeof(len)||
100 			    write(s, buf, buflen) != buflen) {
101 #ifdef DEBUG
102 				if (_res.options & RES_DEBUG)
103 					perror("write failed");
104 #endif DEBUG
105 				(void) close(s);
106 				s = -1;
107 				continue;
108 			}
109 			/*
110 			 * Receive length & response
111 			 */
112 			cp = answer;
113 			len = sizeof(short);
114 			while (len > 0 &&
115 			    (n = read(s, (char *)cp, (int)len)) > 0) {
116 				cp += n;
117 				len -= n;
118 			}
119 			if (n <= 0) {
120 #ifdef DEBUG
121 				if (_res.options & RES_DEBUG)
122 					perror("read failed");
123 #endif DEBUG
124 				(void) close(s);
125 				s = -1;
126 				continue;
127 			}
128 			cp = answer;
129 			resplen = len = ntohs(*(u_short *)cp);
130 			while (len > 0 &&
131 			   (n = read(s, (char *)cp, (int)len)) > 0) {
132 				cp += n;
133 				len -= n;
134 			}
135 			if (n <= 0) {
136 #ifdef DEBUG
137 				if (_res.options & RES_DEBUG)
138 					perror("read failed");
139 #endif DEBUG
140 				(void) close(s);
141 				s = -1;
142 				continue;
143 			}
144 		} else {
145 			/*
146 			 * Use datagrams.
147 			 */
148 			if (s < 0)
149 				s = socket(AF_INET, SOCK_DGRAM, 0);
150 #if	BSD >= 43
151 			if (connect(s, &_res.nsaddr_list[ns],
152 			    sizeof(struct sockaddr)) < 0 ||
153 			    send(s, buf, buflen, 0) != buflen) {
154 #ifdef DEBUG
155 				if (_res.options & RES_DEBUG)
156 					perror("connect");
157 #endif DEBUG
158 				continue;
159 			}
160 #else BSD
161 			if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
162 			    sizeof(struct sockaddr)) != buflen) {
163 #ifdef DEBUG
164 				if (_res.options & RES_DEBUG)
165 					perror("sendto");
166 #endif DEBUG
167 				continue;
168 			}
169 #endif BSD
170 			/*
171 			 * Wait for reply
172 			 */
173 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
174 				/ _res.nscount;
175 			if (timeout.tv_sec <= 0)
176 				timeout.tv_sec = 1;
177 			timeout.tv_usec = 0;
178 wait:
179 			dsmask = 1 << s;
180 			n = select(s+1, &dsmask, 0, 0, &timeout);
181 			if (n < 0) {
182 #ifdef DEBUG
183 				if (_res.options & RES_DEBUG)
184 					perror("select");
185 #endif DEBUG
186 				continue;
187 			}
188 			if (n == 0) {
189 				/*
190 				 * timeout
191 				 */
192 #ifdef DEBUG
193 				if (_res.options & RES_DEBUG)
194 					printf("timeout\n");
195 #endif DEBUG
196 				gotsomewhere = 1;
197 				continue;
198 			}
199 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
200 #ifdef DEBUG
201 				if (_res.options & RES_DEBUG)
202 					perror("recvfrom");
203 #endif DEBUG
204 				continue;
205 			}
206 			gotsomewhere = 1;
207 			if (id != anhp->id) {
208 				/*
209 				 * response from old query, ignore it
210 				 */
211 #ifdef DEBUG
212 				if (_res.options & RES_DEBUG) {
213 					printf("old answer:\n");
214 					p_query(answer);
215 				}
216 #endif DEBUG
217 				goto wait;
218 			}
219 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
220 				/*
221 				 * get rest of answer
222 				 */
223 #ifdef DEBUG
224 				if (_res.options & RES_DEBUG)
225 					printf("truncated answer\n");
226 #endif DEBUG
227 				(void) close(s);
228 				s = -1;
229 				retry = _res.retry;
230 				v_circuit = 1;
231 				continue;
232 			}
233 		}
234 #ifdef DEBUG
235 		if (_res.options & RES_DEBUG) {
236 			printf("got answer:\n");
237 			p_query(answer);
238 		}
239 #endif DEBUG
240 		/*
241 		 * We are going to assume that the first server is preferred
242 		 * over the rest (i.e. it is on the local machine) and only
243 		 * keep that one open.
244 		 */
245 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
246 			return (resplen);
247 		} else {
248 			(void) close(s);
249 			s = -1;
250 			return (resplen);
251 		}
252 	   }
253 	}
254 	(void) close(s);
255 	s = -1;
256 	if (v_circuit == 0 && gotsomewhere == 0)
257 		errno = ECONNREFUSED;
258 	else
259 		errno = ETIMEDOUT;
260 	return (-1);
261 }
262 
263 /*
264  * This routine is for closing the socket if a virtual circuit is used and
265  * the program wants to close it.  This provides support for endhostent()
266  * which expects to close the socket.
267  *
268  * This routine is not expected to be user visible.
269  */
270 _res_close()
271 {
272 	if (s != -1) {
273 		(void) close(s);
274 		s = -1;
275 	}
276 }
277