diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/Makefile.am | 2 | ||||
-rw-r--r-- | src/or/Makefile.nmake | 28 | ||||
-rw-r--r-- | src/or/buffers.c | 46 | ||||
-rw-r--r-- | src/or/circuitlist.c | 10 | ||||
-rw-r--r-- | src/or/config.c | 56 | ||||
-rw-r--r-- | src/or/connection.c | 162 | ||||
-rw-r--r-- | src/or/connection_edge.c | 125 | ||||
-rw-r--r-- | src/or/connection_edge.h | 3 | ||||
-rw-r--r-- | src/or/connection_or.c | 4 | ||||
-rw-r--r-- | src/or/control.c | 43 | ||||
-rw-r--r-- | src/or/control.h | 1 | ||||
-rw-r--r-- | src/or/directory.c | 17 | ||||
-rw-r--r-- | src/or/dnsserv.c | 2 | ||||
-rw-r--r-- | src/or/geoip.c | 308 | ||||
-rw-r--r-- | src/or/geoip.h | 5 | ||||
-rw-r--r-- | src/or/main.c | 16 | ||||
-rw-r--r-- | src/or/microdesc.c | 15 | ||||
-rw-r--r-- | src/or/networkstatus.c | 11 | ||||
-rw-r--r-- | src/or/or.h | 11 | ||||
-rw-r--r-- | src/or/rephist.c | 240 | ||||
-rw-r--r-- | src/or/rephist.h | 4 | ||||
-rw-r--r-- | src/or/status.c | 2 |
22 files changed, 699 insertions, 412 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 344e63ff87..e2a1b6d649 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -7,7 +7,7 @@ else tor_platform_source= endif -EXTRA_DIST=ntmain.c or_sha1.i +EXTRA_DIST=ntmain.c or_sha1.i Makefile.nmake if USE_EXTERNAL_EVDNS evdns_source= diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake new file mode 100644 index 0000000000..919edbbf22 --- /dev/null +++ b/src/or/Makefile.nmake @@ -0,0 +1,28 @@ +all: tor.exe
+
+CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common
+
+LIBS = ..\..\..\build-alpha\lib\libevent.a \
+ ..\..\..\build-alpha\lib\libcrypto.a \
+ ..\..\..\build-alpha\lib\libssl.a \
+ ..\..\..\build-alpha\lib\libz.a \
+ ws2_32.lib advapi32.lib shell32.lib
+
+LIBTOR_OBJECTS = buffers.obj circuitbuild.obj circuitlist.obj circuituse.obj \
+ command.obj config.obj connection.obj connection_edge.obj \
+ connection_or.obj control.obj cpuworker.obj directory.obj \
+ dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \
+ hibernate.obj main.obj microdesc.obj networkstatus.obj \
+ nodelist.obj onion.obj policies.obj reasons.obj relay.obj \
+ rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \
+ rephist.obj router.obj routerlist.obj routerparse.obj status.obj \
+ config_codedigest.obj ntmain.obj
+
+libtor.lib: $(LIBTOR_OBJECTS)
+ lib $(LIBTOR_OBJECTS) /out:libtor.lib
+
+tor.exe: libtor.lib tor_main.obj
+ $(CC) $(CFLAGS) $(LIBS) libtor.lib ..\common\*.lib tor_main.obj
+
+clean:
+ del $(LIBTOR_OBJECTS) *.lib tor.exe
diff --git a/src/or/buffers.c b/src/or/buffers.c index 5b9e55ebd5..85d58e8986 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -1054,14 +1054,14 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto) #ifdef USE_BUFFEREVENTS /** Try to read <b>n</b> bytes from <b>buf</b> at <b>pos</b> (which may be * NULL for the start of the buffer), copying the data only if necessary. Set - * *<b>data</b> to a pointer to the desired bytes. Set <b>free_out</b> to 1 - * if we needed to malloc *<b>data</b> because the original bytes were + * *<b>data_out</b> to a pointer to the desired bytes. Set <b>free_out</b> + * to 1 if we needed to malloc *<b>data</b> because the original bytes were * noncontiguous; 0 otherwise. Return the number of bytes actually available - * at <b>data</b>. + * at *<b>data_out</b>. */ static ssize_t -inspect_evbuffer(struct evbuffer *buf, char **data, size_t n, int *free_out, - struct evbuffer_ptr *pos) +inspect_evbuffer(struct evbuffer *buf, char **data_out, size_t n, + int *free_out, struct evbuffer_ptr *pos) { int n_vecs, i; @@ -1075,25 +1075,15 @@ inspect_evbuffer(struct evbuffer *buf, char **data, size_t n, int *free_out, struct evbuffer_iovec v; i = evbuffer_peek(buf, n, pos, &v, 1); tor_assert(i == 1); - *data = v.iov_base; + *data_out = v.iov_base; *free_out = 0; return v.iov_len; } else { - struct evbuffer_iovec *vecs = - tor_malloc(sizeof(struct evbuffer_iovec)*n_vecs); - size_t copied = 0; - i = evbuffer_peek(buf, n, NULL, vecs, n_vecs); - tor_assert(i == n_vecs); - *data = tor_malloc(n); - for (i=0; i < n_vecs; ++i) { - size_t copy = n - copied; - if (copy > vecs[i].iov_len) - copy = vecs[i].iov_len; - tor_assert(copied+copy <= n); - memcpy(data+copied, vecs[i].iov_base, copy); - copied += copy; - } + ev_ssize_t copied; + *data_out = tor_malloc(n); *free_out = 1; + copied = evbuffer_copyout(buf, *data_out, n); + tor_assert(copied >= 0 && (size_t)copied == n); return copied; } } @@ -1532,8 +1522,14 @@ socks_request_free(socks_request_t *req) { if (!req) return; - tor_free(req->username); - tor_free(req->password); + if (req->username) { + memset(req->username, 0x10, req->usernamelen); + tor_free(req->username); + } + if (req->password) { + memset(req->password, 0x04, req->passwordlen); + tor_free(req->password); + } memset(req, 0xCC, sizeof(socks_request_t)); tor_free(req); } @@ -1661,9 +1657,9 @@ fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req, if (res == 0 && n_drain == 0 && want_length <= last_wanted) { /* If we drained nothing, and we didn't ask for more than last time, - * we're stuck in a loop. That's bad. It shouldn't be possible, but - * let's make sure. */ - log_warn(LD_BUG, "We seem to be caught in a parse loop; breaking out"); + * then we probably wanted more data than the buffer actually had, + * and we're finding out that we're not satisified with it. It's + * time to break until we have more data. */ break; } diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 48c5afc7d0..2222a25af0 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -552,8 +552,14 @@ circuit_free(circuit_t *circ) rend_data_free(ocirc->rend_data); tor_free(ocirc->dest_address); - tor_free(ocirc->socks_username); - tor_free(ocirc->socks_password); + if (ocirc->socks_username) { + memset(ocirc->socks_username, 0x12, ocirc->socks_username_len); + tor_free(ocirc->socks_username); + } + if (ocirc->socks_password) { + memset(ocirc->socks_password, 0x06, ocirc->socks_password_len); + tor_free(ocirc->socks_password); + } } else { or_circuit_t *ocirc = TO_OR_CIRCUIT(circ); /* Remember cell statistics for this circuit before deallocating. */ diff --git a/src/or/config.c b/src/or/config.c index 088617bb49..dbc355d728 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -40,6 +40,9 @@ #include "procmon.h" +/* From main.c */ +extern int quiet_level; + /** Enumeration of types which option values can take */ typedef enum config_type_t { CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ @@ -693,6 +696,9 @@ get_options(void) int set_options(or_options_t *new_val, char **msg) { + int i; + smartlist_t *elements; + config_line_t *line; or_options_t *old_options = global_options; global_options = new_val; /* Note that we pass the *old* options below, for comparison. It @@ -707,7 +713,34 @@ set_options(or_options_t *new_val, char **msg) "Acting on config options left us in a broken state. Dying."); exit(1); } - + /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is + * just starting up then the old_options will be undefined. */ + if (old_options) { + elements = smartlist_create(); + for (i=0; options_format.vars[i].name; ++i) { + const config_var_t *var = &options_format.vars[i]; + const char *var_name = var->name; + if (var->type == CONFIG_TYPE_LINELIST_S || + var->type == CONFIG_TYPE_OBSOLETE) { + continue; + } + if (!option_is_same(&options_format, new_val, old_options, var_name)) { + line = get_assigned_option(&options_format, new_val, var_name, 1); + + if (line) { + for (; line; line = line->next) { + smartlist_add(elements, line->key); + smartlist_add(elements, line->value); + } + } else { + smartlist_add(elements, (char*)options_format.vars[i].name); + smartlist_add(elements, NULL); + } + } + } + control_event_conf_changed(elements); + smartlist_free(elements); + } config_free(&options_format, old_options); return 0; @@ -1087,6 +1120,9 @@ options_act_reversible(const or_options_t *old_options, char **msg) /* No need to roll back, since you can't change the value. */ } + /* Write control ports to disk as appropriate */ + control_ports_write_to_file(); + if (directory_caches_v2_dir_info(options)) { size_t len = strlen(options->DataDirectory)+32; char *fn = tor_malloc(len); @@ -3095,8 +3131,12 @@ options_validate(or_options_t *old_options, or_options_t *options, "misconfigured or something else goes wrong."); /* Special case on first boot if no Log options are given. */ - if (!options->Logs && !options->RunAsDaemon && !from_setconf) - config_line_append(&options->Logs, "Log", "notice stdout"); + if (!options->Logs && !options->RunAsDaemon && !from_setconf) { + if (quiet_level == 0) + config_line_append(&options->Logs, "Log", "notice stdout"); + else if (quiet_level == 1) + config_line_append(&options->Logs, "Log", "warn stdout"); + } if (options_init_logs(options, 1)<0) /* Validate the log(s) */ REJECT("Failed to validate Log options. See logs for details."); @@ -3926,6 +3966,12 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (old->DisableIOCP != new_val->DisableIOCP) { + *msg = tor_strdup("While Tor is running, changing DisableIOCP " + "is not allowed."); + return -1; + } + return 0; } @@ -4947,7 +4993,7 @@ parse_client_port_config(smartlist_t *out, mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL); if (!ok) { log_warn(LD_CONFIG, "%sListenAddress can only be used with a single " - "%sPort with value \"auto\" or 1-65535.", portname, portname); + "%sPort with value \"auto\" or 1-65535.", portname, portname); return -1; } } @@ -5111,7 +5157,7 @@ parse_client_port_config(smartlist_t *out, } } SMARTLIST_FOREACH_END(elt); - if (out) { + if (out && port) { port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); cfg->type = listener_type; cfg->port = port; diff --git a/src/or/connection.c b/src/or/connection.c index 012a3fbd03..97989c07f5 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -544,6 +544,7 @@ _connection_free(connection_t *conn) #ifdef USE_BUFFEREVENTS if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) { ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg); + TO_OR_CONN(conn)->bucket_cfg = NULL; } #endif @@ -581,41 +582,6 @@ connection_free(connection_t *conn) _connection_free(conn); } -/** Call _connection_free() on every connection in our array, and release all - * storage held by connection.c. This is used by cpuworkers and dnsworkers - * when they fork, so they don't keep resources held open (especially - * sockets). - * - * Don't do the checks in connection_free(), because they will - * fail. - */ -void -connection_free_all(void) -{ - smartlist_t *conns = get_connection_array(); - - /* We don't want to log any messages to controllers. */ - SMARTLIST_FOREACH(conns, connection_t *, conn, - if (conn->type == CONN_TYPE_CONTROL) - TO_CONTROL_CONN(conn)->event_mask = 0); - - control_update_global_event_mask(); - - /* Unlink everything from the identity map. */ - connection_or_clear_identity_map(); - - /* Clear out our list of broken connections */ - clear_broken_connection_map(0); - - SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn)); - - if (outgoing_addrs) { - SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr)); - smartlist_free(outgoing_addrs); - outgoing_addrs = NULL; - } -} - /** * Called when we're about to finally unlink and free a connection: * perform necessary accounting and cleanup @@ -1042,9 +1008,6 @@ connection_create_listener(const struct sockaddr *listensockaddr, "%s listening on port %u.", conn_type_to_string(type), gotPort); - if (type == CONN_TYPE_CONTROL_LISTENER) - control_ports_write_to_file(); - conn->state = LISTENER_STATE_READY; if (start_reading) { connection_start_reading(conn); @@ -1271,6 +1234,7 @@ connection_init_accepted_conn(connection_t *conn, TO_ENTRY_CONN(conn)->isolation_flags = listener->isolation_flags; TO_ENTRY_CONN(conn)->session_group = listener->session_group; TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch(); + TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->_base.type; switch (TO_CONN(listener)->type) { case CONN_TYPE_AP_LISTENER: conn->state = AP_CONN_STATE_SOCKS_WAIT; @@ -1841,6 +1805,8 @@ retry_listener_ports(smartlist_t *old_conns, socklen_t listensocklen = 0; char *address=NULL; connection_t *conn; + int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port; + tor_assert(real_port <= UINT16_MAX); if (port->is_unix_addr) { listensockaddr = (struct sockaddr *) @@ -1849,7 +1815,7 @@ retry_listener_ports(smartlist_t *old_conns, } else { listensockaddr = tor_malloc(sizeof(struct sockaddr_storage)); listensocklen = tor_addr_to_sockaddr(&port->addr, - port->port, + real_port, listensockaddr, sizeof(struct sockaddr_storage)); address = tor_dup_addr(&port->addr); @@ -2248,24 +2214,11 @@ global_write_bucket_low(connection_t *conn, size_t attempt, int priority) return 0; } -#ifndef USE_BUFFEREVENTS -/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes - * onto <b>conn</b>. Decrement buckets appropriately. */ +/** DOCDOC */ static void -connection_buckets_decrement(connection_t *conn, time_t now, - size_t num_read, size_t num_written) +record_num_bytes_transferred_impl(connection_t *conn, + time_t now, size_t num_read, size_t num_written) { - if (num_written >= INT_MAX || num_read >= INT_MAX) { - log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, " - "connection type=%s, state=%s", - (unsigned long)num_read, (unsigned long)num_written, - conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state)); - if (num_written >= INT_MAX) num_written = 1; - if (num_read >= INT_MAX) num_read = 1; - tor_fragile_assert(); - } - /* Count bytes of answering direct and tunneled directory requests */ if (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER) { if (num_read > 0) @@ -2289,6 +2242,52 @@ connection_buckets_decrement(connection_t *conn, time_t now, } if (conn->type == CONN_TYPE_EXIT) rep_hist_note_exit_bytes(conn->port, num_written, num_read); +} + +#ifdef USE_BUFFEREVENTS +/** DOCDOC */ +static void +record_num_bytes_transferred(connection_t *conn, + time_t now, size_t num_read, size_t num_written) +{ + /* XXX023 check if this is necessary */ + if (num_written >= INT_MAX || num_read >= INT_MAX) { + log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, " + "connection type=%s, state=%s", + (unsigned long)num_read, (unsigned long)num_written, + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state)); + if (num_written >= INT_MAX) num_written = 1; + if (num_read >= INT_MAX) num_read = 1; + tor_fragile_assert(); + } + + record_num_bytes_transferred_impl(conn,now,num_read,num_written); +} +#endif + +#ifndef USE_BUFFEREVENTS +/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes + * onto <b>conn</b>. Decrement buckets appropriately. */ +static void +connection_buckets_decrement(connection_t *conn, time_t now, + size_t num_read, size_t num_written) +{ + if (num_written >= INT_MAX || num_read >= INT_MAX) { + log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, " + "connection type=%s, state=%s", + (unsigned long)num_read, (unsigned long)num_written, + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state)); + if (num_written >= INT_MAX) num_written = 1; + if (num_read >= INT_MAX) num_read = 1; + tor_fragile_assert(); + } + + record_num_bytes_transferred_impl(conn, now, num_read, num_written); + + if (!connection_is_rate_limited(conn)) + return; /* local IPs are free */ if (connection_counts_as_relayed_traffic(conn, now)) { global_relayed_read_bucket -= (int)num_read; @@ -2498,7 +2497,6 @@ connection_bucket_should_increase(int bucket, or_connection_t *conn) return 1; } #else - static void connection_buckets_decrement(connection_t *conn, time_t now, size_t num_read, size_t num_written) @@ -2509,6 +2507,7 @@ connection_buckets_decrement(connection_t *conn, time_t now, (void) num_written; /* Libevent does this for us. */ } + void connection_bucket_refill(int seconds_elapsed, time_t now) { @@ -2564,7 +2563,7 @@ connection_enable_rate_limiting(connection_t *conn) if (conn->bufev) { if (!global_rate_limit) connection_bucket_init(); - bufferevent_add_to_rate_limit_group(conn->bufev, global_rate_limit); + tor_add_bufferevent_to_rate_limit_group(conn->bufev, global_rate_limit); } } @@ -2883,7 +2882,7 @@ evbuffer_inbuf_callback(struct evbuffer *buf, if (info->n_added) { time_t now = approx_time(); conn->timestamp_lastread = now; - connection_buckets_decrement(conn, now, info->n_added, 0); + record_num_bytes_transferred(conn, now, info->n_added, 0); connection_consider_empty_read_buckets(conn); if (conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); @@ -2904,7 +2903,7 @@ evbuffer_outbuf_callback(struct evbuffer *buf, if (info->n_deleted) { time_t now = approx_time(); conn->timestamp_lastwritten = now; - connection_buckets_decrement(conn, now, 0, info->n_deleted); + record_num_bytes_transferred(conn, now, 0, info->n_deleted); connection_consider_empty_write_buckets(conn); if (conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); @@ -3006,6 +3005,11 @@ connection_configure_bufferevent_callbacks(connection_t *conn) connection_handle_write_cb, connection_handle_event_cb, conn); + /* Set a fairly high write low-watermark so that we get the write callback + called whenever data is written to bring us under 128K. Leave the + high-watermark at 0. + */ + bufferevent_setwatermark(bufev, EV_WRITE, 128*1024, 0); input = bufferevent_get_input(bufev); output = bufferevent_get_output(bufev); @@ -4184,3 +4188,43 @@ proxy_type_to_string(int proxy_type) return NULL; /*Unreached*/ } +/** Call _connection_free() on every connection in our array, and release all + * storage held by connection.c. This is used by cpuworkers and dnsworkers + * when they fork, so they don't keep resources held open (especially + * sockets). + * + * Don't do the checks in connection_free(), because they will + * fail. + */ +void +connection_free_all(void) +{ + smartlist_t *conns = get_connection_array(); + + /* We don't want to log any messages to controllers. */ + SMARTLIST_FOREACH(conns, connection_t *, conn, + if (conn->type == CONN_TYPE_CONTROL) + TO_CONTROL_CONN(conn)->event_mask = 0); + + control_update_global_event_mask(); + + /* Unlink everything from the identity map. */ + connection_or_clear_identity_map(); + + /* Clear out our list of broken connections */ + clear_broken_connection_map(0); + + SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn)); + + if (outgoing_addrs) { + SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr)); + smartlist_free(outgoing_addrs); + outgoing_addrs = NULL; + } + +#ifdef USE_BUFFEREVENTS + if (global_rate_limit) + bufferevent_rate_limit_group_free(global_rate_limit); +#endif +} + diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index df4acc46e5..0d2c10f5dd 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -165,7 +165,8 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial) "data from edge while in '%s' state. Sending it anyway. " "package_partial=%d, buflen=%ld", conn_state_to_string(conn->_base.type, conn->_base.state), - package_partial, connection_get_inbuf_len(TO_CONN(conn))); + package_partial, + (long)connection_get_inbuf_len(TO_CONN(conn))); if (connection_edge_package_raw_inbuf(conn, package_partial, NULL)<0) { /* (We already sent an end cell if possible) */ connection_mark_for_close(TO_CONN(conn)); @@ -938,12 +939,10 @@ addressmap_clear_excluded_trackexithosts(const or_options_t *options) if (len < 6) continue; /* malformed. */ dot = target + len - 6; /* dot now points to just before .exit */ - dot = strrchr(dot, '.'); /* dot now points to the . before .exit or NULL */ - if (!dot) { - nodename = tor_strndup(target, len-5); - } else { - nodename = tor_strndup(dot+1, strlen(dot+1)-5); - } + while(dot > target && *dot != '.') + dot--; + if (*dot == '.') dot++; + nodename = tor_strndup(dot, len-5-(dot-target));; node = node_get_by_nickname(nodename, 0); tor_free(nodename); if (!node || @@ -2455,9 +2454,9 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) ap_conn->sending_optimistic_data) && connection_ap_supports_optimistic_data(ap_conn)) { log_info(LD_APP, "Sending up to %ld + %ld bytes of queued-up data", - connection_get_inbuf_len(base_conn), + (long)connection_get_inbuf_len(base_conn), ap_conn->sending_optimistic_data ? - generic_buffer_len(ap_conn->sending_optimistic_data) : 0); + (long)generic_buffer_len(ap_conn->sending_optimistic_data) : 0); if (connection_edge_package_raw_inbuf(edge_conn, 1, NULL) < 0) { connection_mark_for_close(base_conn); } @@ -2599,6 +2598,7 @@ connection_ap_make_link(connection_t *partner, } /* Populate isolation fields. */ + conn->socks_request->listener_type = CONN_TYPE_DIR_LISTENER; conn->original_dest_address = tor_strdup(address); conn->session_group = session_group; conn->isolation_flags = isolation_flags; @@ -3344,52 +3344,21 @@ parse_extended_hostname(char *address, int allowdotexit) return BAD_HOSTNAME; } -/** Return true iff <b>a</b> and <b>b</b> have isolation rules and fields that - * make it permissible to put them on the same circuit.*/ -int -connection_edge_streams_are_compatible(const entry_connection_t *a, - const entry_connection_t *b) +/** Return true iff the (possibly NULL) <b>alen</b>-byte chunk of memory at + * <b>a</b> is equal to the (possibly NULL) <b>blen</b>-byte chunk of memory + * at <b>b</b>. */ +static int +memeq_opt(const char *a, size_t alen, const char *b, size_t blen) { - const uint8_t iso = a->isolation_flags | b->isolation_flags; - - if (! a->original_dest_address) { - log_warn(LD_BUG, "Reached connection_edge_streams_are_compatible without " - "having set a->original_dest_address"); - ((entry_connection_t*)a)->original_dest_address = - tor_strdup(a->socks_request->address); - } - if (! b->original_dest_address) { - log_warn(LD_BUG, "Reached connection_edge_streams_are_compatible without " - "having set b->original_dest_address"); - ((entry_connection_t*)b)->original_dest_address = - tor_strdup(a->socks_request->address); - } - - if (iso & ISO_STREAM) - return 0; - - if ((iso & ISO_DESTPORT) && a->socks_request->port != b->socks_request->port) + if (a == NULL) { + return (b == NULL); + } else if (b == NULL) { return 0; - if ((iso & ISO_DESTADDR) && - strcasecmp(a->original_dest_address, b->original_dest_address)) - return 0; - if ((iso & ISO_SOCKSAUTH) && - (strcmp_opt(a->socks_request->username, b->socks_request->username) || - strcmp_opt(a->socks_request->password, b->socks_request->password))) + } else if (alen != blen) { return 0; - if ((iso & ISO_CLIENTPROTO) && - (ENTRY_TO_CONN(a)->type != ENTRY_TO_CONN(b)->type || - a->socks_request->socks_version != b->socks_request->socks_version)) - return 0; - if ((iso & ISO_CLIENTADDR) && - !tor_addr_eq(&ENTRY_TO_CONN(a)->addr, &ENTRY_TO_CONN(b)->addr)) - return 0; - if ((iso & ISO_SESSIONGRP) && a->session_group != b->session_group) - return 0; - if ((iso & ISO_NYM_EPOCH) && a->nym_epoch != b->nym_epoch) - return 0; - - return 1; + } else { + return tor_memeq(a, b, alen); + } } /** @@ -3401,6 +3370,7 @@ connection_edge_compatible_with_circuit(const entry_connection_t *conn, const origin_circuit_t *circ) { const uint8_t iso = conn->isolation_flags; + const socks_request_t *sr = conn->socks_request; /* If circ has never been used for an isolated connection, we can * totally use it for this one. */ @@ -3425,9 +3395,9 @@ connection_edge_compatible_with_circuit(const entry_connection_t *conn, tor_strdup(conn->socks_request->address); } - /* If isolation_values_set, then the circuit is not compatible with - * any new ISO_STREAM stream. */ - if (iso & ISO_STREAM) + if ((iso & ISO_STREAM) && + (circ->associated_isolated_stream_global_id != + ENTRY_TO_CONN(conn)->global_identifier)) return 0; if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port) @@ -3436,11 +3406,13 @@ connection_edge_compatible_with_circuit(const entry_connection_t *conn, strcasecmp(conn->original_dest_address, circ->dest_address)) return 0; if ((iso & ISO_SOCKSAUTH) && - (strcmp_opt(conn->socks_request->username, circ->socks_username) || - strcmp_opt(conn->socks_request->password, circ->socks_password))) + (! memeq_opt(sr->username, sr->usernamelen, + circ->socks_username, circ->socks_username_len) || + ! memeq_opt(sr->password, sr->passwordlen, + circ->socks_password, circ->socks_password_len))) return 0; if ((iso & ISO_CLIENTPROTO) && - (ENTRY_TO_CONN(conn)->type != circ->client_proto_type || + (conn->socks_request->listener_type != circ->client_proto_type || conn->socks_request->socks_version != circ->client_proto_socksver)) return 0; if ((iso & ISO_CLIENTADDR) && @@ -3467,6 +3439,7 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn, origin_circuit_t *circ, int dry_run) { + const socks_request_t *sr = conn->socks_request; if (! conn->original_dest_address) { log_warn(LD_BUG, "Reached connection_update_circuit_isolation without " "having set conn->original_dest_address"); @@ -3477,17 +3450,21 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn, if (!circ->isolation_values_set) { if (dry_run) return -1; + circ->associated_isolated_stream_global_id = + ENTRY_TO_CONN(conn)->global_identifier; circ->dest_port = conn->socks_request->port; circ->dest_address = tor_strdup(conn->original_dest_address); - circ->client_proto_type = ENTRY_TO_CONN(conn)->type; + circ->client_proto_type = conn->socks_request->listener_type; circ->client_proto_socksver = conn->socks_request->socks_version; tor_addr_copy(&circ->client_addr, &ENTRY_TO_CONN(conn)->addr); circ->session_group = conn->session_group; circ->nym_epoch = conn->nym_epoch; - circ->socks_username = conn->socks_request->username ? - tor_strdup(conn->socks_request->username) : NULL; - circ->socks_password = conn->socks_request->password ? - tor_strdup(conn->socks_request->password) : NULL; + circ->socks_username = sr->username ? + tor_memdup(sr->username, sr->usernamelen) : NULL; + circ->socks_password = sr->password ? + tor_memdup(sr->password, sr->passwordlen) : NULL; + circ->socks_username_len = sr->usernamelen; + circ->socks_password_len = sr->passwordlen; circ->isolation_values_set = 1; return 0; @@ -3497,10 +3474,12 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn, mixed |= ISO_DESTPORT; if (strcasecmp(conn->original_dest_address, circ->dest_address)) mixed |= ISO_DESTADDR; - if (strcmp_opt(conn->socks_request->username, circ->socks_username) || - strcmp_opt(conn->socks_request->password, circ->socks_password)) + if (!memeq_opt(sr->username, sr->usernamelen, + circ->socks_username, circ->socks_username_len) || + !memeq_opt(sr->password, sr->passwordlen, + circ->socks_password, circ->socks_password_len)) mixed |= ISO_SOCKSAUTH; - if ((ENTRY_TO_CONN(conn)->type != circ->client_proto_type || + if ((conn->socks_request->listener_type != circ->client_proto_type || conn->socks_request->socks_version != circ->client_proto_socksver)) mixed |= ISO_CLIENTPROTO; if (!tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr)) @@ -3514,7 +3493,7 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn, return mixed; if ((mixed & conn->isolation_flags) != 0) { - log_warn(LD_BUG, "Updating a circuit with seemingly incomaptible " + log_warn(LD_BUG, "Updating a circuit with seemingly incompatible " "isolation flags."); } circ->isolation_flags_mixed |= mixed; @@ -3548,6 +3527,7 @@ circuit_clear_isolation(origin_circuit_t *circ) circ->isolation_values_set = 0; circ->isolation_flags_mixed = 0; + circ->associated_isolated_stream_global_id = 0; circ->client_proto_type = 0; circ->client_proto_socksver = 0; circ->dest_port = 0; @@ -3555,7 +3535,14 @@ circuit_clear_isolation(origin_circuit_t *circ) tor_free(circ->dest_address); circ->session_group = -1; circ->nym_epoch = 0; - tor_free(circ->socks_username); - tor_free(circ->socks_password); + if (circ->socks_username) { + memset(circ->socks_username, 0x11, circ->socks_username_len); + tor_free(circ->socks_username); + } + if (circ->socks_password) { + memset(circ->socks_password, 0x05, circ->socks_password_len); + tor_free(circ->socks_password); + } + circ->socks_username_len = circ->socks_password_len = 0; } diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index cf60cf4698..75a54e85e1 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -105,8 +105,7 @@ hostname_type_t parse_extended_hostname(char *address, int allowdotexit); int get_pf_socket(void); #endif -int connection_edge_streams_are_compatible(const entry_connection_t *a, - const entry_connection_t *b); + int connection_edge_compatible_with_circuit(const entry_connection_t *conn, const origin_circuit_t *circ); int connection_edge_update_circuit_isolation(const entry_connection_t *conn, diff --git a/src/or/connection_or.c b/src/or/connection_or.c index e66d36a2f3..a75444e1ed 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -585,7 +585,7 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, burst, tick); old_cfg = conn->bucket_cfg; if (conn->_base.bufev) - bufferevent_set_rate_limit(conn->_base.bufev, cfg); + tor_set_bufferevent_rate_limit(conn->_base.bufev, cfg); if (old_cfg) ev_token_bucket_cfg_free(old_cfg); conn->bucket_cfg = cfg; @@ -1102,7 +1102,7 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving) } conn->_base.bufev = b; if (conn->bucket_cfg) - bufferevent_set_rate_limit(conn->_base.bufev, conn->bucket_cfg); + tor_set_bufferevent_rate_limit(conn->_base.bufev, conn->bucket_cfg); connection_enable_rate_limiting(TO_CONN(conn)); connection_configure_bufferevent_callbacks(TO_CONN(conn)); diff --git a/src/or/control.c b/src/or/control.c index 6a44730f2f..f852659983 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -74,7 +74,8 @@ #define EVENT_NEWCONSENSUS 0x0016 #define EVENT_BUILDTIMEOUT_SET 0x0017 #define EVENT_SIGNAL 0x0018 -#define _EVENT_MAX 0x0018 +#define EVENT_CONF_CHANGED 0x0019 +#define _EVENT_MAX 0x0019 /* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */ /** Bitfield: The bit 1<<e is set if <b>any</b> open control @@ -502,8 +503,8 @@ connection_printf_to_buf(control_connection_t *conn, const char *format, ...) va_end(ap); if (len < 0) { - log_warn(LD_BUG, "Unable to format string for controller."); - return; + log_err(LD_BUG, "Unable to format string for controller."); + tor_assert(0); } connection_write_to_buf(buf, (size_t)len, TO_CONN(conn)); @@ -946,6 +947,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_NEWCONSENSUS, "NEWCONSENSUS" }, { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" }, { EVENT_SIGNAL, "SIGNAL" }, + { EVENT_CONF_CHANGED, "CONF_CHANGED"}, { 0, NULL }, }; @@ -3569,7 +3571,7 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg) severity <= LOG_NOTICE) { char *esc = esc_for_log(msg); ++disable_log_messages; - control_event_general_status(severity, "BUG REASON=\"%s\"", esc); + control_event_general_status(severity, "BUG REASON=%s", esc); --disable_log_messages; tor_free(esc); } @@ -3998,6 +4000,39 @@ control_event_guard(const char *nickname, const char *digest, return 0; } +/** Called when a configuration option changes. This is generally triggered + * by SETCONF requests and RELOAD/SIGHUP signals. The <b>elements</b> is + * a smartlist_t containing (key, value, ...) pairs in sequence. + * <b>value</b> can be NULL. */ +int +control_event_conf_changed(smartlist_t *elements) +{ + int i; + char *result; + smartlist_t *lines; + if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) || + smartlist_len(elements) == 0) { + return 0; + } + lines = smartlist_create(); + for (i = 0; i < smartlist_len(elements); i += 2) { + char *k = smartlist_get(elements, i); + char *v = smartlist_get(elements, i+1); + if (v == NULL) { + smartlist_asprintf_add(lines, "650-%s", k); + } else { + smartlist_asprintf_add(lines, "650-%s=%s", k, v); + } + } + result = smartlist_join_strings(lines, "\r\n", 0, NULL); + send_control_event(EVENT_CONF_CHANGED, 0, + "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result); + tor_free(result); + SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); + smartlist_free(lines); + return 0; +} + /** Helper: Return a newly allocated string containing a path to the * file where we store our authentication cookie. */ static char * diff --git a/src/or/control.h b/src/or/control.h index 3655184690..0d9acd26ef 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -66,6 +66,7 @@ int control_event_server_status(int severity, const char *format, ...) CHECK_PRINTF(2,3); int control_event_guard(const char *nickname, const char *digest, const char *status); +int control_event_conf_changed(smartlist_t *elements); int control_event_buildtimeout_set(const circuit_build_times_t *cbt, buildtimeout_set_event_t type); int control_event_signal(uintptr_t signal); diff --git a/src/or/directory.c b/src/or/directory.c index 16bea8256b..cd0b9b4de7 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3546,8 +3546,21 @@ connection_dir_finished_flushing(dir_connection_t *conn) conn->_base.state = DIR_CONN_STATE_CLIENT_READING; return 0; case DIR_CONN_STATE_SERVER_WRITING: - log_debug(LD_DIRSERV,"Finished writing server response. Closing."); - connection_mark_for_close(TO_CONN(conn)); + if (conn->dir_spool_src != DIR_SPOOL_NONE) { +#ifdef USE_BUFFEREVENTS + /* This can happen with paired bufferevents, since a paired connection + * can flush immediately when you write to it, making the subsequent + * check in connection_handle_write_cb() decide that the connection + * is flushed. */ + log_debug(LD_DIRSERV, "Emptied a dirserv buffer, but still spooling."); +#else + log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!"); + connection_mark_for_close(TO_CONN(conn)); +#endif + } else { + log_debug(LD_DIRSERV, "Finished writing server response. Closing."); + connection_mark_for_close(TO_CONN(conn)); + } return 0; default: log_warn(LD_BUG,"called in unexpected state %d.", diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index a54530c2b1..7f519398fa 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -133,6 +133,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) strlcpy(entry_conn->socks_request->address, q->name, sizeof(entry_conn->socks_request->address)); + entry_conn->socks_request->listener_type = listener->_base.type; entry_conn->dns_server_request = req; entry_conn->isolation_flags = listener->isolation_flags; entry_conn->session_group = listener->session_group; @@ -189,6 +190,7 @@ dnsserv_launch_request(const char *name, int reverse) strlcpy(entry_conn->socks_request->address, name, sizeof(entry_conn->socks_request->address)); + entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER; entry_conn->original_dest_address = tor_strdup(name); entry_conn->session_group = SESSION_GROUP_CONTROL_RESOLVE; entry_conn->nym_epoch = get_signewnym_epoch(); diff --git a/src/or/geoip.c b/src/or/geoip.c index 5596ff3253..67dea965f3 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -930,10 +930,9 @@ geoip_dirreq_stats_init(time_t now) start_of_dirreq_stats_interval = now; } -/** Stop collecting directory request stats in a way that we can re-start - * doing so in geoip_dirreq_stats_init(). */ +/** Reset counters for dirreq stats. */ void -geoip_dirreq_stats_term(void) +geoip_reset_dirreq_stats(time_t now) { SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, { c->n_v2_ns_requests = c->n_v3_ns_requests = 0; @@ -965,59 +964,41 @@ geoip_dirreq_stats_term(void) tor_free(this); } } - start_of_dirreq_stats_interval = 0; + start_of_dirreq_stats_interval = now; } -/** Write dirreq statistics to $DATADIR/stats/dirreq-stats and return when - * we would next want to write. */ -time_t -geoip_dirreq_stats_write(time_t now) +/** Stop collecting directory request stats in a way that we can re-start + * doing so in geoip_dirreq_stats_init(). */ +void +geoip_dirreq_stats_term(void) { - char *statsdir = NULL, *filename = NULL; - char *data_v2 = NULL, *data_v3 = NULL; - char written[ISO_TIME_LEN+1]; - open_file_t *open_file = NULL; + geoip_reset_dirreq_stats(0); +} + +/** Return a newly allocated string containing the dirreq statistics + * until <b>now</b>, or NULL if we're not collecting dirreq stats. */ +char * +geoip_format_dirreq_stats(time_t now) +{ + char t[ISO_TIME_LEN+1]; double v2_share = 0.0, v3_share = 0.0; - FILE *out; int i; + char *v3_ips_string, *v2_ips_string, *v3_reqs_string, *v2_reqs_string, + *v2_share_string = NULL, *v3_share_string = NULL, + *v3_direct_dl_string, *v2_direct_dl_string, + *v3_tunneled_dl_string, *v2_tunneled_dl_string; + char *result; if (!start_of_dirreq_stats_interval) - return 0; /* Not initialized. */ - if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now) - goto done; /* Not ready to write. */ + return NULL; /* Not initialized. */ - /* Discard all items in the client history that are too old. */ - geoip_remove_old_clients(start_of_dirreq_stats_interval); - - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) - goto done; - filename = get_datadir_fname2("stats", "dirreq-stats"); - data_v2 = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2); - data_v3 = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS); - format_iso_time(written, now); - out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT, - 0600, &open_file); - if (!out) - goto done; - if (fprintf(out, "dirreq-stats-end %s (%d s)\ndirreq-v3-ips %s\n" - "dirreq-v2-ips %s\n", written, - (unsigned) (now - start_of_dirreq_stats_interval), - data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0) - goto done; - tor_free(data_v2); - tor_free(data_v3); + format_iso_time(t, now); + v2_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2); + v3_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS); + v2_reqs_string = geoip_get_request_history( + GEOIP_CLIENT_NETWORKSTATUS_V2); + v3_reqs_string = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS); - data_v2 = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS_V2); - data_v3 = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS); - if (fprintf(out, "dirreq-v3-reqs %s\ndirreq-v2-reqs %s\n", - data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0) - goto done; - tor_free(data_v2); - tor_free(data_v3); - SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, { - c->n_v2_ns_requests = c->n_v3_ns_requests = 0; - }); #define RESPONSE_GRANULARITY 8 for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) { ns_v2_responses[i] = round_uint32_to_next_multiple_of( @@ -1026,61 +1007,117 @@ geoip_dirreq_stats_write(time_t now) ns_v3_responses[i], RESPONSE_GRANULARITY); } #undef RESPONSE_GRANULARITY - if (fprintf(out, "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u," - "not-found=%u,not-modified=%u,busy=%u\n", - ns_v3_responses[GEOIP_SUCCESS], - ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS], - ns_v3_responses[GEOIP_REJECT_UNAVAILABLE], - ns_v3_responses[GEOIP_REJECT_NOT_FOUND], - ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED], - ns_v3_responses[GEOIP_REJECT_BUSY]) < 0) - goto done; - if (fprintf(out, "dirreq-v2-resp ok=%u,unavailable=%u," - "not-found=%u,not-modified=%u,busy=%u\n", - ns_v2_responses[GEOIP_SUCCESS], - ns_v2_responses[GEOIP_REJECT_UNAVAILABLE], - ns_v2_responses[GEOIP_REJECT_NOT_FOUND], - ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED], - ns_v2_responses[GEOIP_REJECT_BUSY]) < 0) - goto done; - memset(ns_v2_responses, 0, sizeof(ns_v2_responses)); - memset(ns_v3_responses, 0, sizeof(ns_v3_responses)); + if (!geoip_get_mean_shares(now, &v2_share, &v3_share)) { - if (fprintf(out, "dirreq-v2-share %0.2lf%%\n", v2_share*100) < 0) - goto done; - if (fprintf(out, "dirreq-v3-share %0.2lf%%\n", v3_share*100) < 0) - goto done; + tor_asprintf(&v2_share_string, "dirreq-v2-share %0.2lf%%\n", + v2_share*100); + tor_asprintf(&v3_share_string, "dirreq-v3-share %0.2lf%%\n", + v3_share*100); } - data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2, - DIRREQ_DIRECT); - data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS, - DIRREQ_DIRECT); - if (fprintf(out, "dirreq-v3-direct-dl %s\ndirreq-v2-direct-dl %s\n", - data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0) - goto done; - tor_free(data_v2); - tor_free(data_v3); - data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2, - DIRREQ_TUNNELED); - data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS, - DIRREQ_TUNNELED); - if (fprintf(out, "dirreq-v3-tunneled-dl %s\ndirreq-v2-tunneled-dl %s\n", - data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0) - goto done; + v2_direct_dl_string = geoip_get_dirreq_history( + GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_DIRECT); + v3_direct_dl_string = geoip_get_dirreq_history( + GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_DIRECT); + + v2_tunneled_dl_string = geoip_get_dirreq_history( + GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_TUNNELED); + v3_tunneled_dl_string = geoip_get_dirreq_history( + GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_TUNNELED); + + /* Put everything together into a single string. */ + tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n" + "dirreq-v3-ips %s\n" + "dirreq-v2-ips %s\n" + "dirreq-v3-reqs %s\n" + "dirreq-v2-reqs %s\n" + "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u," + "not-found=%u,not-modified=%u,busy=%u\n" + "dirreq-v2-resp ok=%u,unavailable=%u," + "not-found=%u,not-modified=%u,busy=%u\n" + "%s" + "%s" + "dirreq-v3-direct-dl %s\n" + "dirreq-v2-direct-dl %s\n" + "dirreq-v3-tunneled-dl %s\n" + "dirreq-v2-tunneled-dl %s\n", + t, + (unsigned) (now - start_of_dirreq_stats_interval), + v3_ips_string ? v3_ips_string : "", + v2_ips_string ? v2_ips_string : "", + v3_reqs_string ? v3_reqs_string : "", + v2_reqs_string ? v2_reqs_string : "", + ns_v3_responses[GEOIP_SUCCESS], + ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS], + ns_v3_responses[GEOIP_REJECT_UNAVAILABLE], + ns_v3_responses[GEOIP_REJECT_NOT_FOUND], + ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED], + ns_v3_responses[GEOIP_REJECT_BUSY], + ns_v2_responses[GEOIP_SUCCESS], + ns_v2_responses[GEOIP_REJECT_UNAVAILABLE], + ns_v2_responses[GEOIP_REJECT_NOT_FOUND], + ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED], + ns_v2_responses[GEOIP_REJECT_BUSY], + v2_share_string ? v2_share_string : "", + v3_share_string ? v3_share_string : "", + v3_direct_dl_string ? v3_direct_dl_string : "", + v2_direct_dl_string ? v2_direct_dl_string : "", + v3_tunneled_dl_string ? v3_tunneled_dl_string : "", + v2_tunneled_dl_string ? v2_tunneled_dl_string : ""); + + /* Free partial strings. */ + tor_free(v3_ips_string); + tor_free(v2_ips_string); + tor_free(v3_reqs_string); + tor_free(v2_reqs_string); + tor_free(v2_share_string); + tor_free(v3_share_string); + tor_free(v3_direct_dl_string); + tor_free(v2_direct_dl_string); + tor_free(v3_tunneled_dl_string); + tor_free(v2_tunneled_dl_string); - finish_writing_to_file(open_file); - open_file = NULL; + return result; +} - start_of_dirreq_stats_interval = now; +/** If 24 hours have passed since the beginning of the current dirreq + * stats period, write dirreq stats to $DATADIR/stats/dirreq-stats + * (possibly overwriting an existing file) and reset counters. Return + * when we would next want to write dirreq stats or 0 if we never want to + * write. */ +time_t +geoip_dirreq_stats_write(time_t now) +{ + char *statsdir = NULL, *filename = NULL, *str = NULL; + + if (!start_of_dirreq_stats_interval) + return 0; /* Not initialized. */ + if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now) + goto done; /* Not ready to write. */ + + /* Discard all items in the client history that are too old. */ + geoip_remove_old_clients(start_of_dirreq_stats_interval); + + /* Generate history string .*/ + str = geoip_format_dirreq_stats(now); + + /* Write dirreq-stats string to disk. */ + statsdir = get_datadir_fname("stats"); + if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { + log_warn(LD_HIST, "Unable to create stats/ directory!"); + goto done; + } + filename = get_datadir_fname2("stats", "dirreq-stats"); + if (write_str_to_file(filename, str, 0) < 0) + log_warn(LD_HIST, "Unable to write dirreq statistics to disk!"); + + /* Reset measurement interval start. */ + geoip_reset_dirreq_stats(now); done: - if (open_file) - abort_writing_to_file(open_file); - tor_free(filename); tor_free(statsdir); - tor_free(data_v2); - tor_free(data_v3); + tor_free(filename); + tor_free(str); return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL; } @@ -1160,8 +1197,8 @@ static char *bridge_stats_extrainfo = NULL; /** Return a newly allocated string holding our bridge usage stats by country * in a format suitable for inclusion in an extrainfo document. Return NULL on * failure. */ -static char * -format_bridge_stats_extrainfo(time_t now) +char * +geoip_format_bridge_stats(time_t now) { char *out = NULL, *data = NULL; long duration = now - start_of_bridge_stats_interval; @@ -1169,6 +1206,8 @@ format_bridge_stats_extrainfo(time_t now) if (duration < 0) return NULL; + if (!start_of_bridge_stats_interval) + return NULL; /* Not initialized. */ format_iso_time(written, now); data = geoip_get_client_history(GEOIP_CLIENT_CONNECT); @@ -1218,7 +1257,7 @@ geoip_bridge_stats_write(time_t now) geoip_remove_old_clients(start_of_bridge_stats_interval); /* Generate formatted string */ - val = format_bridge_stats_extrainfo(now); + val = geoip_format_bridge_stats(now); if (val == NULL) goto done; @@ -1295,25 +1334,51 @@ geoip_entry_stats_init(time_t now) start_of_entry_stats_interval = now; } +/** Reset counters for entry stats. */ +void +geoip_reset_entry_stats(time_t now) +{ + client_history_clear(); + start_of_entry_stats_interval = now; +} + /** Stop collecting entry stats in a way that we can re-start doing so in * geoip_entry_stats_init(). */ void geoip_entry_stats_term(void) { - client_history_clear(); - start_of_entry_stats_interval = 0; + geoip_reset_entry_stats(0); } -/** Write entry statistics to $DATADIR/stats/entry-stats and return time - * when we would next want to write. */ +/** Return a newly allocated string containing the entry statistics + * until <b>now</b>, or NULL if we're not collecting entry stats. */ +char * +geoip_format_entry_stats(time_t now) +{ + char t[ISO_TIME_LEN+1]; + char *data = NULL; + char *result; + + if (!start_of_entry_stats_interval) + return NULL; /* Not initialized. */ + + data = geoip_get_client_history(GEOIP_CLIENT_CONNECT); + format_iso_time(t, now); + tor_asprintf(&result, "entry-stats-end %s (%u s)\nentry-ips %s\n", + t, (unsigned) (now - start_of_entry_stats_interval), + data ? data : ""); + tor_free(data); + return result; +} + +/** If 24 hours have passed since the beginning of the current entry stats + * period, write entry stats to $DATADIR/stats/entry-stats (possibly + * overwriting an existing file) and reset counters. Return when we would + * next want to write entry stats or 0 if we never want to write. */ time_t geoip_entry_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL; - char *data = NULL; - char written[ISO_TIME_LEN+1]; - open_file_t *open_file = NULL; - FILE *out; + char *statsdir = NULL, *filename = NULL, *str = NULL; if (!start_of_entry_stats_interval) return 0; /* Not initialized. */ @@ -1323,31 +1388,26 @@ geoip_entry_stats_write(time_t now) /* Discard all items in the client history that are too old. */ geoip_remove_old_clients(start_of_entry_stats_interval); + /* Generate history string .*/ + str = geoip_format_entry_stats(now); + + /* Write entry-stats string to disk. */ statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) + if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { + log_warn(LD_HIST, "Unable to create stats/ directory!"); goto done; + } filename = get_datadir_fname2("stats", "entry-stats"); - data = geoip_get_client_history(GEOIP_CLIENT_CONNECT); - format_iso_time(written, now); - out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT, - 0600, &open_file); - if (!out) - goto done; - if (fprintf(out, "entry-stats-end %s (%u s)\nentry-ips %s\n", - written, (unsigned) (now - start_of_entry_stats_interval), - data ? data : "") < 0) - goto done; + if (write_str_to_file(filename, str, 0) < 0) + log_warn(LD_HIST, "Unable to write entry statistics to disk!"); - start_of_entry_stats_interval = now; + /* Reset measurement interval start. */ + geoip_reset_entry_stats(now); - finish_writing_to_file(open_file); - open_file = NULL; done: - if (open_file) - abort_writing_to_file(open_file); - tor_free(filename); tor_free(statsdir); - tor_free(data); + tor_free(filename); + tor_free(str); return start_of_entry_stats_interval + WRITE_STATS_INTERVAL; } diff --git a/src/or/geoip.h b/src/or/geoip.h index b50da74dc3..ce3841967f 100644 --- a/src/or/geoip.h +++ b/src/or/geoip.h @@ -43,12 +43,17 @@ void geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type, dirreq_state_t new_state); void geoip_dirreq_stats_init(time_t now); +void geoip_reset_dirreq_stats(time_t now); +char *geoip_format_dirreq_stats(time_t now); time_t geoip_dirreq_stats_write(time_t now); void geoip_dirreq_stats_term(void); void geoip_entry_stats_init(time_t now); time_t geoip_entry_stats_write(time_t now); void geoip_entry_stats_term(void); +void geoip_reset_entry_stats(time_t now); +char *geoip_format_entry_stats(time_t now); void geoip_bridge_stats_init(time_t now); +char *geoip_format_bridge_stats(time_t now); time_t geoip_bridge_stats_write(time_t now); void geoip_bridge_stats_term(void); const char *geoip_get_bridge_stats_extrainfo(time_t); diff --git a/src/or/main.c b/src/or/main.c index 6866ccc8ce..2cdbe71f10 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -154,6 +154,12 @@ int can_complete_circuit=0; * they are obsolete? */ #define TLS_HANDSHAKE_TIMEOUT (60) +/** Decides our behavior when no logs are configured/before any + * logs have been configured. For 0, we log notice to stdout as normal. + * For 1, we log warnings only. For 2, we log nothing. + */ +int quiet_level = 0; + /********* END VARIABLES ************/ /**************************************************************************** @@ -2148,9 +2154,15 @@ tor_init(int argc, char *argv[]) default: add_temp_log(LOG_NOTICE); } + quiet_level = quiet; - log(LOG_NOTICE, LD_GENERAL, "Tor v%s. This is experimental software. " - "Do not rely on it for strong anonymity. (Running on %s)",get_version(), + log(LOG_NOTICE, LD_GENERAL, "Tor v%s%s. This is experimental software. " + "Do not rely on it for strong anonymity. (Running on %s)", get_version(), +#ifdef USE_BUFFEREVENTS + " (with bufferevents)", +#else + "", +#endif get_uname()); if (network_init()<0) { diff --git a/src/or/microdesc.c b/src/or/microdesc.c index 1b0c333dae..510b2f40f7 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -698,14 +698,9 @@ we_use_microdescriptors_for_circuits(const or_options_t *options) int ret = options->UseMicrodescriptors; if (ret == -1) { /* UseMicrodescriptors is "auto"; we need to decide: */ -#if 0 - /* So we decide that we'll use microdescriptors iff we are not a server */ - ret = ! server_mode(options); -#else - /* We don't use microdescs for now: not enough caches are running - * 0.2.3.1-alpha */ - ret = 0; -#endif + /* So we decide that we'll use microdescriptors iff we are not a server, + * and we're not autofetching everything. */ + ret = !server_mode(options) && !options->FetchUselessDescriptors; } return ret; } @@ -716,6 +711,8 @@ we_fetch_microdescriptors(const or_options_t *options) { if (directory_caches_dir_info(options)) return 1; + if (options->FetchUselessDescriptors) + return 1; return we_use_microdescriptors_for_circuits(options); } @@ -725,6 +722,8 @@ we_fetch_router_descriptors(const or_options_t *options) { if (directory_caches_dir_info(options)) return 1; + if (options->FetchUselessDescriptors) + return 1; return ! we_use_microdescriptors_for_circuits(options); } diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 2586ce6ebe..398f041532 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1187,6 +1187,10 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor) * it ourselves. */ return 1; } + if (options->FetchUselessDescriptors) { + /* In order to get all descriptors, we need to fetch all consensuses. */ + return 1; + } /* Otherwise, we want the flavor only if we want to use it to build * circuits. */ return flavor == usable_consensus_flavor(); @@ -2006,13 +2010,6 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers, tor_memcmp(rs->identity_digest, router->cache_info.identity_digest, DIGEST_LEN), { -#if 0 - /* We have no routerstatus for this router. Clear flags and skip it. */ - if (!authdir) { - if (router->purpose == ROUTER_PURPOSE_GENERAL) - router_clear_status_flags(router); - } -#endif }) { /* We have a routerstatus for this router. */ const char *digest = router->cache_info.identity_digest; diff --git a/src/or/or.h b/src/or/or.h index ca1433efc3..b67afd7900 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2558,8 +2558,15 @@ typedef struct origin_circuit_t { char *dest_address; int session_group; unsigned nym_epoch; + size_t socks_username_len; + uint8_t socks_password_len; + /* Note that the next two values are NOT NUL-terminated; see + socks_username_len and socks_password_len for their lengths. */ char *socks_username; char *socks_password; + /** Global identifier for the first stream attached here; used by + * ISO_STREAM. */ + uint64_t associated_isolated_stream_global_id; /**@}*/ } origin_circuit_t; @@ -3416,6 +3423,8 @@ struct socks_request_t { uint8_t auth_type; /** What is this stream's goal? One of the SOCKS_COMMAND_* values */ uint8_t command; + /** Which kind of listener created this stream? */ + uint8_t listener_type; size_t replylen; /**< Length of <b>reply</b>. */ uint8_t reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if * we want to specify our own socks reply, @@ -3432,7 +3441,7 @@ struct socks_request_t { unsigned int got_auth : 1; /**< Have we received any authentication data? */ /** Number of bytes in username; 0 if username is NULL */ - uint8_t usernamelen; + size_t usernamelen; /** Number of bytes in password; 0 if password is NULL */ uint8_t passwordlen; /** The negotiated username value if any (for socks5), or the entire diff --git a/src/or/rephist.c b/src/or/rephist.c index 1ad08fe93e..25aece3d59 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -2364,23 +2364,41 @@ typedef struct circ_buffer_stats_t { /** List of circ_buffer_stats_t. */ static smartlist_t *circuits_for_buffer_stats = NULL; +/** Remember cell statistics <b>mean_num_cells_in_queue</b>, + * <b>mean_time_cells_in_queue</b>, and <b>processed_cells</b> of a + * circuit. */ +void +rep_hist_add_buffer_stats(double mean_num_cells_in_queue, + double mean_time_cells_in_queue, uint32_t processed_cells) +{ + circ_buffer_stats_t *stat; + if (!start_of_buffer_stats_interval) + return; /* Not initialized. */ + stat = tor_malloc_zero(sizeof(circ_buffer_stats_t)); + stat->mean_num_cells_in_queue = mean_num_cells_in_queue; + stat->mean_time_cells_in_queue = mean_time_cells_in_queue; + stat->processed_cells = processed_cells; + if (!circuits_for_buffer_stats) + circuits_for_buffer_stats = smartlist_create(); + smartlist_add(circuits_for_buffer_stats, stat); +} + /** Remember cell statistics for circuit <b>circ</b> at time * <b>end_of_interval</b> and reset cell counters in case the circuit * remains open in the next measurement interval. */ void rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval) { - circ_buffer_stats_t *stat; time_t start_of_interval; int interval_length; or_circuit_t *orcirc; + double mean_num_cells_in_queue, mean_time_cells_in_queue; + uint32_t processed_cells; if (CIRCUIT_IS_ORIGIN(circ)) return; orcirc = TO_OR_CIRCUIT(circ); if (!orcirc->processed_cells) return; - if (!circuits_for_buffer_stats) - circuits_for_buffer_stats = smartlist_create(); start_of_interval = (circ->timestamp_created.tv_sec > start_of_buffer_stats_interval) ? circ->timestamp_created.tv_sec : @@ -2388,17 +2406,18 @@ rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval) interval_length = (int) (end_of_interval - start_of_interval); if (interval_length <= 0) return; - stat = tor_malloc_zero(sizeof(circ_buffer_stats_t)); - stat->processed_cells = orcirc->processed_cells; + processed_cells = orcirc->processed_cells; /* 1000.0 for s -> ms; 2.0 because of app-ward and exit-ward queues */ - stat->mean_num_cells_in_queue = (double) orcirc->total_cell_waiting_time / + mean_num_cells_in_queue = (double) orcirc->total_cell_waiting_time / (double) interval_length / 1000.0 / 2.0; - stat->mean_time_cells_in_queue = + mean_time_cells_in_queue = (double) orcirc->total_cell_waiting_time / (double) orcirc->processed_cells; - smartlist_add(circuits_for_buffer_stats, stat); orcirc->total_cell_waiting_time = 0; orcirc->processed_cells = 0; + rep_hist_add_buffer_stats(mean_num_cells_in_queue, + mean_time_cells_in_queue, + processed_cells); } /** Sorting helper: return -1, 1, or 0 based on comparison of two @@ -2420,135 +2439,160 @@ _buffer_stats_compare_entries(const void **_a, const void **_b) void rep_hist_buffer_stats_term(void) { - start_of_buffer_stats_interval = 0; + rep_hist_reset_buffer_stats(0); +} + +/** Clear history of circuit statistics and set the measurement interval + * start to <b>now</b>. */ +void +rep_hist_reset_buffer_stats(time_t now) +{ if (!circuits_for_buffer_stats) circuits_for_buffer_stats = smartlist_create(); SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, stat, tor_free(stat)); smartlist_clear(circuits_for_buffer_stats); + start_of_buffer_stats_interval = now; } -/** Write buffer statistics to $DATADIR/stats/buffer-stats and return when - * we would next want to write exit stats. */ -time_t -rep_hist_buffer_stats_write(time_t now) +/** Return a newly allocated string containing the buffer statistics until + * <b>now</b>, or NULL if we're not collecting buffer stats. */ +char * +rep_hist_format_buffer_stats(time_t now) { - char *statsdir = NULL, *filename = NULL; - char written[ISO_TIME_LEN+1]; - open_file_t *open_file = NULL; - FILE *out; #define SHARES 10 int processed_cells[SHARES], circs_in_share[SHARES], number_of_circuits, i; double queued_cells[SHARES], time_in_queue[SHARES]; - smartlist_t *str_build = NULL; - char *str = NULL, *buf = NULL; - circuit_t *circ; + char *buf = NULL; + smartlist_t *processed_cells_strings, *queued_cells_strings, + *time_in_queue_strings; + char *processed_cells_string, *queued_cells_string, + *time_in_queue_string; + char t[ISO_TIME_LEN+1]; + char *result; if (!start_of_buffer_stats_interval) - return 0; /* Not initialized. */ - if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now) - goto done; /* Not ready to write */ - - str_build = smartlist_create(); + return NULL; /* Not initialized. */ - /* add current circuits to stats */ - for (circ = _circuit_get_global_list(); circ; circ = circ->next) - rep_hist_buffer_stats_add_circ(circ, now); - /* calculate deciles */ + /* Calculate deciles if we saw at least one circuit. */ memset(processed_cells, 0, SHARES * sizeof(int)); memset(circs_in_share, 0, SHARES * sizeof(int)); memset(queued_cells, 0, SHARES * sizeof(double)); memset(time_in_queue, 0, SHARES * sizeof(double)); if (!circuits_for_buffer_stats) circuits_for_buffer_stats = smartlist_create(); - smartlist_sort(circuits_for_buffer_stats, - _buffer_stats_compare_entries); number_of_circuits = smartlist_len(circuits_for_buffer_stats); - if (number_of_circuits < 1) { - log_info(LD_HIST, "Attempt to write cell statistics to disk failed. " - "We haven't seen a single circuit to report about."); - goto done; + if (number_of_circuits > 0) { + smartlist_sort(circuits_for_buffer_stats, + _buffer_stats_compare_entries); + i = 0; + SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats, + circ_buffer_stats_t *, stat) + { + int share = i++ * SHARES / number_of_circuits; + processed_cells[share] += stat->processed_cells; + queued_cells[share] += stat->mean_num_cells_in_queue; + time_in_queue[share] += stat->mean_time_cells_in_queue; + circs_in_share[share]++; + } + SMARTLIST_FOREACH_END(stat); } - i = 0; - SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats, - circ_buffer_stats_t *, stat) - { - int share = i++ * SHARES / number_of_circuits; - processed_cells[share] += stat->processed_cells; - queued_cells[share] += stat->mean_num_cells_in_queue; - time_in_queue[share] += stat->mean_time_cells_in_queue; - circs_in_share[share]++; - } - SMARTLIST_FOREACH_END(stat); - /* clear buffer stats history */ - SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, - stat, tor_free(stat)); - smartlist_clear(circuits_for_buffer_stats); - /* write to file */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) - goto done; - filename = get_datadir_fname2("stats", "buffer-stats"); - out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT, - 0600, &open_file); - if (!out) - goto done; - format_iso_time(written, now); - if (fprintf(out, "cell-stats-end %s (%d s)\n", written, - (unsigned) (now - start_of_buffer_stats_interval)) < 0) - goto done; + + /* Write deciles to strings. */ + processed_cells_strings = smartlist_create(); + queued_cells_strings = smartlist_create(); + time_in_queue_strings = smartlist_create(); for (i = 0; i < SHARES; i++) { tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 : processed_cells[i] / circs_in_share[i]); - smartlist_add(str_build, buf); + smartlist_add(processed_cells_strings, buf); } - str = smartlist_join_strings(str_build, ",", 0, NULL); - if (fprintf(out, "cell-processed-cells %s\n", str) < 0) - goto done; - tor_free(str); - SMARTLIST_FOREACH(str_build, char *, c, tor_free(c)); - smartlist_clear(str_build); for (i = 0; i < SHARES; i++) { tor_asprintf(&buf, "%.2f", circs_in_share[i] == 0 ? 0.0 : queued_cells[i] / (double) circs_in_share[i]); - smartlist_add(str_build, buf); + smartlist_add(queued_cells_strings, buf); } - str = smartlist_join_strings(str_build, ",", 0, NULL); - if (fprintf(out, "cell-queued-cells %s\n", str) < 0) - goto done; - tor_free(str); - SMARTLIST_FOREACH(str_build, char *, c, tor_free(c)); - smartlist_clear(str_build); for (i = 0; i < SHARES; i++) { tor_asprintf(&buf, "%.0f", circs_in_share[i] == 0 ? 0.0 : time_in_queue[i] / (double) circs_in_share[i]); - smartlist_add(str_build, buf); + smartlist_add(time_in_queue_strings, buf); } - str = smartlist_join_strings(str_build, ",", 0, NULL); - if (fprintf(out, "cell-time-in-queue %s\n", str) < 0) - goto done; - tor_free(str); - SMARTLIST_FOREACH(str_build, char *, c, tor_free(c)); - smartlist_free(str_build); - str_build = NULL; - if (fprintf(out, "cell-circuits-per-decile %d\n", - (number_of_circuits + SHARES - 1) / SHARES) < 0) + + /* Join all observations in single strings. */ + processed_cells_string = smartlist_join_strings(processed_cells_strings, + ",", 0, NULL); + queued_cells_string = smartlist_join_strings(queued_cells_strings, + ",", 0, NULL); + time_in_queue_string = smartlist_join_strings(time_in_queue_strings, + ",", 0, NULL); + SMARTLIST_FOREACH(processed_cells_strings, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(queued_cells_strings, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(time_in_queue_strings, char *, cp, tor_free(cp)); + smartlist_free(processed_cells_strings); + smartlist_free(queued_cells_strings); + smartlist_free(time_in_queue_strings); + + /* Put everything together. */ + format_iso_time(t, now); + tor_asprintf(&result, "cell-stats-end %s (%d s)\n" + "cell-processed-cells %s\n" + "cell-queued-cells %s\n" + "cell-time-in-queue %s\n" + "cell-circuits-per-decile %d\n", + t, (unsigned) (now - start_of_buffer_stats_interval), + processed_cells_string, + queued_cells_string, + time_in_queue_string, + (number_of_circuits + SHARES - 1) / SHARES); + tor_free(processed_cells_string); + tor_free(queued_cells_string); + tor_free(time_in_queue_string); + return result; +#undef SHARES +} + +/** If 24 hours have passed since the beginning of the current buffer + * stats period, write buffer stats to $DATADIR/stats/buffer-stats + * (possibly overwriting an existing file) and reset counters. Return + * when we would next want to write buffer stats or 0 if we never want to + * write. */ +time_t +rep_hist_buffer_stats_write(time_t now) +{ + circuit_t *circ; + char *statsdir = NULL, *filename = NULL, *str = NULL; + + if (!start_of_buffer_stats_interval) + return 0; /* Not initialized. */ + if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now) + goto done; /* Not ready to write */ + + /* Add open circuits to the history. */ + for (circ = _circuit_get_global_list(); circ; circ = circ->next) { + rep_hist_buffer_stats_add_circ(circ, now); + } + + /* Generate history string. */ + str = rep_hist_format_buffer_stats(now); + + /* Reset both buffer history and counters of open circuits. */ + rep_hist_reset_buffer_stats(now); + + /* Try to write to disk. */ + statsdir = get_datadir_fname("stats"); + if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { + log_warn(LD_HIST, "Unable to create stats/ directory!"); goto done; - finish_writing_to_file(open_file); - open_file = NULL; - start_of_buffer_stats_interval = now; + } + filename = get_datadir_fname2("stats", "buffer-stats"); + if (write_str_to_file(filename, str, 0) < 0) + log_warn(LD_HIST, "Unable to write buffer stats to disk!"); + done: - if (open_file) - abort_writing_to_file(open_file); + tor_free(str); tor_free(filename); tor_free(statsdir); - if (str_build) { - SMARTLIST_FOREACH(str_build, char *, c, tor_free(c)); - smartlist_free(str_build); - } - tor_free(str); -#undef SHARES return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL; } diff --git a/src/or/rephist.h b/src/or/rephist.h index 5748748a80..e590659441 100644 --- a/src/or/rephist.h +++ b/src/or/rephist.h @@ -77,6 +77,10 @@ void rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval); time_t rep_hist_buffer_stats_write(time_t now); void rep_hist_buffer_stats_term(void); +void rep_hist_add_buffer_stats(double mean_num_cells_in_queue, + double mean_time_cells_in_queue, uint32_t processed_cells); +char *rep_hist_format_buffer_stats(time_t now); +void rep_hist_reset_buffer_stats(time_t now); void rep_hist_conn_stats_init(time_t now); void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read, diff --git a/src/or/status.c b/src/or/status.c index acb8ba4144..3e4cb779a3 100644 --- a/src/or/status.c +++ b/src/or/status.c @@ -103,7 +103,7 @@ log_heartbeat(time_t now) bw_sent = bytes_to_usage(get_bytes_written()); log_fn(LOG_NOTICE, LD_HEARTBEAT, "Heartbeat: Tor's uptime is %s, with %d " - "circuits open. I've pushed %s and received %s.", + "circuits open. I've sent %s and received %s.", uptime, count_circuits(),bw_sent,bw_rcvd); tor_free(uptime); |