http: implement new evhttp_connection_get_addr() api.

Basically tcp final handshake looks like this:
    (C - client, S - server)
    ACK[C] - FIN/ACK[S] - FIN/ACK[S] - ACK [C]

However there are servers, that didn't close connection like this,
while it is still _considered_ as valid, and using libevent http layer
we can do requests to such servers.

Modified handshake:
    (C - client, S - server)
    ACK[C] - RST/ACK[S] - RST/ACK[S]

And in this case we can't extract IP address from socket, because it is
already closed, and getpeername() will return: "transport endpoint is not connected".
So we need to store address that we are connecting to, after we know it,
and that is what this patch do.

I have reproduced it, however it have some extra packages.
(I will try to fix it)
https://github.com/azat/nfq-examples/blob/master/nfqnl_rst_fin.c
This commit is contained in:
Azat Khuzhin 2013-10-01 19:12:13 +04:00 committed by Nick Mathewson
parent 4464bd2396
commit 0c7f0405e3
3 changed files with 34 additions and 0 deletions

View File

@ -99,6 +99,13 @@ struct evhttp_connection {
struct event_base *base; struct event_base *base;
struct evdns_base *dns_base; struct evdns_base *dns_base;
/* Saved conn_addr, to extract IP address from it.
*
* Because some servers may reset/close connection without waiting clients,
* in that case we can't extract IP address even in close_cb.
* So we need to save it, just after we connected to remote server. */
struct sockaddr_storage *conn_address;
}; };
/* A callback for an http server */ /* A callback for an http server */

18
http.c
View File

@ -1168,6 +1168,9 @@ evhttp_connection_free(struct evhttp_connection *evcon)
if (evcon->address != NULL) if (evcon->address != NULL)
mm_free(evcon->address); mm_free(evcon->address);
if (evcon->conn_address != NULL)
mm_free(evcon->conn_address);
mm_free(evcon); mm_free(evcon);
} }
@ -1404,6 +1407,7 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
struct evhttp_connection *evcon = arg; struct evhttp_connection *evcon = arg;
int error; int error;
ev_socklen_t errsz = sizeof(error); ev_socklen_t errsz = sizeof(error);
socklen_t conn_address_len = sizeof(*evcon->conn_address);
if (evcon->fd == -1) if (evcon->fd == -1)
evcon->fd = bufferevent_getfd(bufev); evcon->fd = bufferevent_getfd(bufev);
@ -1454,6 +1458,14 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
evcon->retry_cnt = 0; evcon->retry_cnt = 0;
evcon->state = EVCON_IDLE; evcon->state = EVCON_IDLE;
if (!evcon->conn_address) {
evcon->conn_address = mm_malloc(sizeof(*evcon->conn_address));
}
if (getpeername(evcon->fd, (struct sockaddr *)evcon->conn_address, &conn_address_len)) {
mm_free(evcon->conn_address);
evcon->conn_address = NULL;
}
/* reset the bufferevent cbs */ /* reset the bufferevent cbs */
bufferevent_setcb(evcon->bufev, bufferevent_setcb(evcon->bufev,
evhttp_read_cb, evhttp_read_cb,
@ -2348,6 +2360,12 @@ evhttp_connection_get_peer(struct evhttp_connection *evcon,
*port = evcon->port; *port = evcon->port;
} }
const struct sockaddr*
evhttp_connection_get_addr(struct evhttp_connection *evcon)
{
return (struct sockaddr *)evcon->conn_address;
}
int int
evhttp_connection_connect_(struct evhttp_connection *evcon) evhttp_connection_connect_(struct evhttp_connection *evcon)
{ {

View File

@ -614,6 +614,15 @@ void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
void evhttp_connection_get_peer(struct evhttp_connection *evcon, void evhttp_connection_get_peer(struct evhttp_connection *evcon,
char **address, ev_uint16_t *port); char **address, ev_uint16_t *port);
/** Get the remote address associated with this connection.
* extracted from getpeername().
*
* @return NULL if getpeername() return non success,
* or connection is not connected,
* otherwise it return pointer to struct sockaddr_storage */
const struct sockaddr*
evhttp_connection_get_addr(struct evhttp_connection *evcon);
/** /**
Make an HTTP request over the specified connection. Make an HTTP request over the specified connection.