From 98f9616bf45951901f264b75269d1c554d50511e Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Fri, 7 Sep 2007 02:49:46 +0000 Subject: [PATCH] support setting local address on an evhttp_connection svn:r416 --- ChangeLog | 3 +- evhttp.h | 4 ++ http-internal.h | 8 ++- http.c | 140 +++++++++++++++++++++++++++++++------------- test/regress_http.c | 3 + 5 files changed, 114 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index e6c27181..7fb77107 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,4 +2,5 @@ Changes in current version: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. o demote most http warnings to debug messages o Fix Solaris compilation; from Magne Mahre - o Add a "Date" header to HTTP responses, as required by HTTP 1.1. \ No newline at end of file + o Add a "Date" header to HTTP responses, as required by HTTP 1.1. + o Support specifying the local address of an evhttp_connection using set_local_address diff --git a/evhttp.h b/evhttp.h index 8b0e88d6..1536e977 100644 --- a/evhttp.h +++ b/evhttp.h @@ -196,6 +196,10 @@ struct evhttp_connection *evhttp_connection_new( /* Frees an http connection */ void evhttp_connection_free(struct evhttp_connection *evcon); +/* sets the ip address from which http connections are made */ +void evhttp_connection_set_local_address(struct evhttp_connection *evcon, + const char *address); + /* Sets the timeout for events related to this connection */ void evhttp_connection_set_timeout(struct evhttp_connection *evcon, int timeout_in_secs); diff --git a/http-internal.h b/http-internal.h index 4bbb0fd4..e9a45f12 100644 --- a/http-internal.h +++ b/http-internal.h @@ -39,7 +39,7 @@ struct event_base; struct evhttp_connection { /* we use tailq only if they were created for an http server */ - TAILQ_ENTRY(evhttp_connection) next; + TAILQ_ENTRY(evhttp_connection) (next); int fd; struct event ev; @@ -47,7 +47,9 @@ struct evhttp_connection { struct evbuffer *input_buffer; struct evbuffer *output_buffer; - char *address; + char *bind_address; /* address to use for binding the src */ + + char *address; /* address to connect to */ u_short port; int flags; @@ -66,7 +68,7 @@ struct evhttp_connection { TAILQ_HEAD(evcon_requestq, evhttp_request) requests; - void (*cb)(struct evhttp_connection *, void *); + void (*cb)(struct evhttp_connection *, void *); void *cb_arg; void (*closecb)(struct evhttp_connection *, void *); diff --git a/http.c b/http.c index 892e7159..cd5da3ee 100644 --- a/http.c +++ b/http.c @@ -139,8 +139,9 @@ event_make_socket_nonblocking(int fd) extern int debug; -static int make_socket_ai(int should_bind, struct addrinfo *); -static int make_socket(int should_bind, const char *, u_short); +static int socket_connect(int fd, const char *address, unsigned short port); +static int bind_socket_ai(struct addrinfo *); +static int bind_socket(const char *, u_short); static void name_from_addr(struct sockaddr *, socklen_t, char **, char **); static int evhttp_associate_new_request_with_connection( struct evhttp_connection *evcon); @@ -823,6 +824,9 @@ evhttp_connection_free(struct evhttp_connection *evcon) if (evcon->fd != -1) close(evcon->fd); + if (evcon->bind_address != NULL) + free(evcon->bind_address); + if (evcon->address != NULL) free(evcon->address); @@ -835,6 +839,18 @@ evhttp_connection_free(struct evhttp_connection *evcon) free(evcon); } +void +evhttp_connection_set_local_address(struct evhttp_connection *evcon, + const char *address) +{ + assert(evcon->state == EVCON_DISCONNECTED); + if (evcon->bind_address) + free(evcon->bind_address); + if ((evcon->bind_address = strdup(address)) == NULL) + event_err(1, "%s: strdup", __func__); +} + + static void evhttp_request_dispatch(struct evhttp_connection* evcon) { @@ -1479,11 +1495,15 @@ evhttp_connection_connect(struct evhttp_connection *evcon) assert(!(evcon->flags & EVHTTP_CON_INCOMING)); evcon->flags |= EVHTTP_CON_OUTGOING; - /* Do async connection to HTTP server */ - if ((evcon->fd = make_socket( - 0, evcon->address, evcon->port)) == -1) { - event_debug(("%s: failed to connect to \"%s:%d\"", - __func__, evcon->address, evcon->port)); + evcon->fd = bind_socket(evcon->bind_address, 0); + if (evcon->fd == -1) { + event_debug(("%s: failed to bind to \"%s\"", + __func__, bind_address)); + return (-1); + } + + if (socket_connect(evcon->fd, evcon->address, evcon->port) == -1) { + close(evcon->fd); evcon->fd = -1; return (-1); } @@ -1940,7 +1960,7 @@ evhttp_bind_socket(struct evhttp *http, const char *address, u_short port) struct event *ev = &http->bind_ev; int fd; - if ((fd = make_socket(1, address, port)) == -1) + if ((fd = bind_socket(address, port)) == -1) return (-1); if (listen(fd, 10) == -1) { @@ -2313,7 +2333,7 @@ name_from_addr(struct sockaddr *sa, socklen_t salen, /* Either connect or bind */ static int -make_socket_ai(int should_bind, struct addrinfo *ai) +bind_socket_ai(struct addrinfo *ai) { struct linger linger; int fd, on = 1, r; @@ -2342,23 +2362,9 @@ make_socket_ai(int should_bind, struct addrinfo *ai) linger.l_linger = 5; setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); - if (should_bind) - r = bind(fd, ai->ai_addr, ai->ai_addrlen); - else - r = connect(fd, ai->ai_addr, ai->ai_addrlen); - if (r == -1) { -#ifdef WIN32 - int tmp_error = WSAGetLastError(); - if (tmp_error != WSAEWOULDBLOCK && tmp_error != WSAEINVAL && - tmp_error != WSAEINPROGRESS) { - goto out; - } -#else - if (errno != EINPROGRESS) { - goto out; - } -#endif - } + r = bind(fd, ai->ai_addr, ai->ai_addrlen); + if (r == -1) + goto out; return (fd); @@ -2369,36 +2375,51 @@ make_socket_ai(int should_bind, struct addrinfo *ai) return (-1); } -static int -make_socket(int should_bind, const char *address, u_short port) +static struct addrinfo * +make_addrinfo(const char *address, u_short port) { - int fd; - struct addrinfo ai, *aitop = NULL; + struct addrinfo ai[2], *aitop = NULL; + #ifdef HAVE_GETADDRINFO char strport[NI_MAXSERV]; int ai_result; - memset(&ai, 0, sizeof (ai)); - ai.ai_family = AF_INET; - ai.ai_socktype = SOCK_STREAM; - ai.ai_flags = should_bind ? AI_PASSIVE : 0; + memset(&ai[0], 0, sizeof (ai[0])); + ai[0].ai_family = AF_INET; + ai[0].ai_socktype = SOCK_STREAM; + ai[0].ai_flags = AI_PASSIVE; /* turn NULL host name into INADDR_ANY */ snprintf(strport, sizeof (strport), "%d", port); - if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) { + if ((ai_result = getaddrinfo(address, strport, &ai[0], &aitop)) != 0) { if ( ai_result == EAI_SYSTEM ) event_warn("getaddrinfo"); else event_warnx("getaddrinfo: %s", gai_strerror(ai_result)); - return (-1); + return (NULL); } #else - if (fake_getaddrinfo(address, &ai) < 0) { + static int cur; + if (++cur == 2) cur = 0; /* allow calling this function twice */ + + if (fake_getaddrinfo(address, &ai[cur]) < 0) { event_warn("fake_getaddrinfo"); - return (-1); + return (NULL); } - aitop = &ai; + aitop = &ai[cur]; #endif - fd = make_socket_ai(should_bind, aitop); + return (aitop); +} + +static int +bind_socket(const char *address, u_short port) +{ + int fd; + struct addrinfo *aitop = make_addrinfo(address, port); + + if (aitop == NULL) + return (-1); + + fd = bind_socket_ai(aitop); #ifdef HAVE_GETADDRINFO freeaddrinfo(aitop); @@ -2408,3 +2429,42 @@ make_socket(int should_bind, const char *address, u_short port) return (fd); } + +static int +socket_connect(int fd, const char *address, unsigned short port) +{ + struct addrinfo *ai = make_addrinfo(address, port); + int res = -1; + + if (ai == NULL) { + event_debug(("%s: make_addrinfo: \"%s:%d\"", + __func__, evcon->address, evcon->port)); + return (-1); + } + + if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) { +#ifdef WIN32 + int tmp_error = WSAGetLastError(); + if (tmp_error != WSAEWOULDBLOCK && tmp_error != WSAEINVAL && + tmp_error != WSAEINPROGRESS) { + goto out; + } +#else + if (errno != EINPROGRESS) { + goto out; + } +#endif + } + + /* everything is fine */ + res = 0; + +out: +#ifdef HAVE_GETADDRINFO + freeaddrinfo(ai); +#else + fake_freeaddrinfo(ai); +#endif + + return (res); +} diff --git a/test/regress_http.c b/test/regress_http.c index c15fc63c..2b99d0d1 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -393,6 +393,9 @@ http_dispatcher_test(void) exit(1); } + /* also bind to local host */ + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + /* * At this point, we want to schedule an HTTP GET request * server using our make request method. -- 2.40.0