aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2004-09-08 06:52:33 +0000
committerNick Mathewson <nickm@torproject.org>2004-09-08 06:52:33 +0000
commitc66e4c48704b8be1425c2b6253542beed49ce0eb (patch)
treebab663d3eaff8366181c3dc75ba6a17ec5cfaca1 /src/or
parentb6798866d058cd7ce69fc3c7944aff85a1693170 (diff)
downloadtor-c66e4c48704b8be1425c2b6253542beed49ce0eb.tar
tor-c66e4c48704b8be1425c2b6253542beed49ce0eb.tar.gz
Flush more changes from sandbox
- make clients cache directories and use them to seed their router lists at startup. This means clients have a datadir again. - Introduce a global_write_bucket. We need to respond better to exhausting it. - Remove the last vestiges of LinkPadding and TrafficShaping. - Configuration infrastructure support for warning on obsolete options. - Refactor directory header parsing to use smartlist_split_string. - Respond to content-encoding headers by trying to uncompress as appropriate. - Reply with a deflated directory when a client asks for "dir.z". (We could use allow-encodings instead, but allow-encodings isn't specified in HTTP 1.0.) svn:r2335
Diffstat (limited to 'src/or')
-rw-r--r--src/or/config.c15
-rw-r--r--src/or/connection.c35
-rw-r--r--src/or/directory.c64
-rw-r--r--src/or/dirserv.c16
-rw-r--r--src/or/main.c21
-rw-r--r--src/or/or.h3
-rw-r--r--src/or/routerlist.c39
7 files changed, 137 insertions, 56 deletions
diff --git a/src/or/config.c b/src/or/config.c
index e638dcf67..3e0e0bfb3 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -23,6 +23,7 @@ typedef enum config_type_t {
CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and optional
* whitespace. */
CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */
+ CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */
} config_type_t;
/** Largest allowed config line */
@@ -170,6 +171,9 @@ static int config_compare(struct config_line_t *c, const char *key, config_type_
*(struct config_line_t**)arg =
config_line_prepend(*(struct config_line_t**)arg, c->key, c->value);
break;
+ case CONFIG_TYPE_OBSOLETE:
+ log_fn(LOG_WARN, "Skipping obsolete configuration option '%s'", c->key);
+ break;
}
return 1;
}
@@ -225,7 +229,7 @@ static int config_assign(or_options_t *options, struct config_line_t *list) {
config_compare(list, "LogLevel", CONFIG_TYPE_LINELIST, &options->LogOptions) ||
config_compare(list, "LogFile", CONFIG_TYPE_LINELIST, &options->LogOptions) ||
- config_compare(list, "LinkPadding", CONFIG_TYPE_BOOL, &options->LinkPadding) ||
+ config_compare(list, "LinkPadding", CONFIG_TYPE_OBSOLETE, NULL) ||
config_compare(list, "MaxConn", CONFIG_TYPE_INT, &options->MaxConn) ||
config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
@@ -252,7 +256,7 @@ static int config_assign(or_options_t *options, struct config_line_t *list) {
config_compare(list, "SocksBindAddress",CONFIG_TYPE_LINELIST,&options->SocksBindAddress) ||
config_compare(list, "SocksPolicy", CONFIG_TYPE_LINELIST,&options->SocksPolicy) ||
- config_compare(list, "TrafficShaping", CONFIG_TYPE_BOOL, &options->TrafficShaping) ||
+ config_compare(list, "TrafficShaping", CONFIG_TYPE_OBSOLETE, NULL) ||
config_compare(list, "User", CONFIG_TYPE_STRING, &options->User)
@@ -1024,7 +1028,7 @@ const char *get_data_directory(or_options_t *options) {
const char *d;
if (options->DataDirectory)
d = options->DataDirectory;
- else if (server_mode()) {
+ else {
#ifdef MS_WINDOWS
char *p;
p = tor_malloc(MAX_PATH);
@@ -1037,10 +1041,7 @@ const char *get_data_directory(or_options_t *options) {
#else
d = "~/.tor";
#endif
- } else
- d = NULL; /* XXX008 don't create datadir until we have something
- we'll be putting in it */
-
+ }
if (d && strncmp(d,"~/",2)==0) {
char *fn = expand_filename(d);
tor_free(options->DataDirectory);
diff --git a/src/or/connection.c b/src/or/connection.c
index bb8e762e5..839da4310 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -592,26 +592,22 @@ int retry_all_listeners(void) {
return 0;
}
-extern int global_read_bucket;
+extern int global_read_bucket, global_write_bucket;
/** How many bytes at most can we read onto this connection? */
int connection_bucket_read_limit(connection_t *conn) {
int at_most;
- if(options.LinkPadding) {
- at_most = global_read_bucket;
+ /* do a rudimentary round-robin so one circuit can't hog a connection */
+ if(connection_speaks_cells(conn)) {
+ at_most = 32*(CELL_NETWORK_SIZE);
} else {
- /* do a rudimentary round-robin so one circuit can't hog a connection */
- if(connection_speaks_cells(conn)) {
- at_most = 32*(CELL_NETWORK_SIZE);
- } else {
- at_most = 32*(RELAY_PAYLOAD_SIZE);
- }
-
- if(at_most > global_read_bucket)
- at_most = global_read_bucket;
+ at_most = 32*(RELAY_PAYLOAD_SIZE);
}
+ if(at_most > global_read_bucket)
+ at_most = global_read_bucket;
+
if(connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN)
if(at_most > conn->receiver_bucket)
at_most = conn->receiver_bucket;
@@ -620,7 +616,7 @@ int connection_bucket_read_limit(connection_t *conn) {
}
/** We just read num_read onto conn. Decrement buckets appropriately. */
-void connection_bucket_decrement(connection_t *conn, int num_read) {
+static void connection_read_bucket_decrement(connection_t *conn, int num_read) {
global_read_bucket -= num_read; tor_assert(global_read_bucket >= 0);
if(connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
conn->receiver_bucket -= num_read; tor_assert(conn->receiver_bucket >= 0);
@@ -648,6 +644,7 @@ static struct timeval current_time;
void connection_bucket_init(void) {
tor_gettimeofday(&current_time);
global_read_bucket = options.BandwidthBurst; /* start it at max traffic */
+ global_write_bucket = options.BandwidthBurst; /* start it at max traffic */
}
/** Some time has passed; increment buckets appropriately. */
@@ -662,11 +659,15 @@ void connection_bucket_refill(struct timeval *now) {
current_time.tv_sec = now->tv_sec; /* update current_time */
/* (ignore usecs for now) */
- /* refill the global bucket */
+ /* refill the global buckets */
if(global_read_bucket < options.BandwidthBurst) {
global_read_bucket += options.BandwidthRate;
log_fn(LOG_DEBUG,"global_read_bucket now %d.", global_read_bucket);
}
+ if(global_write_bucket < options.BandwidthBurst) {
+ global_write_bucket += options.BandwidthRate;
+ log_fn(LOG_DEBUG,"global_write_bucket now %d.", global_write_bucket);
+ }
/* refill the per-connection buckets */
get_connection_array(&carray,&n);
@@ -680,6 +681,8 @@ void connection_bucket_refill(struct timeval *now) {
if(conn->wants_to_read == 1 /* it's marked to turn reading back on now */
&& global_read_bucket > 0 /* and we're allowed to read */
+ && global_write_bucket > 0 /* and we're allowed to write (XXXX,
+ * not the best place to check this.) */
&& (!connection_speaks_cells(conn) ||
conn->state != OR_CONN_STATE_OPEN ||
conn->receiver_bucket > 0)) {
@@ -815,7 +818,7 @@ static int connection_read_to_buf(connection_t *conn) {
rep_hist_note_bytes_read(result, time(NULL));
}
- connection_bucket_decrement(conn, result);
+ connection_read_bucket_decrement(conn, result);
return 0;
}
@@ -947,6 +950,8 @@ int connection_handle_write(connection_t *conn) {
rep_hist_note_bytes_written(result, now);
}
+ global_write_bucket -= result;
+
if(!connection_wants_to_flush(conn)) { /* it's done flushing */
if(connection_finished_flushing(conn) < 0) {
/* already marked */
diff --git a/src/or/directory.c b/src/or/directory.c
index 4ca210bc0..e27f61dd3 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -210,6 +210,7 @@ directory_initiate_command(routerinfo_t *router, uint8_t purpose,
static void directory_send_command(connection_t *conn, int purpose,
const char *payload, int payload_len) {
char fetchwholedir[] = "GET / HTTP/1.0\r\n\r\n";
+ char fetchwholedir_z[] = "GET /dir.z HTTP/1.0\r\n\r\n";
char fetchrunninglist[] = "GET /running-routers HTTP/1.0\r\n\r\n";
char tmp[8192];
@@ -288,11 +289,12 @@ parse_http_url(char *headers, char **url)
* Otherwise, return -1.
*/
static int
-parse_http_response(char *headers, int *code, char **message, time_t *date)
+parse_http_response(char *headers, int *code, char **message, time_t *date,
+ int *compression)
{
int n1, n2;
- const char *cp;
char datestr[RFC1123_TIME_LEN+1];
+ smartlist_t *parsed_headers;
tor_assert(headers && code);
while(isspace((int)*headers)) headers++; /* tolerate leading whitespace */
@@ -307,22 +309,40 @@ parse_http_response(char *headers, int *code, char **message, time_t *date)
if(message) {
/* XXX should set *message correctly */
}
+ parsed_headers = smartlist_create();
+ smartlist_split_string(parsed_headers, headers, "\n",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
if (date) {
- cp = headers;
*date = 0;
- while (cp && (cp = strchr(cp, '\n'))) {
- ++cp;
- strlcpy(datestr, cp, 7);
- if (strcmpstart(cp, "Date: ") == 0) {
- strlcpy(datestr, cp+6, sizeof(datestr));
+ SMARTLIST_FOREACH(parsed_headers, const char *, s,
+ if (!strcmpstart(s, "Date: ")) {
+ strlcpy(datestr, s+6, sizeof(datestr));
/* This will do nothing on failure, so we don't need to check
the result. We shouldn't warn, since there are many other valid
date formats besides the one we use. */
parse_rfc1123_time(datestr, date);
break;
- }
+ });
+ }
+ if (compression) {
+ const char *enc = NULL;
+ SMARTLIST_FOREACH(parsed_headers, const char *, s,
+ if (!strcmpstart(s, "Content-Encoding: ")) {
+ enc = s+16; break;
+ });
+ if (!enc || strcmp(enc, "identity")) {
+ *compression = 0;
+ } else if (!strcmp(enc, "deflate") || !strcmp(enc, "x-deflate")) {
+ *compression = ZLIB_METHOD;
+ } else if (!strcmp(enc, "gzip") || !strcmp(enc, "x-gzip")) {
+ *compression = GZIP_METHOD;
+ } else {
+ log_fn(LOG_WARN, "Unrecognized content encoding: '%s'", enc);
+ *compression = 0;
}
}
+ SMARTLIST_FOREACH(parsed_headers, char *, s, tor_free(s));
+ smartlist_free(parsed_headers);
return 0;
}
@@ -342,6 +362,7 @@ connection_dir_client_reached_eof(connection_t *conn)
int status_code;
time_t now, date_header=0;
int delta;
+ int compression;
switch(fetch_from_buf_http(conn->inbuf,
&headers, MAX_HEADERS_SIZE,
@@ -355,7 +376,8 @@ connection_dir_client_reached_eof(connection_t *conn)
/* case 1, fall through */
}
- if(parse_http_response(headers, &status_code, NULL, &date_header) < 0) {
+ if(parse_http_response(headers, &status_code, NULL, &date_header,
+ &compression) < 0) {
log_fn(LOG_WARN,"Unparseable headers. Closing.");
free(body); free(headers);
return -1;
@@ -372,6 +394,19 @@ connection_dir_client_reached_eof(connection_t *conn)
}
}
+ if (compression != 0) {
+ char *new_body;
+ size_t new_len;
+ if (tor_gzip_uncompress(&new_body, &new_len, body, body_len, compression)) {
+ log_fn(LOG_WARN, "Unable to decompress HTTP body.");
+ tor_free(body); tor_free(headers);
+ return -1;
+ }
+ tor_free(body);
+ body = new_body;
+ body_len = (int)new_len;
+ }
+
if(conn->purpose == DIR_PURPOSE_FETCH_DIR) {
/* fetch/process the directory to learn about new routers. */
log_fn(LOG_INFO,"Received directory (size %d):\n%s", body_len, body);
@@ -545,8 +580,8 @@ directory_handle_command_get(connection_t *conn, char *headers,
return 0;
}
- if(!strcmp(url,"/")) { /* directory fetch */
- dlen = dirserv_get_directory(&cp, 0);
+ if(!strcmp(url,"/") || !strcmp(url,"/dir.z")) { /* directory fetch */
+ dlen = dirserv_get_directory(&cp, !strcmp(url,"/dir.z"));
if(dlen == 0) {
log_fn(LOG_WARN,"My directory is empty. Closing.");
@@ -556,9 +591,10 @@ directory_handle_command_get(connection_t *conn, char *headers,
log_fn(LOG_DEBUG,"Dumping directory to client.");
format_rfc1123_time(date, time(NULL));
- snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: text/plain\r\n\r\n",
+ snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: text/plain\r\nContent-Encoding: %s\r\n\r\n",
date,
- (int)dlen);
+ (int)dlen,
+ strcmp(url,"/dir.z")?"identity":"deflate");
connection_write_to_buf(tmp, strlen(tmp), conn);
connection_write_to_buf(cp, strlen(cp), conn);
return 0;
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 984c70288..ba21c0f00 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -630,10 +630,16 @@ void dirserv_set_cached_directory(const char *directory, time_t when)
{
time_t now;
size_t z_len;
+ char filename[512];
tor_assert(!options.AuthoritativeDir);
now = time(NULL);
- if (when>cached_directory_published &&
- when<now+ROUTER_ALLOW_SKEW) {
+ if (when<=cached_directory_published) {
+ log_fn(LOG_INFO, "Ignoring old directory; not caching.");
+ } else if (when>=now+ROUTER_ALLOW_SKEW) {
+ log_fn(LOG_INFO, "Ignoring future directory; not caching.");
+ } if (when>cached_directory_published &&
+ when<now+ROUTER_ALLOW_SKEW) {
+ log_fn(LOG_DEBUG, "Caching directory.");
tor_free(cached_directory);
cached_directory = tor_strdup(directory);
cached_directory_len = strlen(cached_directory);
@@ -644,6 +650,12 @@ void dirserv_set_cached_directory(const char *directory, time_t when)
log_fn(LOG_WARN,"Error compressing cached directory");
}
cached_directory_published = when;
+ if(get_data_directory(&options)) {
+ sprintf(filename,"%s/cached-directory", get_data_directory(&options));
+ if(write_str_to_file(filename,cached_directory) < 0) {
+ log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring.");
+ }
+ }
}
}
diff --git a/src/or/main.c b/src/or/main.c
index 9e558315a..9bdebbbb1 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -21,12 +21,17 @@ extern char *conn_state_to_string[][_CONN_TYPE_MAX+1];
or_options_t options; /**< Command-line and config-file options. */
int global_read_bucket; /**< Max number of bytes I can read this second. */
+int global_write_bucket; /**< Max number of bytes I can write this second. */
/** What was the read bucket before the last call to prepare_for_pool?
* (used to determine how many bytes we've read). */
static int stats_prev_global_read_bucket;
-/** How many bytes have we read since we started the process? */
+/** What was the write bucket before the last call to prepare_for_pool?
+ * (used to determine how many bytes we've written). */
+static int stats_prev_global_write_bucket;
+/** How many bytes have we read/written since we started the process? */
static uint64_t stats_n_bytes_read = 0;
+static uint64_t stats_n_bytes_written = 0;
/** How many seconds have we been running? */
long stats_n_seconds_uptime = 0;
@@ -632,8 +637,10 @@ static int prepare_for_poll(void) {
/* Check how much bandwidth we've consumed, and increment the token
* buckets. */
stats_n_bytes_read += stats_prev_global_read_bucket - global_read_bucket;
+ stats_n_bytes_written += stats_prev_global_write_bucket - global_write_bucket;
connection_bucket_refill(&now);
stats_prev_global_read_bucket = global_read_bucket;
+ stats_prev_global_write_bucket = global_write_bucket;
if(now.tv_sec > current_second) { /* the second has rolled over. check more stuff. */
@@ -698,6 +705,7 @@ static int init_from_config(int argc, char **argv) {
/* Set up our buckets */
connection_bucket_init();
stats_prev_global_read_bucket = global_read_bucket;
+ stats_prev_global_write_bucket = global_write_bucket;
/* Finish backgrounding the process */
if(options.RunAsDaemon) {
@@ -782,15 +790,8 @@ static int do_main_loop(void) {
}
/* load the routers file, or assign the defaults. */
- if(options.RouterFile) {
- routerlist_clear_trusted_directories();
- if (router_load_routerlist_from_file(options.RouterFile, 1) < 0) {
- log_fn(LOG_ERR,"Error loading router list.");
- return -1;
- }
- } else {
- if(config_assign_default_dirservers() < 0)
- return -1;
+ if(router_reload_router_list()) {
+ return -1;
}
if(authdir_mode()) {
diff --git a/src/or/or.h b/src/or/or.h
index ab5ed36bb..c04403aac 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -867,8 +867,6 @@ typedef struct {
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
int ClientOnly; /**< Boolean: should we never evolve into a server role? */
int MaxConn; /**< Maximum number of simultaneous connections. */
- int TrafficShaping; /**< Unused. */
- int LinkPadding; /**< Unused. */
int IgnoreVersion; /**< If true, run no matter what versions of Tor the
* directory recommends. */
int RunAsDaemon; /**< If true, run in the background. (Unix only) */
@@ -1392,6 +1390,7 @@ int is_legal_nickname_or_hexdigest(const char *s);
/********************************* routerlist.c ***************************/
+int router_reload_router_list(void);
routerinfo_t *router_pick_directory_server(int requireauth, int requireothers);
int all_directory_servers_down(void);
struct smartlist_t;
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 4b10b3c90..b7187984b 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -40,6 +40,37 @@ static routerlist_t *routerlist = NULL;
extern int has_fetched_directory; /**< from main.c */
+/**
+ * Reload the original list of trusted dirservers, and the most recent
+ * cached directory (if present).
+ */
+int router_reload_router_list(void)
+{
+ char filename[512];
+ routerlist_clear_trusted_directories();
+ if (options.RouterFile) {
+ if (router_load_routerlist_from_file(options.RouterFile, 1) < 0) {
+ log_fn(LOG_ERR,"Error loading router list.");
+ return -1;
+ }
+ } else {
+ if (config_assign_default_dirservers() < 0)
+ return -1;
+ }
+ if (get_data_directory(&options)) {
+ char *s;
+ sprintf(filename,"%s/cached-directory", get_data_directory(&options));
+ s = read_file_to_str(filename);
+ if (s) {
+ log_fn(LOG_INFO, "Loading cached directory from %s", filename);
+ if (router_load_routerlist_from_string(s, 0) < 0) {
+ log_fn(LOG_WARN, "Cached directory was unparseable; ignoring.");
+ }
+ }
+ }
+ return 0;
+}
+
/** Try to find a running dirserver. If there are no running dirservers
* in our routerlist, set all the authoritative ones as running again,
* and pick one. If there are no dirservers at all in our routerlist,
@@ -63,12 +94,8 @@ routerinfo_t *router_pick_directory_server(int requireauth, int requireothers) {
options.FascistFirewall ? "reachable" : "known");
has_fetched_directory=0; /* reset it */
routerlist_clear_trusted_directories();
- if(options.RouterFile) {
- if(router_load_routerlist_from_file(options.RouterFile, 1) < 0)
- return NULL;
- } else {
- if(config_assign_default_dirservers() < 0)
- return NULL;
+ if(router_reload_router_list()) {
+ return NULL;
}
/* give it one last try */
choice = router_pick_directory_server_impl(requireauth, requireothers, 0);