Skip to content

Commit

Permalink
Add a bufferevent function to resolve a name then connect to it.
Browse files Browse the repository at this point in the history
This function, bufferevent_socket_connect_hostname() can either use
evdns to do the resolve, or use a new function (evutil_resolve) that
uses getaddrinfo or gethostbyname, like http.c does now.

This function is meant to eventually replace the hostname resolution mess in
http.c.

svn:r1496
  • Loading branch information
nmathewson committed Nov 3, 2009
1 parent fcc7668 commit 0b9eb1b
Show file tree
Hide file tree
Showing 13 changed files with 490 additions and 6 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Expand Up @@ -37,6 +37,7 @@ Changes in 2.0.3-alpha:
o Add a return value to event_assign so that it can fail rather than asserting when the user gives it bad input. event_set still dies on bad input.
o The event_base_new() and event_base_new_with_config() functions now never call exit() on failure. For backward "compatibility", event_init() still does, but more consistently.
o Remove compat/sys/_time.h. It interfered with system headers on HPUX, and its functionality has been subsumed by event2/util.h and util-internal.h.
o Add a new bufferevent_socket_connect_hostname() to encapsulate the resolve-then-connect operation.


Changes in 2.0.2-alpha:
Expand Down
2 changes: 1 addition & 1 deletion Makefile.am
Expand Up @@ -112,7 +112,7 @@ CORE_SRC = event.c buffer.c \
bufferevent.c bufferevent_sock.c bufferevent_filter.c \
bufferevent_pair.c listener.c \
evmap.c log.c evutil.c strlcpy.c $(SYS_SRC)
EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c
EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c bufferevent_evdns.c


libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
Expand Down
11 changes: 11 additions & 0 deletions bufferevent-internal.h
Expand Up @@ -227,6 +227,17 @@ void _bufferevent_generic_adj_timeouts(struct bufferevent *bev);
EVLOCK_UNLOCK(locking->lock, EVTHREAD_WRITE); \
} while(0)

struct evdns_base;
int _bufferevent_socket_connect_hostname_evdns(
struct bufferevent *bufev,
struct evdns_base *evdns_base,
int family,
const char *hostname,
int port);
void _bufferevent_set_socket_connect_hostname_evdns_fn(
int (*fn)(struct bufferevent *, struct evdns_base *, int,
const char *, int));

#ifdef __cplusplus
}
#endif
Expand Down
73 changes: 73 additions & 0 deletions bufferevent_sock.c
Expand Up @@ -54,6 +54,12 @@
#ifdef _EVENT_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef _EVENT_HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef _EVENT_HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif

#include "event2/util.h"
#include "event2/bufferevent.h"
Expand Down Expand Up @@ -352,6 +358,73 @@ bufferevent_socket_connect(struct bufferevent *bev,
return result;
}

static int (*_bufferevent_socket_connect_hostname_evdns_fn)(
struct bufferevent *, struct evdns_base *, int,
const char *, int) = NULL;

void _bufferevent_set_socket_connect_hostname_evdns_fn(
int (*fn)(struct bufferevent *, struct evdns_base *, int,
const char *, int))
{
if (!_bufferevent_socket_connect_hostname_evdns_fn)
_bufferevent_socket_connect_hostname_evdns_fn = fn;
}

int
bufferevent_socket_connect_hostname(struct bufferevent *bev,
struct evdns_base *evdns_base, int family, const char *hostname, int port)
{
struct sockaddr_storage ss;
ev_socklen_t socklen = sizeof(ss);
int socklen_int = sizeof(ss);

if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
return -1;
if (port < 1 || port > 65535)
return -1;

memset(&ss, 0, sizeof(ss));
if (!evutil_parse_sockaddr_port(hostname, (struct sockaddr*)&ss,
&socklen_int)) {
socklen = socklen_int;
if (ss.ss_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)&ss;
if (family == AF_INET6)
return -1;
if (sin->sin_port)
return -1;
sin->sin_port = htons(port);
} else if (ss.ss_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&ss;
if (family == AF_INET)
return -1;
if (sin6->sin6_port)
return -1;
sin6->sin6_port = htons(port);
}
return bufferevent_socket_connect(bev, (struct sockaddr*)&ss,
socklen);
}

if (evdns_base) {
EVUTIL_ASSERT(_bufferevent_socket_connect_hostname_evdns_fn);
return _bufferevent_socket_connect_hostname_evdns_fn(
bev, evdns_base, family, hostname, port);
}

memset(&ss, 0, sizeof(ss));

if (evutil_resolve(family, hostname, (struct sockaddr*)&ss,
&socklen, port)<0) {
_bufferevent_incref_and_lock(bev);
_bufferevent_run_eventcb(bev, BEV_EVENT_ERROR);
_bufferevent_decref_and_unlock(bev);
return -1;
}

return bufferevent_socket_connect(bev, (struct sockaddr*)&ss, socklen);
}

/*
* Create a new buffered event object.
*
Expand Down
2 changes: 1 addition & 1 deletion configure.in
Expand Up @@ -83,7 +83,7 @@ AC_SUBST(OPENSSL_LIBS)

dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h)
AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h netdb.h)
if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
AC_EGREP_CPP(yes,
Expand Down
11 changes: 11 additions & 0 deletions evdns.c
Expand Up @@ -111,6 +111,10 @@
#include <event2/event_struct.h>
#include <event2/thread.h>

#include <event2/bufferevent.h>
#include <event2/bufferevent_struct.h>
#include "bufferevent-internal.h"

#include "defer-internal.h"
#include "log-internal.h"
#include "mm-internal.h"
Expand Down Expand Up @@ -3584,6 +3588,13 @@ struct evdns_base *
evdns_base_new(struct event_base *event_base, int initialize_nameservers)
{
struct evdns_base *base;

/* Give the bufferevent library a hook into its evdns-enabled
* functionality. We can't do this correctly or else libevent-core
* will depend on libevent-extras. */
_bufferevent_set_socket_connect_hostname_evdns_fn(
_bufferevent_socket_connect_hostname_evdns);

base = mm_malloc(sizeof(struct evdns_base));
if (base == NULL)
return (NULL);
Expand Down
89 changes: 89 additions & 0 deletions evutil.c
Expand Up @@ -60,6 +60,9 @@
#ifdef _EVENT_HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif
#ifdef _EVENT_HAVE_NETDB_H
#include <netdb.h>
#endif

#ifndef _EVENT_HAVE_GETTIMEOFDAY
#include <sys/timeb.h>
Expand Down Expand Up @@ -320,6 +323,92 @@ evutil_socket_finished_connecting(evutil_socket_t fd)
return 1;
}

/** Internal helper: use the host's (blocking) resolver to look up 'hostname',
* and set the sockaddr pointed to by 'sa' to the answer. Assume we have
* *socklen bytes of storage; adjust *socklen to the number of bytes used.
* Try to return answers of type 'family', unless family is AF_UNSPEC.
* Return 0 on success and -1 on failure. If 'port' is nonzero, it is
* a port number in host order: set the port in any resulting sockaddr to
* the specified port.
*/
int
evutil_resolve(int family, const char *hostname, struct sockaddr *sa,
ev_socklen_t *socklen, int port)
{
#ifdef _EVENT_HAVE_GETADDRINFO_XXX
struct addrinfo hint, *hintp=NULL;
struct addrinfo *ai=NULL;
int r;
memset(&hint, 0, sizeof(hint));

if (family != AF_UNSPEC) {
hint.ai_family = family;
hintp = &hint;
}

r = getaddrinfo(hostname, NULL, hintp, &ai);
if (!ai)
return -1;
if (r || ai->ai_addrlen > *socklen) {
/* log/report error? */
freeaddrinfo(ai);
return -1;
}
/* XXX handle multiple return values better. */
memcpy(sa, ai->ai_addr, ai->ai_addrlen);
if (port) {
if (sa->sa_family == AF_INET)
((struct sockaddr_in*)sa)->sin_port = htons(port);
else if (sa->sa_family == AF_INET6)
((struct sockaddr_in6*)sa)->sin6_port = htons(port);
}
*socklen = ai->ai_addrlen;
freeaddrinfo(ai);
return 0;
#else
/* XXXX use gethostbyname_r/gethostbyname2_r where available */
struct hostent *he;
struct sockaddr *sa_ptr;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
ev_socklen_t slen;
he = gethostbyname(hostname);
if (!he || !he->h_length) {
return -1;
}
/* XXX handle multiple return values better. */
if (he->h_addrtype == AF_INET) {
if (family != AF_INET && family != AF_UNSPEC)
return -1;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
memcpy(&sin.sin_addr, he->h_addr_list[0], 4);
sa_ptr = (struct sockaddr*)&sin;
slen = sizeof(struct sockaddr_in);
} else if (he->h_addrtype == AF_INET6) {
if (family != AF_INET6 && family != AF_UNSPEC)
return -1;
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
memset(&sin6, 0, sizeof(sin6));
memcpy(sin6.sin6_addr.s6_addr, &he->h_addr_list[1], 16);
sa_ptr = (struct sockaddr*)&sin6;
slen = sizeof(struct sockaddr_in6);
} else {
event_warnx("gethostbyname returned unknown family %d",
he->h_addrtype);
return -1;
}
if (slen > *socklen) {
return -1;
}
memcpy(sa, sa_ptr, slen);
*socklen = slen;
return 0;
#endif
}

#ifdef WIN32
#define E(code, s) { code, (s " [" #code " ]") }
static struct { int code; const char *msg; } windows_socket_errors[] = {
Expand Down
4 changes: 3 additions & 1 deletion http.c
Expand Up @@ -53,8 +53,10 @@

#include <sys/queue.h>

#ifndef WIN32
#ifdef _EVENT_HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef _EVENT_HAVE_NETDB_H
#include <netdb.h>
#endif

Expand Down
30 changes: 30 additions & 0 deletions include/event2/bufferevent.h
Expand Up @@ -162,6 +162,36 @@ struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socke
*/
int bufferevent_socket_connect(struct bufferevent *, struct sockaddr *, int);

struct evdns_base;
/**
Resolve the hostname 'hostname' and connect to it as with
bufferevent_socket_connect().
@param bufev An existing bufferevent allocated with bufferevent_socket_new()
@param evdns_base Optionally, an evdns_base to use for resolving hostnames
asynchronously. May be set to NULL for a blocking resolve.
@param family A preferred address family to resolve addresses to, or
AF_UNSPEC for no preference. Only AF_INET, AF_INET6, and AF_UNSPEC are
supported.
@param hostname The hostname to resolve; see below for notes on recognized
formats
@param port The port to connect to on the resolved address.
@return 0 if successful, -1 on failure.
Recognized hostname formats are:
www.example.com (hostname)
1.2.3.4 (ipv4address)
::1 (ipv6address)
[::1] ([ipv6address])
Performance note: If you do not provide an evdns_base, this function
may block while it waits for a DNS response. This is probably not
what you want.
*/
int bufferevent_socket_connect_hostname(struct bufferevent *b,
struct evdns_base *, int, const char *, int);

/**
Assign a bufferevent to a specific event_base.
Expand Down
5 changes: 4 additions & 1 deletion include/event2/dns.h
Expand Up @@ -205,10 +205,13 @@ extern "C" {

/**
* The callback that contains the results from a lookup.
* - result is one of the DNS_ERR_* values (DNS_ERR_NONE for success)
* - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA
* - count contains the number of addresses of form type
* - ttl is the number of seconds the resolution may be cached for.
* - addresses needs to be cast according to type
* - addresses needs to be cast according to type. It will be an array of
* 4-byte sequences for ipv4, or an array of 16-byte sequences for ipv6,
* or a nul-terminated string for PTR.
*/
typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);

Expand Down

0 comments on commit 0b9eb1b

Please sign in to comment.