xref: /freebsd/contrib/ldns/net.c (revision 2787e39aaaaffd1fb07657ffaa2e3561858b3c73)
17b5038d7SDag-Erling Smørgrav /*
27b5038d7SDag-Erling Smørgrav  * net.c
37b5038d7SDag-Erling Smørgrav  *
47b5038d7SDag-Erling Smørgrav  * Network implementation
57b5038d7SDag-Erling Smørgrav  * All network related functions are grouped here
67b5038d7SDag-Erling Smørgrav  *
77b5038d7SDag-Erling Smørgrav  * a Net::DNS like library for C
87b5038d7SDag-Erling Smørgrav  *
97b5038d7SDag-Erling Smørgrav  * (c) NLnet Labs, 2004-2006
107b5038d7SDag-Erling Smørgrav  *
117b5038d7SDag-Erling Smørgrav  * See the file LICENSE for the license
127b5038d7SDag-Erling Smørgrav  */
137b5038d7SDag-Erling Smørgrav 
147b5038d7SDag-Erling Smørgrav #include <ldns/config.h>
157b5038d7SDag-Erling Smørgrav 
167b5038d7SDag-Erling Smørgrav #include <ldns/ldns.h>
177b5038d7SDag-Erling Smørgrav 
187b5038d7SDag-Erling Smørgrav #ifdef HAVE_NETINET_IN_H
197b5038d7SDag-Erling Smørgrav #include <netinet/in.h>
207b5038d7SDag-Erling Smørgrav #endif
217b5038d7SDag-Erling Smørgrav #ifdef HAVE_SYS_SOCKET_H
227b5038d7SDag-Erling Smørgrav #include <sys/socket.h>
237b5038d7SDag-Erling Smørgrav #endif
247b5038d7SDag-Erling Smørgrav #ifdef HAVE_NETDB_H
257b5038d7SDag-Erling Smørgrav #include <netdb.h>
267b5038d7SDag-Erling Smørgrav #endif
277b5038d7SDag-Erling Smørgrav #ifdef HAVE_ARPA_INET_H
287b5038d7SDag-Erling Smørgrav #include <arpa/inet.h>
297b5038d7SDag-Erling Smørgrav #endif
307b5038d7SDag-Erling Smørgrav #include <sys/time.h>
317b5038d7SDag-Erling Smørgrav #include <errno.h>
327b5038d7SDag-Erling Smørgrav #include <fcntl.h>
337b5038d7SDag-Erling Smørgrav 
347b5038d7SDag-Erling Smørgrav ldns_status
357b5038d7SDag-Erling Smørgrav ldns_send(ldns_pkt **result_packet, ldns_resolver *r, const ldns_pkt *query_pkt)
367b5038d7SDag-Erling Smørgrav {
377b5038d7SDag-Erling Smørgrav 	ldns_buffer *qb;
387b5038d7SDag-Erling Smørgrav 	ldns_status result;
397b5038d7SDag-Erling Smørgrav 	ldns_rdf *tsig_mac = NULL;
407b5038d7SDag-Erling Smørgrav 
417b5038d7SDag-Erling Smørgrav 	qb = ldns_buffer_new(LDNS_MIN_BUFLEN);
427b5038d7SDag-Erling Smørgrav 
437b5038d7SDag-Erling Smørgrav 	if (query_pkt && ldns_pkt_tsig(query_pkt)) {
447b5038d7SDag-Erling Smørgrav 		tsig_mac = ldns_rr_rdf(ldns_pkt_tsig(query_pkt), 3);
457b5038d7SDag-Erling Smørgrav 	}
467b5038d7SDag-Erling Smørgrav 
477b5038d7SDag-Erling Smørgrav 	if (!query_pkt ||
487b5038d7SDag-Erling Smørgrav 	    ldns_pkt2buffer_wire(qb, query_pkt) != LDNS_STATUS_OK) {
497b5038d7SDag-Erling Smørgrav 		result = LDNS_STATUS_ERR;
507b5038d7SDag-Erling Smørgrav 	} else {
517b5038d7SDag-Erling Smørgrav         	result = ldns_send_buffer(result_packet, r, qb, tsig_mac);
527b5038d7SDag-Erling Smørgrav 	}
537b5038d7SDag-Erling Smørgrav 
547b5038d7SDag-Erling Smørgrav 	ldns_buffer_free(qb);
557b5038d7SDag-Erling Smørgrav 
567b5038d7SDag-Erling Smørgrav 	return result;
577b5038d7SDag-Erling Smørgrav }
587b5038d7SDag-Erling Smørgrav 
597b5038d7SDag-Erling Smørgrav ldns_status
607b5038d7SDag-Erling Smørgrav ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac)
617b5038d7SDag-Erling Smørgrav {
627b5038d7SDag-Erling Smørgrav 	uint8_t i;
637b5038d7SDag-Erling Smørgrav 
647b5038d7SDag-Erling Smørgrav 	struct sockaddr_storage *ns;
657b5038d7SDag-Erling Smørgrav 	size_t ns_len;
667b5038d7SDag-Erling Smørgrav 	struct timeval tv_s;
677b5038d7SDag-Erling Smørgrav 	struct timeval tv_e;
687b5038d7SDag-Erling Smørgrav 
697b5038d7SDag-Erling Smørgrav 	ldns_rdf **ns_array;
707b5038d7SDag-Erling Smørgrav 	size_t *rtt;
717b5038d7SDag-Erling Smørgrav 	ldns_pkt *reply;
727b5038d7SDag-Erling Smørgrav 	bool all_servers_rtt_inf;
737b5038d7SDag-Erling Smørgrav 	uint8_t retries;
747b5038d7SDag-Erling Smørgrav 
757b5038d7SDag-Erling Smørgrav 	uint8_t *reply_bytes = NULL;
767b5038d7SDag-Erling Smørgrav 	size_t reply_size = 0;
777b5038d7SDag-Erling Smørgrav 	ldns_status status, send_status;
787b5038d7SDag-Erling Smørgrav 
797b5038d7SDag-Erling Smørgrav 	assert(r != NULL);
807b5038d7SDag-Erling Smørgrav 
817b5038d7SDag-Erling Smørgrav 	status = LDNS_STATUS_OK;
827b5038d7SDag-Erling Smørgrav 	rtt = ldns_resolver_rtt(r);
837b5038d7SDag-Erling Smørgrav 	ns_array = ldns_resolver_nameservers(r);
847b5038d7SDag-Erling Smørgrav 	reply = NULL;
857b5038d7SDag-Erling Smørgrav 	ns_len = 0;
867b5038d7SDag-Erling Smørgrav 
877b5038d7SDag-Erling Smørgrav 	all_servers_rtt_inf = true;
887b5038d7SDag-Erling Smørgrav 
897b5038d7SDag-Erling Smørgrav 	if (ldns_resolver_random(r)) {
907b5038d7SDag-Erling Smørgrav 		ldns_resolver_nameservers_randomize(r);
917b5038d7SDag-Erling Smørgrav 	}
927b5038d7SDag-Erling Smørgrav 
937b5038d7SDag-Erling Smørgrav 	/* loop through all defined nameservers */
947b5038d7SDag-Erling Smørgrav 	for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
957b5038d7SDag-Erling Smørgrav 		if (rtt[i] == LDNS_RESOLV_RTT_INF) {
967b5038d7SDag-Erling Smørgrav 			/* not reachable nameserver! */
977b5038d7SDag-Erling Smørgrav 			continue;
987b5038d7SDag-Erling Smørgrav 		}
997b5038d7SDag-Erling Smørgrav 
1007b5038d7SDag-Erling Smørgrav 		/* maybe verbosity setting?
1017b5038d7SDag-Erling Smørgrav 		printf("Sending to ");
1027b5038d7SDag-Erling Smørgrav 		ldns_rdf_print(stdout, ns_array[i]);
1037b5038d7SDag-Erling Smørgrav 		printf("\n");
1047b5038d7SDag-Erling Smørgrav 		*/
1057b5038d7SDag-Erling Smørgrav 		ns = ldns_rdf2native_sockaddr_storage(ns_array[i],
1067b5038d7SDag-Erling Smørgrav 				ldns_resolver_port(r), &ns_len);
1077b5038d7SDag-Erling Smørgrav 
1087b5038d7SDag-Erling Smørgrav 
1097b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
1107b5038d7SDag-Erling Smørgrav 		if ((ns->ss_family == AF_INET) &&
1117b5038d7SDag-Erling Smørgrav 				(ldns_resolver_ip6(r) == LDNS_RESOLV_INET6)) {
1127b5038d7SDag-Erling Smørgrav 			/* not reachable */
113*2787e39aSDag-Erling Smørgrav 			LDNS_FREE(ns);
1147b5038d7SDag-Erling Smørgrav 			continue;
1157b5038d7SDag-Erling Smørgrav 		}
1167b5038d7SDag-Erling Smørgrav 
1177b5038d7SDag-Erling Smørgrav 		if ((ns->ss_family == AF_INET6) &&
1187b5038d7SDag-Erling Smørgrav 				 (ldns_resolver_ip6(r) == LDNS_RESOLV_INET)) {
1197b5038d7SDag-Erling Smørgrav 			/* not reachable */
120*2787e39aSDag-Erling Smørgrav 			LDNS_FREE(ns);
1217b5038d7SDag-Erling Smørgrav 			continue;
1227b5038d7SDag-Erling Smørgrav 		}
1237b5038d7SDag-Erling Smørgrav #endif
1247b5038d7SDag-Erling Smørgrav 
1257b5038d7SDag-Erling Smørgrav 		all_servers_rtt_inf = false;
1267b5038d7SDag-Erling Smørgrav 
1277b5038d7SDag-Erling Smørgrav 		gettimeofday(&tv_s, NULL);
1287b5038d7SDag-Erling Smørgrav 
1297b5038d7SDag-Erling Smørgrav 		send_status = LDNS_STATUS_ERR;
1307b5038d7SDag-Erling Smørgrav 
1317b5038d7SDag-Erling Smørgrav 		/* reply_bytes implicitly handles our error */
1327b5038d7SDag-Erling Smørgrav 		if (1 == ldns_resolver_usevc(r)) {
1337b5038d7SDag-Erling Smørgrav 			for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
1347b5038d7SDag-Erling Smørgrav 				send_status =
1357b5038d7SDag-Erling Smørgrav 					ldns_tcp_send(&reply_bytes, qb, ns,
1367b5038d7SDag-Erling Smørgrav 					(socklen_t)ns_len, ldns_resolver_timeout(r),
1377b5038d7SDag-Erling Smørgrav 					&reply_size);
1387b5038d7SDag-Erling Smørgrav 				if (send_status == LDNS_STATUS_OK) {
1397b5038d7SDag-Erling Smørgrav 					break;
1407b5038d7SDag-Erling Smørgrav 				}
1417b5038d7SDag-Erling Smørgrav 			}
1427b5038d7SDag-Erling Smørgrav 		} else {
1437b5038d7SDag-Erling Smørgrav 			for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
1447b5038d7SDag-Erling Smørgrav 				/* ldns_rdf_print(stdout, ns_array[i]); */
1457b5038d7SDag-Erling Smørgrav 				send_status =
1467b5038d7SDag-Erling Smørgrav 					ldns_udp_send(&reply_bytes, qb, ns,
1477b5038d7SDag-Erling Smørgrav 							(socklen_t)ns_len, ldns_resolver_timeout(r),
1487b5038d7SDag-Erling Smørgrav 							&reply_size);
1497b5038d7SDag-Erling Smørgrav 
1507b5038d7SDag-Erling Smørgrav 				if (send_status == LDNS_STATUS_OK) {
1517b5038d7SDag-Erling Smørgrav 					break;
1527b5038d7SDag-Erling Smørgrav 				}
1537b5038d7SDag-Erling Smørgrav 			}
1547b5038d7SDag-Erling Smørgrav 		}
1557b5038d7SDag-Erling Smørgrav 
1567b5038d7SDag-Erling Smørgrav 		if (send_status != LDNS_STATUS_OK) {
1577b5038d7SDag-Erling Smørgrav 			ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF);
1587b5038d7SDag-Erling Smørgrav 			status = send_status;
1597b5038d7SDag-Erling Smørgrav 		}
1607b5038d7SDag-Erling Smørgrav 
1617b5038d7SDag-Erling Smørgrav 		/* obey the fail directive */
1627b5038d7SDag-Erling Smørgrav 		if (!reply_bytes) {
1637b5038d7SDag-Erling Smørgrav 			/* the current nameserver seems to have a problem, blacklist it */
1647b5038d7SDag-Erling Smørgrav 			if (ldns_resolver_fail(r)) {
1657b5038d7SDag-Erling Smørgrav 				LDNS_FREE(ns);
1667b5038d7SDag-Erling Smørgrav 				return LDNS_STATUS_ERR;
1677b5038d7SDag-Erling Smørgrav 			} else {
1687b5038d7SDag-Erling Smørgrav 				LDNS_FREE(ns);
1697b5038d7SDag-Erling Smørgrav 				continue;
1707b5038d7SDag-Erling Smørgrav 			}
1717b5038d7SDag-Erling Smørgrav 		}
1727b5038d7SDag-Erling Smørgrav 
1737b5038d7SDag-Erling Smørgrav 		status = ldns_wire2pkt(&reply, reply_bytes, reply_size);
1747b5038d7SDag-Erling Smørgrav 		if (status != LDNS_STATUS_OK) {
1757b5038d7SDag-Erling Smørgrav 			LDNS_FREE(reply_bytes);
1767b5038d7SDag-Erling Smørgrav 			LDNS_FREE(ns);
1777b5038d7SDag-Erling Smørgrav 			return status;
1787b5038d7SDag-Erling Smørgrav 		}
1797b5038d7SDag-Erling Smørgrav 
1807b5038d7SDag-Erling Smørgrav 		LDNS_FREE(ns);
1817b5038d7SDag-Erling Smørgrav 		gettimeofday(&tv_e, NULL);
1827b5038d7SDag-Erling Smørgrav 
1837b5038d7SDag-Erling Smørgrav 		if (reply) {
1847b5038d7SDag-Erling Smørgrav 			ldns_pkt_set_querytime(reply, (uint32_t)
1857b5038d7SDag-Erling Smørgrav 				((tv_e.tv_sec - tv_s.tv_sec) * 1000) +
1867b5038d7SDag-Erling Smørgrav 				(tv_e.tv_usec - tv_s.tv_usec) / 1000);
187*2787e39aSDag-Erling Smørgrav 			ldns_pkt_set_answerfrom(reply,
188*2787e39aSDag-Erling Smørgrav 					ldns_rdf_clone(ns_array[i]));
1897b5038d7SDag-Erling Smørgrav 			ldns_pkt_set_timestamp(reply, tv_s);
1907b5038d7SDag-Erling Smørgrav 			ldns_pkt_set_size(reply, reply_size);
1917b5038d7SDag-Erling Smørgrav 			break;
1927b5038d7SDag-Erling Smørgrav 		} else {
1937b5038d7SDag-Erling Smørgrav 			if (ldns_resolver_fail(r)) {
1947b5038d7SDag-Erling Smørgrav 				/* if fail is set bail out, after the first
1957b5038d7SDag-Erling Smørgrav 				 * one */
1967b5038d7SDag-Erling Smørgrav 				break;
1977b5038d7SDag-Erling Smørgrav 			}
1987b5038d7SDag-Erling Smørgrav 		}
1997b5038d7SDag-Erling Smørgrav 
2007b5038d7SDag-Erling Smørgrav 		/* wait retrans seconds... */
2017b5038d7SDag-Erling Smørgrav 		sleep((unsigned int) ldns_resolver_retrans(r));
2027b5038d7SDag-Erling Smørgrav 	}
2037b5038d7SDag-Erling Smørgrav 
2047b5038d7SDag-Erling Smørgrav 	if (all_servers_rtt_inf) {
2057b5038d7SDag-Erling Smørgrav 		LDNS_FREE(reply_bytes);
2067b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_RES_NO_NS;
2077b5038d7SDag-Erling Smørgrav 	}
2087b5038d7SDag-Erling Smørgrav #ifdef HAVE_SSL
209*2787e39aSDag-Erling Smørgrav 	if (tsig_mac && reply && reply_bytes) {
2107b5038d7SDag-Erling Smørgrav 		if (!ldns_pkt_tsig_verify(reply,
2117b5038d7SDag-Erling Smørgrav 		                          reply_bytes,
2127b5038d7SDag-Erling Smørgrav 					  reply_size,
2137b5038d7SDag-Erling Smørgrav 		                          ldns_resolver_tsig_keyname(r),
2147b5038d7SDag-Erling Smørgrav 		                          ldns_resolver_tsig_keydata(r), tsig_mac)) {
2157b5038d7SDag-Erling Smørgrav 			status = LDNS_STATUS_CRYPTO_TSIG_BOGUS;
2167b5038d7SDag-Erling Smørgrav 		}
2177b5038d7SDag-Erling Smørgrav 	}
2187b5038d7SDag-Erling Smørgrav #else
2197b5038d7SDag-Erling Smørgrav 	(void)tsig_mac;
2207b5038d7SDag-Erling Smørgrav #endif /* HAVE_SSL */
2217b5038d7SDag-Erling Smørgrav 
2227b5038d7SDag-Erling Smørgrav 	LDNS_FREE(reply_bytes);
2237b5038d7SDag-Erling Smørgrav 	if (result) {
2247b5038d7SDag-Erling Smørgrav 		*result = reply;
2257b5038d7SDag-Erling Smørgrav 	}
2267b5038d7SDag-Erling Smørgrav 
2277b5038d7SDag-Erling Smørgrav 	return status;
2287b5038d7SDag-Erling Smørgrav }
2297b5038d7SDag-Erling Smørgrav 
2307b5038d7SDag-Erling Smørgrav /** best effort to set nonblocking */
2317b5038d7SDag-Erling Smørgrav static void
2327b5038d7SDag-Erling Smørgrav ldns_sock_nonblock(int sockfd)
2337b5038d7SDag-Erling Smørgrav {
2347b5038d7SDag-Erling Smørgrav #ifdef HAVE_FCNTL
2357b5038d7SDag-Erling Smørgrav 	int flag;
2367b5038d7SDag-Erling Smørgrav 	if((flag = fcntl(sockfd, F_GETFL)) != -1) {
2377b5038d7SDag-Erling Smørgrav 		flag |= O_NONBLOCK;
2387b5038d7SDag-Erling Smørgrav 		if(fcntl(sockfd, F_SETFL, flag) == -1) {
2397b5038d7SDag-Erling Smørgrav 			/* ignore error, continue blockingly */
2407b5038d7SDag-Erling Smørgrav 		}
2417b5038d7SDag-Erling Smørgrav 	}
2427b5038d7SDag-Erling Smørgrav #elif defined(HAVE_IOCTLSOCKET)
2437b5038d7SDag-Erling Smørgrav 	unsigned long on = 1;
2447b5038d7SDag-Erling Smørgrav 	if(ioctlsocket(sockfd, FIONBIO, &on) != 0) {
2457b5038d7SDag-Erling Smørgrav 		/* ignore error, continue blockingly */
2467b5038d7SDag-Erling Smørgrav 	}
2477b5038d7SDag-Erling Smørgrav #endif
2487b5038d7SDag-Erling Smørgrav }
2497b5038d7SDag-Erling Smørgrav 
2507b5038d7SDag-Erling Smørgrav /** best effort to set blocking */
2517b5038d7SDag-Erling Smørgrav static void
2527b5038d7SDag-Erling Smørgrav ldns_sock_block(int sockfd)
2537b5038d7SDag-Erling Smørgrav {
2547b5038d7SDag-Erling Smørgrav #ifdef HAVE_FCNTL
2557b5038d7SDag-Erling Smørgrav 	int flag;
2567b5038d7SDag-Erling Smørgrav 	if((flag = fcntl(sockfd, F_GETFL)) != -1) {
2577b5038d7SDag-Erling Smørgrav 		flag &= ~O_NONBLOCK;
2587b5038d7SDag-Erling Smørgrav 		if(fcntl(sockfd, F_SETFL, flag) == -1) {
2597b5038d7SDag-Erling Smørgrav 			/* ignore error, continue */
2607b5038d7SDag-Erling Smørgrav 		}
2617b5038d7SDag-Erling Smørgrav 	}
2627b5038d7SDag-Erling Smørgrav #elif defined(HAVE_IOCTLSOCKET)
2637b5038d7SDag-Erling Smørgrav 	unsigned long off = 0;
2647b5038d7SDag-Erling Smørgrav 	if(ioctlsocket(sockfd, FIONBIO, &off) != 0) {
2657b5038d7SDag-Erling Smørgrav 		/* ignore error, continue */
2667b5038d7SDag-Erling Smørgrav 	}
2677b5038d7SDag-Erling Smørgrav #endif
2687b5038d7SDag-Erling Smørgrav }
2697b5038d7SDag-Erling Smørgrav 
2707b5038d7SDag-Erling Smørgrav /** wait for a socket to become ready */
2717b5038d7SDag-Erling Smørgrav static int
2727b5038d7SDag-Erling Smørgrav ldns_sock_wait(int sockfd, struct timeval timeout, int write)
2737b5038d7SDag-Erling Smørgrav {
2747b5038d7SDag-Erling Smørgrav 	int ret;
2757b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
2767b5038d7SDag-Erling Smørgrav 	fd_set fds;
2777b5038d7SDag-Erling Smørgrav 	FD_ZERO(&fds);
2787b5038d7SDag-Erling Smørgrav 	FD_SET(FD_SET_T sockfd, &fds);
2797b5038d7SDag-Erling Smørgrav 	if(write)
2807b5038d7SDag-Erling Smørgrav 		ret = select(sockfd+1, NULL, &fds, NULL, &timeout);
2817b5038d7SDag-Erling Smørgrav 	else
2827b5038d7SDag-Erling Smørgrav 		ret = select(sockfd+1, &fds, NULL, NULL, &timeout);
2837b5038d7SDag-Erling Smørgrav #endif
2847b5038d7SDag-Erling Smørgrav 	if(ret == 0)
2857b5038d7SDag-Erling Smørgrav 		/* timeout expired */
2867b5038d7SDag-Erling Smørgrav 		return 0;
2877b5038d7SDag-Erling Smørgrav 	else if(ret == -1)
2887b5038d7SDag-Erling Smørgrav 		/* error */
2897b5038d7SDag-Erling Smørgrav 		return 0;
2907b5038d7SDag-Erling Smørgrav 	return 1;
2917b5038d7SDag-Erling Smørgrav }
2927b5038d7SDag-Erling Smørgrav 
2937b5038d7SDag-Erling Smørgrav ldns_status
2947b5038d7SDag-Erling Smørgrav ldns_udp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to,
2957b5038d7SDag-Erling Smørgrav 		socklen_t tolen, struct timeval timeout, size_t *answer_size)
2967b5038d7SDag-Erling Smørgrav {
2977b5038d7SDag-Erling Smørgrav 	int sockfd;
2987b5038d7SDag-Erling Smørgrav 	uint8_t *answer;
2997b5038d7SDag-Erling Smørgrav 
3007b5038d7SDag-Erling Smørgrav 	sockfd = ldns_udp_bgsend(qbin, to, tolen, timeout);
3017b5038d7SDag-Erling Smørgrav 
3027b5038d7SDag-Erling Smørgrav 	if (sockfd == 0) {
3037b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_SOCKET_ERROR;
3047b5038d7SDag-Erling Smørgrav 	}
3057b5038d7SDag-Erling Smørgrav 
3067b5038d7SDag-Erling Smørgrav 	/* wait for an response*/
3077b5038d7SDag-Erling Smørgrav 	if(!ldns_sock_wait(sockfd, timeout, 0)) {
3087b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
3097b5038d7SDag-Erling Smørgrav 		close(sockfd);
3107b5038d7SDag-Erling Smørgrav #else
3117b5038d7SDag-Erling Smørgrav                 closesocket(sockfd);
3127b5038d7SDag-Erling Smørgrav #endif
3137b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_NETWORK_ERR;
3147b5038d7SDag-Erling Smørgrav 	}
3157b5038d7SDag-Erling Smørgrav 
3167b5038d7SDag-Erling Smørgrav         /* set to nonblocking, so if the checksum is bad, it becomes
3177b5038d7SDag-Erling Smørgrav          * an EGAIN error and the ldns_udp_send function does not block,
3187b5038d7SDag-Erling Smørgrav          * but returns a 'NETWORK_ERROR' much like a timeout. */
3197b5038d7SDag-Erling Smørgrav         ldns_sock_nonblock(sockfd);
3207b5038d7SDag-Erling Smørgrav 
3217b5038d7SDag-Erling Smørgrav 	answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL);
3227b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
3237b5038d7SDag-Erling Smørgrav 	close(sockfd);
3247b5038d7SDag-Erling Smørgrav #else
3257b5038d7SDag-Erling Smørgrav         closesocket(sockfd);
3267b5038d7SDag-Erling Smørgrav #endif
3277b5038d7SDag-Erling Smørgrav 
3287b5038d7SDag-Erling Smørgrav 	if (*answer_size == 0) {
3297b5038d7SDag-Erling Smørgrav 		/* oops */
3307b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_NETWORK_ERR;
3317b5038d7SDag-Erling Smørgrav 	}
3327b5038d7SDag-Erling Smørgrav 
3337b5038d7SDag-Erling Smørgrav 	*result = answer;
3347b5038d7SDag-Erling Smørgrav 	return LDNS_STATUS_OK;
3357b5038d7SDag-Erling Smørgrav }
3367b5038d7SDag-Erling Smørgrav 
3377b5038d7SDag-Erling Smørgrav int
3387b5038d7SDag-Erling Smørgrav ldns_udp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen,
3397b5038d7SDag-Erling Smørgrav 		struct timeval timeout)
3407b5038d7SDag-Erling Smørgrav {
3417b5038d7SDag-Erling Smørgrav 	int sockfd;
3427b5038d7SDag-Erling Smørgrav 
3437b5038d7SDag-Erling Smørgrav 	sockfd = ldns_udp_connect(to, timeout);
3447b5038d7SDag-Erling Smørgrav 
3457b5038d7SDag-Erling Smørgrav 	if (sockfd == 0) {
3467b5038d7SDag-Erling Smørgrav 		return 0;
3477b5038d7SDag-Erling Smørgrav 	}
3487b5038d7SDag-Erling Smørgrav 
3497b5038d7SDag-Erling Smørgrav 	if (ldns_udp_send_query(qbin, sockfd, to, tolen) == 0) {
3507b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
3517b5038d7SDag-Erling Smørgrav 		close(sockfd);
3527b5038d7SDag-Erling Smørgrav #else
3537b5038d7SDag-Erling Smørgrav 		closesocket(sockfd);
3547b5038d7SDag-Erling Smørgrav #endif
3557b5038d7SDag-Erling Smørgrav 		return 0;
3567b5038d7SDag-Erling Smørgrav 	}
3577b5038d7SDag-Erling Smørgrav 	return sockfd;
3587b5038d7SDag-Erling Smørgrav }
3597b5038d7SDag-Erling Smørgrav 
3607b5038d7SDag-Erling Smørgrav int
3617b5038d7SDag-Erling Smørgrav ldns_udp_connect(const struct sockaddr_storage *to, struct timeval ATTR_UNUSED(timeout))
3627b5038d7SDag-Erling Smørgrav {
3637b5038d7SDag-Erling Smørgrav 	int sockfd;
3647b5038d7SDag-Erling Smørgrav 
3657b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
3667b5038d7SDag-Erling Smørgrav 	if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_DGRAM,
3677b5038d7SDag-Erling Smørgrav 					IPPROTO_UDP))
3687b5038d7SDag-Erling Smørgrav 			== -1) {
3697b5038d7SDag-Erling Smørgrav                 return 0;
3707b5038d7SDag-Erling Smørgrav         }
3717b5038d7SDag-Erling Smørgrav #endif
3727b5038d7SDag-Erling Smørgrav 	return sockfd;
3737b5038d7SDag-Erling Smørgrav }
3747b5038d7SDag-Erling Smørgrav 
3757b5038d7SDag-Erling Smørgrav int
3767b5038d7SDag-Erling Smørgrav ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen,
3777b5038d7SDag-Erling Smørgrav 		struct timeval timeout)
3787b5038d7SDag-Erling Smørgrav {
3797b5038d7SDag-Erling Smørgrav 	int sockfd;
3807b5038d7SDag-Erling Smørgrav 
3817b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
3827b5038d7SDag-Erling Smørgrav 	if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_STREAM,
3837b5038d7SDag-Erling Smørgrav 					IPPROTO_TCP)) == -1) {
3847b5038d7SDag-Erling Smørgrav 		return 0;
3857b5038d7SDag-Erling Smørgrav 	}
3867b5038d7SDag-Erling Smørgrav #endif
3877b5038d7SDag-Erling Smørgrav 
3887b5038d7SDag-Erling Smørgrav 	/* perform nonblocking connect, to be able to wait with select() */
3897b5038d7SDag-Erling Smørgrav 	ldns_sock_nonblock(sockfd);
3907b5038d7SDag-Erling Smørgrav 	if (connect(sockfd, (struct sockaddr*)to, tolen) == -1) {
3917b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
3927b5038d7SDag-Erling Smørgrav #ifdef EINPROGRESS
3937b5038d7SDag-Erling Smørgrav 		if(errno != EINPROGRESS) {
3947b5038d7SDag-Erling Smørgrav #else
3957b5038d7SDag-Erling Smørgrav 		if(1) {
3967b5038d7SDag-Erling Smørgrav #endif
3977b5038d7SDag-Erling Smørgrav 			close(sockfd);
3987b5038d7SDag-Erling Smørgrav 			return 0;
3997b5038d7SDag-Erling Smørgrav 		}
4007b5038d7SDag-Erling Smørgrav #else /* USE_WINSOCK */
4017b5038d7SDag-Erling Smørgrav 		if(WSAGetLastError() != WSAEINPROGRESS &&
4027b5038d7SDag-Erling Smørgrav 			WSAGetLastError() != WSAEWOULDBLOCK) {
4037b5038d7SDag-Erling Smørgrav 			closesocket(sockfd);
4047b5038d7SDag-Erling Smørgrav 			return 0;
4057b5038d7SDag-Erling Smørgrav 		}
4067b5038d7SDag-Erling Smørgrav #endif
4077b5038d7SDag-Erling Smørgrav 		/* error was only telling us that it would block */
4087b5038d7SDag-Erling Smørgrav 	}
4097b5038d7SDag-Erling Smørgrav 
4107b5038d7SDag-Erling Smørgrav 	/* wait(write) until connected or error */
4117b5038d7SDag-Erling Smørgrav 	while(1) {
4127b5038d7SDag-Erling Smørgrav 		int error = 0;
4137b5038d7SDag-Erling Smørgrav 		socklen_t len = (socklen_t)sizeof(error);
4147b5038d7SDag-Erling Smørgrav 
4157b5038d7SDag-Erling Smørgrav 		if(!ldns_sock_wait(sockfd, timeout, 1)) {
4167b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
4177b5038d7SDag-Erling Smørgrav 			close(sockfd);
4187b5038d7SDag-Erling Smørgrav #else
4197b5038d7SDag-Erling Smørgrav 			closesocket(sockfd);
4207b5038d7SDag-Erling Smørgrav #endif
4217b5038d7SDag-Erling Smørgrav 			return 0;
4227b5038d7SDag-Erling Smørgrav 		}
4237b5038d7SDag-Erling Smørgrav 
4247b5038d7SDag-Erling Smørgrav 		/* check if there is a pending error for nonblocking connect */
4257b5038d7SDag-Erling Smørgrav 		if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&error,
4267b5038d7SDag-Erling Smørgrav 			&len) < 0) {
4277b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
4287b5038d7SDag-Erling Smørgrav 			error = errno; /* on solaris errno is error */
4297b5038d7SDag-Erling Smørgrav #else
4307b5038d7SDag-Erling Smørgrav 			error = WSAGetLastError();
4317b5038d7SDag-Erling Smørgrav #endif
4327b5038d7SDag-Erling Smørgrav 		}
4337b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
4347b5038d7SDag-Erling Smørgrav #if defined(EINPROGRESS) && defined(EWOULDBLOCK)
4357b5038d7SDag-Erling Smørgrav 		if(error == EINPROGRESS || error == EWOULDBLOCK)
4367b5038d7SDag-Erling Smørgrav 			continue; /* try again */
4377b5038d7SDag-Erling Smørgrav #endif
4387b5038d7SDag-Erling Smørgrav 		else if(error != 0) {
4397b5038d7SDag-Erling Smørgrav 			close(sockfd);
4407b5038d7SDag-Erling Smørgrav 			/* error in errno for our user */
4417b5038d7SDag-Erling Smørgrav 			errno = error;
4427b5038d7SDag-Erling Smørgrav 			return 0;
4437b5038d7SDag-Erling Smørgrav 		}
4447b5038d7SDag-Erling Smørgrav #else /* USE_WINSOCK */
4457b5038d7SDag-Erling Smørgrav 		if(error == WSAEINPROGRESS)
4467b5038d7SDag-Erling Smørgrav 			continue;
4477b5038d7SDag-Erling Smørgrav 		else if(error == WSAEWOULDBLOCK)
4487b5038d7SDag-Erling Smørgrav 			continue;
4497b5038d7SDag-Erling Smørgrav 		else if(error != 0) {
4507b5038d7SDag-Erling Smørgrav 			closesocket(sockfd);
4517b5038d7SDag-Erling Smørgrav 			errno = error;
4527b5038d7SDag-Erling Smørgrav 			return 0;
4537b5038d7SDag-Erling Smørgrav 		}
4547b5038d7SDag-Erling Smørgrav #endif /* USE_WINSOCK */
4557b5038d7SDag-Erling Smørgrav 		/* connected */
4567b5038d7SDag-Erling Smørgrav 		break;
4577b5038d7SDag-Erling Smørgrav 	}
4587b5038d7SDag-Erling Smørgrav 
4597b5038d7SDag-Erling Smørgrav 	/* set the socket blocking again */
4607b5038d7SDag-Erling Smørgrav 	ldns_sock_block(sockfd);
4617b5038d7SDag-Erling Smørgrav 
4627b5038d7SDag-Erling Smørgrav 	return sockfd;
4637b5038d7SDag-Erling Smørgrav }
4647b5038d7SDag-Erling Smørgrav 
4657b5038d7SDag-Erling Smørgrav ssize_t
4667b5038d7SDag-Erling Smørgrav ldns_tcp_send_query(ldns_buffer *qbin, int sockfd,
4677b5038d7SDag-Erling Smørgrav                     const struct sockaddr_storage *to, socklen_t tolen)
4687b5038d7SDag-Erling Smørgrav {
4697b5038d7SDag-Erling Smørgrav 	uint8_t *sendbuf;
4707b5038d7SDag-Erling Smørgrav 	ssize_t bytes;
4717b5038d7SDag-Erling Smørgrav 
4727b5038d7SDag-Erling Smørgrav 	/* add length of packet */
4737b5038d7SDag-Erling Smørgrav 	sendbuf = LDNS_XMALLOC(uint8_t, ldns_buffer_position(qbin) + 2);
4747b5038d7SDag-Erling Smørgrav 	if(!sendbuf) return 0;
4757b5038d7SDag-Erling Smørgrav 	ldns_write_uint16(sendbuf, ldns_buffer_position(qbin));
476*2787e39aSDag-Erling Smørgrav 	memcpy(sendbuf + 2, ldns_buffer_begin(qbin), ldns_buffer_position(qbin));
4777b5038d7SDag-Erling Smørgrav 
4787b5038d7SDag-Erling Smørgrav 	bytes = sendto(sockfd, (void*)sendbuf,
4797b5038d7SDag-Erling Smørgrav 			ldns_buffer_position(qbin) + 2, 0, (struct sockaddr *)to, tolen);
4807b5038d7SDag-Erling Smørgrav 
4817b5038d7SDag-Erling Smørgrav         LDNS_FREE(sendbuf);
4827b5038d7SDag-Erling Smørgrav 
4837b5038d7SDag-Erling Smørgrav 	if (bytes == -1 || (size_t) bytes != ldns_buffer_position(qbin) + 2 ) {
4847b5038d7SDag-Erling Smørgrav 		return 0;
4857b5038d7SDag-Erling Smørgrav 	}
4867b5038d7SDag-Erling Smørgrav 	return bytes;
4877b5038d7SDag-Erling Smørgrav }
4887b5038d7SDag-Erling Smørgrav 
4897b5038d7SDag-Erling Smørgrav /* don't wait for an answer */
4907b5038d7SDag-Erling Smørgrav ssize_t
4917b5038d7SDag-Erling Smørgrav ldns_udp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to,
4927b5038d7SDag-Erling Smørgrav 		socklen_t tolen)
4937b5038d7SDag-Erling Smørgrav {
4947b5038d7SDag-Erling Smørgrav 	ssize_t bytes;
4957b5038d7SDag-Erling Smørgrav 
4967b5038d7SDag-Erling Smørgrav 	bytes = sendto(sockfd, (void*)ldns_buffer_begin(qbin),
4977b5038d7SDag-Erling Smørgrav 			ldns_buffer_position(qbin), 0, (struct sockaddr *)to, tolen);
4987b5038d7SDag-Erling Smørgrav 
4997b5038d7SDag-Erling Smørgrav 	if (bytes == -1 || (size_t)bytes != ldns_buffer_position(qbin)) {
5007b5038d7SDag-Erling Smørgrav 		return 0;
5017b5038d7SDag-Erling Smørgrav 	}
5027b5038d7SDag-Erling Smørgrav 	if ((size_t) bytes != ldns_buffer_position(qbin)) {
5037b5038d7SDag-Erling Smørgrav 		return 0;
5047b5038d7SDag-Erling Smørgrav 	}
5057b5038d7SDag-Erling Smørgrav 	return bytes;
5067b5038d7SDag-Erling Smørgrav }
5077b5038d7SDag-Erling Smørgrav 
5087b5038d7SDag-Erling Smørgrav uint8_t *
5097b5038d7SDag-Erling Smørgrav ldns_udp_read_wire(int sockfd, size_t *size, struct sockaddr_storage *from,
5107b5038d7SDag-Erling Smørgrav 		socklen_t *fromlen)
5117b5038d7SDag-Erling Smørgrav {
5127b5038d7SDag-Erling Smørgrav 	uint8_t *wire, *wireout;
5137b5038d7SDag-Erling Smørgrav 	ssize_t wire_size;
5147b5038d7SDag-Erling Smørgrav 
5157b5038d7SDag-Erling Smørgrav 	wire = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN);
5167b5038d7SDag-Erling Smørgrav 	if (!wire) {
5177b5038d7SDag-Erling Smørgrav 		*size = 0;
5187b5038d7SDag-Erling Smørgrav 		return NULL;
5197b5038d7SDag-Erling Smørgrav 	}
5207b5038d7SDag-Erling Smørgrav 
5217b5038d7SDag-Erling Smørgrav 	wire_size = recvfrom(sockfd, (void*)wire, LDNS_MAX_PACKETLEN, 0,
5227b5038d7SDag-Erling Smørgrav 			(struct sockaddr *)from, fromlen);
5237b5038d7SDag-Erling Smørgrav 
5247b5038d7SDag-Erling Smørgrav 	/* recvfrom can also return 0 */
5257b5038d7SDag-Erling Smørgrav 	if (wire_size == -1 || wire_size == 0) {
5267b5038d7SDag-Erling Smørgrav 		*size = 0;
5277b5038d7SDag-Erling Smørgrav 		LDNS_FREE(wire);
5287b5038d7SDag-Erling Smørgrav 		return NULL;
5297b5038d7SDag-Erling Smørgrav 	}
5307b5038d7SDag-Erling Smørgrav 
5317b5038d7SDag-Erling Smørgrav 	*size = (size_t)wire_size;
5327b5038d7SDag-Erling Smørgrav 	wireout = LDNS_XREALLOC(wire, uint8_t, (size_t)wire_size);
5337b5038d7SDag-Erling Smørgrav 	if(!wireout) LDNS_FREE(wire);
5347b5038d7SDag-Erling Smørgrav 
5357b5038d7SDag-Erling Smørgrav 	return wireout;
5367b5038d7SDag-Erling Smørgrav }
5377b5038d7SDag-Erling Smørgrav 
5387b5038d7SDag-Erling Smørgrav uint8_t *
5397b5038d7SDag-Erling Smørgrav ldns_tcp_read_wire_timeout(int sockfd, size_t *size, struct timeval timeout)
5407b5038d7SDag-Erling Smørgrav {
5417b5038d7SDag-Erling Smørgrav 	uint8_t *wire;
5427b5038d7SDag-Erling Smørgrav 	uint16_t wire_size;
5437b5038d7SDag-Erling Smørgrav 	ssize_t bytes = 0, rc = 0;
5447b5038d7SDag-Erling Smørgrav 
5457b5038d7SDag-Erling Smørgrav 	wire = LDNS_XMALLOC(uint8_t, 2);
5467b5038d7SDag-Erling Smørgrav 	if (!wire) {
5477b5038d7SDag-Erling Smørgrav 		*size = 0;
5487b5038d7SDag-Erling Smørgrav 		return NULL;
5497b5038d7SDag-Erling Smørgrav 	}
5507b5038d7SDag-Erling Smørgrav 
5517b5038d7SDag-Erling Smørgrav 	while (bytes < 2) {
5527b5038d7SDag-Erling Smørgrav 		if(!ldns_sock_wait(sockfd, timeout, 0)) {
5537b5038d7SDag-Erling Smørgrav 			*size = 0;
5547b5038d7SDag-Erling Smørgrav 			LDNS_FREE(wire);
5557b5038d7SDag-Erling Smørgrav 			return NULL;
5567b5038d7SDag-Erling Smørgrav 		}
5577b5038d7SDag-Erling Smørgrav 		rc = recv(sockfd, (void*) (wire + bytes),
5587b5038d7SDag-Erling Smørgrav 				(size_t) (2 - bytes), 0);
5597b5038d7SDag-Erling Smørgrav 		if (rc == -1 || rc == 0) {
5607b5038d7SDag-Erling Smørgrav 			*size = 0;
5617b5038d7SDag-Erling Smørgrav 			LDNS_FREE(wire);
5627b5038d7SDag-Erling Smørgrav 			return NULL;
5637b5038d7SDag-Erling Smørgrav 		}
5647b5038d7SDag-Erling Smørgrav                 bytes += rc;
5657b5038d7SDag-Erling Smørgrav 	}
5667b5038d7SDag-Erling Smørgrav 
5677b5038d7SDag-Erling Smørgrav 	wire_size = ldns_read_uint16(wire);
5687b5038d7SDag-Erling Smørgrav 
5697b5038d7SDag-Erling Smørgrav 	LDNS_FREE(wire);
5707b5038d7SDag-Erling Smørgrav 	wire = LDNS_XMALLOC(uint8_t, wire_size);
5717b5038d7SDag-Erling Smørgrav 	if (!wire) {
5727b5038d7SDag-Erling Smørgrav 		*size = 0;
5737b5038d7SDag-Erling Smørgrav 		return NULL;
5747b5038d7SDag-Erling Smørgrav 	}
5757b5038d7SDag-Erling Smørgrav 	bytes = 0;
5767b5038d7SDag-Erling Smørgrav 
5777b5038d7SDag-Erling Smørgrav 	while (bytes < (ssize_t) wire_size) {
5787b5038d7SDag-Erling Smørgrav 		if(!ldns_sock_wait(sockfd, timeout, 0)) {
5797b5038d7SDag-Erling Smørgrav 			*size = 0;
5807b5038d7SDag-Erling Smørgrav 			LDNS_FREE(wire);
5817b5038d7SDag-Erling Smørgrav 			return NULL;
5827b5038d7SDag-Erling Smørgrav 		}
5837b5038d7SDag-Erling Smørgrav 		rc = recv(sockfd, (void*) (wire + bytes),
5847b5038d7SDag-Erling Smørgrav 				(size_t) (wire_size - bytes), 0);
5857b5038d7SDag-Erling Smørgrav 		if (rc == -1 || rc == 0) {
5867b5038d7SDag-Erling Smørgrav 			LDNS_FREE(wire);
5877b5038d7SDag-Erling Smørgrav 			*size = 0;
5887b5038d7SDag-Erling Smørgrav 			return NULL;
5897b5038d7SDag-Erling Smørgrav 		}
5907b5038d7SDag-Erling Smørgrav                 bytes += rc;
5917b5038d7SDag-Erling Smørgrav 	}
5927b5038d7SDag-Erling Smørgrav 
5937b5038d7SDag-Erling Smørgrav 	*size = (size_t) bytes;
5947b5038d7SDag-Erling Smørgrav 	return wire;
5957b5038d7SDag-Erling Smørgrav }
5967b5038d7SDag-Erling Smørgrav 
5977b5038d7SDag-Erling Smørgrav uint8_t *
5987b5038d7SDag-Erling Smørgrav ldns_tcp_read_wire(int sockfd, size_t *size)
5997b5038d7SDag-Erling Smørgrav {
6007b5038d7SDag-Erling Smørgrav 	uint8_t *wire;
6017b5038d7SDag-Erling Smørgrav 	uint16_t wire_size;
6027b5038d7SDag-Erling Smørgrav 	ssize_t bytes = 0, rc = 0;
6037b5038d7SDag-Erling Smørgrav 
6047b5038d7SDag-Erling Smørgrav 	wire = LDNS_XMALLOC(uint8_t, 2);
6057b5038d7SDag-Erling Smørgrav 	if (!wire) {
6067b5038d7SDag-Erling Smørgrav 		*size = 0;
6077b5038d7SDag-Erling Smørgrav 		return NULL;
6087b5038d7SDag-Erling Smørgrav 	}
6097b5038d7SDag-Erling Smørgrav 
6107b5038d7SDag-Erling Smørgrav 	while (bytes < 2) {
6117b5038d7SDag-Erling Smørgrav 		rc = recv(sockfd, (void*) (wire + bytes),
6127b5038d7SDag-Erling Smørgrav 				(size_t) (2 - bytes), 0);
6137b5038d7SDag-Erling Smørgrav 		if (rc == -1 || rc == 0) {
6147b5038d7SDag-Erling Smørgrav 			*size = 0;
6157b5038d7SDag-Erling Smørgrav 			LDNS_FREE(wire);
6167b5038d7SDag-Erling Smørgrav 			return NULL;
6177b5038d7SDag-Erling Smørgrav 		}
6187b5038d7SDag-Erling Smørgrav                 bytes += rc;
6197b5038d7SDag-Erling Smørgrav 	}
6207b5038d7SDag-Erling Smørgrav 
6217b5038d7SDag-Erling Smørgrav 	wire_size = ldns_read_uint16(wire);
6227b5038d7SDag-Erling Smørgrav 
6237b5038d7SDag-Erling Smørgrav 	LDNS_FREE(wire);
6247b5038d7SDag-Erling Smørgrav 	wire = LDNS_XMALLOC(uint8_t, wire_size);
6257b5038d7SDag-Erling Smørgrav 	if (!wire) {
6267b5038d7SDag-Erling Smørgrav 		*size = 0;
6277b5038d7SDag-Erling Smørgrav 		return NULL;
6287b5038d7SDag-Erling Smørgrav 	}
6297b5038d7SDag-Erling Smørgrav 	bytes = 0;
6307b5038d7SDag-Erling Smørgrav 
6317b5038d7SDag-Erling Smørgrav 	while (bytes < (ssize_t) wire_size) {
6327b5038d7SDag-Erling Smørgrav 		rc = recv(sockfd, (void*) (wire + bytes),
6337b5038d7SDag-Erling Smørgrav 				(size_t) (wire_size - bytes), 0);
6347b5038d7SDag-Erling Smørgrav 		if (rc == -1 || rc == 0) {
6357b5038d7SDag-Erling Smørgrav 			LDNS_FREE(wire);
6367b5038d7SDag-Erling Smørgrav 			*size = 0;
6377b5038d7SDag-Erling Smørgrav 			return NULL;
6387b5038d7SDag-Erling Smørgrav 		}
6397b5038d7SDag-Erling Smørgrav                 bytes += rc;
6407b5038d7SDag-Erling Smørgrav 	}
6417b5038d7SDag-Erling Smørgrav 
6427b5038d7SDag-Erling Smørgrav 	*size = (size_t) bytes;
6437b5038d7SDag-Erling Smørgrav 	return wire;
6447b5038d7SDag-Erling Smørgrav }
6457b5038d7SDag-Erling Smørgrav 
6467b5038d7SDag-Erling Smørgrav /* keep in mind that in DNS tcp messages the first 2 bytes signal the
6477b5038d7SDag-Erling Smørgrav  * amount data to expect
6487b5038d7SDag-Erling Smørgrav  */
6497b5038d7SDag-Erling Smørgrav ldns_status
6507b5038d7SDag-Erling Smørgrav ldns_tcp_send(uint8_t **result,  ldns_buffer *qbin, const struct sockaddr_storage *to,
6517b5038d7SDag-Erling Smørgrav 		socklen_t tolen, struct timeval timeout, size_t *answer_size)
6527b5038d7SDag-Erling Smørgrav {
6537b5038d7SDag-Erling Smørgrav 	int sockfd;
6547b5038d7SDag-Erling Smørgrav 	uint8_t *answer;
6557b5038d7SDag-Erling Smørgrav 
6567b5038d7SDag-Erling Smørgrav 	sockfd = ldns_tcp_bgsend(qbin, to, tolen, timeout);
6577b5038d7SDag-Erling Smørgrav 
6587b5038d7SDag-Erling Smørgrav 	if (sockfd == 0) {
6597b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_ERR;
6607b5038d7SDag-Erling Smørgrav 	}
6617b5038d7SDag-Erling Smørgrav 
6627b5038d7SDag-Erling Smørgrav 	answer = ldns_tcp_read_wire_timeout(sockfd, answer_size, timeout);
6637b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
6647b5038d7SDag-Erling Smørgrav 	close(sockfd);
6657b5038d7SDag-Erling Smørgrav #else
6667b5038d7SDag-Erling Smørgrav 	closesocket(sockfd);
6677b5038d7SDag-Erling Smørgrav #endif
6687b5038d7SDag-Erling Smørgrav 
6697b5038d7SDag-Erling Smørgrav 	if (*answer_size == 0) {
6707b5038d7SDag-Erling Smørgrav 		/* oops */
6717b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_NETWORK_ERR;
6727b5038d7SDag-Erling Smørgrav 	}
6737b5038d7SDag-Erling Smørgrav 
6747b5038d7SDag-Erling Smørgrav 	/* resize accordingly */
675*2787e39aSDag-Erling Smørgrav 	*result = LDNS_XREALLOC(answer, uint8_t, (size_t)*answer_size);
6767b5038d7SDag-Erling Smørgrav         if(!*result) {
6777b5038d7SDag-Erling Smørgrav                 LDNS_FREE(answer);
6787b5038d7SDag-Erling Smørgrav                 return LDNS_STATUS_MEM_ERR;
6797b5038d7SDag-Erling Smørgrav         }
6807b5038d7SDag-Erling Smørgrav 	return LDNS_STATUS_OK;
6817b5038d7SDag-Erling Smørgrav }
6827b5038d7SDag-Erling Smørgrav 
6837b5038d7SDag-Erling Smørgrav int
6847b5038d7SDag-Erling Smørgrav ldns_tcp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen,
6857b5038d7SDag-Erling Smørgrav 		struct timeval timeout)
6867b5038d7SDag-Erling Smørgrav {
6877b5038d7SDag-Erling Smørgrav 	int sockfd;
6887b5038d7SDag-Erling Smørgrav 
6897b5038d7SDag-Erling Smørgrav 	sockfd = ldns_tcp_connect(to, tolen, timeout);
6907b5038d7SDag-Erling Smørgrav 
6917b5038d7SDag-Erling Smørgrav 	if (sockfd == 0) {
6927b5038d7SDag-Erling Smørgrav 		return 0;
6937b5038d7SDag-Erling Smørgrav 	}
6947b5038d7SDag-Erling Smørgrav 
6957b5038d7SDag-Erling Smørgrav 	if (ldns_tcp_send_query(qbin, sockfd, to, tolen) == 0) {
6967b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
6977b5038d7SDag-Erling Smørgrav 		close(sockfd);
6987b5038d7SDag-Erling Smørgrav #else
6997b5038d7SDag-Erling Smørgrav 		closesocket(sockfd);
7007b5038d7SDag-Erling Smørgrav #endif
7017b5038d7SDag-Erling Smørgrav 		return 0;
7027b5038d7SDag-Erling Smørgrav 	}
7037b5038d7SDag-Erling Smørgrav 
7047b5038d7SDag-Erling Smørgrav 	return sockfd;
7057b5038d7SDag-Erling Smørgrav }
7067b5038d7SDag-Erling Smørgrav 
7077b5038d7SDag-Erling Smørgrav /* code from rdata.c */
7087b5038d7SDag-Erling Smørgrav struct sockaddr_storage *
7097b5038d7SDag-Erling Smørgrav ldns_rdf2native_sockaddr_storage(const ldns_rdf *rd, uint16_t port, size_t *size)
7107b5038d7SDag-Erling Smørgrav {
7117b5038d7SDag-Erling Smørgrav         struct sockaddr_storage *data;
7127b5038d7SDag-Erling Smørgrav         struct sockaddr_in  *data_in;
7137b5038d7SDag-Erling Smørgrav         struct sockaddr_in6 *data_in6;
7147b5038d7SDag-Erling Smørgrav 
7157b5038d7SDag-Erling Smørgrav         data = LDNS_MALLOC(struct sockaddr_storage);
7167b5038d7SDag-Erling Smørgrav         if (!data) {
7177b5038d7SDag-Erling Smørgrav                 return NULL;
7187b5038d7SDag-Erling Smørgrav         }
7197b5038d7SDag-Erling Smørgrav 		/* zero the structure for portability */
7207b5038d7SDag-Erling Smørgrav 		memset(data, 0, sizeof(struct sockaddr_storage));
7217b5038d7SDag-Erling Smørgrav         if (port == 0) {
7227b5038d7SDag-Erling Smørgrav                 port =  LDNS_PORT;
7237b5038d7SDag-Erling Smørgrav         }
7247b5038d7SDag-Erling Smørgrav 
7257b5038d7SDag-Erling Smørgrav         switch(ldns_rdf_get_type(rd)) {
7267b5038d7SDag-Erling Smørgrav                 case LDNS_RDF_TYPE_A:
7277b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
7287b5038d7SDag-Erling Smørgrav                         data->ss_family = AF_INET;
7297b5038d7SDag-Erling Smørgrav #endif
7307b5038d7SDag-Erling Smørgrav                         data_in = (struct sockaddr_in*) data;
7317b5038d7SDag-Erling Smørgrav                         data_in->sin_port = (in_port_t)htons(port);
7327b5038d7SDag-Erling Smørgrav                         memcpy(&(data_in->sin_addr), ldns_rdf_data(rd), ldns_rdf_size(rd));
7337b5038d7SDag-Erling Smørgrav                         *size = sizeof(struct sockaddr_in);
7347b5038d7SDag-Erling Smørgrav                         return data;
7357b5038d7SDag-Erling Smørgrav                 case LDNS_RDF_TYPE_AAAA:
7367b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
7377b5038d7SDag-Erling Smørgrav                         data->ss_family = AF_INET6;
7387b5038d7SDag-Erling Smørgrav #endif
7397b5038d7SDag-Erling Smørgrav                         data_in6 = (struct sockaddr_in6*) data;
7407b5038d7SDag-Erling Smørgrav                         data_in6->sin6_port = (in_port_t)htons(port);
7417b5038d7SDag-Erling Smørgrav                         memcpy(&data_in6->sin6_addr, ldns_rdf_data(rd), ldns_rdf_size(rd));
7427b5038d7SDag-Erling Smørgrav                         *size = sizeof(struct sockaddr_in6);
7437b5038d7SDag-Erling Smørgrav                         return data;
7447b5038d7SDag-Erling Smørgrav                 default:
7457b5038d7SDag-Erling Smørgrav                         LDNS_FREE(data);
7467b5038d7SDag-Erling Smørgrav                         return NULL;
7477b5038d7SDag-Erling Smørgrav         }
7487b5038d7SDag-Erling Smørgrav }
7497b5038d7SDag-Erling Smørgrav 
7507b5038d7SDag-Erling Smørgrav #ifndef S_SPLINT_S
7517b5038d7SDag-Erling Smørgrav ldns_rdf *
7527b5038d7SDag-Erling Smørgrav ldns_sockaddr_storage2rdf(struct sockaddr_storage *sock, uint16_t *port)
7537b5038d7SDag-Erling Smørgrav {
7547b5038d7SDag-Erling Smørgrav         ldns_rdf *addr;
7557b5038d7SDag-Erling Smørgrav         struct sockaddr_in *data_in;
7567b5038d7SDag-Erling Smørgrav         struct sockaddr_in6 *data_in6;
7577b5038d7SDag-Erling Smørgrav 
7587b5038d7SDag-Erling Smørgrav         switch(sock->ss_family) {
7597b5038d7SDag-Erling Smørgrav                 case AF_INET:
7607b5038d7SDag-Erling Smørgrav                         data_in = (struct sockaddr_in*)sock;
7617b5038d7SDag-Erling Smørgrav                         if (port) {
7627b5038d7SDag-Erling Smørgrav                                 *port = ntohs((uint16_t)data_in->sin_port);
7637b5038d7SDag-Erling Smørgrav                         }
7647b5038d7SDag-Erling Smørgrav                         addr = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_A,
7657b5038d7SDag-Erling Smørgrav                                         LDNS_IP4ADDRLEN, &data_in->sin_addr);
7667b5038d7SDag-Erling Smørgrav                         break;
7677b5038d7SDag-Erling Smørgrav                 case AF_INET6:
7687b5038d7SDag-Erling Smørgrav                         data_in6 = (struct sockaddr_in6*)sock;
7697b5038d7SDag-Erling Smørgrav                         if (port) {
7707b5038d7SDag-Erling Smørgrav                                 *port = ntohs((uint16_t)data_in6->sin6_port);
7717b5038d7SDag-Erling Smørgrav                         }
7727b5038d7SDag-Erling Smørgrav                         addr = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_AAAA,
7737b5038d7SDag-Erling Smørgrav                                         LDNS_IP6ADDRLEN, &data_in6->sin6_addr);
7747b5038d7SDag-Erling Smørgrav                         break;
7757b5038d7SDag-Erling Smørgrav                 default:
7767b5038d7SDag-Erling Smørgrav                         if (port) {
7777b5038d7SDag-Erling Smørgrav                                 *port = 0;
7787b5038d7SDag-Erling Smørgrav                         }
7797b5038d7SDag-Erling Smørgrav                         return NULL;
7807b5038d7SDag-Erling Smørgrav         }
7817b5038d7SDag-Erling Smørgrav         return addr;
7827b5038d7SDag-Erling Smørgrav }
7837b5038d7SDag-Erling Smørgrav #endif
7847b5038d7SDag-Erling Smørgrav 
7857b5038d7SDag-Erling Smørgrav /* code from resolver.c */
7867b5038d7SDag-Erling Smørgrav ldns_status
7877b5038d7SDag-Erling Smørgrav ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class class)
7887b5038d7SDag-Erling Smørgrav {
7897b5038d7SDag-Erling Smørgrav         ldns_pkt *query;
7907b5038d7SDag-Erling Smørgrav         ldns_buffer *query_wire;
7917b5038d7SDag-Erling Smørgrav 
7927b5038d7SDag-Erling Smørgrav         struct sockaddr_storage *ns = NULL;
7937b5038d7SDag-Erling Smørgrav         size_t ns_len = 0;
7947b5038d7SDag-Erling Smørgrav         size_t ns_i;
7957b5038d7SDag-Erling Smørgrav         ldns_status status;
7967b5038d7SDag-Erling Smørgrav 
7977b5038d7SDag-Erling Smørgrav         if (!resolver || ldns_resolver_nameserver_count(resolver) < 1) {
7987b5038d7SDag-Erling Smørgrav                 return LDNS_STATUS_ERR;
7997b5038d7SDag-Erling Smørgrav         }
8007b5038d7SDag-Erling Smørgrav 
8017b5038d7SDag-Erling Smørgrav         query = ldns_pkt_query_new(ldns_rdf_clone(domain), LDNS_RR_TYPE_AXFR, class, 0);
8027b5038d7SDag-Erling Smørgrav 
8037b5038d7SDag-Erling Smørgrav         if (!query) {
8047b5038d7SDag-Erling Smørgrav                 return LDNS_STATUS_ADDRESS_ERR;
8057b5038d7SDag-Erling Smørgrav         }
8067b5038d7SDag-Erling Smørgrav         /* For AXFR, we have to make the connection ourselves */
8077b5038d7SDag-Erling Smørgrav         /* try all nameservers (which usually would mean v4 fallback if
8087b5038d7SDag-Erling Smørgrav          * @hostname is used */
8097b5038d7SDag-Erling Smørgrav         for (ns_i = 0;
8107b5038d7SDag-Erling Smørgrav              ns_i < ldns_resolver_nameserver_count(resolver) &&
8117b5038d7SDag-Erling Smørgrav              resolver->_socket == 0;
8127b5038d7SDag-Erling Smørgrav              ns_i++) {
813*2787e39aSDag-Erling Smørgrav 		if (ns != NULL) {
814*2787e39aSDag-Erling Smørgrav 			LDNS_FREE(ns);
815*2787e39aSDag-Erling Smørgrav 		}
8167b5038d7SDag-Erling Smørgrav 	        ns = ldns_rdf2native_sockaddr_storage(
8177b5038d7SDag-Erling Smørgrav 	        	resolver->_nameservers[ns_i],
8187b5038d7SDag-Erling Smørgrav 			ldns_resolver_port(resolver), &ns_len);
8197b5038d7SDag-Erling Smørgrav 
8207b5038d7SDag-Erling Smørgrav 		resolver->_socket = ldns_tcp_connect(ns, (socklen_t)ns_len,
8217b5038d7SDag-Erling Smørgrav 				ldns_resolver_timeout(resolver));
8227b5038d7SDag-Erling Smørgrav 	}
8237b5038d7SDag-Erling Smørgrav 
8247b5038d7SDag-Erling Smørgrav 	if (resolver->_socket == 0) {
8257b5038d7SDag-Erling Smørgrav 		ldns_pkt_free(query);
8267b5038d7SDag-Erling Smørgrav 		LDNS_FREE(ns);
8277b5038d7SDag-Erling Smørgrav 		return LDNS_STATUS_NETWORK_ERR;
8287b5038d7SDag-Erling Smørgrav 	}
8297b5038d7SDag-Erling Smørgrav 
8307b5038d7SDag-Erling Smørgrav #ifdef HAVE_SSL
8317b5038d7SDag-Erling Smørgrav 	if (ldns_resolver_tsig_keyname(resolver) && ldns_resolver_tsig_keydata(resolver)) {
8327b5038d7SDag-Erling Smørgrav 		status = ldns_pkt_tsig_sign(query,
8337b5038d7SDag-Erling Smørgrav 		                            ldns_resolver_tsig_keyname(resolver),
8347b5038d7SDag-Erling Smørgrav 		                            ldns_resolver_tsig_keydata(resolver),
8357b5038d7SDag-Erling Smørgrav 		                            300, ldns_resolver_tsig_algorithm(resolver), NULL);
8367b5038d7SDag-Erling Smørgrav 		if (status != LDNS_STATUS_OK) {
8377b5038d7SDag-Erling Smørgrav 			/* RoRi: to prevent problems on subsequent calls to ldns_axfr_start
8387b5038d7SDag-Erling Smørgrav 			   we have to close the socket here! */
8397b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
8407b5038d7SDag-Erling Smørgrav 			close(resolver->_socket);
8417b5038d7SDag-Erling Smørgrav #else
8427b5038d7SDag-Erling Smørgrav 			closesocket(resolver->_socket);
8437b5038d7SDag-Erling Smørgrav #endif
8447b5038d7SDag-Erling Smørgrav 			resolver->_socket = 0;
8457b5038d7SDag-Erling Smørgrav 
846*2787e39aSDag-Erling Smørgrav 			ldns_pkt_free(query);
847*2787e39aSDag-Erling Smørgrav 			LDNS_FREE(ns);
848*2787e39aSDag-Erling Smørgrav 
8497b5038d7SDag-Erling Smørgrav 			return LDNS_STATUS_CRYPTO_TSIG_ERR;
8507b5038d7SDag-Erling Smørgrav 		}
8517b5038d7SDag-Erling Smørgrav 	}
8527b5038d7SDag-Erling Smørgrav #endif /* HAVE_SSL */
8537b5038d7SDag-Erling Smørgrav 
8547b5038d7SDag-Erling Smørgrav         /* Convert the query to a buffer
8557b5038d7SDag-Erling Smørgrav          * Is this necessary?
8567b5038d7SDag-Erling Smørgrav          */
8577b5038d7SDag-Erling Smørgrav         query_wire = ldns_buffer_new(LDNS_MAX_PACKETLEN);
8587b5038d7SDag-Erling Smørgrav         if(!query_wire) {
8597b5038d7SDag-Erling Smørgrav                 ldns_pkt_free(query);
8607b5038d7SDag-Erling Smørgrav                 LDNS_FREE(ns);
8617b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
8627b5038d7SDag-Erling Smørgrav 		close(resolver->_socket);
8637b5038d7SDag-Erling Smørgrav #else
8647b5038d7SDag-Erling Smørgrav 		closesocket(resolver->_socket);
8657b5038d7SDag-Erling Smørgrav #endif
8667b5038d7SDag-Erling Smørgrav 		resolver->_socket = 0;
8677b5038d7SDag-Erling Smørgrav 
8687b5038d7SDag-Erling Smørgrav                 return LDNS_STATUS_MEM_ERR;
8697b5038d7SDag-Erling Smørgrav         }
8707b5038d7SDag-Erling Smørgrav         status = ldns_pkt2buffer_wire(query_wire, query);
8717b5038d7SDag-Erling Smørgrav         if (status != LDNS_STATUS_OK) {
8727b5038d7SDag-Erling Smørgrav                 ldns_pkt_free(query);
8737b5038d7SDag-Erling Smørgrav 		ldns_buffer_free(query_wire);
8747b5038d7SDag-Erling Smørgrav                 LDNS_FREE(ns);
8757b5038d7SDag-Erling Smørgrav 
8767b5038d7SDag-Erling Smørgrav 		/* RoRi: to prevent problems on subsequent calls to ldns_axfr_start
8777b5038d7SDag-Erling Smørgrav 		    we have to close the socket here! */
8787b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
8797b5038d7SDag-Erling Smørgrav 		close(resolver->_socket);
8807b5038d7SDag-Erling Smørgrav #else
8817b5038d7SDag-Erling Smørgrav 		closesocket(resolver->_socket);
8827b5038d7SDag-Erling Smørgrav #endif
8837b5038d7SDag-Erling Smørgrav 		resolver->_socket = 0;
8847b5038d7SDag-Erling Smørgrav 
8857b5038d7SDag-Erling Smørgrav                 return status;
8867b5038d7SDag-Erling Smørgrav         }
8877b5038d7SDag-Erling Smørgrav         /* Send the query */
8887b5038d7SDag-Erling Smørgrav         if (ldns_tcp_send_query(query_wire, resolver->_socket, ns,
8897b5038d7SDag-Erling Smørgrav 				(socklen_t)ns_len) == 0) {
8907b5038d7SDag-Erling Smørgrav                 ldns_pkt_free(query);
8917b5038d7SDag-Erling Smørgrav                 ldns_buffer_free(query_wire);
8927b5038d7SDag-Erling Smørgrav                 LDNS_FREE(ns);
8937b5038d7SDag-Erling Smørgrav 
8947b5038d7SDag-Erling Smørgrav 		/* RoRi: to prevent problems on subsequent calls to ldns_axfr_start
8957b5038d7SDag-Erling Smørgrav 		         we have to close the socket here! */
8967b5038d7SDag-Erling Smørgrav 
8977b5038d7SDag-Erling Smørgrav #ifndef USE_WINSOCK
8987b5038d7SDag-Erling Smørgrav 		close(resolver->_socket);
8997b5038d7SDag-Erling Smørgrav #else
9007b5038d7SDag-Erling Smørgrav 		closesocket(resolver->_socket);
9017b5038d7SDag-Erling Smørgrav #endif
9027b5038d7SDag-Erling Smørgrav 		resolver->_socket = 0;
9037b5038d7SDag-Erling Smørgrav 
9047b5038d7SDag-Erling Smørgrav                 return LDNS_STATUS_NETWORK_ERR;
9057b5038d7SDag-Erling Smørgrav         }
9067b5038d7SDag-Erling Smørgrav 
9077b5038d7SDag-Erling Smørgrav         ldns_pkt_free(query);
9087b5038d7SDag-Erling Smørgrav         ldns_buffer_free(query_wire);
9097b5038d7SDag-Erling Smørgrav         LDNS_FREE(ns);
9107b5038d7SDag-Erling Smørgrav 
9117b5038d7SDag-Erling Smørgrav         /*
9127b5038d7SDag-Erling Smørgrav          * The AXFR is done once the second SOA record is sent
9137b5038d7SDag-Erling Smørgrav          */
9147b5038d7SDag-Erling Smørgrav         resolver->_axfr_soa_count = 0;
9157b5038d7SDag-Erling Smørgrav         return LDNS_STATUS_OK;
9167b5038d7SDag-Erling Smørgrav }
917