| /* |
| * 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); |
| } |