diff options
author | Roger Dingledine <arma@torproject.org> | 2003-09-18 08:11:31 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2003-09-18 08:11:31 +0000 |
commit | 078c5ab6171ff37329381ec3b88c9c9c96a7d2e0 (patch) | |
tree | 5cd33c8090355cd13d388a58de87dc41552b2aa6 /src/or | |
parent | b97945e41156f1da022c118d934e79c375d2a44e (diff) | |
download | tor-078c5ab6171ff37329381ec3b88c9c9c96a7d2e0.tar.gz tor-078c5ab6171ff37329381ec3b88c9c9c96a7d2e0.zip |
leave the socks handshake on the inbuf until it's complete
this paves the way for supporting socks5 and other handshakes
it also removes those pesky AP-only variables from connection_t
also hacked a fix for a bug where some streams weren't ending properly --
maybe because marked connections weren't flushing properly?
svn:r472
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/buffers.c | 84 | ||||
-rw-r--r-- | src/or/circuit.c | 5 | ||||
-rw-r--r-- | src/or/connection.c | 16 | ||||
-rw-r--r-- | src/or/connection_edge.c | 104 | ||||
-rw-r--r-- | src/or/directory.c | 6 | ||||
-rw-r--r-- | src/or/main.c | 6 | ||||
-rw-r--r-- | src/or/or.h | 22 | ||||
-rw-r--r-- | src/or/routers.c | 56 |
8 files changed, 149 insertions, 150 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index eba43e30ec..ad00234c49 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -126,8 +126,8 @@ int flush_buf(int s, char **buf, int *buflen, int *buf_flushlen, int *buf_datale *buf_datalen -= write_result; *buf_flushlen -= write_result; memmove(*buf, *buf+write_result, *buf_datalen); -// log_fn(LOG_DEBUG,"flushed %d bytes, %d ready to flush, %d remain.", -// write_result,*buf_flushlen,*buf_datalen); + log_fn(LOG_DEBUG,"%d: flushed %d bytes, %d ready to flush, %d remain.", + s,write_result,*buf_flushlen,*buf_datalen); return *buf_flushlen; /* XXX USE_TLS should change to return write_result like any sane function would */ } @@ -257,6 +257,86 @@ int fetch_from_buf_http(char *buf, int *buf_datalen, return 1; } +/* There is a (possibly incomplete) socks handshake on *buf, of the + * forms + * socks4: "socksheader || username\0". + * socks4a: "socksheader || username\0 || destaddr\0". + * If it's a complete and valid handshake, and destaddr fits in addr_out, + * then pull the handshake off the buf, assign to addr_out and port_out, + * and return 1. + * If it's invalid or too big, return -1. + * Else it's not all there yet, change nothing return 0. + */ +int fetch_from_buf_socks(char *buf, int *buf_datalen, + char *addr_out, int max_addrlen, + uint16_t *port_out) { + socks4_t *socks4_info; + char tmpbuf[512]; + uint16_t port; + enum {socks4, socks4a } socks_prot = socks4a; + char *next, *startaddr; + + if(*buf_datalen < sizeof(socks4_t)) /* basic info available? */ + return 0; /* not yet */ + + socks4_info = (socks4_t *)buf; + + if(socks4_info->version != 4) { + log_fn(LOG_NOTICE,"Unrecognized version %d.",socks4_info->version); + return -1; + } + + if(socks4_info->command != 1) { /* not a connect? we don't support it. */ + log_fn(LOG_NOTICE,"command %d not '1'.",socks4_info->command); + return -1; + } + + port = ntohs(*(uint16_t*)&socks4_info->destport); + if(!port) { + log_fn(LOG_NOTICE,"Port is zero."); + return -1; + } + + if(socks4_info->destip[0] || socks4_info->destip[1] || + socks4_info->destip[2] || !socks4_info->destip[3]) { /* not 0.0.0.x */ + log_fn(LOG_NOTICE,"destip not in form 0.0.0.x."); + sprintf(tmpbuf, "%d.%d.%d.%d", socks4_info->destip[0], + socks4_info->destip[1], socks4_info->destip[2], socks4_info->destip[3]); + if(max_addrlen <= strlen(tmpbuf)) { + log_fn(LOG_DEBUG,"socks4-addr too long."); + return -1; + } + log_fn(LOG_DEBUG,"Successfully read destip (%s)", tmpbuf); + socks_prot = socks4; + } + + next = memchr(buf+sizeof(socks4_t), 0, *buf_datalen); + if(!next) { + log_fn(LOG_DEBUG,"Username not here yet."); + return 0; + } + + startaddr = next+1; + if(socks_prot == socks4a) { + next = memchr(startaddr, 0, buf+*buf_datalen-startaddr); + if(!next) { + log_fn(LOG_DEBUG,"Destaddr not here yet."); + return 0; + } + if(max_addrlen <= next-startaddr) { + log_fn(LOG_DEBUG,"Destaddr not here yet."); + return -1; + } + } + log_fn(LOG_DEBUG,"Everything is here. Success."); + *port_out = port; + strcpy(addr_out, socks_prot == socks4 ? tmpbuf : startaddr); + *buf_datalen -= (next-buf+1); /* next points to the final \0 on inbuf */ + memmove(buf, next+1, *buf_datalen); +// log_fn(LOG_DEBUG,"buf_datalen is now %d:'%s'",*buf_datalen,buf); + return 1; +} + int find_on_inbuf(char *string, int string_len, char *buf, int buf_datalen) { /* find first instance of needle 'string' on haystack 'buf'. return how diff --git a/src/or/circuit.c b/src/or/circuit.c index 50d0591345..98953738bf 100644 --- a/src/or/circuit.c +++ b/src/or/circuit.c @@ -807,8 +807,9 @@ int circuit_send_next_onion_skin(circuit_t *circ) { return 0; } -/* take the 'extend' cell, pull out addr/port plus the onion skin. Connect - * to the next hop, and pass it the onion skin in a create cell. +/* take the 'extend' cell, pull out addr/port plus the onion skin. Make + * sure we're connected to the next hop, and pass it the onion skin in + * a create cell. */ int circuit_extend(cell_t *cell, circuit_t *circ) { connection_t *n_conn; diff --git a/src/or/connection.c b/src/or/connection.c index 5dead3f567..64355e1aad 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -98,8 +98,6 @@ void connection_free(connection_t *conn) { buf_free(conn->outbuf); if(conn->address) free(conn->address); - if(conn->dest_addr) - free(conn->dest_addr); if(connection_speaks_cells(conn)) { directory_set_dirty(); @@ -527,13 +525,6 @@ int connection_fetch_from_buf(char *string, int len, connection_t *conn) { return fetch_from_buf(string, len, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen); } -int connection_fetch_from_buf_http(connection_t *conn, - char *headers_out, int max_headerlen, - char *body_out, int max_bodylen) { - return fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen, - headers_out, max_headerlen, body_out, max_bodylen); -} - int connection_find_on_inbuf(char *string, int len, connection_t *conn) { return find_on_inbuf(string, len, conn->inbuf, conn->inbuf_datalen); } @@ -782,13 +773,6 @@ void assert_connection_ok(connection_t *conn, time_t now) /* XXX unchecked, package window, deliver window. */ } - if (conn->type != CONN_TYPE_AP) { - assert(!conn->socks_version); - assert(!conn->read_username); - assert(!conn->dest_addr); - assert(!conn->dest_port); - } - switch(conn->type) { case CONN_TYPE_OR_LISTENER: diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index cb53b8efce..1657e645a8 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -7,7 +7,8 @@ extern or_options_t options; /* command-line and config-file options */ static int connection_ap_handshake_process_socks(connection_t *conn); -static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ); +static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ, + char *destaddr, uint16_t destport); static int connection_ap_handshake_socks_reply(connection_t *conn, char result); static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); @@ -17,16 +18,6 @@ static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); #define SOCKS4_REQUEST_IDENT_FAILED 92 #define SOCKS4_REQUEST_IDENT_CONFLICT 93 -/* structure of a socks client operation */ -typedef struct { - unsigned char version; /* socks version number */ - unsigned char command; /* command code */ - unsigned char destport[2]; /* destination port, network order */ - unsigned char destip[4]; /* destination address */ - /* userid follows, terminated by a NULL */ - /* dest host follows, terminated by a NULL */ -} socks4_t; - int connection_edge_process_inbuf(connection_t *conn) { assert(conn); @@ -441,86 +432,24 @@ int connection_consider_sending_sendme(connection_t *conn, int edge_type) { } static int connection_ap_handshake_process_socks(connection_t *conn) { - socks4_t socks4_info; circuit_t *circ; - char tmpbuf[512]; - int amt; + char destaddr[200]; + uint16_t destport; assert(conn); log_fn(LOG_DEBUG,"entered."); - if(!conn->socks_version) { /* try to pull it in */ - - if(conn->inbuf_datalen < sizeof(socks4_t)) /* basic info available? */ - return 0; /* not yet */ - - connection_fetch_from_buf((char *)&socks4_info,sizeof(socks4_t),conn); - - log_fn(LOG_DEBUG,"Successfully read socks info."); - - if(socks4_info.version != 4) { - log_fn(LOG_NOTICE,"Unrecognized version %d.",socks4_info.version); - connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_REJECT); - return -1; - } - conn->socks_version = socks4_info.version; - - if(socks4_info.command != 1) { /* not a connect? we don't support it. */ - log_fn(LOG_NOTICE,"command %d not '1'.",socks4_info.command); - connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_REJECT); - return -1; - } - - conn->dest_port = ntohs(*(uint16_t*)&socks4_info.destport); - if(!conn->dest_port) { - log_fn(LOG_NOTICE,"Port is zero."); - connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_REJECT); - return -1; - } - log_fn(LOG_NOTICE,"Dest port is %d.",conn->dest_port); - - if(socks4_info.destip[0] || - socks4_info.destip[1] || - socks4_info.destip[2] || - !socks4_info.destip[3]) { /* not 0.0.0.x */ - log_fn(LOG_NOTICE,"destip not in form 0.0.0.x."); - sprintf(tmpbuf, "%d.%d.%d.%d", socks4_info.destip[0], - socks4_info.destip[1], socks4_info.destip[2], socks4_info.destip[3]); - conn->dest_addr = strdup(tmpbuf); - log_fn(LOG_DEBUG,"Successfully read destip (%s)", conn->dest_addr); - } - - } - - if(!conn->read_username) { /* the socks spec says we've got to read stuff until we get a null */ - amt = connection_find_on_inbuf("\0", 1, conn); - if(amt < 0) /* not there yet */ - return 0; - if(amt > 500) { - log_fn(LOG_NOTICE,"username too long."); + switch(fetch_from_buf_socks(conn->inbuf,&conn->inbuf_datalen, + destaddr, sizeof(destaddr), &destport)) { + case -1: + log_fn(LOG_DEBUG,"Fetching socks handshake failed. Closing."); connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_REJECT); return -1; - } - connection_fetch_from_buf(tmpbuf,amt,conn); - conn->read_username = 1; - log_fn(LOG_DEBUG,"Successfully read username."); - } - - if(!conn->dest_addr) { /* no dest_addr found yet */ - amt = connection_find_on_inbuf("\0", 1, conn); - if(amt < 0) /* not there yet */ + case 0: + log_fn(LOG_DEBUG,"Fetching socks handshake, not all here yet. Ignoring."); return 0; - if(amt > 500) { - log_fn(LOG_NOTICE,"dest_addr too long."); - connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_REJECT); - return -1; - } - connection_fetch_from_buf(tmpbuf,amt,conn); - - conn->dest_addr = strdup(tmpbuf); - log_fn(LOG_NOTICE,"successfully read dest addr '%s'", - conn->dest_addr); + /* case 1, fall through */ } /* find the circuit that we should use, if there is one. */ @@ -542,7 +471,7 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { assert(circ->cpath->prev->state == CPATH_STATE_OPEN); conn->cpath_layer = circ->cpath->prev; - if(connection_ap_handshake_send_begin(conn, circ) < 0) { + if(connection_ap_handshake_send_begin(conn, circ, destaddr, destport) < 0) { circuit_close(circ); return -1; } @@ -550,11 +479,12 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { return 0; } -static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ) { +/* deliver the destaddr:destport in a relay cell */ +static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ, + char *destaddr, uint16_t destport) { cell_t cell; - memset(&cell, 0, sizeof(cell_t)); - /* deliver the dest_addr in a relay cell */ + cell.command = CELL_RELAY; cell.aci = circ->n_aci; SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_BEGIN); @@ -566,7 +496,7 @@ static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t * memcpy(cell.payload+RELAY_HEADER_SIZE, ap_conn->stream_id, STREAM_ID_SIZE); cell.length = snprintf(cell.payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE, CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE, - "%s:%d", ap_conn->dest_addr, ap_conn->dest_port) + + "%s:%d", destaddr, destport) + 1 + STREAM_ID_SIZE + RELAY_HEADER_SIZE; log_fn(LOG_DEBUG,"Sending relay cell (id %d) to begin stream %d.", *(int *)(cell.payload+1),*(int *)ap_conn->stream_id); if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_OUT, ap_conn->cpath_layer) < 0) { diff --git a/src/or/directory.c b/src/or/directory.c index ac70ba08dc..293da09ddd 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -141,7 +141,8 @@ int connection_dir_process_inbuf(connection_t *conn) { switch(conn->state) { case DIR_CONN_STATE_CLIENT_READING_GET: /* kill it, but first process the_directory and learn about new routers. */ - switch(connection_fetch_from_buf_http(conn, NULL, 0, the_directory, MAX_DIR_SIZE)) { + switch(fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen, + NULL, 0, the_directory, MAX_DIR_SIZE)) { case -1: /* overflow */ log_fn(LOG_DEBUG,"'get' response too large. Failing."); return -1; @@ -191,7 +192,8 @@ static int directory_handle_command(connection_t *conn) { assert(conn && conn->type == CONN_TYPE_DIR); - switch(connection_fetch_from_buf_http(conn, headers, sizeof(headers), body, sizeof(body))) { + switch(fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen, + headers, sizeof(headers), body, sizeof(body))) { case -1: /* overflow */ log_fn(LOG_DEBUG,"input too large. Failing."); return -1; diff --git a/src/or/main.c b/src/or/main.c index 7d9be0f061..6fc3c1c5cc 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -301,9 +301,11 @@ static void check_conn_marked(int i) { assert(conn); if(conn->marked_for_close) { log_fn(LOG_DEBUG,"Cleaning up connection."); - if(conn->s >= 0) { /* might be an incomplete exit connection */ + if(conn->s >= 0) { /* might be an incomplete edge connection */ /* FIXME there's got to be a better way to check for this -- and make other checks? */ - connection_flush_buf(conn); /* flush it first */ + connection_handle_write(conn); /* flush it first */ + if(connection_wants_to_flush(conn)) /* not done flushing */ + log_fn(LOG_WARNING,"Conn (socket %d) still wants to flush. Losing %d bytes!",conn->s, conn->inbuf_datalen); } connection_remove(conn); connection_free(conn); diff --git a/src/or/or.h b/src/or/or.h index dbd27d0fa6..915c6deffd 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -215,6 +215,16 @@ /* legal characters in a filename */ #define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/" +/* structure of a socks client operation */ +typedef struct { + unsigned char version; /* socks version number */ + unsigned char command; /* command code */ + unsigned char destport[2]; /* destination port, network order */ + unsigned char destip[4]; /* destination address */ + /* userid follows, terminated by a NULL */ + /* dest host follows, terminated by a NULL */ +} socks4_t; + typedef uint16_t aci_t; /* cell definition */ @@ -293,12 +303,6 @@ struct connection_t { int done_sending; /* for half-open connections; not used currently */ int done_receiving; - -/* Used only by AP connections: */ - char socks_version; /* what socks version are they speaking at me? */ - char read_username; /* have i read the username yet? */ - char *dest_addr; /* what address and port are this stream's destination? */ - uint16_t dest_port; /* host order */ }; typedef struct connection_t connection_t; @@ -443,6 +447,9 @@ int fetch_from_buf(char *string, int string_len, char **buf, int *buflen, int *b int fetch_from_buf_http(char *buf, int *buf_datalen, char *headers_out, int max_headerlen, char *body_out, int max_bodylen); +int fetch_from_buf_socks(char *buf, int *buf_datalen, + char *addr_out, int max_addrlen, + uint16_t *port_out); int find_on_inbuf(char *string, int string_len, char *buf, int buf_datalen); /********************************* circuit.c ***************************/ @@ -510,9 +517,6 @@ int connection_handle_read(connection_t *conn); int connection_read_to_buf(connection_t *conn); int connection_fetch_from_buf(char *string, int len, connection_t *conn); -int connection_fetch_from_buf_http(connection_t *conn, - char *headers_out, int max_headerlen, - char *body_out, int max_bodylen); int connection_find_on_inbuf(char *string, int len, connection_t *conn); int connection_wants_to_flush(connection_t *conn); diff --git a/src/or/routers.c b/src/or/routers.c index ca8f567e3d..6341d335f1 100644 --- a/src/or/routers.c +++ b/src/or/routers.c @@ -42,39 +42,35 @@ router_resolve_directory(directory_t *dir); int learn_my_address(struct sockaddr_in *me) { /* local host information */ char localhostname[512]; - static struct hostent *localhost; + struct hostent *localhost; + static struct sockaddr_in answer; static int already_learned=0; - if(already_learned) { - memset(me,0,sizeof(struct sockaddr_in)); - me->sin_family = AF_INET; - memcpy((void *)&me->sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr)); - me->sin_port = htons((uint16_t) options.ORPort); - return 0; - } - - /* obtain local host information */ - if(gethostname(localhostname,512) < 0) { - log_fn(LOG_ERR,"Error obtaining local hostname"); - return -1; - } - log_fn(LOG_DEBUG,"localhostname is '%s'.",localhostname); - localhost = gethostbyname(localhostname); - if (!localhost) { - log_fn(LOG_ERR,"Error obtaining local host info."); - return -1; + if(!already_learned) { + /* obtain local host information */ + if(gethostname(localhostname,512) < 0) { + log_fn(LOG_ERR,"Error obtaining local hostname"); + return -1; + } + log_fn(LOG_DEBUG,"localhostname is '%s'.",localhostname); + localhost = gethostbyname(localhostname); + if (!localhost) { + log_fn(LOG_ERR,"Error obtaining local host info."); + return -1; + } + memset(&answer,0,sizeof(struct sockaddr_in)); + answer.sin_family = AF_INET; + memcpy((void *)&answer.sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr)); + answer.sin_port = htons((uint16_t) options.ORPort); + log_fn(LOG_DEBUG,"chose address as '%s'.",inet_ntoa(answer.sin_addr)); + if (!strncmp("127.",inet_ntoa(answer.sin_addr), 4) && + strcasecmp(localhostname, "localhost")) { + /* We're a loopback IP but we're not called localhost. Uh oh! */ + log_fn(LOG_WARNING, "Got a loopback address: /etc/hosts may be wrong"); + } + already_learned = 1; } - memset(me,0,sizeof(struct sockaddr_in)); - me->sin_family = AF_INET; - memcpy((void *)&me->sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr)); - me->sin_port = htons((uint16_t) options.ORPort); - log_fn(LOG_DEBUG,"chose address as '%s'.",inet_ntoa(me->sin_addr)); - if (!strncmp("127.",inet_ntoa(me->sin_addr), 4) && - strcasecmp(localhostname, "localhost")) { - /* We're a loopback IP but we're not called localhost. Uh oh! */ - log_fn(LOG_WARNING, "Got a loopback address: /etc/hosts may be wrong"); - } - already_learned=1; + memcpy(me,&answer,sizeof(struct sockaddr_in)); return 0; } |