From 01f6727306c784f790fcf50077007a8b958c4e03 Mon Sep 17 00:00:00 2001 From: Roger Dingledine Date: Thu, 26 Sep 2002 12:09:10 +0000 Subject: [PATCH] directory servers in and functional proxies now periodically pull down an hourly-updated directory, and replace their router list with it if it parses correctly. svn:r112 --- src/or/Makefile.am | 2 +- src/or/config.c | 47 ++++++-- src/or/connection.c | 43 ++++--- src/or/connection_exit.c | 4 +- src/or/connection_or.c | 2 +- src/or/directory.c | 250 +++++++++++++++++++++++++++++++++++++++ src/or/main.c | 139 +++++++++++++--------- src/or/or.h | 43 +++++-- src/or/routers.c | 137 +++++++++++++++++---- 9 files changed, 559 insertions(+), 108 deletions(-) create mode 100644 src/or/directory.c diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 0bf5f4a787..44127d873b 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -9,7 +9,7 @@ or_LDADD = -L../common -lor or_SOURCES = buffers.c cell.c circuit.c command.c connection.c \ connection_exit.c connection_ap.c connection_op.c connection_or.c config.c \ - main.c onion.c routers.c + main.c onion.c routers.c directory.c test_config_SOURCES = test_config.c diff --git a/src/or/config.c b/src/or/config.c index 454cd117a8..3f6dcd26a0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -85,6 +85,8 @@ RETURN VALUE: 0 on success, non-zero on error 0, "onion proxy port", "" }, { "ORPort", 'p', POPT_ARG_INT, &options->ORPort, 0, "onion router port", "" }, + { "DirPort", 'd', POPT_ARG_INT, &options->DirPort, + 0, "directory server port", "" }, { "PrivateKeyFile", 'k', POPT_ARG_STRING, &options->PrivateKeyFile, 0, "maximum number of incoming connections", "" }, { "RouterFile", 'r', POPT_ARG_STRING, &options->RouterFile, @@ -92,8 +94,14 @@ RETURN VALUE: 0 on success, non-zero on error { "TrafficShaping", 't', POPT_ARG_INT, &options->TrafficShaping, 0, "which traffic shaping policy to use", "" }, { "LinkPadding", 'P', POPT_ARG_INT, &options->LinkPadding, - 0, "whether to use link padding", "" }, - { "Role", 'g', POPT_ARG_INT, &options->Role, + 0, "whether to use link padding", "" }, + { "DirRebuildPeriod",'D', POPT_ARG_INT, &options->DirRebuildPeriod, + 0, "how many seconds between directory rebuilds", "" }, + { "DirFetchPeriod", 'F', POPT_ARG_INT, &options->DirFetchPeriod, + 0, "how many seconds between directory fetches", "" }, +// { "ReconnectPeriod", 'e', POPT_ARG_INT, &options->ReconnectPeriod, +// 0, "how many seconds between retrying all OR connections", "" }, + { "Role", 'R', POPT_ARG_INT, &options->Role, 0, "4-bit global role id", "" }, { "Verbose", 'v', POPT_ARG_NONE, &Verbose, 0, "display options selected before execution", NULL }, @@ -112,7 +120,11 @@ RETURN VALUE: 0 on success, non-zero on error options->loglevel = LOG_DEBUG; options->CoinWeight = 0.8; options->LinkPadding = 0; - options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN; + options->DirRebuildPeriod = 600; + options->DirFetchPeriod = 6000; +// options->ReconnectPeriod = 6001; + options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN | + ROLE_DIR_LISTEN | ROLE_DIR_SERVER; code = poptGetNextOpt(optCon); /* first we handle command-line args */ if ( code == -1 ) @@ -151,14 +163,17 @@ RETURN VALUE: 0 on success, non-zero on error printf("RouterFile=%s, PrivateKeyFile=%s\n", options->RouterFile, options->PrivateKeyFile); - printf("ORPort=%d, OPPort=%d, APPort=%d\n", + printf("ORPort=%d, OPPort=%d, APPort=%d DirPort=%d\n", options->ORPort,options->OPPort, - options->APPort); + options->APPort,options->DirPort); printf("CoinWeight=%6.4f, MaxConn=%d, TrafficShaping=%d, LinkPadding=%d\n", options->CoinWeight, options->MaxConn, options->TrafficShaping, options->LinkPadding); + printf("DirRebuildPeriod=%d, DirFetchPeriod=%d\n", + options->DirRebuildPeriod, + options->DirFetchPeriod); } /* Validate options */ @@ -188,9 +203,9 @@ RETURN VALUE: 0 on success, non-zero on error } } - if ( options->Role < 0 || options->Role > 15 ) + if ( options->Role < 0 || options->Role > 63 ) { - log(LOG_ERR,"Role option must be an integer between 0 and 15 (inclusive)."); + log(LOG_ERR,"Role option must be an integer between 0 and 63 (inclusive)."); code = -1; } @@ -224,6 +239,12 @@ RETURN VALUE: 0 on success, non-zero on error code = -1; } + if ( (options->Role & ROLE_DIR_LISTEN) && options->DirPort < 1 ) + { + log(LOG_ERR,"DirPort option required and must be a positive integer value."); + code = -1; + } + if ( (options->Role & ROLE_AP_LISTEN) && (options->CoinWeight < 0.0 || options->CoinWeight >= 1.0) ) { @@ -255,6 +276,18 @@ RETURN VALUE: 0 on success, non-zero on error code = -1; } + if ( options->DirRebuildPeriod < 1) + { + log(LOG_ERR,"DirRebuildPeriod option must be positive."); + code = -1; + } + + if ( options->DirFetchPeriod < 1) + { + log(LOG_ERR,"DirFetchPeriod option must be positive."); + code = -1; + } + return code; } diff --git a/src/or/connection.c b/src/or/connection.c index 4eeeae0208..8403011498 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -16,10 +16,12 @@ char *conn_type_to_string[] = { "OR", /* 4 */ "Exit", /* 5 */ "App listener",/* 6 */ - "App" /* 7 */ + "App", /* 7 */ + "Dir listener",/* 8 */ + "Dir", /* 9 */ }; -char *conn_state_to_string[][10] = { +char *conn_state_to_string[][15] = { { }, /* no type associated with 0 */ { "ready" }, /* op listener, 0 */ { "awaiting keys", /* op, 0 */ @@ -43,7 +45,13 @@ char *conn_state_to_string[][10] = { { "ready" }, /* app listener, 0 */ { "awaiting dest info", /* app, 0 */ "waiting for OR connection", /* 1 */ - "open" } /* 2 */ + "open" }, /* 2 */ + { "ready" }, /* dir listener, 0 */ + { "connecting", /* 0 */ + "sending command", /* 1 */ + "reading", /* 2 */ + "awaiting command", /* 3 */ + "writing" }, /* 4 */ }; /********* END VARIABLES ************/ @@ -261,27 +269,20 @@ static int learn_local(struct sockaddr_in *local) { return 0; } -int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len, - crypto_pk_env_t *prkey, uint16_t or_listenport, uint16_t op_listenport, uint16_t ap_listenport) { +int retry_all_connections(int role, crypto_pk_env_t *prkey, uint16_t or_listenport, + uint16_t op_listenport, uint16_t ap_listenport, uint16_t dir_listenport) { /* start all connections that should be up but aren't */ - routerinfo_t *router; - int i; struct sockaddr_in local; /* local address */ if(learn_local(&local) < 0) return -1; local.sin_port = htons(or_listenport); + if(role & ROLE_OR_CONNECT_ALL) { - for (i=0;iaddr,router->or_port)) { /* not in the list */ - log(LOG_DEBUG,"retry_all_connections(): connecting to OR %s:%u.",router->address,router->or_port); - connection_or_connect_as_or(router, prkey, &local); - } - } + router_retry_connections(prkey, &local); } if(role & ROLE_OR_LISTEN) { @@ -303,6 +304,13 @@ int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len, connection_ap_create_listener(NULL, &local); /* no need to tell it the private key. */ } } + + if(role & ROLE_DIR_LISTEN) { + local.sin_port = htons(dir_listenport); + if(!connection_get_by_type(CONN_TYPE_DIR_LISTENER)) { + connection_dir_create_listener(NULL, &local); /* no need to tell it the private key. */ + } + } return 0; } @@ -415,7 +423,8 @@ int connection_speaks_cells(connection_t *conn) { int connection_is_listener(connection_t *conn) { if(conn->type == CONN_TYPE_OP_LISTENER || conn->type == CONN_TYPE_OR_LISTENER || - conn->type == CONN_TYPE_AP_LISTENER) + conn->type == CONN_TYPE_AP_LISTENER || + conn->type == CONN_TYPE_DIR_LISTENER) return 1; return 0; } @@ -586,6 +595,8 @@ int connection_process_inbuf(connection_t *conn) { return connection_exit_process_inbuf(conn); case CONN_TYPE_AP: return connection_ap_process_inbuf(conn); + case CONN_TYPE_DIR: + return connection_dir_process_inbuf(conn); default: log(LOG_DEBUG,"connection_process_inbuf() got unexpected conn->type."); return -1; @@ -709,6 +720,8 @@ int connection_finished_flushing(connection_t *conn) { return connection_or_finished_flushing(conn); case CONN_TYPE_EXIT: return connection_exit_finished_flushing(conn); + case CONN_TYPE_DIR: + return connection_dir_finished_flushing(conn); default: log(LOG_DEBUG,"connection_finished_flushing() got unexpected conn->type."); return -1; diff --git a/src/or/connection_exit.c b/src/or/connection_exit.c index c087ebf719..5f79b64e25 100644 --- a/src/or/connection_exit.c +++ b/src/or/connection_exit.c @@ -179,8 +179,8 @@ int connection_exit_process_data_cell(cell_t *cell, connection_t *conn) { /* also, deliver a 'connected' cell back through the circuit. */ return connection_exit_send_connected(conn); } else { - log(LOG_DEBUG,"connection_exit_process_data_cell(): in connecting_wait, but I've already received everything. Closing."); - return -1; + log(LOG_DEBUG,"connection_exit_process_data_cell(): in connecting_wait, but I've already received everything. Closing."); + return -1; } return 0; case EXIT_CONN_STATE_CONNECTING: diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 53bf38837b..08423f13eb 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -66,7 +66,7 @@ int connection_or_finished_flushing(connection_t *conn) { if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */ if(errno != EINPROGRESS){ /* yuck. kill it. */ - log(LOG_DEBUG,"connection_or_finished_flushing(): in-progress connect failed. Removing."); + log(LOG_DEBUG,"connection_or_finished_flushing(): in-progress connect failed. Removing."); return -1; } else { return 0; /* no change, see if next time is better */ diff --git a/src/or/directory.c b/src/or/directory.c new file mode 100644 index 0000000000..be0f6cb1ee --- /dev/null +++ b/src/or/directory.c @@ -0,0 +1,250 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfaêfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +#define MAX_DIR_SIZE 50000 /* XXX, big enough? */ + +/********* START VARIABLES **********/ + +extern or_options_t options; /* command-line and config-file options */ + +static char the_directory[MAX_DIR_SIZE+1]; +static int directorylen=0; + +static char getstring[] = "GET / HTTP/1.0\n\r"; + +/********* END VARIABLES ************/ + +void directory_initiate_fetch(routerinfo_t *router) { + connection_t *conn; + struct sockaddr_in router_addr; + int s; + + log(LOG_DEBUG,"directory_initiate_fetch(): initiating directory fetch"); + + if(!router) /* i guess they didn't have one in mind for me to use */ + return; + + conn = connection_new(CONN_TYPE_DIR); + if(!conn) + return; + + /* set up conn so it's got all the data we need to remember */ + conn->addr = router->addr, conn->port = router->or_port; /* NOTE we store or_port here always */ + conn->address = strdup(router->address); + conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */ + conn->bandwidth = -1; + + s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) + { + log(LOG_ERR,"directory_initiate_fetch(): Error creating network socket."); + connection_free(conn); + return; + } + fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */ + + memset((void *)&router_addr,0,sizeof(router_addr)); + router_addr.sin_family = AF_INET; + router_addr.sin_port = htons(router->dir_port); + router_addr.sin_addr.s_addr = router->addr; + + log(LOG_DEBUG,"directory_initiate_fetch(): Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),router->dir_port); + + if(connect(s,(struct sockaddr *)&router_addr,sizeof(router_addr)) < 0){ + if(errno != EINPROGRESS){ + /* yuck. kill it. */ + connection_free(conn); + return; + } else { + /* it's in progress. set state appropriately and return. */ + conn->s = s; + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return; + } + + log(LOG_DEBUG,"directory_initiate_fetch(): connect in progress."); + connection_watch_events(conn, POLLIN | POLLOUT); /* writable indicates finish, readable indicates broken link */ + conn->state = DIR_CONN_STATE_CONNECTING; + return; + } + } + + /* it succeeded. we're connected. */ + conn->s = s; + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return; + } + + log(LOG_DEBUG,"directory_initiate_fetch(): Connection to router %s:%u established.",router->address,router->dir_port); + + if(directory_send_command(conn) < 0) { + connection_remove(conn); + connection_free(conn); + } +} + +int directory_send_command(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_DIR); + + if(connection_write_to_buf(getstring, strlen(getstring), conn) < 0) { + log(LOG_DEBUG,"directory_send_command(): Couldn't write command to buffer."); + return -1; + } + + conn->state = DIR_CONN_STATE_SENDING_COMMAND; + return 0; +} + +void directory_rebuild(void) { + + dump_directory_to_string(the_directory, MAX_DIR_SIZE); + log(LOG_DEBUG,"New directory:\n'%s'",the_directory); + directorylen = strlen(the_directory); + +} + +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_READING) { + log(LOG_DEBUG,"connection_dir_process_inbuf(): conn reached eof, not reading. Closing."); + return -1; + } + /* eof reached, kill it, but first process the_directory and learn about new routers. */ + log(LOG_DEBUG,"connection_dir_process_inbuf(): conn reached eof. Processing directory."); + log(LOG_DEBUG,"connection_dir_process_inbuf(): Received directory (size %d) '%s'", directorylen, the_directory); + if(directorylen == 0) { + log(LOG_DEBUG,"connection_dir_process_inbuf(): Empty directory. Ignoring."); + return -1; + } + if(router_get_list_from_string(the_directory, options.ORPort) < 0) { + log(LOG_DEBUG,"connection_dir_process_inbuf(): ...but parsing failed. Ignoring."); + } + return -1; + } + + switch(conn->state) { + case DIR_CONN_STATE_COMMAND_WAIT: + return directory_handle_command(conn); + case DIR_CONN_STATE_READING: + return directory_handle_reading(conn); + default: + log(LOG_DEBUG,"connection_dir_process_inbuf(): Got data while writing; Ignoring."); + break; + } + + return 0; +} + +int directory_handle_command(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_DIR); + + if(conn->inbuf_datalen < strlen(getstring)) { /* entire response available? */ + log(LOG_DEBUG,"directory_handle_command(): Entire command not here yet. Waiting."); + return 0; /* not yet */ + } + + /* for now, don't bother reading it. */ + + if(directorylen == 0) { + log(LOG_DEBUG,"directory_handle_command(): My directory is empty. Closing."); + return -1; + } + + log(LOG_DEBUG,"directory_handle_command(): Dumping directory to client."); + if(connection_write_to_buf(the_directory, directorylen, conn) < 0) { + log(LOG_DEBUG,"directory_handle_command(): my outbuf is full. Oops."); + return -1; + } + + conn->state = DIR_CONN_STATE_WRITING; + return 0; +} + +int directory_handle_reading(connection_t *conn) { + int amt; + + assert(conn && conn->type == CONN_TYPE_DIR); + + amt = conn->inbuf_datalen; + + if(amt + directorylen >= MAX_DIR_SIZE) { + log(LOG_DEBUG,"directory_handle_reading(): Directory too large. Failing messily."); + return -1; + } + + log(LOG_DEBUG,"directory_handle_reading(): Pulling %d bytes in at offset %d.", + amt, directorylen); + + if(connection_fetch_from_buf(the_directory+directorylen,amt,conn) < 0) { + log(LOG_DEBUG,"directory_handle_reading(): fetch_from_buf failed."); + return -1; + } + + directorylen += amt; + + the_directory[directorylen] = 0; + + return 0; +} + +int connection_dir_finished_flushing(connection_t *conn) { + int e, len=sizeof(e); + + assert(conn && conn->type == CONN_TYPE_DIR); + + switch(conn->state) { + case DIR_CONN_STATE_CONNECTING: + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */ + if(errno != EINPROGRESS){ + /* yuck. kill it. */ + log(LOG_DEBUG,"connection_dir_finished_flushing(): in-progress connect failed. Removing."); + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* the connect has finished. */ + + log(LOG_DEBUG,"connection_dir_finished_flushing(): DIR connection to router %s:%u established.", + conn->address,conn->port); + + return directory_send_command(conn); + case DIR_CONN_STATE_SENDING_COMMAND: + log(LOG_DEBUG,"connection_dir_finished_flushing(): client finished sending command."); + directorylen = 0; + conn->state = DIR_CONN_STATE_READING; + connection_watch_events(conn, POLLIN); + return 0; + case DIR_CONN_STATE_WRITING: + log(LOG_DEBUG,"connection_dir_finished_flushing(): Finished writing directory. Closing."); + return -1; /* kill it */ + default: + log(LOG_DEBUG,"Bug: connection_dir_finished_flushing() called in unexpected state."); + return 0; + } + + return 0; +} + +int connection_dir_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local) { + log(LOG_DEBUG,"connection_create_dir_listener starting"); + return connection_create_listener(prkey, local, CONN_TYPE_DIR_LISTENER); +} + +int connection_dir_handle_listener_read(connection_t *conn) { + log(LOG_NOTICE,"Dir: Received a connection request. Waiting for command."); + return connection_handle_listener_read(conn, CONN_TYPE_DIR, DIR_CONN_STATE_COMMAND_WAIT); +} + diff --git a/src/or/main.c b/src/or/main.c index ccf98f6551..7a1fbc39f8 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -21,10 +21,6 @@ static int please_dumpstats=0; /* whether we should dump stats during the loop * /* private key */ static crypto_pk_env_t *prkey; -/* router array */ -static routerinfo_t **router_array = NULL; -static int rarray_len = 0; - /********* END VARIABLES ************/ /**************************************************************************** @@ -154,44 +150,6 @@ connection_t *connection_get_by_type(int type) { -/* the next 4 functions should move to routers.c once we get it - * cleaned up more. The router_array and rarray_len variables should - * move there too. - */ - -routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { - int i; - routerinfo_t *router; - - assert(router_array); - - for(i=0;iaddr == addr) && (router->or_port == port)) - return router; - } - - return NULL; -} - -routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen) { - return router_array[route[routelen-1]]; -} - -/* a wrapper around new_route. put all these in routers.c perhaps? */ -unsigned int *router_new_route(int *routelen) { - return new_route(options.CoinWeight, router_array, rarray_len, routelen); -} - -/* a wrapper around create_onion */ -unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath) { - return create_onion(router_array,rarray_len,route,routelen,len,cpath); -} - - - - - /* FIXME can we cut this function out? */ connection_t *connect_to_router_as_op(routerinfo_t *router) { return connection_connect_to_router_as_op(router, options.ORPort); @@ -252,12 +210,13 @@ void check_conn_read(int i) { retval = connection_or_handle_listener_read(conn); } else if (conn->type == CONN_TYPE_AP_LISTENER) { retval = connection_ap_handle_listener_read(conn); + } else if (conn->type == CONN_TYPE_DIR_LISTENER) { + retval = connection_dir_handle_listener_read(conn); } else { - /* else it's an OP, OR, or exit */ retval = connection_read_to_buf(conn); if (retval >= 0) { /* all still well */ retval = connection_process_inbuf(conn); -// log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval); +// log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval); if(retval >= 0 && !connection_state_is_open(conn) && conn->receiver_bucket == 0) { log(LOG_DEBUG,"check_conn_read(): receiver bucket reached 0 before handshake finished. Closing."); retval = -1; @@ -286,8 +245,7 @@ void check_conn_write(int i) { conn = connection_array[i]; // log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s); - if(conn->type == CONN_TYPE_OP_LISTENER || - conn->type == CONN_TYPE_OR_LISTENER) { + if(connection_is_listener(conn)) { log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!"); retval = -1; } else { @@ -337,6 +295,8 @@ int prepare_for_poll(int *timeout) { connection_t *tmpconn; struct timeval now, soonest; static int current_second = 0; /* from previous calls to gettimeofday */ + static int time_to_rebuild_directory = 0; + static int time_to_fetch_directory = 0; int ms_until_conn; *timeout = -1; /* set it to never timeout, possibly overridden below */ @@ -352,6 +312,34 @@ int prepare_for_poll(int *timeout) { if(gettimeofday(&now,NULL) < 0) return -1; + if(options.Role & ROLE_DIR_SERVER) { + if(time_to_rebuild_directory < now.tv_sec) { + /* it's time to rebuild our directory */ + if(time_to_rebuild_directory == 0) { + /* we just started up. if we build a directory now it will be meaningless. */ + log(LOG_DEBUG,"prepare_for_poll(): Delaying initial dir build for 15 seconds."); + time_to_rebuild_directory = now.tv_sec + 15; /* try in 15 seconds */ + } else { + directory_rebuild(); + time_to_rebuild_directory = now.tv_sec + options.DirRebuildPeriod; + } + } + *timeout = 1000*(time_to_rebuild_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000)); +// log(LOG_DEBUG,"prepare_for_poll(): DirBuild timeout is %d",*timeout); + } + + if(!(options.Role & ROLE_DIR_SERVER)) { + if(time_to_fetch_directory < now.tv_sec) { + /* it's time to fetch a new directory */ + /* NOTE directory servers do not currently fetch directories. + * Hope this doesn't bite us later. + */ + directory_initiate_fetch(router_pick_directory_server()); + time_to_fetch_directory = now.tv_sec + options.DirFetchPeriod; + } + *timeout = 1000*(time_to_fetch_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000)); + } + if(need_to_refill_buckets) { if(now.tv_sec > current_second) { /* the second has already rolled over! */ // log(LOG_DEBUG,"prepare_for_poll(): The second has rolled over, immediately refilling."); @@ -359,8 +347,8 @@ int prepare_for_poll(int *timeout) { connection_increment_receiver_bucket(connection_array[i]); current_second = now.tv_sec; /* remember which second it is, for next time */ } + /* this timeout is definitely sooner than either of the above two */ *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */ -// log(LOG_DEBUG,"prepare_for_poll(): %d milliseconds til next second.",*timeout); } if(options.LinkPadding) { @@ -406,9 +394,7 @@ int do_main_loop(void) { int poll_result; /* load the routers file */ - router_array = router_get_list_from_file(options.RouterFile,&rarray_len, options.ORPort); - if (!router_array) - { + if(router_get_list_from_file(options.RouterFile, options.ORPort) < 0) { log(LOG_ERR,"Error loading router list."); return -1; } @@ -429,8 +415,8 @@ int do_main_loop(void) { /* start-up the necessary connections based on global_role. This is where we * try to connect to all the other ORs, and start the listeners */ - retry_all_connections(options.Role, router_array, rarray_len, prkey, - options.ORPort, options.OPPort, options.APPort); + retry_all_connections(options.Role, prkey, options.ORPort, + options.OPPort, options.APPort, options.DirPort); for(;;) { if(please_dumpstats) { @@ -496,7 +482,7 @@ void dumpstats (void) { /* dump stats to stdout */ int i; connection_t *conn; extern char *conn_type_to_string[]; - extern char *conn_state_to_string[][10]; + extern char *conn_state_to_string[][15]; printf("Dumping stats:\n"); @@ -515,6 +501,53 @@ void dumpstats (void) { /* dump stats to stdout */ please_dumpstats = 0; } +void dump_directory_to_string(char *s, int maxlen) { + int i; + connection_t *conn; + char *pkey; + int pkeylen; + int written; + routerinfo_t *router; + + for(i=0;itype != CONN_TYPE_OR) + continue; /* we only want to list ORs */ + router = router_get_by_addr_port(conn->addr,conn->port); + if(!router) { + log(LOG_ERR,"dump_directory_to_string(): couldn't find router %d:%d!",conn->addr,conn->port); + return; + } + if(crypto_pk_write_public_key_to_string(router->pkey,&pkey,&pkeylen)<0) { + log(LOG_ERR,"dump_directory_to_string(): write pkey to string failed!"); + return; + } + written = snprintf(s, maxlen, "%s %d %d %d %d %d\n%s\n", + router->address, + router->or_port, + router->op_port, + router->ap_port, + router->dir_port, + router->bandwidth, + pkey); + + free(pkey); + + if(written < 0 || written > maxlen) { + /* apparently different glibcs do different things on error.. so check both */ + log(LOG_ERR,"dump_directory_to_string(): tried to exceed string length."); + s[maxlen-1] = 0; /* make sure it's null terminated */ + return; + } + + maxlen -= written; + s += written; + + } + +} + int main(int argc, char *argv[]) { int retval = 0; diff --git a/src/or/or.h b/src/or/or.h index dc39956c19..e417cc9de7 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -58,6 +58,8 @@ #define ROLE_OR_CONNECT_ALL 2 #define ROLE_OP_LISTEN 4 #define ROLE_AP_LISTEN 8 +#define ROLE_DIR_LISTEN 16 +#define ROLE_DIR_SERVER 32 #define ROLE_IS_OR(role) ((role & ROLE_OR_LISTEN) || (role & ROLE_OR_CONNECT_ALL) || (role & ROLE_OP_LISTEN)) @@ -68,6 +70,8 @@ #define CONN_TYPE_EXIT 5 #define CONN_TYPE_AP_LISTENER 6 #define CONN_TYPE_AP 7 +#define CONN_TYPE_DIR_LISTENER 8 +#define CONN_TYPE_DIR 9 #define LISTENER_STATE_READY 0 @@ -105,6 +109,12 @@ #define AP_CONN_STATE_OR_WAIT 1 #define AP_CONN_STATE_OPEN 2 +#define DIR_CONN_STATE_CONNECTING 0 +#define DIR_CONN_STATE_SENDING_COMMAND 1 +#define DIR_CONN_STATE_READING 2 +#define DIR_CONN_STATE_COMMAND_WAIT 3 +#define DIR_CONN_STATE_WRITING 4 + #define CIRCUIT_STATE_OPEN_WAIT 0 /* receiving/processing the onion */ #define CIRCUIT_STATE_OR_WAIT 1 /* I'm at the beginning of the path, my firsthop is still connecting */ #define CIRCUIT_STATE_OPEN 2 /* onion processed, ready to send data along the connection */ @@ -342,9 +352,12 @@ typedef struct int ORPort; int OPPort; int APPort; + int DirPort; int MaxConn; int TrafficShaping; int LinkPadding; + int DirRebuildPeriod; + int DirFetchPeriod; int Role; int loglevel; } or_options_t; @@ -441,8 +454,8 @@ int connection_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local int connection_handle_listener_read(connection_t *conn, int new_type, int new_state); /* start all connections that should be up but aren't */ -int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len, - crypto_pk_env_t *prkey, uint16_t or_port, uint16_t op_port, uint16_t ap_port); +int retry_all_connections(int role, crypto_pk_env_t *prkey, uint16_t or_listenport, + uint16_t op_listenport, uint16_t ap_listenport, uint16_t dir_listenport); connection_t *connection_connect_to_router_as_op(routerinfo_t *router, uint16_t local_or_port); int connection_read_to_buf(connection_t *conn); @@ -548,6 +561,18 @@ connection_t *connection_or_connect_as_op(routerinfo_t *router, struct sockaddr_ int connection_or_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local); int connection_or_handle_listener_read(connection_t *conn); +/********************************* directory.c ***************************/ + +void directory_initiate_fetch(routerinfo_t *router); +int directory_send_command(connection_t *conn); +void directory_rebuild(void); +int connection_dir_process_inbuf(connection_t *conn); +int directory_handle_command(connection_t *conn); +int directory_handle_reading(connection_t *conn); +int connection_dir_finished_flushing(connection_t *conn); +int connection_dir_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local); +int connection_dir_handle_listener_read(connection_t *conn); + /********************************* main.c ***************************/ int connection_add(connection_t *conn); @@ -559,10 +584,6 @@ connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port); connection_t *connection_get_by_type(int type); -routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); -unsigned int *router_new_route(int *routelen); -unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath); -routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen); connection_t *connect_to_router_as_op(routerinfo_t *router); void connection_watch_events(connection_t *conn, short events); @@ -582,6 +603,7 @@ int do_main_loop(void); void catchint(); void catchusr1(); void dumpstats(void); +void dump_directory_to_string(char *s, int maxlen); int main(int argc, char *argv[]); @@ -625,6 +647,13 @@ tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, track /********************************* routers.c ***************************/ -routerinfo_t **router_get_list_from_file(char *routerfile, int *len, uint16_t or_listenport); +void router_retry_connections(crypto_pk_env_t *prkey, struct sockaddr_in *local); +routerinfo_t *router_pick_directory_server(void); +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); +unsigned int *router_new_route(int *routelen); +unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath); +routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen); +int router_get_list_from_file(char *routerfile, uint16_t or_listenport); +int router_get_list_from_string(char *s, uint16_t or_listenport); #endif diff --git a/src/or/routers.c b/src/or/routers.c index 4f9b0846ea..b7984fe593 100644 --- a/src/or/routers.c +++ b/src/or/routers.c @@ -13,7 +13,16 @@ #include "or.h" +/****************************************************************************/ + +/* router array */ +static routerinfo_t **router_array = NULL; +static int rarray_len = 0; + extern int global_role; /* from main.c */ +extern or_options_t options; /* command-line and config-file options */ + +/****************************************************************************/ /* static function prototypes */ static int router_is_me(uint32_t or_address, uint16_t or_listenport, uint16_t my_or_listenport); @@ -23,6 +32,67 @@ static char *eat_whitespace(char *s); static char *find_whitespace(char *s); static routerinfo_t *router_get_entry_from_string(char **s); +/****************************************************************************/ + +void router_retry_connections(crypto_pk_env_t *prkey, struct sockaddr_in *local) { + int i; + routerinfo_t *router; + + for (i=0;iaddr,router->or_port)) { /* not in the list */ + log(LOG_DEBUG,"retry_all_connections(): connecting to OR %s:%u.",router->address,router->or_port); + connection_or_connect_as_or(router, prkey, local); + } + } +} + +routerinfo_t *router_pick_directory_server(void) { + /* currently, pick the first router with a positive dir_port */ + int i; + routerinfo_t *router; + + if(!router_array) + return NULL; + + for(i=0;idir_port > 0) + return router; + } + + return NULL; +} + +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { + int i; + routerinfo_t *router; + + assert(router_array); + + for(i=0;iaddr == addr) && (router->or_port == port)) + return router; + } + + return NULL; +} + +routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen) { + return router_array[route[routelen-1]]; +} + +/* a wrapper around new_route. put all these in routers.c perhaps? */ +unsigned int *router_new_route(int *routelen) { + return new_route(options.CoinWeight, router_array, rarray_len, routelen); +} + +/* a wrapper around create_onion */ +unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath) { + return create_onion(router_array,rarray_len,route,routelen,len,cpath); +} + /* private function, to determine whether the current entry in the router list is actually us */ static int router_is_me(uint32_t or_address, uint16_t or_listenport, uint16_t my_or_listenport) { @@ -58,8 +128,8 @@ static int router_is_me(uint32_t or_address, uint16_t or_listenport, uint16_t my a = (struct in_addr *)addr; tmp1 = strdup(inet_ntoa(*a)); /* can't call inet_ntoa twice in the same - printf, since it overwrites its static - memory each time */ + printf, since it overwrites its static + memory each time */ log(LOG_DEBUG,"router_is_me(): Comparing '%s' to '%s'.",tmp1, inet_ntoa( *((struct in_addr *)&or_address) ) ); free(tmp1); @@ -151,64 +221,87 @@ static routerinfo_t **make_rarray(routerinfo_t* list, int *len) /* load the router list */ -routerinfo_t **router_get_list_from_file(char *routerfile, int *len, uint16_t or_listenport) +int router_get_list_from_file(char *routerfile, uint16_t or_listenport) { - routerinfo_t *routerlist=NULL; - routerinfo_t *router; int fd; /* router file */ struct stat statbuf; char *string; - char *tmps; - assert(routerfile && len); + assert(routerfile); if (strcspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0) { log(LOG_ERR,"router_get_list_from_file(): Filename %s contains illegal characters.",routerfile); - return NULL; + return -1; } if(stat(routerfile, &statbuf) < 0) { log(LOG_ERR,"router_get_list_from_file(): Could not stat %s.",routerfile); - return NULL; + return -1; } /* open the router list */ fd = open(routerfile,O_RDONLY,0); if (fd<0) { log(LOG_ERR,"router_get_list_from_file(): Could not open %s.",routerfile); - return NULL; + return -1; } string = malloc(statbuf.st_size+1); if(!string) { log(LOG_ERR,"router_get_list_from_file(): Out of memory."); - return NULL; + return -1; } if(read(fd,string,statbuf.st_size) != statbuf.st_size) { log(LOG_ERR,"router_get_list_from_file(): Couldn't read all %d bytes of file '%s'.",statbuf.st_size,routerfile); - return NULL; + free(string); + close(fd); + return -1; } close(fd); string[statbuf.st_size] = 0; /* null terminate it */ - tmps = string; - while(*tmps) { /* while not at the end of the string */ - router = router_get_entry_from_string(&tmps); + + if(router_get_list_from_string(string, or_listenport) < 0) { + log(LOG_ERR,"router_get_list_from_file(): The routerfile itself was corrupt."); + free(string); + return -1; + } + + free(string); + return 0; +} + +int router_get_list_from_string(char *s, uint16_t or_listenport) { + routerinfo_t *routerlist=NULL; + routerinfo_t *router; + routerinfo_t **new_router_array; + int new_rarray_len; + + assert(s); + + while(*s) { /* while not at the end of the string */ + router = router_get_entry_from_string(&s); if(router == NULL) { routerlist_free(routerlist); - free(string); - return NULL; + return -1; } if(!router_is_me(router->addr, router->or_port, or_listenport)) { router->next = routerlist; routerlist = router; } - tmps = eat_whitespace(tmps); + s = eat_whitespace(s); } - free(string); - return make_rarray(routerlist, len); -} + + new_router_array = make_rarray(routerlist, &new_rarray_len); + if(new_router_array) { /* success! replace the old one */ + rarray_free(router_array); /* free the old one first */ + router_array = new_router_array; + rarray_len = new_rarray_len; + return 0; + } + return -1; +} /* return the first char of s that is not whitespace and not a comment */ static char *eat_whitespace(char *s) { @@ -322,7 +415,7 @@ static routerinfo_t *router_get_entry_from_string(char **s) { next = strchr(next, '\n'); assert(next); /* can't fail, we just checked it was here */ *next = 0; - log(LOG_DEBUG,"Key about to be read is: '%s'",*s); +// log(LOG_DEBUG,"Key about to be read is: '%s'",*s); if((crypto_pk_read_public_key_from_string(router->pkey, *s, strlen(*s))<0)) { log(LOG_ERR,"router_get_entry_from_string(): Couldn't read pk from string"); goto router_read_failed;