Merge commit 'niels/http_dns'

This commit is contained in:
Nick Mathewson 2010-01-14 23:28:16 -05:00
commit 854645797b
9 changed files with 414 additions and 172 deletions

View File

@ -97,6 +97,7 @@ struct evhttp_connection {
void *closecb_arg;
struct event_base *base;
struct evdns_base *dns_base;
};
struct evhttp_cb {

77
http.c
View File

@ -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);
}

View File

@ -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
*

View File

@ -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 \

View File

@ -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 \

View File

@ -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);

View File

@ -50,12 +50,15 @@
#include <string.h>
#include <errno.h>
#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),

185
test/regress_testutils.c Normal file
View File

@ -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 <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#endif
#include "event-config.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _EVENT_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 _EVENT_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 "event2/event.h"
#include "event2/event_compat.h"
#include <event2/util.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#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;
}

59
test/regress_testutils.h Normal file
View File

@ -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 */