diff --git a/http-internal.h b/http-internal.h index 49ee9bcb..5e6769f1 100644 --- a/http-internal.h +++ b/http-internal.h @@ -97,6 +97,7 @@ struct evhttp_connection { void *closecb_arg; struct event_base *base; + struct evdns_base *dns_base; }; struct evhttp_cb { diff --git a/http.c b/http.c index 8a919707..ec47d13c 100644 --- a/http.c +++ b/http.c @@ -155,7 +155,6 @@ fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host, extern int debug; -static int socket_connect(evutil_socket_t kefd, const char *address, unsigned short port); static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse); static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse); static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **); @@ -1149,16 +1148,26 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg) } /* - * Call back for asynchronous connection attempt. + * Event callback for asynchronous connection attempt. */ - static void -evhttp_connection_cb(struct bufferevent *bufev, void *arg) +evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg) { struct evhttp_connection *evcon = arg; int error; ev_socklen_t errsz = sizeof(error); + if (!(what & BEV_EVENT_CONNECTED)) { + /* some operating systems return ECONNREFUSED immediately + * when connecting to a local address. the cleanup is going + * to reschedule this function call. + */ + if (errno == ECONNREFUSED) + goto cleanup; + evhttp_error_cb(bufev, what, arg); + return; + } + /* Check if the connection completed */ if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &errsz) == -1) { @@ -1730,11 +1739,11 @@ evhttp_read_header(struct evhttp_connection *evcon, struct evhttp_connection * evhttp_connection_new(const char *address, unsigned short port) { - return (evhttp_connection_base_new(NULL, address, port)); + return (evhttp_connection_base_new(NULL, NULL, address, port)); } struct evhttp_connection * -evhttp_connection_base_new(struct event_base *base, +evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase, const char *address, unsigned short port) { struct evhttp_connection *evcon = NULL; @@ -1776,6 +1785,8 @@ evhttp_connection_base_new(struct event_base *base, bufferevent_base_set(base, evcon->bufev); } + evcon->dns_base = dnsbase; + return (evcon); error: @@ -1850,7 +1861,20 @@ evhttp_connection_connect(struct evhttp_connection *evcon) return (-1); } - if (socket_connect(evcon->fd, evcon->address, evcon->port) == -1) { + /* Set up a callback for successful connection setup */ + bufferevent_setfd(evcon->bufev, evcon->fd); + bufferevent_setcb(evcon->bufev, + NULL /* evhttp_read_cb */, + NULL /* evhttp_write_cb */, + evhttp_connection_cb, + evcon); + bufferevent_settimeout(evcon->bufev, 0, + evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT); + /* make sure that we get a write callback */ + bufferevent_enable(evcon->bufev, EV_WRITE); + + if (bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base, + AF_UNSPEC, evcon->address, evcon->port) < 0) { event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed", __func__, evcon->address); /* some operating systems return ECONNREFUSED immediately @@ -1861,17 +1885,6 @@ evhttp_connection_connect(struct evhttp_connection *evcon) return (0); } - /* Set up a callback for successful connection setup */ - bufferevent_setfd(evcon->bufev, evcon->fd); - bufferevent_setcb(evcon->bufev, - NULL /* evhttp_read_cb */, - evhttp_connection_cb, - evhttp_error_cb, evcon); - bufferevent_settimeout(evcon->bufev, 0, - evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT); - /* make sure that we get a write callback */ - bufferevent_enable(evcon->bufev, EV_WRITE); - evcon->state = EVCON_CONNECTING; return (0); @@ -2906,7 +2919,7 @@ evhttp_get_request_connection( /* we need a connection object to put the http request on */ evcon = evhttp_connection_base_new( - http->base, hostname, atoi(portname)); + http->base, NULL, hostname, atoi(portname)); mm_free(hostname); mm_free(portname); if (evcon == NULL) @@ -3114,29 +3127,3 @@ bind_socket(const char *address, ev_uint16_t port, int reuse) return (fd); } -static int -socket_connect(evutil_socket_t fd, const char *address, unsigned short port) -{ - struct evutil_addrinfo *ai = make_addrinfo(address, port); - int res = -1; - - if (ai == NULL) { - event_debug(("%s: make_addrinfo: \"%s:%d\"", - __func__, address, port)); - return (-1); - } - - if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) { - int err = evutil_socket_geterror(fd); - if (! EVUTIL_ERR_CONNECT_RETRIABLE(err)) - goto out; - } - - /* everything is fine */ - res = 0; - -out: - evutil_freeaddrinfo(ai); - - return (res); -} diff --git a/include/event2/http.h b/include/event2/http.h index 328eefae..0edaf7e7 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -353,13 +353,23 @@ void evhttp_request_set_chunked_cb(struct evhttp_request *, /** Frees the request object and removes associated events. */ void evhttp_request_free(struct evhttp_request *req); +struct evdns_base; + /** * A connection object that can be used to for making HTTP requests. The - * connection object tries to establish the connection when it is given an - * http request object. + * connection object tries to resolve address and establish the connection + * when it is given an http request object. + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests */ struct evhttp_connection *evhttp_connection_base_new( - struct event_base *base, const char *address, unsigned short port); + struct event_base *base, struct evdns_base *dnsbase, + const char *address, unsigned short port); /** Takes ownership of the request object * diff --git a/test/Makefile.am b/test/Makefile.am index 5d2eab77..8fecf000 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -21,6 +21,7 @@ test_ratelim_SOURCES = test-ratelim.c test_ratelim_LDADD = ../libevent_core.la -lm regress_SOURCES = regress.c regress_buffer.c regress_http.c regress_dns.c \ + regress_testutils.c regress_testutils.h \ regress_rpc.c regress.gen.c regress.gen.h regress_et.c \ regress_bufferevent.c regress_listener.c \ regress_util.c tinytest.c regress_main.c regress_minheap.c \ diff --git a/test/Makefile.nmake b/test/Makefile.nmake index ffdd3d92..dc245653 100644 --- a/test/Makefile.nmake +++ b/test/Makefile.nmake @@ -4,6 +4,7 @@ CFLAGS=/I.. /I../include /I../WIN32-Code /I../compat /DWIN32 /DHAVE_CONFIG_H CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo REGRESS_OBJS=regress.obj regress_buffer.obj regress_http.obj regress_dns.obj \ + regress_testutils.obj \ regress_rpc.obj regress.gen.obj \ regress_et.obj regress_bufferevent.obj \ regress_listener.obj regress_util.obj tinytest.obj \ diff --git a/test/regress_dns.c b/test/regress_dns.c index ece78164..14308799 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -66,12 +66,12 @@ #include "evdns.h" #include "log-internal.h" #include "regress.h" +#include "regress_testutils.h" static int dns_ok = 0; static int dns_got_cancel = 0; static int dns_err = 0; -static int get_socket_port(evutil_socket_t fd); static void dns_gethostbyname_cb(int result, char type, int count, int ttl, @@ -430,89 +430,6 @@ end: evdns_base_free(base, 0); } -struct generic_dns_server_table { - const char *q; - const char *anstype; - const char *ans; - int seen; -}; - -static void -generic_dns_server_cb(struct evdns_server_request *req, void *data) -{ - struct generic_dns_server_table *tab = data; - const char *question; - - if (req->nquestions != 1) - TT_DIE(("Only handling one question at a time; got %d", - req->nquestions)); - - question = req->questions[0]->name; - - while (tab->q && evutil_ascii_strcasecmp(question, tab->q) && - strcmp("*", tab->q)) - ++tab; - if (tab->q == NULL) - TT_DIE(("Unexpected question: '%s'", question)); - - ++tab->seen; - - if (!strcmp(tab->anstype, "err")) { - int err = atoi(tab->ans); - tt_assert(! evdns_server_request_respond(req, err)); - return; - } else if (!strcmp(tab->anstype, "A")) { - struct in_addr in; - evutil_inet_pton(AF_INET, tab->ans, &in); - evdns_server_request_add_a_reply(req, question, 1, &in.s_addr, - 100); - } else if (!strcmp(tab->anstype, "AAAA")) { - struct in6_addr in6; - evutil_inet_pton(AF_INET6, tab->ans, &in6); - evdns_server_request_add_aaaa_reply(req, - question, 1, &in6.s6_addr, 100); - } else { - TT_DIE(("Weird table entry with type '%s'", tab->anstype)); - } - tt_assert(! evdns_server_request_respond(req, 0)) - return; -end: - tt_want(! evdns_server_request_drop(req)); -} - -static struct evdns_server_port * -get_generic_server(struct event_base *base, - ev_uint16_t *portnum, - evdns_request_callback_fn_type cb, - void *arg) -{ - struct evdns_server_port *port = NULL; - evutil_socket_t sock; - struct sockaddr_in my_addr; - - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock<=0) { - tt_abort_perror("socket"); - } - - evutil_make_socket_nonblocking(sock); - - memset(&my_addr, 0, sizeof(my_addr)); - my_addr.sin_family = AF_INET; - my_addr.sin_port = htons(*portnum); - my_addr.sin_addr.s_addr = htonl(0x7f000001UL); - if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { - tt_abort_perror("bind"); - } - port = evdns_add_server_port_with_base(base, sock, 0, cb, arg); - if (!*portnum) - *portnum = get_socket_port(sock); - - return port; -end: - return NULL; -} - static int n_replies_left; static struct event_base *exit_base; @@ -554,7 +471,7 @@ generic_dns_callback(int result, char type, int count, int ttl, void *addresses, event_base_loopexit(exit_base, NULL); } -static struct generic_dns_server_table search_table[] = { +static struct regress_dns_server_table search_table[] = { { "host.a.example.com", "err", "3", 0 }, { "host.b.example.com", "err", "3", 0 }, { "host.c.example.com", "A", "11.22.33.44", 0 }, @@ -573,15 +490,12 @@ dns_search_test(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; - struct evdns_server_port *port = NULL; struct evdns_base *dns = NULL; ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/ struct generic_dns_callback_result r1, r2, r3, r4, r5; - port = get_generic_server(base, &portnum, generic_dns_server_cb, - search_table); - tt_assert(port); + tt_assert(regress_dnsserver(base, &portnum, search_table)); dns = evdns_base_new(base, 0); tt_assert(!evdns_base_nameserver_ip_add(dns, "127.0.0.1:53900")); @@ -614,8 +528,8 @@ dns_search_test(void *arg) end: if (dns) evdns_base_free(dns, 0); - if (port) - evdns_close_server_port(port); + + regress_clean_dnsserver(); } static void @@ -664,8 +578,8 @@ dns_retry_test(void *arg) struct generic_dns_callback_result r1; - port = get_generic_server(base, &portnum, fail_server_cb, - &drop_count); + port = regress_get_dnsserver(base, &portnum, NULL, + fail_server_cb, &drop_count); tt_assert(port); dns = evdns_base_new(base, 0); @@ -724,7 +638,7 @@ end: evdns_close_server_port(port); } -static struct generic_dns_server_table internal_error_table[] = { +static struct regress_dns_server_table internal_error_table[] = { /* Error 4 (NOTIMPL) makes us reissue the request to another server if we can. @@ -734,7 +648,7 @@ static struct generic_dns_server_table internal_error_table[] = { { NULL, NULL, NULL, 0 } }; -static struct generic_dns_server_table reissue_table[] = { +static struct regress_dns_server_table reissue_table[] = { { "foof.example.com", "A", "240.15.240.15", 0 }, { NULL, NULL, NULL, 0 } }; @@ -749,11 +663,11 @@ dns_reissue_test(void *arg) struct generic_dns_callback_result r1; ev_uint16_t portnum1 = 53900, portnum2=53901; - port1 = get_generic_server(base, &portnum1, generic_dns_server_cb, - internal_error_table); + port1 = regress_get_dnsserver(base, &portnum1, NULL, + regress_dns_server_cb, internal_error_table); tt_assert(port1); - port2 = get_generic_server(base, &portnum2, generic_dns_server_cb, - reissue_table); + port2 = regress_get_dnsserver(base, &portnum2, NULL, + regress_dns_server_cb, reissue_table); tt_assert(port2); dns = evdns_base_new(base, 0); @@ -805,16 +719,13 @@ dns_inflight_test(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; - struct evdns_server_port *port = NULL; struct evdns_base *dns = NULL; ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/ struct generic_dns_callback_result r[20]; int i; - port = get_generic_server(base, &portnum, generic_dns_server_cb, - reissue_table); - tt_assert(port); + tt_assert(regress_dnsserver(base, &portnum, reissue_table)); /* Make sure that having another (very bad!) RNG doesn't mess us * up. */ @@ -842,8 +753,7 @@ dns_inflight_test(void *arg) end: if (dns) evdns_base_free(dns, 0); - if (port) - evdns_close_server_port(port); + regress_clean_dnsserver(); } /* === Test for bufferevent_socket_connect_hostname */ @@ -985,22 +895,6 @@ nil_accept_cb(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *s, /* don't do anything with the socket; let it close when we exit() */ } -/* Helper: return the port that a socket is bound on, in host order. */ -static int -get_socket_port(evutil_socket_t fd) -{ - struct sockaddr_storage ss; - ev_socklen_t socklen = sizeof(ss); - if (getsockname(fd, (struct sockaddr*)&ss, &socklen) != 0) - return -1; - if (ss.ss_family == AF_INET) - return ntohs( ((struct sockaddr_in*)&ss)->sin_port); - else if (ss.ss_family == AF_INET6) - return ntohs( ((struct sockaddr_in6*)&ss)->sin6_port); - else - return -1; -} - /* Bufferevent event callback for the connect_hostname test: remembers what * event we got. */ static void @@ -1052,9 +946,10 @@ test_bufferevent_connect_hostname(void *arg) &n_accept, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC, -1, (struct sockaddr *)&sin, sizeof(sin)); - listener_port = get_socket_port(evconnlistener_get_fd(listener)); + listener_port = regress_get_socket_port( + evconnlistener_get_fd(listener)); - port = get_generic_server(data->base, &dns_port, + port = regress_get_dnsserver(data->base, &dns_port, NULL, be_getaddrinfo_server_cb, &n_dns); tt_assert(port); tt_int_op(dns_port, >=, 0); @@ -1329,7 +1224,7 @@ test_getaddrinfo_async(void *arg) /* 2. Okay, now we can actually test the asynchronous resolver. */ /* Start a dummy local dns server... */ - port = get_generic_server(data->base, &dns_port, + port = regress_get_dnsserver(data->base, &dns_port, NULL, be_getaddrinfo_server_cb, &n_dns_questions); tt_assert(port); tt_int_op(dns_port, >=, 0); diff --git a/test/regress_http.c b/test/regress_http.c index c13edee8..dfe54b5d 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -50,12 +50,15 @@ #include #include +#include "event2/dns.h" + #include "event.h" #include "evhttp.h" #include "log-internal.h" #include "util-internal.h" #include "http-internal.h" #include "regress.h" +#include "regress_testutils.h" static struct evhttp *http; /* set if a test needs to call loopexit on a base */ @@ -725,6 +728,105 @@ http_persist_connection_test(void) _http_connection_test(1); } +static struct regress_dns_server_table search_table[] = { + { "localhost", "A", "127.0.0.1", 0 }, + { NULL, NULL, NULL, 0 } +}; + +static void +http_connection_async_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct evdns_base *dns_base = NULL; + ev_uint16_t portnum = 0; + char address[64]; + + tt_assert(regress_dnsserver(base, &portnum, search_table)); + + dns_base = evdns_base_new(base, 0/* init name servers */); + tt_assert(dns_base); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evutil_snprintf(address, sizeof (address), "127.0.0.1:%d", portnum); + evdns_base_nameserver_ip_add(dns_base, address); + + test_ok = 0; + + http = http_setup(&port, NULL); + + evcon = evhttp_connection_base_new(base, dns_base, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + tt_assert(test_ok); + + /* try to make another request over the same connection */ + test_ok = 0; + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* + * if our connections are not supposed to be persistent; request + * a close from the server. + */ + evhttp_add_header(req->output_headers, "Connection", "close"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } + + event_dispatch(); + + /* make another request: request empty reply */ + test_ok = 0; + + req = evhttp_request_new(http_request_empty_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Empty", "itis"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + exit(1); + } + + event_dispatch(); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); + if (dns_base) + evdns_base_free(dns_base, 0); + regress_clean_dnsserver(); +} + static void http_request_never_call(struct evhttp_request *req, void *arg) { @@ -2426,6 +2528,7 @@ struct testcase_t http_testcases[] = { HTTP_LEGACY(failure), HTTP_LEGACY(connection), HTTP_LEGACY(persist_connection), + HTTP_LEGACY(connection_async), HTTP_LEGACY(close_detection), HTTP_LEGACY(close_detection_delay), HTTP_LEGACY(bad_request), diff --git a/test/regress_testutils.c b/test/regress_testutils.c new file mode 100644 index 00000000..95a61193 --- /dev/null +++ b/test/regress_testutils.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010 Niels Provos and Nick Mathewson + * + * 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 +#include +#include +#endif + +#include "event-config.h" + +#include +#include +#ifdef _EVENT_HAVE_SYS_TIME_H +#include +#endif +#include +#ifndef WIN32 +#include +#include +#include +#include +#include +#endif +#ifdef _EVENT_HAVE_NETINET_IN6_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#include +#include +#include +#include +#include + +#include "event2/event.h" +#include "event2/event_compat.h" +#include +#include +#include +#include "evdns.h" +#include "log-internal.h" +#include "regress.h" +#include "regress_testutils.h" + +/* globals */ +static struct evdns_server_port *dns_port; +evutil_socket_t dns_sock = -1; + +/* Helper: return the port that a socket is bound on, in host order. */ +int +regress_get_socket_port(evutil_socket_t fd) +{ + struct sockaddr_storage ss; + ev_socklen_t socklen = sizeof(ss); + if (getsockname(fd, (struct sockaddr*)&ss, &socklen) != 0) + return -1; + if (ss.ss_family == AF_INET) + return ntohs( ((struct sockaddr_in*)&ss)->sin_port); + else if (ss.ss_family == AF_INET6) + return ntohs( ((struct sockaddr_in6*)&ss)->sin6_port); + else + return -1; +} + +struct evdns_server_port * +regress_get_dnsserver(struct event_base *base, + ev_uint16_t *portnum, + evutil_socket_t *psock, + evdns_request_callback_fn_type cb, + void *arg) +{ + struct evdns_server_port *port = NULL; + evutil_socket_t sock; + struct sockaddr_in my_addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock <= 0) { + tt_abort_perror("socket"); + } + + evutil_make_socket_nonblocking(sock); + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(*portnum); + my_addr.sin_addr.s_addr = htonl(0x7f000001UL); + if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { + tt_abort_perror("bind"); + } + port = evdns_add_server_port_with_base(base, sock, 0, cb, arg); + if (!*portnum) + *portnum = regress_get_socket_port(sock); + if (psock) + *psock = sock; + + return port; +end: + return NULL; +} + +void +regress_clean_dnsserver(void) +{ + if (dns_port) + evdns_close_server_port(dns_port); + if (dns_sock >= 0) + EVUTIL_CLOSESOCKET(dns_sock); +} + +void +regress_dns_server_cb(struct evdns_server_request *req, void *data) +{ + struct regress_dns_server_table *tab = data; + const char *question; + + if (req->nquestions != 1) + TT_DIE(("Only handling one question at a time; got %d", + req->nquestions)); + + question = req->questions[0]->name; + + while (tab->q && evutil_ascii_strcasecmp(question, tab->q) && + strcmp("*", tab->q)) + ++tab; + if (tab->q == NULL) + TT_DIE(("Unexpected question: '%s'", question)); + + ++tab->seen; + + if (!strcmp(tab->anstype, "err")) { + int err = atoi(tab->ans); + tt_assert(! evdns_server_request_respond(req, err)); + return; + } else if (!strcmp(tab->anstype, "A")) { + struct in_addr in; + evutil_inet_pton(AF_INET, tab->ans, &in); + evdns_server_request_add_a_reply(req, question, 1, &in.s_addr, + 100); + } else if (!strcmp(tab->anstype, "AAAA")) { + struct in6_addr in6; + evutil_inet_pton(AF_INET6, tab->ans, &in6); + evdns_server_request_add_aaaa_reply(req, + question, 1, &in6.s6_addr, 100); + } else { + TT_DIE(("Weird table entry with type '%s'", tab->anstype)); + } + tt_assert(! evdns_server_request_respond(req, 0)) + return; +end: + tt_want(! evdns_server_request_drop(req)); +} + +int +regress_dnsserver(struct event_base *base, ev_uint16_t *port, + struct regress_dns_server_table *search_table) +{ + dns_port = regress_get_dnsserver(base, port, &dns_sock, + regress_dns_server_cb, search_table); + return dns_port != NULL; +} diff --git a/test/regress_testutils.h b/test/regress_testutils.h new file mode 100644 index 00000000..064047c8 --- /dev/null +++ b/test/regress_testutils.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010 Niels Provos and Nick Mathewson + * + * 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. + */ + +#ifndef _TESTUTILS_H +#define _TESTUTILS_H + +struct regress_dns_server_table { + const char *q; + const char *anstype; + const char *ans; + int seen; +}; + +struct evdns_server_port * +regress_get_dnsserver(struct event_base *base, + ev_uint16_t *portnum, + evutil_socket_t *psock, + evdns_request_callback_fn_type cb, + void *arg); + +/* Helper: return the port that a socket is bound on, in host order. */ +int regress_get_socket_port(evutil_socket_t fd); + +/* used to look up pre-canned responses in a search table */ +void regress_dns_server_cb( + struct evdns_server_request *req, void *data); + +/* globally allocates a dns server that serves from a search table */ +int regress_dnsserver(struct event_base *base, ev_uint16_t *port, + struct regress_dns_server_table *seach_table); + +/* clean up the global dns server resources */ +void regress_clean_dnsserver(void); + +#endif /* _TESTUTILS_H */ +