]> granicus.if.org Git - libevent/commitdiff
http: implement new evhttp_connection_get_addr() api.
authorAzat Khuzhin <a3at.mail@gmail.com>
Tue, 1 Oct 2013 15:12:13 +0000 (19:12 +0400)
committerNick Mathewson <nickm@torproject.org>
Mon, 14 Oct 2013 15:22:29 +0000 (11:22 -0400)
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

http-internal.h
http.c
include/event2/http.h

index d25753bbf96ed35e3ec045611f653e940d715b27..82dd402a06264fba874bcc0a1e171a8efa0f2508 100644 (file)
@@ -99,6 +99,13 @@ struct evhttp_connection {
 
        struct event_base *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 */
diff --git a/http.c b/http.c
index 5600e2fc75cb450300bf3dbf7a49c1a25270ea93..e8672b778095f04b08bb17dcb2c8fcaadf27dc6a 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1168,6 +1168,9 @@ evhttp_connection_free(struct evhttp_connection *evcon)
        if (evcon->address != NULL)
                mm_free(evcon->address);
 
+       if (evcon->conn_address != NULL)
+               mm_free(evcon->conn_address);
+
        mm_free(evcon);
 }
 
@@ -1404,6 +1407,7 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
        struct evhttp_connection *evcon = arg;
        int error;
        ev_socklen_t errsz = sizeof(error);
+       socklen_t conn_address_len = sizeof(*evcon->conn_address);
 
        if (evcon->fd == -1)
                evcon->fd = bufferevent_getfd(bufev);
@@ -1454,6 +1458,14 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
        evcon->retry_cnt = 0;
        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 */
        bufferevent_setcb(evcon->bufev,
            evhttp_read_cb,
@@ -2348,6 +2360,12 @@ evhttp_connection_get_peer(struct evhttp_connection *evcon,
        *port = evcon->port;
 }
 
+const struct sockaddr*
+evhttp_connection_get_addr(struct evhttp_connection *evcon)
+{
+       return (struct sockaddr *)evcon->conn_address;
+}
+
 int
 evhttp_connection_connect_(struct evhttp_connection *evcon)
 {
index cf44941de8078b7b468997e8b2200ce118dbef2d..956d9d6c86a83c2dc83850c6e5a11f0668267bc8 100644 (file)
@@ -614,6 +614,15 @@ void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
 void evhttp_connection_get_peer(struct evhttp_connection *evcon,
     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.