bugfixes and note missing features

deal with content-length headers better when reading http
don't assume struct socks4_info is a packed struct
fail the socks handshake if destip is zero
flesh out conn_state_to_string() for dir conn
fix typo (bug) in connection_handle_read()
directory get is now called fetch, post is now upload
reopen logs on sighup


svn:r475
This commit is contained in:
Roger Dingledine 2003-09-21 06:15:43 +00:00
parent 7afe2adbaf
commit ed51df7453
6 changed files with 92 additions and 71 deletions

View File

@ -230,9 +230,8 @@ int fetch_from_buf_http(char *buf, int *buf_datalen,
return -1; return -1;
} }
#define CONTENT_LENGTH "Content-Length: " #define CONTENT_LENGTH "\r\nContent-Length: "
i = find_on_inbuf(CONTENT_LENGTH, strlen(CONTENT_LENGTH), headers, headerlen); i = find_on_inbuf(CONTENT_LENGTH, strlen(CONTENT_LENGTH), headers, headerlen);
/* This includes headers like Not-Content-Length. But close enough. */
if(i > 0) { if(i > 0) {
contentlen = atoi(headers+i); contentlen = atoi(headers+i);
if(bodylen < contentlen) { if(bodylen < contentlen) {
@ -265,13 +264,13 @@ int fetch_from_buf_http(char *buf, int *buf_datalen,
* then pull the handshake off the buf, assign to addr_out and port_out, * then pull the handshake off the buf, assign to addr_out and port_out,
* and return 1. * and return 1.
* If it's invalid or too big, return -1. * If it's invalid or too big, return -1.
* Else it's not all there yet, change nothing return 0. * Else it's not all there yet, change nothing and return 0.
*/ */
int fetch_from_buf_socks(char *buf, int *buf_datalen, int fetch_from_buf_socks(char *buf, int *buf_datalen,
char *addr_out, int max_addrlen, char *addr_out, int max_addrlen,
uint16_t *port_out) { uint16_t *port_out) {
socks4_t *socks4_info; socks4_t socks4_info;
char tmpbuf[512]; char *tmpbuf=NULL;
uint16_t port; uint16_t port;
enum {socks4, socks4a } socks_prot = socks4a; enum {socks4, socks4a } socks_prot = socks4a;
char *next, *startaddr; char *next, *startaddr;
@ -279,38 +278,47 @@ int fetch_from_buf_socks(char *buf, int *buf_datalen,
if(*buf_datalen < sizeof(socks4_t)) /* basic info available? */ if(*buf_datalen < sizeof(socks4_t)) /* basic info available? */
return 0; /* not yet */ return 0; /* not yet */
socks4_info = (socks4_t *)buf; /* an inlined socks4_unpack() */
socks4_info.version = *buf;
socks4_info.command = *(buf+1);
socks4_info.destport = *(uint16_t*)(buf+2);
socks4_info.destip = *(uint32_t*)(buf+4);
if(socks4_info->version != 4) { if(socks4_info.version != 4) {
log_fn(LOG_NOTICE,"Unrecognized version %d.",socks4_info->version); log_fn(LOG_NOTICE,"Unrecognized version %d.",socks4_info.version);
return -1; return -1;
} }
if(socks4_info->command != 1) { /* not a connect? we don't support it. */ if(socks4_info.command != 1) { /* not a connect? we don't support it. */
log_fn(LOG_NOTICE,"command %d not '1'.",socks4_info->command); log_fn(LOG_NOTICE,"command %d not '1'.",socks4_info.command);
return -1; return -1;
} }
port = ntohs(*(uint16_t*)&socks4_info->destport); port = ntohs(socks4_info.destport);
if(!port) { if(!port) {
log_fn(LOG_NOTICE,"Port is zero."); log_fn(LOG_NOTICE,"Port is zero.");
return -1; return -1;
} }
if(socks4_info->destip[0] || socks4_info->destip[1] || if(!socks4_info.destip) {
socks4_info->destip[2] || !socks4_info->destip[3]) { /* not 0.0.0.x */ log_fn(LOG_NOTICE,"DestIP is zero.");
return -1;
}
if(socks4_info.destip >> 8) {
struct in_addr in;
log_fn(LOG_NOTICE,"destip not in form 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], in.s_addr = htonl(socks4_info.destip);
socks4_info->destip[1], socks4_info->destip[2], socks4_info->destip[3]); tmpbuf = inet_ntoa(in);
if(max_addrlen <= strlen(tmpbuf)) { if(max_addrlen <= strlen(tmpbuf)) {
log_fn(LOG_DEBUG,"socks4-addr too long."); log_fn(LOG_DEBUG,"socks4 addr too long.");
return -1; return -1;
} }
log_fn(LOG_DEBUG,"Successfully read destip (%s)", tmpbuf); log_fn(LOG_DEBUG,"Successfully read destip (%s)", tmpbuf);
socks_prot = socks4; socks_prot = socks4;
} }
next = memchr(buf+sizeof(socks4_t), 0, *buf_datalen); next = memchr(buf+SOCKS4_NETWORK_LEN, 0, *buf_datalen);
if(!next) { if(!next) {
log_fn(LOG_DEBUG,"Username not here yet."); log_fn(LOG_DEBUG,"Username not here yet.");
return 0; return 0;

View File

@ -47,11 +47,14 @@ char *conn_state_to_string[][15] = {
"waiting for OR connection", /* 4 */ "waiting for OR connection", /* 4 */
"open" }, /* 5 */ "open" }, /* 5 */
{ "ready" }, /* dir listener, 0 */ { "ready" }, /* dir listener, 0 */
{ "connecting", /* 0 */ { "connecting (fetch)", /* 0 */
"sending command", /* 1 */ "connecting (upload)", /* 1 */
"reading", /* 2 */ "client sending fetch", /* 2 */
"awaiting command", /* 3 */ "client sending upload", /* 3 */
"writing" }, /* 4 */ "client reading fetch", /* 4 */
"client reading upload", /* 5 */
"awaiting command", /* 6 */
"writing" }, /* 7 */
{ "idle", /* dns worker, 0 */ { "idle", /* dns worker, 0 */
"busy" }, /* 1 */ "busy" }, /* 1 */
{ "idle", /* cpu worker, 0 */ { "idle", /* cpu worker, 0 */
@ -437,7 +440,8 @@ int connection_handle_read(connection_t *conn) {
if(connection_read_to_buf(conn) < 0) { if(connection_read_to_buf(conn) < 0) {
if(conn->type == CONN_TYPE_DIR && if(conn->type == CONN_TYPE_DIR &&
(conn->state == DIR_CONN_STATE_CONNECTING_GET || DIR_CONN_STATE_CONNECTING_POST)) { (conn->state == DIR_CONN_STATE_CONNECTING_FETCH ||
conn->state == DIR_CONN_STATE_CONNECTING_UPLOAD)) {
/* it's a directory server and connecting failed: forget about this router */ /* it's a directory server and connecting failed: forget about this router */
/* XXX I suspect pollerr may make Windows not get to this point. :( */ /* XXX I suspect pollerr may make Windows not get to this point. :( */
router_forget_router(conn->addr,conn->port); router_forget_router(conn->addr,conn->port);

View File

@ -433,7 +433,7 @@ int connection_consider_sending_sendme(connection_t *conn, int edge_type) {
static int connection_ap_handshake_process_socks(connection_t *conn) { static int connection_ap_handshake_process_socks(connection_t *conn) {
circuit_t *circ; circuit_t *circ;
char destaddr[200]; char destaddr[200]; /* XXX why 200? but not 256, because it won't fit in a cell */
uint16_t destport; uint16_t destport;
assert(conn); assert(conn);
@ -511,16 +511,16 @@ static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *
} }
static int connection_ap_handshake_socks_reply(connection_t *conn, char result) { static int connection_ap_handshake_socks_reply(connection_t *conn, char result) {
socks4_t socks4_info; char buf[SOCKS4_NETWORK_LEN];
assert(conn); assert(conn);
socks4_info.version = 0; /* an inlined socks4_pack() */
socks4_info.command = result; memset(buf,0,sizeof(buf));
socks4_info.destport[0] = socks4_info.destport[1] = 0; buf[1] = result; /* command */
socks4_info.destip[0] = socks4_info.destip[1] = socks4_info.destip[2] = socks4_info.destip[3] = 0; /* leave version, destport, destip zero */
if(connection_write_to_buf((char *)&socks4_info, sizeof(socks4_t), conn) < 0) if(connection_write_to_buf(buf, sizeof(buf), conn) < 0)
return -1; return -1;
return connection_flush_buf(conn); /* try to flush it, in case we're about to close the conn */ return connection_flush_buf(conn); /* try to flush it, in case we're about to close the conn */
} }

View File

@ -18,8 +18,8 @@ static char the_directory[MAX_DIR_SIZE+1];
static int directorylen=0; static int directorylen=0;
static int directory_dirty=1; static int directory_dirty=1;
static char getstring[] = "GET / HTTP/1.0\r\n\r\n"; static char fetchstring[] = "GET / HTTP/1.0\r\n\r\n";
static char poststring[] = "POST / HTTP/1.0\r\n\r\n"; static char uploadstring[] = "POST / HTTP/1.0\r\n\r\n";
static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n"; static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n";
/********* END VARIABLES ************/ /********* END VARIABLES ************/
@ -35,10 +35,10 @@ void directory_initiate_command(routerinfo_t *router, int command) {
return; return;
} }
if(command == DIR_CONN_STATE_CONNECTING_GET) if(command == DIR_CONN_STATE_CONNECTING_FETCH)
log_fn(LOG_DEBUG,"initiating directory get"); log_fn(LOG_DEBUG,"initiating directory fetch");
else else
log_fn(LOG_DEBUG,"initiating directory post"); log_fn(LOG_DEBUG,"initiating directory upload");
conn = connection_new(CONN_TYPE_DIR); conn = connection_new(CONN_TYPE_DIR);
if(!conn) if(!conn)
@ -90,25 +90,25 @@ static int directory_send_command(connection_t *conn, int command) {
assert(conn && conn->type == CONN_TYPE_DIR); assert(conn && conn->type == CONN_TYPE_DIR);
switch(command) { switch(command) {
case DIR_CONN_STATE_CONNECTING_GET: case DIR_CONN_STATE_CONNECTING_FETCH:
if(connection_write_to_buf(getstring, strlen(getstring), conn) < 0) { if(connection_write_to_buf(fetchstring, strlen(fetchstring), conn) < 0) {
log_fn(LOG_DEBUG,"Couldn't write get to buffer."); log_fn(LOG_DEBUG,"Couldn't write fetch to buffer.");
return -1; return -1;
} }
conn->state = DIR_CONN_STATE_CLIENT_SENDING_GET; conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH;
break; break;
case DIR_CONN_STATE_CONNECTING_POST: case DIR_CONN_STATE_CONNECTING_UPLOAD:
s = router_get_my_descriptor(); s = router_get_my_descriptor();
if(!s) { if(!s) {
log_fn(LOG_DEBUG,"Failed to get my descriptor."); log_fn(LOG_DEBUG,"Failed to get my descriptor.");
return -1; return -1;
} }
if(connection_write_to_buf(poststring, strlen(poststring), conn) < 0 || if(connection_write_to_buf(uploadstring, strlen(uploadstring), conn) < 0 ||
connection_write_to_buf(s, strlen(s), conn) < 0) { connection_write_to_buf(s, strlen(s), conn) < 0) {
log_fn(LOG_DEBUG,"Couldn't write post/descriptor to buffer."); log_fn(LOG_DEBUG,"Couldn't write post/descriptor to buffer.");
return -1; return -1;
} }
conn->state = DIR_CONN_STATE_CLIENT_SENDING_POST; conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
break; break;
} }
return 0; return 0;
@ -139,18 +139,19 @@ int connection_dir_process_inbuf(connection_t *conn) {
if(conn->inbuf_reached_eof) { if(conn->inbuf_reached_eof) {
switch(conn->state) { switch(conn->state) {
case DIR_CONN_STATE_CLIENT_READING_GET: case DIR_CONN_STATE_CLIENT_READING_FETCH:
/* kill it, but first process the_directory and learn about new routers. */ /* kill it, but first process the_directory and learn about new routers. */
switch(fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen, switch(fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen,
NULL, 0, the_directory, MAX_DIR_SIZE)) { NULL, 0, the_directory, MAX_DIR_SIZE)) {
case -1: /* overflow */ case -1: /* overflow */
log_fn(LOG_DEBUG,"'get' response too large. Failing."); log_fn(LOG_DEBUG,"'fetch' response too large. Failing.");
return -1; return -1;
case 0: case 0:
log_fn(LOG_DEBUG,"'get' response not all here, but we're at eof. Closing."); log_fn(LOG_DEBUG,"'fetch' response not all here, but we're at eof. Closing.");
return -1; return -1;
/* case 1, fall through */ /* case 1, fall through */
} }
/* XXX check headers, at least make sure returned 2xx */
directorylen = strlen(the_directory); directorylen = strlen(the_directory);
log_fn(LOG_DEBUG,"Received directory (size %d):\n%s", directorylen, the_directory); log_fn(LOG_DEBUG,"Received directory (size %d):\n%s", directorylen, the_directory);
if(directorylen == 0) { if(directorylen == 0) {
@ -167,9 +168,9 @@ int connection_dir_process_inbuf(connection_t *conn) {
router_retry_connections(); router_retry_connections();
} }
return -1; return -1;
case DIR_CONN_STATE_CLIENT_READING_POST: case DIR_CONN_STATE_CLIENT_READING_UPLOAD:
/* XXX make sure there's a 200 OK on the buffer */ /* XXX make sure there's a 200 OK on the buffer */
log_fn(LOG_DEBUG,"eof while reading post response. Finished."); log_fn(LOG_DEBUG,"eof while reading upload response. Finished.");
return -1; return -1;
default: default:
log_fn(LOG_DEBUG,"conn reached eof, not reading. Closing."); log_fn(LOG_DEBUG,"conn reached eof, not reading. Closing.");
@ -205,6 +206,7 @@ static int directory_handle_command(connection_t *conn) {
log_fn(LOG_DEBUG,"headers '%s', body '%s'.",headers,body); log_fn(LOG_DEBUG,"headers '%s', body '%s'.",headers,body);
if(!strncasecmp(headers,"GET",3)) { if(!strncasecmp(headers,"GET",3)) {
/* XXX should check url and http version */
directory_rebuild(); /* rebuild it now, iff it's dirty */ directory_rebuild(); /* rebuild it now, iff it's dirty */
@ -224,6 +226,7 @@ static int directory_handle_command(connection_t *conn) {
} }
if(!strncasecmp(headers,"POST",4)) { if(!strncasecmp(headers,"POST",4)) {
/* XXX should check url and http version */
log_fn(LOG_DEBUG,"Received POST command, body '%s'", body); log_fn(LOG_DEBUG,"Received POST command, body '%s'", body);
if(connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) { if(connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) {
log_fn(LOG_DEBUG,"Failed to write answerstring to outbuf."); log_fn(LOG_DEBUG,"Failed to write answerstring to outbuf.");
@ -243,8 +246,8 @@ int connection_dir_finished_flushing(connection_t *conn) {
assert(conn && conn->type == CONN_TYPE_DIR); assert(conn && conn->type == CONN_TYPE_DIR);
switch(conn->state) { switch(conn->state) {
case DIR_CONN_STATE_CONNECTING_GET: case DIR_CONN_STATE_CONNECTING_FETCH:
case DIR_CONN_STATE_CONNECTING_POST: case DIR_CONN_STATE_CONNECTING_UPLOAD:
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */ if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */
if(!ERRNO_CONN_EINPROGRESS(errno)) { if(!ERRNO_CONN_EINPROGRESS(errno)) {
log_fn(LOG_DEBUG,"in-progress connect failed. Removing."); log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
@ -260,14 +263,14 @@ int connection_dir_finished_flushing(connection_t *conn) {
conn->address,conn->port); conn->address,conn->port);
return directory_send_command(conn, conn->state); return directory_send_command(conn, conn->state);
case DIR_CONN_STATE_CLIENT_SENDING_GET: case DIR_CONN_STATE_CLIENT_SENDING_FETCH:
log_fn(LOG_DEBUG,"client finished sending 'get' command."); log_fn(LOG_DEBUG,"client finished sending fetch command.");
conn->state = DIR_CONN_STATE_CLIENT_READING_GET; conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH;
connection_watch_events(conn, POLLIN); connection_watch_events(conn, POLLIN);
return 0; return 0;
case DIR_CONN_STATE_CLIENT_SENDING_POST: case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD:
log_fn(LOG_DEBUG,"client finished sending 'post' command."); log_fn(LOG_DEBUG,"client finished sending upload command.");
conn->state = DIR_CONN_STATE_CLIENT_READING_POST; conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD;
connection_watch_events(conn, POLLIN); connection_watch_events(conn, POLLIN);
return 0; return 0;
case DIR_CONN_STATE_SERVER_WRITING: case DIR_CONN_STATE_SERVER_WRITING:

View File

@ -25,7 +25,7 @@ static int nfds=0; /* number of connections currently active */
#ifndef MS_WINDOWS /* do signal stuff only on unix */ #ifndef MS_WINDOWS /* do signal stuff only on unix */
static int please_dumpstats=0; /* whether we should dump stats during the loop */ static int please_dumpstats=0; /* whether we should dump stats during the loop */
static int please_fetch_directory=0; /* whether we should fetch a new directory */ static int please_reset =0; /* whether we just got a sighup */
static int please_reap_children=0; /* whether we should waitpid for exited children*/ static int please_reap_children=0; /* whether we should waitpid for exited children*/
#endif /* signal stuff */ #endif /* signal stuff */
@ -337,7 +337,7 @@ static int prepare_for_poll(void) {
/* NOTE directory servers do not currently fetch directories. /* NOTE directory servers do not currently fetch directories.
* Hope this doesn't bite us later. * Hope this doesn't bite us later.
*/ */
directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_GET); directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_FETCH);
time_to_fetch_directory = now.tv_sec + options.DirFetchPeriod; time_to_fetch_directory = now.tv_sec + options.DirFetchPeriod;
} }
} }
@ -488,15 +488,19 @@ static int do_main_loop(void) {
dumpstats(); dumpstats();
please_dumpstats = 0; please_dumpstats = 0;
} }
if(please_fetch_directory) { if(please_reset) {
/* fetch a new directory */
if(options.DirPort) { if(options.DirPort) {
if(router_get_list_from_file(options.RouterFile) < 0) { if(router_get_list_from_file(options.RouterFile) < 0) {
log(LOG_ERR,"Error reloading router list. Continuing with old list."); log(LOG_ERR,"Error reloading router list. Continuing with old list.");
} }
} else { } else {
directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_GET); directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_FETCH);
} }
please_fetch_directory = 0;
reset_logs(); /* close and reopen the log files */
please_reset = 0;
} }
if(please_reap_children) { if(please_reap_children) {
while(waitpid(-1,NULL,WNOHANG)) ; /* keep reaping until no more zombies */ while(waitpid(-1,NULL,WNOHANG)) ; /* keep reaping until no more zombies */
@ -546,7 +550,7 @@ static void catch(int the_signal) {
log(LOG_NOTICE,"Catching signal %d, exiting cleanly.", the_signal); log(LOG_NOTICE,"Catching signal %d, exiting cleanly.", the_signal);
exit(0); exit(0);
case SIGHUP: case SIGHUP:
please_fetch_directory = 1; please_reset = 1;
break; break;
case SIGUSR1: case SIGUSR1:
please_dumpstats = 1; please_dumpstats = 1;

View File

@ -156,12 +156,12 @@
#define _AP_CONN_STATE_MAX 5 #define _AP_CONN_STATE_MAX 5
#define _DIR_CONN_STATE_MIN 0 #define _DIR_CONN_STATE_MIN 0
#define DIR_CONN_STATE_CONNECTING_GET 0 #define DIR_CONN_STATE_CONNECTING_FETCH 0
#define DIR_CONN_STATE_CONNECTING_POST 1 #define DIR_CONN_STATE_CONNECTING_UPLOAD 1
#define DIR_CONN_STATE_CLIENT_SENDING_GET 2 #define DIR_CONN_STATE_CLIENT_SENDING_FETCH 2
#define DIR_CONN_STATE_CLIENT_SENDING_POST 3 #define DIR_CONN_STATE_CLIENT_SENDING_UPLOAD 3
#define DIR_CONN_STATE_CLIENT_READING_GET 4 #define DIR_CONN_STATE_CLIENT_READING_FETCH 4
#define DIR_CONN_STATE_CLIENT_READING_POST 5 #define DIR_CONN_STATE_CLIENT_READING_UPLOAD 5
#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 6 #define DIR_CONN_STATE_SERVER_COMMAND_WAIT 6
#define DIR_CONN_STATE_SERVER_WRITING 7 #define DIR_CONN_STATE_SERVER_WRITING 7
#define _DIR_CONN_STATE_MAX 7 #define _DIR_CONN_STATE_MAX 7
@ -219,12 +219,14 @@
typedef struct { typedef struct {
unsigned char version; /* socks version number */ unsigned char version; /* socks version number */
unsigned char command; /* command code */ unsigned char command; /* command code */
unsigned char destport[2]; /* destination port, network order */ uint16_t destport; /* destination port, network order */
unsigned char destip[4]; /* destination address */ uint32_t destip; /* destination address, host order */
/* userid follows, terminated by a NULL */ /* userid follows, terminated by a \0 */
/* dest host follows, terminated by a NULL */ /* dest host follows, terminated by a \0 */
} socks4_t; } socks4_t;
#define SOCKS4_NETWORK_LEN 8
typedef uint16_t aci_t; typedef uint16_t aci_t;
/* cell definition */ /* cell definition */