commit 82a19273e86660c743bce5aed7c5ef8112dd5c01 Author: Zack Weinberg zackw@panix.com Date: Mon Jul 25 11:01:46 2011 -0700
Separate socks event handling from regular output-side event handling --- src/network.c | 176 ++++++++++++++++++++++++++++++++------------------------- 1 files changed, 98 insertions(+), 78 deletions(-)
diff --git a/src/network.c b/src/network.c index 32d14ee..3337aed 100644 --- a/src/network.c +++ b/src/network.c @@ -58,6 +58,7 @@ static void socks_read_cb(struct bufferevent *bev, void *arg); static void obfuscated_read_cb(struct bufferevent *bev, void *arg); static void input_event_cb(struct bufferevent *bev, short what, void *arg); static void output_event_cb(struct bufferevent *bev, short what, void *arg); +static void socks_event_cb(struct bufferevent *bev, short what, void *arg);
/** Puts obfsproxy's networking subsystem on "closing time" mode. This @@ -213,12 +214,13 @@ simple_client_listener_cb(struct evconnlistener *evcl, goto err;
bufferevent_setcb(conn->input, plaintext_read_cb, NULL, input_event_cb, conn); - bufferevent_enable(conn->input, EV_READ|EV_WRITE); + /* don't enable the input side for reading at this point; wait till we + have a connection to the target */
bufferevent_setcb(conn->output, obfuscated_read_cb, NULL, output_event_cb, conn);
- /* Queue output right now. */ + /* Queue handshake, if any, before connecting. */ if (proto_handshake(conn->proto, bufferevent_get_output(conn->output)) < 0) goto err;
@@ -283,7 +285,7 @@ socks_client_listener_cb(struct evconnlistener *evcl, bufferevent_setcb(conn->input, socks_read_cb, NULL, input_event_cb, conn); bufferevent_enable(conn->input, EV_READ|EV_WRITE);
- /* Do not create a target bufferevent at this time; the socks + /* Do not create an output bufferevent at this time; the socks handler will do it after we know where we're connecting */
/* add conn to the connection list */ @@ -334,7 +336,9 @@ simple_server_listener_cb(struct evconnlistener *evcl, fd = -1; /* prevent double-close */
bufferevent_setcb(conn->input, obfuscated_read_cb, NULL, input_event_cb, conn); - bufferevent_enable(conn->input, EV_READ|EV_WRITE); + + /* don't enable the input side for reading at this point; wait till we + have a connection to the target */
/* New bufferevent to connect to the target address */ conn->output = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); @@ -436,8 +440,7 @@ socks_read_cb(struct bufferevent *bev, void *arg) conn_t *conn = arg; //struct bufferevent *other; enum socks_ret socks_ret; - obfs_assert(bev == conn->input); /* socks must be on the initial bufferevent */ - + obfs_assert(bev == conn->input); /* socks only makes sense on the input side */
do { enum socks_status_t status = socks_state_get_status(conn->socks_state); @@ -453,7 +456,10 @@ socks_read_cb(struct bufferevent *bev, void *arg) -1, BEV_OPT_CLOSE_ON_FREE);
- /* queue handshake, if any, before connecting */ + bufferevent_setcb(conn->output, obfuscated_read_cb, NULL, + socks_event_cb, conn); + + /* Queue handshake, if any, before connecting. */ if (proto_handshake(conn->proto, bufferevent_get_output(conn->output))<0) { /* XXXX send socks reply */ @@ -484,7 +490,7 @@ socks_read_cb(struct bufferevent *bev, void *arg) if (socks_ret == SOCKS_INCOMPLETE) return; /* need to read more data. */ else if (socks_ret == SOCKS_BROKEN) - close_conn(conn); /* XXXX maybe send socks reply */ + close_conn(conn); /* XXXX send socks reply */ else if (socks_ret == SOCKS_CMD_NOT_CONNECT) { bufferevent_enable(bev, EV_WRITE); bufferevent_disable(bev, EV_READ); @@ -567,101 +573,115 @@ error_or_eof(conn_t *conn, }
/** - We land in here when an event happens on conn->input. -*/ + Called when an "event" happens on conn->input. + On the input side, all such events are error conditions. + */ static void input_event_cb(struct bufferevent *bev, short what, void *arg) { conn_t *conn = arg; obfs_assert(bev == conn->input);
- if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { - log_warn("Got error: %s", + /* It should be impossible to get BEV_EVENT_CONNECTED on this side. */ + obfs_assert(what & (BEV_EVENT_EOF|BEV_EVENT_ERROR|BEV_EVENT_TIMEOUT)); + obfs_assert(!(what & BEV_EVENT_CONNECTED)); + + log_warn("Got error: %s", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); - error_or_eof(conn, bev, conn->output); - } - /* XXX we don't expect any other events */ + error_or_eof(conn, bev, conn->output); }
/** - We land in here when an event happens on conn->output. -*/ + Called when an "event" happens on conn->output. + In addition to the error cases dealt with above, this side can see + BEV_EVENT_CONNECTED which indicates that the output connection is + now open. + */ static void output_event_cb(struct bufferevent *bev, short what, void *arg) { conn_t *conn = arg; obfs_assert(bev == conn->output);
- /** - If we got the BEV_EVENT_ERROR flag *AND* we are in socks mode - *AND* we are in the ST_HAVE_ADDR state, chances are that we - failed connecting to the host requested by the CONNECT call. This - means that we should send a negative SOCKS reply back to the - client and terminate the connection. - */ - if (what & BEV_EVENT_ERROR) { - if ((conn->mode == LSN_SOCKS_CLIENT) && - (conn->socks_state) && - (socks_state_get_status(conn->socks_state) == ST_HAVE_ADDR)) { - log_debug("Connection failed") ; - /* Enable EV_WRITE so that we can send the response. - Disable EV_READ so that we don't get more stuff from the client. */ - bufferevent_enable(conn->input, EV_WRITE); - bufferevent_disable(conn->input, EV_READ); - socks_send_reply(conn->socks_state, bufferevent_get_output(conn->input), - evutil_socket_geterror(bufferevent_getfd(bev))); - bufferevent_setcb(conn->input, NULL, - close_conn_on_flush, output_event_cb, conn); - return; - } - } - - /** - If the connection is terminating *OR* if we got a BEV_EVENT_ERROR - but we don't match the case above, we most probably have to close - this connection soon. - */ - if (conn->flushing || (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR))) { + /* If the connection is terminating *OR* if we got one of the error + events, close this connection soon. */ + if (conn->flushing || + (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR|BEV_EVENT_TIMEOUT))) { log_warn("Got error: %s", - evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); + evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); error_or_eof(conn, bev, conn->input); return; }
- /** - If we got the BEV_EVENT_CONNECTED flag it means that a connection - request was succesfull and normally that should have been off a - CONNECT request by the SOCKS client. If that's the case we should - send a happy response to the client and switch to start serving - our pluggable transport protocol. - */ + /* Upon successful connection, go ahead and enable traffic on the + input side. */ if (what & BEV_EVENT_CONNECTED) { - /* woo, we're connected. Now the input buffer can start reading. */ conn->is_open = 1; log_debug("Connection done") ; bufferevent_enable(conn->input, EV_READ|EV_WRITE); - if (conn->mode == LSN_SOCKS_CLIENT) { - struct sockaddr_storage ss; - struct sockaddr *sa = (struct sockaddr*)&ss; - socklen_t slen = sizeof(&ss); - obfs_assert(conn->socks_state); - if (getpeername(bufferevent_getfd(bev), sa, &slen) == 0) { - /* Figure out where we actually connected to so that we can tell the - * socks client */ - socks_state_set_address(conn->socks_state, sa); - } - socks_send_reply(conn->socks_state, - bufferevent_get_output(conn->input), 0); - /* we sent a socks reply. We can finally move over to being a regular - input bufferevent. */ - socks_state_free(conn->socks_state); - conn->socks_state = NULL; - bufferevent_setcb(conn->input, - plaintext_read_cb, NULL, input_event_cb, conn); - if (evbuffer_get_length(bufferevent_get_input(conn->input)) != 0) - obfuscated_read_cb(bev, conn->input); - } return; } - /* XXX we don't expect any other events */ + + /* unrecognized event */ + obfs_abort(); +} + +/** + Called when an "event" happens on conn->output in socks mode. + Handles the same cases as output_event_cb but must also generate + appropriate socks messages back on the input side. + */ +static void +socks_event_cb(struct bufferevent *bev, short what, void *arg) +{ + conn_t *conn = arg; + obfs_assert(bev == conn->output); + + /* If we got an error while in the ST_HAVE_ADDR state, chances are + that we failed connecting to the host requested by the CONNECT + call. This means that we should send a negative SOCKS reply back + to the client and terminate the connection. */ + if ((what & BEV_EVENT_ERROR) && + socks_state_get_status(conn->socks_state) == ST_HAVE_ADDR) { + log_debug("Connection failed"); + /* Enable EV_WRITE so that we can send the response. + Disable EV_READ so that we don't get more stuff from the client. */ + bufferevent_enable(conn->input, EV_WRITE); + bufferevent_disable(conn->input, EV_READ); + socks_send_reply(conn->socks_state, bufferevent_get_output(conn->input), + evutil_socket_geterror(bufferevent_getfd(bev))); + bufferevent_setcb(conn->input, NULL, + close_conn_on_flush, output_event_cb, conn); + return; + } + + /* Additional work to do for BEV_EVENT_CONNECTED: send a happy + response to the client and switch to the actual obfuscated + protocol handlers. */ + if (what & BEV_EVENT_CONNECTED) { + struct sockaddr_storage ss; + struct sockaddr *sa = (struct sockaddr*)&ss; + socklen_t slen = sizeof(&ss); + obfs_assert(conn->socks_state); + if (getpeername(bufferevent_getfd(bev), sa, &slen) == 0) { + /* Figure out where we actually connected to so that we can tell the + * socks client */ + socks_state_set_address(conn->socks_state, sa); + } + socks_send_reply(conn->socks_state, + bufferevent_get_output(conn->input), 0); + /* we sent a socks reply. We can finally move over to being a regular + input bufferevent. */ + socks_state_free(conn->socks_state); + conn->socks_state = NULL; + bufferevent_setcb(conn->input, + plaintext_read_cb, NULL, input_event_cb, conn); + bufferevent_setcb(conn->output, + obfuscated_read_cb, NULL, output_event_cb, conn); + if (evbuffer_get_length(bufferevent_get_input(conn->input)) != 0) + obfuscated_read_cb(bev, conn->input); + } + + /* also do everything that's done on a normal connection */ + output_event_cb(bev, what, arg); }