| /* | 
 |  * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu> | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. The name of the author may not be used to endorse or promote products | 
 |  *    derived from this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #ifdef WIN32 | 
 | #include <winsock2.h> | 
 | #include <windows.h> | 
 | #endif | 
 |  | 
 | #ifdef HAVE_CONFIG_H | 
 | #include "config.h" | 
 | #endif | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #ifdef HAVE_SYS_TIME_H | 
 | #include <sys/time.h> | 
 | #endif | 
 | #include <sys/queue.h> | 
 | #ifndef WIN32 | 
 | #include <sys/socket.h> | 
 | #include <signal.h> | 
 | #include <netinet/in.h> | 
 | #include <arpa/inet.h> | 
 | #include <unistd.h> | 
 | #endif | 
 | #ifdef HAVE_NETINET_IN6_H | 
 | #include <netinet/in6.h> | 
 | #endif | 
 | #ifdef HAVE_NETDB_H | 
 | #include <netdb.h> | 
 | #endif | 
 | #include <fcntl.h> | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <errno.h> | 
 |  | 
 | #include "event.h" | 
 | #include "evdns.h" | 
 | #include "log.h" | 
 |  | 
 | static int dns_ok = 0; | 
 | static int dns_err = 0; | 
 |  | 
 | void dns_suite(void); | 
 |  | 
 | static void | 
 | dns_gethostbyname_cb(int result, char type, int count, int ttl, | 
 |     void *addresses, void *arg) | 
 | { | 
 | 	dns_ok = dns_err = 0; | 
 |  | 
 | 	if (result == DNS_ERR_TIMEOUT) { | 
 | 		fprintf(stdout, "[Timed out] "); | 
 | 		dns_err = result; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (result != DNS_ERR_NONE) { | 
 | 		fprintf(stdout, "[Error code %d] ", result); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	fprintf(stderr, "type: %d, count: %d, ttl: %d: ", type, count, ttl); | 
 |  | 
 | 	switch (type) { | 
 | 	case DNS_IPv6_AAAA: { | 
 | #if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) | 
 | 		struct in6_addr *in6_addrs = addresses; | 
 | 		char buf[INET6_ADDRSTRLEN+1]; | 
 | 		int i; | 
 | 		/* a resolution that's not valid does not help */ | 
 | 		if (ttl < 0) | 
 | 			goto out; | 
 | 		for (i = 0; i < count; ++i) { | 
 | 			const char *b = inet_ntop(AF_INET6, &in6_addrs[i], buf,sizeof(buf)); | 
 | 			if (b) | 
 | 				fprintf(stderr, "%s ", b); | 
 | 			else | 
 | 				fprintf(stderr, "%s ", strerror(errno)); | 
 | 		} | 
 | #endif | 
 | 		break; | 
 | 	} | 
 | 	case DNS_IPv4_A: { | 
 | 		struct in_addr *in_addrs = addresses; | 
 | 		int i; | 
 | 		/* a resolution that's not valid does not help */ | 
 | 		if (ttl < 0) | 
 | 			goto out; | 
 | 		for (i = 0; i < count; ++i) | 
 | 			fprintf(stderr, "%s ", inet_ntoa(in_addrs[i])); | 
 | 		break; | 
 | 	} | 
 | 	case DNS_PTR: | 
 | 		/* may get at most one PTR */ | 
 | 		if (count != 1) | 
 | 			goto out; | 
 |  | 
 | 		fprintf(stderr, "%s ", *(char **)addresses); | 
 | 		break; | 
 | 	default: | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	dns_ok = type; | 
 |  | 
 | out: | 
 | 	event_loopexit(NULL); | 
 | } | 
 |  | 
 | static void | 
 | dns_gethostbyname(void) | 
 | { | 
 | 	fprintf(stdout, "Simple DNS resolve: "); | 
 | 	dns_ok = 0; | 
 | 	evdns_resolve_ipv4("www.monkey.org", 0, dns_gethostbyname_cb, NULL); | 
 | 	event_dispatch(); | 
 |  | 
 | 	if (dns_ok == DNS_IPv4_A) { | 
 | 		fprintf(stdout, "OK\n"); | 
 | 	} else { | 
 | 		fprintf(stdout, "FAILED\n"); | 
 | 		exit(1); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | dns_gethostbyname6(void) | 
 | { | 
 | 	fprintf(stdout, "IPv6 DNS resolve: "); | 
 | 	dns_ok = 0; | 
 | 	evdns_resolve_ipv6("www.ietf.org", 0, dns_gethostbyname_cb, NULL); | 
 | 	event_dispatch(); | 
 |  | 
 | 	if (dns_ok == DNS_IPv6_AAAA) { | 
 | 		fprintf(stdout, "OK\n"); | 
 | 	} else if (!dns_ok && dns_err == DNS_ERR_TIMEOUT) { | 
 | 		fprintf(stdout, "SKIPPED\n"); | 
 | 	} else { | 
 | 		fprintf(stdout, "FAILED (%d)\n", dns_ok); | 
 | 		exit(1); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | dns_gethostbyaddr(void) | 
 | { | 
 | 	struct in_addr in; | 
 | 	in.s_addr = htonl(0x7f000001ul); /* 127.0.0.1 */ | 
 | 	fprintf(stdout, "Simple reverse DNS resolve: "); | 
 | 	dns_ok = 0; | 
 | 	evdns_resolve_reverse(&in, 0, dns_gethostbyname_cb, NULL); | 
 | 	event_dispatch(); | 
 |  | 
 | 	if (dns_ok == DNS_PTR) { | 
 | 		fprintf(stdout, "OK\n"); | 
 | 	} else { | 
 | 		fprintf(stdout, "FAILED\n"); | 
 | 		exit(1); | 
 | 	} | 
 | } | 
 |  | 
 | static int n_server_responses = 0; | 
 |  | 
 | static void | 
 | dns_server_request_cb(struct evdns_server_request *req, void *data) | 
 | { | 
 | 	int i, r; | 
 | 	const char TEST_ARPA[] = "11.11.168.192.in-addr.arpa"; | 
 | 	for (i = 0; i < req->nquestions; ++i) { | 
 | 		struct in_addr ans; | 
 | 		ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ | 
 | 		if (req->questions[i]->type == EVDNS_TYPE_A && | 
 | 			req->questions[i]->dns_question_class == EVDNS_CLASS_INET && | 
 | 			!strcmp(req->questions[i]->name, "zz.example.com")) { | 
 | 			r = evdns_server_request_add_a_reply(req, "zz.example.com", | 
 | 												 1, &ans.s_addr, 12345); | 
 | 			if (r<0) | 
 | 				dns_ok = 0; | 
 | 		} else if (req->questions[i]->type == EVDNS_TYPE_AAAA && | 
 | 				   req->questions[i]->dns_question_class == EVDNS_CLASS_INET && | 
 | 				   !strcmp(req->questions[i]->name, "zz.example.com")) { | 
 | 			char addr6[17] = "abcdefghijklmnop"; | 
 | 			r = evdns_server_request_add_aaaa_reply(req, "zz.example.com", | 
 | 												 1, addr6, 123); | 
 | 			if (r<0) | 
 | 				dns_ok = 0; | 
 | 		} else if (req->questions[i]->type == EVDNS_TYPE_PTR && | 
 | 				   req->questions[i]->dns_question_class == EVDNS_CLASS_INET && | 
 | 				   !strcmp(req->questions[i]->name, TEST_ARPA)) { | 
 | 			r = evdns_server_request_add_ptr_reply(req, NULL, TEST_ARPA, | 
 | 					   "ZZ.EXAMPLE.COM", 54321); | 
 | 			if (r<0) | 
 | 				dns_ok = 0; | 
 | 		} else { | 
 | 			fprintf(stdout, "Unexpected question %d %d \"%s\" ", | 
 | 					req->questions[i]->type, | 
 | 					req->questions[i]->dns_question_class, | 
 | 					req->questions[i]->name); | 
 | 			dns_ok = 0; | 
 | 		} | 
 | 	} | 
 | 	r = evdns_server_request_respond(req, 0); | 
 | 	if (r<0) { | 
 | 		fprintf(stdout, "Couldn't send reply. "); | 
 | 		dns_ok = 0; | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | dns_server_gethostbyname_cb(int result, char type, int count, int ttl, | 
 | 							void *addresses, void *arg) | 
 | { | 
 | 	if (result != DNS_ERR_NONE) { | 
 | 		fprintf(stdout, "Unexpected result %d. ", result); | 
 | 		dns_ok = 0; | 
 | 		goto out; | 
 | 	} | 
 | 	if (count != 1) { | 
 | 		fprintf(stdout, "Unexpected answer count %d. ", count); | 
 | 		dns_ok = 0; | 
 | 		goto out; | 
 | 	} | 
 | 	switch (type) { | 
 | 	case DNS_IPv4_A: { | 
 | 		struct in_addr *in_addrs = addresses; | 
 | 		if (in_addrs[0].s_addr != htonl(0xc0a80b0bUL) || ttl != 12345) { | 
 | 			fprintf(stdout, "Bad IPv4 response \"%s\" %d. ", | 
 | 					inet_ntoa(in_addrs[0]), ttl); | 
 | 			dns_ok = 0; | 
 | 			goto out; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	case DNS_IPv6_AAAA: { | 
 | #if defined (HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) | 
 | 		struct in6_addr *in6_addrs = addresses; | 
 | 		char buf[INET6_ADDRSTRLEN+1]; | 
 | 		if (memcmp(&in6_addrs[0].s6_addr, "abcdefghijklmnop", 16) | 
 | 			|| ttl != 123) { | 
 | 			const char *b = inet_ntop(AF_INET6, &in6_addrs[0],buf,sizeof(buf)); | 
 | 			fprintf(stdout, "Bad IPv6 response \"%s\" %d. ", b, ttl); | 
 | 			dns_ok = 0; | 
 | 			goto out; | 
 | 		} | 
 | #endif | 
 | 		break; | 
 | 	} | 
 | 	case DNS_PTR: { | 
 | 		char **addrs = addresses; | 
 | 		if (strcmp(addrs[0], "ZZ.EXAMPLE.COM") || ttl != 54321) { | 
 | 			fprintf(stdout, "Bad PTR response \"%s\" %d. ", | 
 | 					addrs[0], ttl); | 
 | 			dns_ok = 0; | 
 | 			goto out; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	default: | 
 | 		fprintf(stdout, "Bad response type %d. ", type); | 
 | 		dns_ok = 0; | 
 | 	} | 
 |  | 
 |  out: | 
 | 	if (++n_server_responses == 3) { | 
 | 		event_loopexit(NULL); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | dns_server(void) | 
 | { | 
 | 	int sock; | 
 | 	struct sockaddr_in my_addr; | 
 | 	struct evdns_server_port *port; | 
 | 	struct in_addr resolve_addr; | 
 |  | 
 | 	dns_ok = 1; | 
 | 	fprintf(stdout, "DNS server support: "); | 
 |  | 
 | 	/* Add ourself as the only nameserver, and make sure we really are | 
 | 	 * the only nameserver. */ | 
 | 	evdns_nameserver_ip_add("127.0.0.1:35353"); | 
 | 	if (evdns_count_nameservers() != 1) { | 
 | 		fprintf(stdout, "Couldn't set up.\n"); | 
 | 		exit(1); | 
 | 	} | 
 |  | 
 | 	/* Now configure a nameserver port. */ | 
 | 	sock = socket(AF_INET, SOCK_DGRAM, 0); | 
 | 	if (sock == -1) { | 
 | 		perror("socket"); | 
 | 		exit(1); | 
 | 	} | 
 | #ifdef WIN32 | 
 | 	{ | 
 | 		u_long nonblocking = 1; | 
 | 		ioctlsocket(sock, FIONBIO, &nonblocking); | 
 | 	} | 
 | #else | 
 | 	fcntl(sock, F_SETFL, O_NONBLOCK); | 
 | #endif | 
 | 	memset(&my_addr, 0, sizeof(my_addr)); | 
 | 	my_addr.sin_family = AF_INET; | 
 | 	my_addr.sin_port = htons(35353); | 
 | 	my_addr.sin_addr.s_addr = htonl(0x7f000001UL); | 
 | 	if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { | 
 | 		perror("bind"); | 
 | 		exit (1); | 
 | 	} | 
 | 	port = evdns_add_server_port(sock, 0, dns_server_request_cb, NULL); | 
 |  | 
 | 	/* Send two queries. */ | 
 | 	evdns_resolve_ipv4("zz.example.com", DNS_QUERY_NO_SEARCH, | 
 | 					   dns_server_gethostbyname_cb, NULL); | 
 | 	evdns_resolve_ipv6("zz.example.com", DNS_QUERY_NO_SEARCH, | 
 | 					   dns_server_gethostbyname_cb, NULL); | 
 | 	resolve_addr.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ | 
 | 	evdns_resolve_reverse(&resolve_addr, 0, | 
 | 						  dns_server_gethostbyname_cb, NULL); | 
 |  | 
 | 	event_dispatch(); | 
 |  | 
 | 	if (dns_ok) { | 
 | 		fprintf(stdout, "OK\n"); | 
 | 	} else { | 
 | 		fprintf(stdout, "FAILED\n"); | 
 | 		exit(1); | 
 | 	} | 
 |  | 
 | 	evdns_close_server_port(port); | 
 | 	evdns_shutdown(0); /* remove ourself as nameserver. */ | 
 | #ifdef WIN32 | 
 | 	closesocket(sock); | 
 | #else | 
 | 	close(sock); | 
 | #endif | 
 | } | 
 |  | 
 | void | 
 | dns_suite(void) | 
 | { | 
 | 	dns_server(); /* Do this before we call evdns_init. */ | 
 |  | 
 | 	evdns_init(); | 
 | 	dns_gethostbyname(); | 
 | 	dns_gethostbyname6(); | 
 | 	dns_gethostbyaddr(); | 
 |  | 
 | 	evdns_shutdown(0); | 
 | } |