Commit | Line | Data |
---|---|---|
825eb42b JL |
1 | /* |
2 | * net.c | |
3 | * | |
4 | * Network implementation | |
5 | * All network related functions are grouped here | |
6 | * | |
7 | * a Net::DNS like library for C | |
8 | * | |
9 | * (c) NLnet Labs, 2004-2006 | |
10 | * | |
11 | * See the file LICENSE for the license | |
12 | */ | |
13 | ||
14 | #include <ldns/config.h> | |
15 | ||
16 | #include <ldns/ldns.h> | |
17 | ||
18 | #ifdef HAVE_NETINET_IN_H | |
19 | #include <netinet/in.h> | |
20 | #endif | |
21 | #ifdef HAVE_SYS_SOCKET_H | |
22 | #include <sys/socket.h> | |
23 | #endif | |
24 | #ifdef HAVE_NETDB_H | |
25 | #include <netdb.h> | |
26 | #endif | |
27 | #ifdef HAVE_ARPA_INET_H | |
28 | #include <arpa/inet.h> | |
29 | #endif | |
30 | #include <sys/time.h> | |
31 | #include <errno.h> | |
32 | #include <fcntl.h> | |
33 | ||
34 | ldns_status | |
35 | ldns_send(ldns_pkt **result_packet, ldns_resolver *r, const ldns_pkt *query_pkt) | |
36 | { | |
37 | ldns_buffer *qb; | |
38 | ldns_status result; | |
39 | ldns_rdf *tsig_mac = NULL; | |
40 | ||
41 | qb = ldns_buffer_new(LDNS_MIN_BUFLEN); | |
42 | ||
43 | if (query_pkt && ldns_pkt_tsig(query_pkt)) { | |
44 | tsig_mac = ldns_rr_rdf(ldns_pkt_tsig(query_pkt), 3); | |
45 | } | |
46 | ||
825eb42b JL |
47 | if (!query_pkt || |
48 | ldns_pkt2buffer_wire(qb, query_pkt) != LDNS_STATUS_OK) { | |
49 | result = LDNS_STATUS_ERR; | |
50 | } else { | |
51 | result = ldns_send_buffer(result_packet, r, qb, tsig_mac); | |
52 | } | |
53 | ||
54 | ldns_buffer_free(qb); | |
ac996e71 | 55 | |
825eb42b JL |
56 | return result; |
57 | } | |
58 | ||
59 | ldns_status | |
60 | ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac) | |
61 | { | |
62 | uint8_t i; | |
63 | ||
64 | struct sockaddr_storage *ns; | |
65 | size_t ns_len; | |
66 | struct timeval tv_s; | |
67 | struct timeval tv_e; | |
68 | ||
69 | ldns_rdf **ns_array; | |
70 | size_t *rtt; | |
71 | ldns_pkt *reply; | |
72 | bool all_servers_rtt_inf; | |
73 | uint8_t retries; | |
74 | ||
75 | uint8_t *reply_bytes = NULL; | |
76 | size_t reply_size = 0; | |
77 | ldns_status status, send_status; | |
78 | ||
79 | assert(r != NULL); | |
80 | ||
81 | status = LDNS_STATUS_OK; | |
82 | rtt = ldns_resolver_rtt(r); | |
83 | ns_array = ldns_resolver_nameservers(r); | |
84 | reply = NULL; | |
85 | ns_len = 0; | |
86 | ||
87 | all_servers_rtt_inf = true; | |
88 | ||
89 | if (ldns_resolver_random(r)) { | |
90 | ldns_resolver_nameservers_randomize(r); | |
91 | } | |
92 | ||
93 | /* loop through all defined nameservers */ | |
94 | for (i = 0; i < ldns_resolver_nameserver_count(r); i++) { | |
95 | if (rtt[i] == LDNS_RESOLV_RTT_INF) { | |
96 | /* not reachable nameserver! */ | |
97 | continue; | |
98 | } | |
825eb42b JL |
99 | |
100 | /* maybe verbosity setting? | |
101 | printf("Sending to "); | |
102 | ldns_rdf_print(stdout, ns_array[i]); | |
103 | printf("\n"); | |
104 | */ | |
105 | ns = ldns_rdf2native_sockaddr_storage(ns_array[i], | |
106 | ldns_resolver_port(r), &ns_len); | |
ac996e71 | 107 | |
d1b2b5ca JM |
108 | |
109 | #ifndef S_SPLINT_S | |
ac996e71 | 110 | if ((ns->ss_family == AF_INET) && |
825eb42b | 111 | (ldns_resolver_ip6(r) == LDNS_RESOLV_INET6)) { |
ac996e71 | 112 | /* not reachable */ |
d1b2b5ca | 113 | LDNS_FREE(ns); |
825eb42b JL |
114 | continue; |
115 | } | |
116 | ||
117 | if ((ns->ss_family == AF_INET6) && | |
118 | (ldns_resolver_ip6(r) == LDNS_RESOLV_INET)) { | |
ac996e71 | 119 | /* not reachable */ |
d1b2b5ca | 120 | LDNS_FREE(ns); |
825eb42b JL |
121 | continue; |
122 | } | |
d1b2b5ca | 123 | #endif |
825eb42b | 124 | |
ac996e71 JL |
125 | all_servers_rtt_inf = false; |
126 | ||
825eb42b JL |
127 | gettimeofday(&tv_s, NULL); |
128 | ||
129 | send_status = LDNS_STATUS_ERR; | |
130 | ||
131 | /* reply_bytes implicitly handles our error */ | |
132 | if (1 == ldns_resolver_usevc(r)) { | |
133 | for (retries = ldns_resolver_retry(r); retries > 0; retries--) { | |
134 | send_status = | |
135 | ldns_tcp_send(&reply_bytes, qb, ns, | |
136 | (socklen_t)ns_len, ldns_resolver_timeout(r), | |
137 | &reply_size); | |
138 | if (send_status == LDNS_STATUS_OK) { | |
139 | break; | |
140 | } | |
141 | } | |
142 | } else { | |
143 | for (retries = ldns_resolver_retry(r); retries > 0; retries--) { | |
144 | /* ldns_rdf_print(stdout, ns_array[i]); */ | |
145 | send_status = | |
146 | ldns_udp_send(&reply_bytes, qb, ns, | |
147 | (socklen_t)ns_len, ldns_resolver_timeout(r), | |
148 | &reply_size); | |
149 | ||
150 | if (send_status == LDNS_STATUS_OK) { | |
151 | break; | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | if (send_status != LDNS_STATUS_OK) { | |
157 | ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF); | |
158 | status = send_status; | |
159 | } | |
160 | ||
161 | /* obey the fail directive */ | |
162 | if (!reply_bytes) { | |
163 | /* the current nameserver seems to have a problem, blacklist it */ | |
164 | if (ldns_resolver_fail(r)) { | |
165 | LDNS_FREE(ns); | |
166 | return LDNS_STATUS_ERR; | |
167 | } else { | |
168 | LDNS_FREE(ns); | |
169 | continue; | |
170 | } | |
171 | } | |
172 | ||
173 | status = ldns_wire2pkt(&reply, reply_bytes, reply_size); | |
174 | if (status != LDNS_STATUS_OK) { | |
175 | LDNS_FREE(reply_bytes); | |
176 | LDNS_FREE(ns); | |
177 | return status; | |
178 | } | |
179 | ||
180 | LDNS_FREE(ns); | |
181 | gettimeofday(&tv_e, NULL); | |
182 | ||
183 | if (reply) { | |
184 | ldns_pkt_set_querytime(reply, (uint32_t) | |
185 | ((tv_e.tv_sec - tv_s.tv_sec) * 1000) + | |
186 | (tv_e.tv_usec - tv_s.tv_usec) / 1000); | |
d1b2b5ca JM |
187 | ldns_pkt_set_answerfrom(reply, |
188 | ldns_rdf_clone(ns_array[i])); | |
825eb42b JL |
189 | ldns_pkt_set_timestamp(reply, tv_s); |
190 | ldns_pkt_set_size(reply, reply_size); | |
191 | break; | |
192 | } else { | |
193 | if (ldns_resolver_fail(r)) { | |
194 | /* if fail is set bail out, after the first | |
195 | * one */ | |
196 | break; | |
197 | } | |
198 | } | |
199 | ||
200 | /* wait retrans seconds... */ | |
201 | sleep((unsigned int) ldns_resolver_retrans(r)); | |
202 | } | |
203 | ||
204 | if (all_servers_rtt_inf) { | |
205 | LDNS_FREE(reply_bytes); | |
206 | return LDNS_STATUS_RES_NO_NS; | |
207 | } | |
208 | #ifdef HAVE_SSL | |
d1b2b5ca | 209 | if (tsig_mac && reply && reply_bytes) { |
825eb42b JL |
210 | if (!ldns_pkt_tsig_verify(reply, |
211 | reply_bytes, | |
212 | reply_size, | |
213 | ldns_resolver_tsig_keyname(r), | |
214 | ldns_resolver_tsig_keydata(r), tsig_mac)) { | |
215 | status = LDNS_STATUS_CRYPTO_TSIG_BOGUS; | |
216 | } | |
217 | } | |
218 | #else | |
219 | (void)tsig_mac; | |
220 | #endif /* HAVE_SSL */ | |
ac996e71 | 221 | |
825eb42b JL |
222 | LDNS_FREE(reply_bytes); |
223 | if (result) { | |
224 | *result = reply; | |
225 | } | |
226 | ||
227 | return status; | |
228 | } | |
229 | ||
230 | /** best effort to set nonblocking */ | |
231 | static void | |
232 | ldns_sock_nonblock(int sockfd) | |
233 | { | |
234 | #ifdef HAVE_FCNTL | |
235 | int flag; | |
236 | if((flag = fcntl(sockfd, F_GETFL)) != -1) { | |
237 | flag |= O_NONBLOCK; | |
238 | if(fcntl(sockfd, F_SETFL, flag) == -1) { | |
239 | /* ignore error, continue blockingly */ | |
240 | } | |
241 | } | |
242 | #elif defined(HAVE_IOCTLSOCKET) | |
243 | unsigned long on = 1; | |
244 | if(ioctlsocket(sockfd, FIONBIO, &on) != 0) { | |
245 | /* ignore error, continue blockingly */ | |
246 | } | |
247 | #endif | |
248 | } | |
249 | ||
250 | /** best effort to set blocking */ | |
251 | static void | |
252 | ldns_sock_block(int sockfd) | |
253 | { | |
254 | #ifdef HAVE_FCNTL | |
255 | int flag; | |
256 | if((flag = fcntl(sockfd, F_GETFL)) != -1) { | |
257 | flag &= ~O_NONBLOCK; | |
258 | if(fcntl(sockfd, F_SETFL, flag) == -1) { | |
259 | /* ignore error, continue */ | |
260 | } | |
261 | } | |
262 | #elif defined(HAVE_IOCTLSOCKET) | |
263 | unsigned long off = 0; | |
264 | if(ioctlsocket(sockfd, FIONBIO, &off) != 0) { | |
265 | /* ignore error, continue */ | |
266 | } | |
267 | #endif | |
268 | } | |
269 | ||
270 | /** wait for a socket to become ready */ | |
271 | static int | |
272 | ldns_sock_wait(int sockfd, struct timeval timeout, int write) | |
273 | { | |
825eb42b | 274 | int ret; |
fd185f4d | 275 | #ifndef S_SPLINT_S |
d1b2b5ca | 276 | fd_set fds; |
825eb42b JL |
277 | FD_ZERO(&fds); |
278 | FD_SET(FD_SET_T sockfd, &fds); | |
279 | if(write) | |
280 | ret = select(sockfd+1, NULL, &fds, NULL, &timeout); | |
281 | else | |
282 | ret = select(sockfd+1, &fds, NULL, NULL, &timeout); | |
d1b2b5ca | 283 | #endif |
825eb42b JL |
284 | if(ret == 0) |
285 | /* timeout expired */ | |
286 | return 0; | |
287 | else if(ret == -1) | |
288 | /* error */ | |
289 | return 0; | |
290 | return 1; | |
291 | } | |
292 | ||
293 | ldns_status | |
ac996e71 | 294 | ldns_udp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to, |
825eb42b JL |
295 | socklen_t tolen, struct timeval timeout, size_t *answer_size) |
296 | { | |
297 | int sockfd; | |
298 | uint8_t *answer; | |
299 | ||
300 | sockfd = ldns_udp_bgsend(qbin, to, tolen, timeout); | |
301 | ||
302 | if (sockfd == 0) { | |
303 | return LDNS_STATUS_SOCKET_ERROR; | |
304 | } | |
305 | ||
306 | /* wait for an response*/ | |
307 | if(!ldns_sock_wait(sockfd, timeout, 0)) { | |
ac996e71 | 308 | #ifndef USE_WINSOCK |
825eb42b | 309 | close(sockfd); |
ac996e71 JL |
310 | #else |
311 | closesocket(sockfd); | |
312 | #endif | |
825eb42b JL |
313 | return LDNS_STATUS_NETWORK_ERR; |
314 | } | |
ac996e71 | 315 | |
fd185f4d JL |
316 | /* set to nonblocking, so if the checksum is bad, it becomes |
317 | * an EGAIN error and the ldns_udp_send function does not block, | |
318 | * but returns a 'NETWORK_ERROR' much like a timeout. */ | |
319 | ldns_sock_nonblock(sockfd); | |
320 | ||
825eb42b | 321 | answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL); |
ac996e71 | 322 | #ifndef USE_WINSOCK |
825eb42b | 323 | close(sockfd); |
ac996e71 JL |
324 | #else |
325 | closesocket(sockfd); | |
326 | #endif | |
825eb42b JL |
327 | |
328 | if (*answer_size == 0) { | |
329 | /* oops */ | |
330 | return LDNS_STATUS_NETWORK_ERR; | |
331 | } | |
332 | ||
333 | *result = answer; | |
334 | return LDNS_STATUS_OK; | |
335 | } | |
336 | ||
337 | int | |
338 | ldns_udp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, | |
339 | struct timeval timeout) | |
340 | { | |
341 | int sockfd; | |
342 | ||
343 | sockfd = ldns_udp_connect(to, timeout); | |
344 | ||
345 | if (sockfd == 0) { | |
346 | return 0; | |
347 | } | |
348 | ||
349 | if (ldns_udp_send_query(qbin, sockfd, to, tolen) == 0) { | |
b5dedcca JL |
350 | #ifndef USE_WINSOCK |
351 | close(sockfd); | |
352 | #else | |
353 | closesocket(sockfd); | |
354 | #endif | |
825eb42b JL |
355 | return 0; |
356 | } | |
357 | return sockfd; | |
358 | } | |
359 | ||
360 | int | |
361 | ldns_udp_connect(const struct sockaddr_storage *to, struct timeval ATTR_UNUSED(timeout)) | |
362 | { | |
363 | int sockfd; | |
ac996e71 | 364 | |
d1b2b5ca | 365 | #ifndef S_SPLINT_S |
825eb42b JL |
366 | if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_DGRAM, |
367 | IPPROTO_UDP)) | |
368 | == -1) { | |
369 | return 0; | |
370 | } | |
d1b2b5ca | 371 | #endif |
825eb42b JL |
372 | return sockfd; |
373 | } | |
374 | ||
375 | int | |
376 | ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen, | |
377 | struct timeval timeout) | |
378 | { | |
379 | int sockfd; | |
ac996e71 | 380 | |
d1b2b5ca | 381 | #ifndef S_SPLINT_S |
825eb42b JL |
382 | if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_STREAM, |
383 | IPPROTO_TCP)) == -1) { | |
384 | return 0; | |
385 | } | |
d1b2b5ca | 386 | #endif |
825eb42b JL |
387 | |
388 | /* perform nonblocking connect, to be able to wait with select() */ | |
389 | ldns_sock_nonblock(sockfd); | |
390 | if (connect(sockfd, (struct sockaddr*)to, tolen) == -1) { | |
391 | #ifndef USE_WINSOCK | |
392 | #ifdef EINPROGRESS | |
393 | if(errno != EINPROGRESS) { | |
394 | #else | |
395 | if(1) { | |
396 | #endif | |
397 | close(sockfd); | |
398 | return 0; | |
399 | } | |
400 | #else /* USE_WINSOCK */ | |
401 | if(WSAGetLastError() != WSAEINPROGRESS && | |
402 | WSAGetLastError() != WSAEWOULDBLOCK) { | |
403 | closesocket(sockfd); | |
404 | return 0; | |
405 | } | |
406 | #endif | |
407 | /* error was only telling us that it would block */ | |
408 | } | |
409 | ||
410 | /* wait(write) until connected or error */ | |
411 | while(1) { | |
412 | int error = 0; | |
413 | socklen_t len = (socklen_t)sizeof(error); | |
414 | ||
415 | if(!ldns_sock_wait(sockfd, timeout, 1)) { | |
ac996e71 | 416 | #ifndef USE_WINSOCK |
825eb42b | 417 | close(sockfd); |
ac996e71 JL |
418 | #else |
419 | closesocket(sockfd); | |
420 | #endif | |
825eb42b JL |
421 | return 0; |
422 | } | |
423 | ||
424 | /* check if there is a pending error for nonblocking connect */ | |
425 | if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&error, | |
426 | &len) < 0) { | |
427 | #ifndef USE_WINSOCK | |
428 | error = errno; /* on solaris errno is error */ | |
429 | #else | |
430 | error = WSAGetLastError(); | |
431 | #endif | |
432 | } | |
433 | #ifndef USE_WINSOCK | |
434 | #if defined(EINPROGRESS) && defined(EWOULDBLOCK) | |
435 | if(error == EINPROGRESS || error == EWOULDBLOCK) | |
436 | continue; /* try again */ | |
437 | #endif | |
438 | else if(error != 0) { | |
439 | close(sockfd); | |
440 | /* error in errno for our user */ | |
441 | errno = error; | |
442 | return 0; | |
443 | } | |
444 | #else /* USE_WINSOCK */ | |
445 | if(error == WSAEINPROGRESS) | |
446 | continue; | |
447 | else if(error == WSAEWOULDBLOCK) | |
448 | continue; | |
449 | else if(error != 0) { | |
450 | closesocket(sockfd); | |
451 | errno = error; | |
452 | return 0; | |
453 | } | |
454 | #endif /* USE_WINSOCK */ | |
455 | /* connected */ | |
456 | break; | |
457 | } | |
458 | ||
459 | /* set the socket blocking again */ | |
460 | ldns_sock_block(sockfd); | |
461 | ||
462 | return sockfd; | |
463 | } | |
464 | ||
465 | ssize_t | |
466 | ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, | |
467 | const struct sockaddr_storage *to, socklen_t tolen) | |
468 | { | |
469 | uint8_t *sendbuf; | |
470 | ssize_t bytes; | |
471 | ||
472 | /* add length of packet */ | |
473 | sendbuf = LDNS_XMALLOC(uint8_t, ldns_buffer_position(qbin) + 2); | |
ac996e71 | 474 | if(!sendbuf) return 0; |
825eb42b | 475 | ldns_write_uint16(sendbuf, ldns_buffer_position(qbin)); |
d1b2b5ca | 476 | memcpy(sendbuf + 2, ldns_buffer_begin(qbin), ldns_buffer_position(qbin)); |
825eb42b JL |
477 | |
478 | bytes = sendto(sockfd, (void*)sendbuf, | |
479 | ldns_buffer_position(qbin) + 2, 0, (struct sockaddr *)to, tolen); | |
480 | ||
481 | LDNS_FREE(sendbuf); | |
482 | ||
483 | if (bytes == -1 || (size_t) bytes != ldns_buffer_position(qbin) + 2 ) { | |
484 | return 0; | |
485 | } | |
486 | return bytes; | |
487 | } | |
488 | ||
489 | /* don't wait for an answer */ | |
490 | ssize_t | |
491 | ldns_udp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, | |
492 | socklen_t tolen) | |
493 | { | |
494 | ssize_t bytes; | |
495 | ||
496 | bytes = sendto(sockfd, (void*)ldns_buffer_begin(qbin), | |
497 | ldns_buffer_position(qbin), 0, (struct sockaddr *)to, tolen); | |
498 | ||
499 | if (bytes == -1 || (size_t)bytes != ldns_buffer_position(qbin)) { | |
500 | return 0; | |
501 | } | |
502 | if ((size_t) bytes != ldns_buffer_position(qbin)) { | |
503 | return 0; | |
504 | } | |
505 | return bytes; | |
506 | } | |
507 | ||
508 | uint8_t * | |
509 | ldns_udp_read_wire(int sockfd, size_t *size, struct sockaddr_storage *from, | |
510 | socklen_t *fromlen) | |
511 | { | |
ac996e71 | 512 | uint8_t *wire, *wireout; |
825eb42b JL |
513 | ssize_t wire_size; |
514 | ||
515 | wire = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN); | |
516 | if (!wire) { | |
517 | *size = 0; | |
518 | return NULL; | |
519 | } | |
520 | ||
521 | wire_size = recvfrom(sockfd, (void*)wire, LDNS_MAX_PACKETLEN, 0, | |
522 | (struct sockaddr *)from, fromlen); | |
523 | ||
524 | /* recvfrom can also return 0 */ | |
525 | if (wire_size == -1 || wire_size == 0) { | |
526 | *size = 0; | |
527 | LDNS_FREE(wire); | |
528 | return NULL; | |
529 | } | |
530 | ||
531 | *size = (size_t)wire_size; | |
ac996e71 JL |
532 | wireout = LDNS_XREALLOC(wire, uint8_t, (size_t)wire_size); |
533 | if(!wireout) LDNS_FREE(wire); | |
825eb42b | 534 | |
ac996e71 | 535 | return wireout; |
825eb42b JL |
536 | } |
537 | ||
538 | uint8_t * | |
539 | ldns_tcp_read_wire_timeout(int sockfd, size_t *size, struct timeval timeout) | |
540 | { | |
541 | uint8_t *wire; | |
542 | uint16_t wire_size; | |
ac996e71 | 543 | ssize_t bytes = 0, rc = 0; |
825eb42b JL |
544 | |
545 | wire = LDNS_XMALLOC(uint8_t, 2); | |
546 | if (!wire) { | |
547 | *size = 0; | |
548 | return NULL; | |
549 | } | |
550 | ||
551 | while (bytes < 2) { | |
552 | if(!ldns_sock_wait(sockfd, timeout, 0)) { | |
553 | *size = 0; | |
554 | LDNS_FREE(wire); | |
555 | return NULL; | |
556 | } | |
ac996e71 JL |
557 | rc = recv(sockfd, (void*) (wire + bytes), |
558 | (size_t) (2 - bytes), 0); | |
559 | if (rc == -1 || rc == 0) { | |
825eb42b JL |
560 | *size = 0; |
561 | LDNS_FREE(wire); | |
562 | return NULL; | |
563 | } | |
ac996e71 | 564 | bytes += rc; |
825eb42b JL |
565 | } |
566 | ||
567 | wire_size = ldns_read_uint16(wire); | |
568 | ||
569 | LDNS_FREE(wire); | |
570 | wire = LDNS_XMALLOC(uint8_t, wire_size); | |
fd185f4d JL |
571 | if (!wire) { |
572 | *size = 0; | |
573 | return NULL; | |
574 | } | |
825eb42b JL |
575 | bytes = 0; |
576 | ||
577 | while (bytes < (ssize_t) wire_size) { | |
578 | if(!ldns_sock_wait(sockfd, timeout, 0)) { | |
579 | *size = 0; | |
580 | LDNS_FREE(wire); | |
581 | return NULL; | |
582 | } | |
ac996e71 | 583 | rc = recv(sockfd, (void*) (wire + bytes), |
825eb42b | 584 | (size_t) (wire_size - bytes), 0); |
ac996e71 | 585 | if (rc == -1 || rc == 0) { |
825eb42b JL |
586 | LDNS_FREE(wire); |
587 | *size = 0; | |
588 | return NULL; | |
589 | } | |
ac996e71 | 590 | bytes += rc; |
825eb42b JL |
591 | } |
592 | ||
593 | *size = (size_t) bytes; | |
594 | return wire; | |
595 | } | |
596 | ||
597 | uint8_t * | |
598 | ldns_tcp_read_wire(int sockfd, size_t *size) | |
599 | { | |
600 | uint8_t *wire; | |
601 | uint16_t wire_size; | |
ac996e71 | 602 | ssize_t bytes = 0, rc = 0; |
825eb42b JL |
603 | |
604 | wire = LDNS_XMALLOC(uint8_t, 2); | |
605 | if (!wire) { | |
606 | *size = 0; | |
607 | return NULL; | |
608 | } | |
609 | ||
610 | while (bytes < 2) { | |
ac996e71 JL |
611 | rc = recv(sockfd, (void*) (wire + bytes), |
612 | (size_t) (2 - bytes), 0); | |
613 | if (rc == -1 || rc == 0) { | |
825eb42b JL |
614 | *size = 0; |
615 | LDNS_FREE(wire); | |
616 | return NULL; | |
617 | } | |
ac996e71 | 618 | bytes += rc; |
825eb42b JL |
619 | } |
620 | ||
621 | wire_size = ldns_read_uint16(wire); | |
622 | ||
623 | LDNS_FREE(wire); | |
624 | wire = LDNS_XMALLOC(uint8_t, wire_size); | |
fd185f4d JL |
625 | if (!wire) { |
626 | *size = 0; | |
627 | return NULL; | |
628 | } | |
825eb42b JL |
629 | bytes = 0; |
630 | ||
631 | while (bytes < (ssize_t) wire_size) { | |
ac996e71 | 632 | rc = recv(sockfd, (void*) (wire + bytes), |
825eb42b | 633 | (size_t) (wire_size - bytes), 0); |
ac996e71 | 634 | if (rc == -1 || rc == 0) { |
825eb42b JL |
635 | LDNS_FREE(wire); |
636 | *size = 0; | |
637 | return NULL; | |
638 | } | |
ac996e71 | 639 | bytes += rc; |
825eb42b JL |
640 | } |
641 | ||
642 | *size = (size_t) bytes; | |
643 | return wire; | |
644 | } | |
645 | ||
646 | /* keep in mind that in DNS tcp messages the first 2 bytes signal the | |
647 | * amount data to expect | |
648 | */ | |
649 | ldns_status | |
650 | ldns_tcp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to, | |
651 | socklen_t tolen, struct timeval timeout, size_t *answer_size) | |
652 | { | |
653 | int sockfd; | |
654 | uint8_t *answer; | |
655 | ||
656 | sockfd = ldns_tcp_bgsend(qbin, to, tolen, timeout); | |
657 | ||
658 | if (sockfd == 0) { | |
659 | return LDNS_STATUS_ERR; | |
660 | } | |
661 | ||
662 | answer = ldns_tcp_read_wire_timeout(sockfd, answer_size, timeout); | |
ac996e71 | 663 | #ifndef USE_WINSOCK |
825eb42b | 664 | close(sockfd); |
ac996e71 JL |
665 | #else |
666 | closesocket(sockfd); | |
667 | #endif | |
825eb42b JL |
668 | |
669 | if (*answer_size == 0) { | |
670 | /* oops */ | |
671 | return LDNS_STATUS_NETWORK_ERR; | |
672 | } | |
673 | ||
674 | /* resize accordingly */ | |
d1b2b5ca | 675 | *result = LDNS_XREALLOC(answer, uint8_t, (size_t)*answer_size); |
fd185f4d JL |
676 | if(!*result) { |
677 | LDNS_FREE(answer); | |
678 | return LDNS_STATUS_MEM_ERR; | |
679 | } | |
825eb42b JL |
680 | return LDNS_STATUS_OK; |
681 | } | |
682 | ||
683 | int | |
684 | ldns_tcp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, | |
685 | struct timeval timeout) | |
686 | { | |
687 | int sockfd; | |
688 | ||
689 | sockfd = ldns_tcp_connect(to, tolen, timeout); | |
690 | ||
691 | if (sockfd == 0) { | |
692 | return 0; | |
693 | } | |
694 | ||
695 | if (ldns_tcp_send_query(qbin, sockfd, to, tolen) == 0) { | |
b5dedcca JL |
696 | #ifndef USE_WINSOCK |
697 | close(sockfd); | |
698 | #else | |
699 | closesocket(sockfd); | |
700 | #endif | |
825eb42b JL |
701 | return 0; |
702 | } | |
703 | ||
704 | return sockfd; | |
705 | } | |
706 | ||
707 | /* code from rdata.c */ | |
708 | struct sockaddr_storage * | |
709 | ldns_rdf2native_sockaddr_storage(const ldns_rdf *rd, uint16_t port, size_t *size) | |
710 | { | |
711 | struct sockaddr_storage *data; | |
712 | struct sockaddr_in *data_in; | |
713 | struct sockaddr_in6 *data_in6; | |
714 | ||
715 | data = LDNS_MALLOC(struct sockaddr_storage); | |
716 | if (!data) { | |
717 | return NULL; | |
718 | } | |
719 | /* zero the structure for portability */ | |
720 | memset(data, 0, sizeof(struct sockaddr_storage)); | |
721 | if (port == 0) { | |
722 | port = LDNS_PORT; | |
723 | } | |
724 | ||
725 | switch(ldns_rdf_get_type(rd)) { | |
726 | case LDNS_RDF_TYPE_A: | |
d1b2b5ca | 727 | #ifndef S_SPLINT_S |
825eb42b | 728 | data->ss_family = AF_INET; |
d1b2b5ca | 729 | #endif |
825eb42b JL |
730 | data_in = (struct sockaddr_in*) data; |
731 | data_in->sin_port = (in_port_t)htons(port); | |
732 | memcpy(&(data_in->sin_addr), ldns_rdf_data(rd), ldns_rdf_size(rd)); | |
733 | *size = sizeof(struct sockaddr_in); | |
734 | return data; | |
735 | case LDNS_RDF_TYPE_AAAA: | |
d1b2b5ca | 736 | #ifndef S_SPLINT_S |
825eb42b | 737 | data->ss_family = AF_INET6; |
d1b2b5ca | 738 | #endif |
825eb42b JL |
739 | data_in6 = (struct sockaddr_in6*) data; |
740 | data_in6->sin6_port = (in_port_t)htons(port); | |
741 | memcpy(&data_in6->sin6_addr, ldns_rdf_data(rd), ldns_rdf_size(rd)); | |
742 | *size = sizeof(struct sockaddr_in6); | |
743 | return data; | |
744 | default: | |
745 | LDNS_FREE(data); | |
746 | return NULL; | |
747 | } | |
748 | } | |
749 | ||
d1b2b5ca | 750 | #ifndef S_SPLINT_S |
825eb42b JL |
751 | ldns_rdf * |
752 | ldns_sockaddr_storage2rdf(struct sockaddr_storage *sock, uint16_t *port) | |
753 | { | |
754 | ldns_rdf *addr; | |
755 | struct sockaddr_in *data_in; | |
756 | struct sockaddr_in6 *data_in6; | |
757 | ||
758 | switch(sock->ss_family) { | |
759 | case AF_INET: | |
760 | data_in = (struct sockaddr_in*)sock; | |
761 | if (port) { | |
762 | *port = ntohs((uint16_t)data_in->sin_port); | |
763 | } | |
764 | addr = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_A, | |
765 | LDNS_IP4ADDRLEN, &data_in->sin_addr); | |
766 | break; | |
767 | case AF_INET6: | |
768 | data_in6 = (struct sockaddr_in6*)sock; | |
769 | if (port) { | |
770 | *port = ntohs((uint16_t)data_in6->sin6_port); | |
771 | } | |
772 | addr = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_AAAA, | |
773 | LDNS_IP6ADDRLEN, &data_in6->sin6_addr); | |
774 | break; | |
775 | default: | |
776 | if (port) { | |
777 | *port = 0; | |
778 | } | |
779 | return NULL; | |
780 | } | |
781 | return addr; | |
782 | } | |
d1b2b5ca | 783 | #endif |
825eb42b JL |
784 | |
785 | /* code from resolver.c */ | |
786 | ldns_status | |
787 | ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class class) | |
788 | { | |
789 | ldns_pkt *query; | |
790 | ldns_buffer *query_wire; | |
791 | ||
792 | struct sockaddr_storage *ns = NULL; | |
793 | size_t ns_len = 0; | |
794 | size_t ns_i; | |
795 | ldns_status status; | |
796 | ||
797 | if (!resolver || ldns_resolver_nameserver_count(resolver) < 1) { | |
798 | return LDNS_STATUS_ERR; | |
799 | } | |
800 | ||
801 | query = ldns_pkt_query_new(ldns_rdf_clone(domain), LDNS_RR_TYPE_AXFR, class, 0); | |
802 | ||
803 | if (!query) { | |
804 | return LDNS_STATUS_ADDRESS_ERR; | |
805 | } | |
806 | /* For AXFR, we have to make the connection ourselves */ | |
ac996e71 | 807 | /* try all nameservers (which usually would mean v4 fallback if |
825eb42b JL |
808 | * @hostname is used */ |
809 | for (ns_i = 0; | |
810 | ns_i < ldns_resolver_nameserver_count(resolver) && | |
811 | resolver->_socket == 0; | |
812 | ns_i++) { | |
d1b2b5ca JM |
813 | if (ns != NULL) { |
814 | LDNS_FREE(ns); | |
815 | } | |
825eb42b JL |
816 | ns = ldns_rdf2native_sockaddr_storage( |
817 | resolver->_nameservers[ns_i], | |
818 | ldns_resolver_port(resolver), &ns_len); | |
ac996e71 JL |
819 | |
820 | resolver->_socket = ldns_tcp_connect(ns, (socklen_t)ns_len, | |
825eb42b JL |
821 | ldns_resolver_timeout(resolver)); |
822 | } | |
823 | ||
824 | if (resolver->_socket == 0) { | |
825 | ldns_pkt_free(query); | |
826 | LDNS_FREE(ns); | |
827 | return LDNS_STATUS_NETWORK_ERR; | |
828 | } | |
829 | ||
830 | #ifdef HAVE_SSL | |
831 | if (ldns_resolver_tsig_keyname(resolver) && ldns_resolver_tsig_keydata(resolver)) { | |
832 | status = ldns_pkt_tsig_sign(query, | |
833 | ldns_resolver_tsig_keyname(resolver), | |
834 | ldns_resolver_tsig_keydata(resolver), | |
835 | 300, ldns_resolver_tsig_algorithm(resolver), NULL); | |
836 | if (status != LDNS_STATUS_OK) { | |
ac996e71 JL |
837 | /* RoRi: to prevent problems on subsequent calls to ldns_axfr_start |
838 | we have to close the socket here! */ | |
839 | #ifndef USE_WINSOCK | |
840 | close(resolver->_socket); | |
841 | #else | |
842 | closesocket(resolver->_socket); | |
843 | #endif | |
844 | resolver->_socket = 0; | |
845 | ||
d1b2b5ca JM |
846 | ldns_pkt_free(query); |
847 | LDNS_FREE(ns); | |
848 | ||
825eb42b JL |
849 | return LDNS_STATUS_CRYPTO_TSIG_ERR; |
850 | } | |
851 | } | |
852 | #endif /* HAVE_SSL */ | |
853 | ||
ac996e71 JL |
854 | /* Convert the query to a buffer |
855 | * Is this necessary? | |
825eb42b JL |
856 | */ |
857 | query_wire = ldns_buffer_new(LDNS_MAX_PACKETLEN); | |
fd185f4d JL |
858 | if(!query_wire) { |
859 | ldns_pkt_free(query); | |
860 | LDNS_FREE(ns); | |
861 | #ifndef USE_WINSOCK | |
862 | close(resolver->_socket); | |
863 | #else | |
864 | closesocket(resolver->_socket); | |
865 | #endif | |
866 | resolver->_socket = 0; | |
867 | ||
868 | return LDNS_STATUS_MEM_ERR; | |
869 | } | |
825eb42b JL |
870 | status = ldns_pkt2buffer_wire(query_wire, query); |
871 | if (status != LDNS_STATUS_OK) { | |
872 | ldns_pkt_free(query); | |
ac996e71 | 873 | ldns_buffer_free(query_wire); |
825eb42b | 874 | LDNS_FREE(ns); |
ac996e71 JL |
875 | |
876 | /* RoRi: to prevent problems on subsequent calls to ldns_axfr_start | |
877 | we have to close the socket here! */ | |
878 | #ifndef USE_WINSOCK | |
879 | close(resolver->_socket); | |
880 | #else | |
881 | closesocket(resolver->_socket); | |
882 | #endif | |
883 | resolver->_socket = 0; | |
884 | ||
825eb42b JL |
885 | return status; |
886 | } | |
887 | /* Send the query */ | |
ac996e71 | 888 | if (ldns_tcp_send_query(query_wire, resolver->_socket, ns, |
825eb42b JL |
889 | (socklen_t)ns_len) == 0) { |
890 | ldns_pkt_free(query); | |
891 | ldns_buffer_free(query_wire); | |
892 | LDNS_FREE(ns); | |
ac996e71 JL |
893 | |
894 | /* RoRi: to prevent problems on subsequent calls to ldns_axfr_start | |
895 | we have to close the socket here! */ | |
896 | ||
897 | #ifndef USE_WINSOCK | |
898 | close(resolver->_socket); | |
899 | #else | |
900 | closesocket(resolver->_socket); | |
901 | #endif | |
902 | resolver->_socket = 0; | |
903 | ||
825eb42b JL |
904 | return LDNS_STATUS_NETWORK_ERR; |
905 | } | |
906 | ||
907 | ldns_pkt_free(query); | |
908 | ldns_buffer_free(query_wire); | |
909 | LDNS_FREE(ns); | |
910 | ||
911 | /* | |
912 | * The AXFR is done once the second SOA record is sent | |
913 | */ | |
914 | resolver->_axfr_soa_count = 0; | |
915 | return LDNS_STATUS_OK; | |
916 | } |