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