X Abandoned
0.0.9:
- - the user interface interface
- - let tor clients use http proxies for dir fetching
- - let tor servers use http proxies for port 80 exits
- - write instructions for port-forwarding directives or programs
+ o Fix OutboundBindAddress
+ o Config defaults should be consistant with config file and no
+ config file.
+ o write instructions for port-forwarding directives or programs
to let people run on ports 80 and 443 without needing to bind
tor to them.
- - learn from ben about his openssl-reinitialization-trick to
- rotate tls keys without making new connections.
- - figure out how to handle rendezvousing with unverified nodes.
- - clean up all the comma-separated stuff (eg exit policies) into
+ o clean up all the comma-separated stuff (eg exit policies) into
smartlists.
- - per-month byte allowances.
- - node 'groups' that are known to be in the same zone of control.
- - figure out enclaves, e.g. so we know what to recommend that people
+ o investigate sctp for alternate transport.
+ o Document all undocumented options, or mark them as undocumented
+ in the source.
+ o bandwidth buckets for write as well as read.
+ . Cached-directory changes:
+ o make clients store the cached-directory to disk,
+ o and use it when they startup, so they don't need to bootstrap
+ from the authdirservers every time they start.
+ - also, once we've reduced authdirserver entries to config
+ lines, we can have lines that list cacheddirservers too.
+ . compress the directory.
+ o Implement gzip/zlib wrappers
+ o Compress directories as they're cached/generated
+ o When requested, give a compressed directory.
+ o Decompress incoming HTTP based on Content-Encoding
+ - Once dirservers are running new code, make clients
+ request compressed directories. (Alternative: Switch
+ to HTTP/1.1 and use Allowed-Encoding. Is that really
+ what we want?)
+N - switch dirservers entries to config lines.
+N - let tor clients use http proxies for dir fetching
+N - per-month byte allowances.
+Nr - figure out how to handle rendezvousing with unverified nodes.
+Nr - figure out enclaves, e.g. so we know what to recommend that people
do, and so running a tor server on your website is helpful.
- - compress the directory.
- - switch dirservers entries to config lines.
- - investigate sctp for alternate transport.
- - nt services on win32.
- - bandwidth buckets for write as well as read.
- - make clients store the cached-directory to disk, and use it
- when they startup, so they don't need to bootstrap from the
- authdirservers every time they start. also, once we've reduced
- authdirserver entries to config lines, we can have lines that
- list cacheddirservers too.
+ - node 'groups' that are known to be in the same zone of control.
+ - let tor servers use http proxies for port 80 exits
+ - the user interface interface
- add ipv6 support.
+ - learn from ben about his openssl-reinitialization-trick to
+ rotate tls keys without making new connections.
+ D nt services on win32.
0.0.8:
- fix sprintf's to snprintf's?
- if destination IP is running a tor node, extend a circuit there
before sending begin.
* don't do this for now. figure out how enclaves work. but do enclaves soon.
+ - Track max ten-second b/w ever seen, to show operator
more features, complex:
- compress the directory. client sends http header
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 */
*(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;
}
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) ||
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)
const char *d;
if (options->DataDirectory)
d = options->DataDirectory;
- else if (server_mode()) {
+ else {
#ifdef MS_WINDOWS
char *p;
p = tor_malloc(MAX_PATH);
#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);
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;
}
/** 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);
void connection_bucket_init(void) {
tor_gettimeofday(¤t_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. */
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);
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)) {
rep_hist_note_bytes_read(result, time(NULL));
}
- connection_bucket_decrement(conn, result);
+ connection_read_bucket_decrement(conn, result);
return 0;
}
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 */
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];
* 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 */
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;
}
int status_code;
time_t now, date_header=0;
int delta;
+ int compression;
switch(fetch_from_buf_http(conn->inbuf,
&headers, MAX_HEADERS_SIZE,
/* 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;
}
}
+ 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);
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.");
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;
{
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);
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.");
+ }
+ }
}
}
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;
/* 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. */
/* 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) {
}
/* 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()) {
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) */
/********************************* 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;
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,
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);