diff options
Diffstat (limited to 'src/or/directory.c')
-rw-r--r-- | src/or/directory.c | 263 |
1 files changed, 183 insertions, 80 deletions
diff --git a/src/or/directory.c b/src/or/directory.c index c73d4d1e7..941ad4d56 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -4,7 +4,8 @@ #include "or.h" -static int directory_send_command(connection_t *conn, int command); +static void directory_send_command(connection_t *conn, + int purpose, const char *payload); static int directory_handle_command(connection_t *conn); /********* START VARIABLES **********/ @@ -17,20 +18,26 @@ extern int has_fetched_directory; /********* END VARIABLES ************/ -void directory_initiate_command(routerinfo_t *router, int command) { +void directory_initiate_command(routerinfo_t *router, int purpose, const char *payload) { connection_t *conn; - switch(command) { - case DIR_CONN_STATE_CONNECTING_FETCH: + switch(purpose) { + case DIR_PURPOSE_FETCH_DIR: log_fn(LOG_DEBUG,"initiating directory fetch"); break; - case DIR_CONN_STATE_CONNECTING_UPLOAD: - log_fn(LOG_DEBUG,"initiating directory upload"); + case DIR_PURPOSE_FETCH_HIDSERV: + log_fn(LOG_DEBUG,"initiating hidden-service descriptor fetch"); + break; + case DIR_PURPOSE_UPLOAD_DIR: + log_fn(LOG_DEBUG,"initiating server descriptor upload"); + break; + case DIR_PURPOSE_UPLOAD_HIDSERV: + log_fn(LOG_DEBUG,"initiating hidden-service descriptor upload"); break; } if (!router) { /* i guess they didn't have one in mind for me to use */ - log_fn(LOG_WARN,"No running dirservers known. Not trying."); + log_fn(LOG_WARN,"No running dirservers known. Not trying. (purpose %d)", purpose); return; } @@ -44,57 +51,99 @@ void directory_initiate_command(routerinfo_t *router, int command) { assert(router->identity_pkey); conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); - conn->state = command; + conn->purpose = purpose; if(connection_add(conn) < 0) { /* no space, forget it */ connection_free(conn); return; } - switch(connection_connect(conn, router->address, router->addr, router->dir_port)) { - case -1: - router_mark_as_down(conn->nickname); /* don't try him again */ - connection_mark_for_close(conn, 0); - return; - case 0: - connection_set_poll_socket(conn); - connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); - /* writable indicates finish, readable indicates broken link, - error indicates broken link in windowsland. */ - return; - /* case 1: fall through */ - } + /* queue the command on the outbuf */ + directory_send_command(conn, purpose, payload); - connection_set_poll_socket(conn); - if(directory_send_command(conn, command) < 0) { - connection_mark_for_close(conn, 0); + if(purpose == DIR_PURPOSE_FETCH_DIR || + purpose == DIR_PURPOSE_UPLOAD_DIR) { + + /* then we want to connect directly */ + conn->state = DIR_CONN_STATE_CONNECTING; + + switch(connection_connect(conn, conn->address, conn->addr, conn->port)) { + case -1: + router_mark_as_down(conn->nickname); /* don't try him again */ + connection_mark_for_close(conn, 0); + return; + case 1: + conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */ + /* fall through */ + case 0: + connection_set_poll_socket(conn); + connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); + /* writable indicates finish, readable indicates broken link, + error indicates broken link in windowsland. */ + } + } else { /* we want to connect via tor */ + /* make an AP connection + * populate it and add it at the right state + * socketpair and hook up both sides + */ + + conn->state = DIR_CONN_STATE_CLIENT_SENDING; + connection_set_poll_socket(conn); } } -static int directory_send_command(connection_t *conn, int command) { +static void directory_send_command(connection_t *conn, + int purpose, const char *payload) { char fetchstring[] = "GET / HTTP/1.0\r\n\r\n"; - const char *s; char tmp[8192]; assert(conn && conn->type == CONN_TYPE_DIR); - switch(command) { - case DIR_CONN_STATE_CONNECTING_FETCH: + switch(purpose) { + case DIR_PURPOSE_FETCH_DIR: + assert(payload == NULL); connection_write_to_buf(fetchstring, strlen(fetchstring), conn); - conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH; break; - case DIR_CONN_STATE_CONNECTING_UPLOAD: - s = router_get_my_descriptor(); - if(!s) { - log_fn(LOG_WARN,"Failed to get my descriptor."); - return -1; - } + case DIR_PURPOSE_UPLOAD_DIR: + assert(payload); snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s", - (int)strlen(s), s); + (int)strlen(payload), payload); + connection_write_to_buf(tmp, strlen(tmp), conn); + break; + case DIR_PURPOSE_FETCH_HIDSERV: + assert(payload); + snprintf(tmp, sizeof(tmp), "GET /hidserv/%s HTTP/1.0\r\n\r\n", payload); + connection_write_to_buf(tmp, strlen(tmp), conn); + break; + case DIR_PURPOSE_UPLOAD_HIDSERV: + assert(payload); + snprintf(tmp, sizeof(tmp), + "POST /hidserv/ HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s", + (int)strlen(payload), payload); connection_write_to_buf(tmp, strlen(tmp), conn); - conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD; break; } +} + +/* Parse "%s %s HTTP/1..." + * If it's well-formed, point *url to the second %s, + * null-terminate it (this modifies headers!) and return 0. + * Otherwise, return -1. + */ +int parse_http_url(char *headers, char **url) { + char *s, *tmp; + + s = (char *)eat_whitespace_no_nl(headers); + if (!*s) return -1; + s = (char *)find_whitespace(s); /* get past GET/POST */ + if (!*s) return -1; + s = (char *)eat_whitespace_no_nl(s); + if (!*s) return -1; + tmp = s; /* this is it, assuming it's valid */ + s = (char *)find_whitespace(s); + if (!*s) return -1; + *s = 0; + *url = tmp; return 0; } @@ -131,8 +180,7 @@ int connection_dir_process_inbuf(connection_t *conn) { assert(conn && conn->type == CONN_TYPE_DIR); if(conn->inbuf_reached_eof) { - if(conn->state != DIR_CONN_STATE_CLIENT_READING_FETCH && - conn->state != DIR_CONN_STATE_CLIENT_READING_UPLOAD) { + if(conn->state != DIR_CONN_STATE_CLIENT_READING) { 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); @@ -160,7 +208,7 @@ int connection_dir_process_inbuf(connection_t *conn) { return -1; } - if(conn->state == DIR_CONN_STATE_CLIENT_READING_FETCH) { + if(conn->purpose == DIR_PURPOSE_FETCH_DIR) { /* fetch/process the directory to learn about new routers. */ int directorylen; directorylen = strlen(directory); @@ -192,7 +240,7 @@ int connection_dir_process_inbuf(connection_t *conn) { return 0; } - if(conn->state == DIR_CONN_STATE_CLIENT_READING_UPLOAD) { + if(conn->purpose == DIR_PURPOSE_UPLOAD_DIR) { switch(status_code) { case 200: log_fn(LOG_INFO,"eof (status 200) while reading upload response: finished."); @@ -204,15 +252,35 @@ int connection_dir_process_inbuf(connection_t *conn) { log_fn(LOG_WARN,"http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed arma your identity fingerprint? Are you using the right key?"); break; + default: + log_fn(LOG_WARN,"http status %d response unrecognized.", status_code); + break; } free(directory); free(headers); connection_mark_for_close(conn,0); return 0; } + + if(conn->purpose == DIR_PURPOSE_FETCH_HIDSERV) { + + + } + + if(conn->purpose == DIR_PURPOSE_UPLOAD_HIDSERV) { + + + } + assert(0); /* never reached */ } - if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) - return directory_handle_command(conn); + if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) { + if (directory_handle_command(conn) < 0) { + connection_mark_for_close(conn,0); + return -1; + } else { + return 0; + } + } /* XXX for READ states, might want to make sure inbuf isn't too big */ @@ -223,54 +291,94 @@ int connection_dir_process_inbuf(connection_t *conn) { 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 answer404[] = "HTTP/1.0 404 Not found\r\n\r\n"; static char answer503[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n"; +/* always returns 0 */ static int directory_handle_command_get(connection_t *conn, char *headers, char *body) { size_t dlen; const char *cp; + char *url; - /* XXX should check url and http version */ log_fn(LOG_DEBUG,"Received GET command."); - dlen = dirserv_get_directory(&cp); + conn->state = DIR_CONN_STATE_SERVER_WRITING; - if(dlen == 0) { - log_fn(LOG_WARN,"My directory is empty. Closing."); - connection_write_to_buf(answer503, strlen(answer503), conn); - conn->state = DIR_CONN_STATE_SERVER_WRITING; + if (parse_http_url(headers, &url) < 0) { + connection_write_to_buf(answer400, strlen(answer400), conn); return 0; } - log_fn(LOG_DEBUG,"Dumping directory to client."); - connection_write_to_buf(answer200, strlen(answer200), conn); - connection_write_to_buf(cp, dlen, conn); - conn->state = DIR_CONN_STATE_SERVER_WRITING; + if(!strcmp(url,"/")) { /* directory fetch */ + dlen = dirserv_get_directory(&cp); + + if(dlen == 0) { + log_fn(LOG_WARN,"My directory is empty. Closing."); + connection_write_to_buf(answer503, strlen(answer503), conn); + return 0; + } + + log_fn(LOG_DEBUG,"Dumping directory to client."); + connection_write_to_buf(answer200, strlen(answer200), conn); + connection_write_to_buf(cp, dlen, conn); + return 0; + } + + if(!strncmp(url,"/hidserv/",9)) { /* hidserv descriptor fetch */ + /* ask back-end for the hidden-services descriptor in + * url+9, and return it with a 200 if valid, or give a 404 + * otherwise + */ + + } + + /* we didn't recognize the url */ + connection_write_to_buf(answer404, strlen(answer404), conn); return 0; } +/* always returns 0 */ static int directory_handle_command_post(connection_t *conn, char *headers, char *body) { const char *cp; + char *url; - /* XXX should check url and http version */ log_fn(LOG_DEBUG,"Received POST command."); - cp = body; - switch(dirserv_add_descriptor(&cp)) { - case -1: - /* malformed descriptor, 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; - } + conn->state = DIR_CONN_STATE_SERVER_WRITING; + + if (parse_http_url(headers, &url) < 0) { + connection_write_to_buf(answer400, strlen(answer400), conn); + return 0; + } + + if(!strcmp(url,"/")) { /* server descriptor post */ + cp = body; + switch(dirserv_add_descriptor(&cp)) { + case -1: + /* malformed descriptor, 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; + } + } + + if(!strncmp(url,"/hidserv/",9)) { /* hidserv descriptor post */ + /* pass 'body' to the backend */ + /* return 400, 403, or 200 as appropriate */ + + } + + /* we didn't recognize the url */ + connection_write_to_buf(answer404, strlen(answer404), conn); return 0; } @@ -312,8 +420,7 @@ int connection_dir_finished_flushing(connection_t *conn) { assert(conn && conn->type == CONN_TYPE_DIR); switch(conn->state) { - case DIR_CONN_STATE_CONNECTING_FETCH: - case DIR_CONN_STATE_CONNECTING_UPLOAD: + case DIR_CONN_STATE_CONNECTING: if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */ if(!ERRNO_CONN_EINPROGRESS(errno)) { log_fn(LOG_DEBUG,"in-progress connect failed. Removing."); @@ -329,16 +436,12 @@ int connection_dir_finished_flushing(connection_t *conn) { log_fn(LOG_INFO,"Dir connection to router %s:%u established.", conn->address,conn->port); - return directory_send_command(conn, conn->state); - case DIR_CONN_STATE_CLIENT_SENDING_FETCH: - log_fn(LOG_DEBUG,"client finished sending fetch command."); - conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH; - connection_watch_events(conn, POLLIN); + conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */ return 0; - case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD: - log_fn(LOG_DEBUG,"client finished sending upload command."); - conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD; - connection_watch_events(conn, POLLIN); + case DIR_CONN_STATE_CLIENT_SENDING: + log_fn(LOG_DEBUG,"client finished sending command."); + conn->state = DIR_CONN_STATE_CLIENT_READING; + connection_stop_writing(conn); return 0; case DIR_CONN_STATE_SERVER_WRITING: log_fn(LOG_INFO,"Finished writing server response. Closing."); |