diff options
author | Roger Dingledine <arma@torproject.org> | 2004-03-12 12:43:13 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2004-03-12 12:43:13 +0000 |
commit | 6af8d0606f1be27d572f46e8ef03112eedc73928 (patch) | |
tree | 20b3858abb8af97d09dbbb0f06c8e59b8af26a66 /src/or/directory.c | |
parent | 623cb0e311cb2d152b0e2af9bd654707679b38a3 (diff) | |
download | tor-6af8d0606f1be27d572f46e8ef03112eedc73928.tar tor-6af8d0606f1be27d572f46e8ef03112eedc73928.tar.gz |
inform unapproved servers when we reject their descriptors
svn:r1263
Diffstat (limited to 'src/or/directory.c')
-rw-r--r-- | src/or/directory.c | 178 |
1 files changed, 124 insertions, 54 deletions
diff --git a/src/or/directory.c b/src/or/directory.c index 6816d5b40..d6be10eff 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -12,8 +12,8 @@ static int directory_handle_command(connection_t *conn); extern or_options_t options; /* command-line and config-file options */ extern int has_fetched_directory; -static char fetchstring[] = "GET / HTTP/1.0\r\n\r\n"; -static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n"; +#define MAX_HEADERS_SIZE 2048 +#define MAX_BODY_SIZE 500000 /********* END VARIABLES ************/ @@ -69,6 +69,7 @@ void directory_initiate_command(routerinfo_t *router, int command) { } static int directory_send_command(connection_t *conn, int command) { + char fetchstring[] = "GET / HTTP/1.0\r\n\r\n"; const char *s; char tmp[8192]; @@ -94,59 +95,116 @@ static int directory_send_command(connection_t *conn, int command) { return 0; } +/* Parse "HTTP/1.%d %d%s\r\n". + * If it's well-formed, assign *code, point *message to the first + * non-space character after code if there is one and message is non-NULL + * (else leave it alone), and return 0. + * Otherwise, return -1. + */ +int parse_http_response(char *headers, int *code, char **message) { + int n1, n2; + assert(headers && code); + + while(isspace(*headers)) headers++; /* tolerate leading whitespace */ + + if(sscanf(headers, "HTTP/1.%d %d", &n1, &n2) < 2 || + (n1 != 0 && n1 != 1) || + (n2 < 100 || n2 >= 600)) { + log_fn(LOG_WARN,"Failed to parse header '%s'",headers); + return -1; + } + *code = n2; + if(message) { + /* XXX should set *message correctly */ + } + return 0; +} + int connection_dir_process_inbuf(connection_t *conn) { char *directory; - int directorylen=0; + char *headers; + int status_code; assert(conn && conn->type == CONN_TYPE_DIR); if(conn->inbuf_reached_eof) { - switch(conn->state) { - case DIR_CONN_STATE_CLIENT_READING_FETCH: - /* kill it, but first fetch/process the directory to learn about new routers. */ - switch(fetch_from_buf_http(conn->inbuf, - NULL, 0, &directory, MAX_DIR_SIZE)) { - case -1: /* overflow */ - log_fn(LOG_WARN,"'fetch' response too large. Failing."); - connection_mark_for_close(conn,0); - return -1; - case 0: - log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing."); - connection_mark_for_close(conn,0); - return -1; - /* case 1, fall through */ - } - /* XXX check headers, at least make sure returned 2xx */ - directorylen = strlen(directory); - log_fn(LOG_INFO,"Received directory (size %d):\n%s", directorylen, directory); - if(directorylen == 0) { - log_fn(LOG_INFO,"Empty directory. Ignoring."); - free(directory); - connection_mark_for_close(conn,0); - return 0; - } - if(router_set_routerlist_from_directory(directory, conn->identity_pkey) < 0){ - log_fn(LOG_INFO,"...but parsing failed. Ignoring."); - } else { - log_fn(LOG_INFO,"updated routers."); - } - has_fetched_directory=1; - if(options.ORPort) { /* connect to them all */ - router_retry_connections(); - } - free(directory); + if(conn->state != DIR_CONN_STATE_CLIENT_READING_FETCH && + conn->state != DIR_CONN_STATE_CLIENT_READING_UPLOAD) { + log_fn(LOG_INFO,"conn reached eof, not reading. Closing."); + connection_close_immediate(conn); /* it was an error; give up on flushing */ + connection_mark_for_close(conn,0); + return -1; + } + + switch(fetch_from_buf_http(conn->inbuf, + &headers, MAX_HEADERS_SIZE, + &directory, MAX_DIR_SIZE)) { + case -1: /* overflow */ + log_fn(LOG_WARN,"'fetch' response too large. Failing."); connection_mark_for_close(conn,0); - return 0; - case DIR_CONN_STATE_CLIENT_READING_UPLOAD: - /* XXX make sure there's a 200 OK on the buffer */ - log_fn(LOG_INFO,"eof while reading upload response. Finished."); + return -1; + case 0: + log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing."); + connection_mark_for_close(conn,0); + return -1; + /* case 1, fall through */ + } + + if(parse_http_response(headers, &status_code, NULL) < 0) { + log_fn(LOG_WARN,"Unparseable headers. Closing."); + free(directory); free(headers); + connection_mark_for_close(conn,0); + return -1; + } + + if(conn->state == DIR_CONN_STATE_CLIENT_READING_FETCH) { + /* fetch/process the directory to learn about new routers. */ + int directorylen; + directorylen = strlen(directory); + log_fn(LOG_INFO,"Received directory (size %d):\n%s", directorylen, directory); + if(status_code == 503 || directorylen == 0) { + log_fn(LOG_INFO,"Empty directory. Ignoring."); + free(directory); free(headers); connection_mark_for_close(conn,0); return 0; - default: - log_fn(LOG_INFO,"conn reached eof, not reading. Closing."); - connection_close_immediate(conn); /* it was an error; give up on flushing */ + } + if(status_code != 200) { + log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.", + status_code); + free(directory); free(headers); connection_mark_for_close(conn,0); return -1; + } + if(router_set_routerlist_from_directory(directory, conn->identity_pkey) < 0){ + log_fn(LOG_INFO,"...but parsing failed. Ignoring."); + } else { + log_fn(LOG_INFO,"updated routers."); + } + has_fetched_directory=1; + if(options.ORPort) { /* connect to them all */ + router_retry_connections(); + } + free(directory); free(headers); + connection_mark_for_close(conn,0); + return 0; + } + + if(conn->state == DIR_CONN_STATE_CLIENT_READING_UPLOAD) { + switch(status_code) { + case 200: + log_fn(LOG_INFO,"eof (status 200) while reading upload response: finished."); + break; + case 400: + log_fn(LOG_WARN,"http status 400 (bad request) from dirserver. Is your clock skewed?"); + break; + case 403: + log_fn(LOG_WARN,"http status 403 (unapproved server) from dirserver. Have you mailed arma your identity fingerprint? Are you using the right key?"); + + break; + } + free(directory); free(headers); + connection_mark_for_close(conn,0); + return 0; } } @@ -159,6 +217,11 @@ int connection_dir_process_inbuf(connection_t *conn) { return 0; } +static char answer200[] = "HTTP/1.0 200 OK\r\n\r\n"; +static char answer400[] = "HTTP/1.0 400 Bad request\r\n\r\n"; +static char answer403[] = "HTTP/1.0 403 Unapproved server\r\n\r\n"; +static char answer503[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n"; + static int directory_handle_command_get(connection_t *conn, char *headers, char *body) { size_t dlen; @@ -171,11 +234,13 @@ static int directory_handle_command_get(connection_t *conn, if(dlen == 0) { log_fn(LOG_WARN,"My directory is empty. Closing."); - return -1; /* XXX send some helpful http error code */ + connection_write_to_buf(answer503, strlen(answer503), conn); + conn->state = DIR_CONN_STATE_SERVER_WRITING; + return 0; } log_fn(LOG_DEBUG,"Dumping directory to client."); - connection_write_to_buf(answerstring, strlen(answerstring), conn); + connection_write_to_buf(answer200, strlen(answer200), conn); connection_write_to_buf(cp, dlen, conn); conn->state = DIR_CONN_STATE_SERVER_WRITING; return 0; @@ -188,12 +253,20 @@ static int directory_handle_command_post(connection_t *conn, /* XXX should check url and http version */ log_fn(LOG_DEBUG,"Received POST command."); cp = body; - if(dirserv_add_descriptor(&cp) < 0) { - log_fn(LOG_WARN,"dirserv_add_descriptor() failed. Dropping."); - return -1; /* XXX should write an http failed code */ + switch(dirserv_add_descriptor(&cp)) { + case -1: + /* malformed descriptor, or clock is skewed, or something wrong */ + connection_write_to_buf(answer400, strlen(answer400), conn); + break; + case 0: + /* descriptor was well-formed but server has not been approved */ + connection_write_to_buf(answer403, strlen(answer403), conn); + break; + case 1: + dirserv_get_directory(&cp); /* rebuild and write to disk */ + connection_write_to_buf(answer200, strlen(answer200), conn); + break; } - dirserv_get_directory(&cp); /* rebuild and write to disk */ - connection_write_to_buf(answerstring, strlen(answerstring), conn); conn->state = DIR_CONN_STATE_SERVER_WRITING; return 0; } @@ -202,9 +275,6 @@ static int directory_handle_command(connection_t *conn) { char *headers=NULL, *body=NULL; int r; -#define MAX_HEADERS_SIZE 2048 -#define MAX_BODY_SIZE 500000 - assert(conn && conn->type == CONN_TYPE_DIR); switch(fetch_from_buf_http(conn->inbuf, |