diff options
Diffstat (limited to 'src/or')
130 files changed, 4336 insertions, 3218 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 7e92633601..96ce275578 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -90,34 +90,47 @@ addressmap_init(void) virtaddress_reversemap = strmap_new(); } +#define addressmap_ent_free(ent) \ + FREE_AND_NULL(addressmap_entry_t, addressmap_ent_free_, (ent)) + /** Free the memory associated with the addressmap entry <b>_ent</b>. */ static void -addressmap_ent_free(void *_ent) +addressmap_ent_free_(addressmap_entry_t *ent) { - addressmap_entry_t *ent; - if (!_ent) + if (!ent) return; - ent = _ent; tor_free(ent->new_address); tor_free(ent); } +static void +addressmap_ent_free_void(void *ent) +{ + addressmap_ent_free_(ent); +} + +#define addressmap_virtaddress_ent_free(ent) \ + FREE_AND_NULL(virtaddress_entry_t, addressmap_virtaddress_ent_free_, (ent)) + /** Free storage held by a virtaddress_entry_t* entry in <b>_ent</b>. */ static void -addressmap_virtaddress_ent_free(void *_ent) +addressmap_virtaddress_ent_free_(virtaddress_entry_t *ent) { - virtaddress_entry_t *ent; - if (!_ent) + if (!ent) return; - - ent = _ent; tor_free(ent->ipv4_address); tor_free(ent->ipv6_address); tor_free(ent->hostname_address); tor_free(ent); } +static void +addressmap_virtaddress_ent_free_void(void *ent) +{ + addressmap_virtaddress_ent_free_(ent); +} + /** Remove <b>address</b> (which must map to <b>ent</b>) from the * virtual address map. */ static void @@ -311,10 +324,10 @@ addressmap_clean(time_t now) void addressmap_free_all(void) { - strmap_free(addressmap, addressmap_ent_free); + strmap_free(addressmap, addressmap_ent_free_void); addressmap = NULL; - strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free); + strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free_void); virtaddress_reversemap = NULL; } @@ -541,7 +554,7 @@ addressmap_have_mapping(const char *address, int update_expiry) * (virtual address mapping) from the controller.) * * <b>new_address</b> should be a newly dup'ed string, which we'll use or - * free as appropriate. We will leave address alone. + * free as appropriate. We will leave <b>address</b> alone. * * If <b>wildcard_addr</b> is true, then the mapping will match any address * equal to <b>address</b>, or any address ending with a period followed by @@ -554,7 +567,6 @@ addressmap_have_mapping(const char *address, int update_expiry) * <b>wildcard_new_addr</b>, remove any mappings that exist from * <b>address</b>. * - * * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is * not set. */ void diff --git a/src/or/bridges.c b/src/or/bridges.c index 0b1bbbd158..383c33fa61 100644 --- a/src/or/bridges.c +++ b/src/or/bridges.c @@ -53,7 +53,10 @@ struct bridge_info_t { smartlist_t *socks_args; }; -static void bridge_free(bridge_info_t *bridge); +#define bridge_free(bridge) \ + FREE_AND_NULL(bridge_info_t, bridge_free_, (bridge)) + +static void bridge_free_(bridge_info_t *bridge); static void rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node); @@ -101,7 +104,7 @@ clear_bridge_list(void) /** Free the bridge <b>bridge</b>. */ static void -bridge_free(bridge_info_t *bridge) +bridge_free_(bridge_info_t *bridge) { if (!bridge) return; @@ -716,7 +719,6 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) if (node->ri) { routerinfo_t *ri = node->ri; tor_addr_from_ipv4h(&addr, ri->addr); - if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && bridge->port == ri->or_port) || (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && @@ -774,16 +776,58 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) routerstatus_t *rs = node->rs; tor_addr_from_ipv4h(&addr, rs->addr); - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == rs->or_port) { + if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == rs->or_port) || + (!tor_addr_compare(&bridge->addr, &rs->ipv6_addr, CMP_EXACT) && + bridge->port == rs->ipv6_orport)) { /* they match, so no need to do anything */ } else { - rs->addr = tor_addr_to_ipv4h(&bridge->addr); - rs->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerstatus for '%s' to match " - "configured address %s.", - rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); + if (tor_addr_family(&bridge->addr) == AF_INET) { + rs->addr = tor_addr_to_ipv4h(&bridge->addr); + rs->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerstatus for '%s' to match " + "configured address %s.", + rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); + /* set IPv6 preferences even if there is no ri */ + } else if (tor_addr_family(&bridge->addr) == AF_INET6) { + tor_addr_copy(&rs->ipv6_addr, &bridge->addr); + rs->ipv6_orport = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerstatus for '%s' to match configured" + " address %s.", + rs->nickname, fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); + } else { + log_err(LD_BUG, "Address family not supported: %d.", + tor_addr_family(&bridge->addr)); + return; + } + } + + if (options->ClientPreferIPv6ORPort == -1) { + /* Mark which address to use based on which bridge_t we got. */ + node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && + !tor_addr_is_null(&node->rs->ipv6_addr)); + } else { + /* Mark which address to use based on user preference */ + node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) && + !tor_addr_is_null(&node->rs->ipv6_addr)); + } + + /* XXXipv6 we lack support for falling back to another address for + the same relay, warn the user */ + if (!tor_addr_is_null(&rs->ipv6_addr)) { + tor_addr_port_t ap; + node_get_pref_orport(node, &ap); + log_notice(LD_CONFIG, + "Bridge '%s' has both an IPv4 and an IPv6 address. " + "Will prefer using its %s address (%s) based on %s.", + rs->nickname, + node->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addrport(&ap.addr, ap.port), + options->ClientPreferIPv6ORPort == -1 ? + "the configured Bridge address" : + "ClientPreferIPv6ORPort"); } } } diff --git a/src/or/channel.c b/src/or/channel.c index 0b5a7fde90..345a90a004 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1,3 +1,4 @@ + /* * Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ @@ -5,9 +6,8 @@ * \file channel.c * * \brief OR/OP-to-OR channel abstraction layer. A channel's job is to - * transfer cells from Tor instance to Tor instance. - * Currently, there is only one implementation of the channel abstraction: in - * channeltls.c. + * transfer cells from Tor instance to Tor instance. Currently, there is only + * one implementation of the channel abstraction: in channeltls.c. * * Channels are a higher-level abstraction than or_connection_t: In general, * any means that two Tor relays use to exchange cells, or any means that a @@ -24,16 +24,28 @@ * connection. * * Every channel implementation is responsible for being able to transmit - * cells that are added to it with channel_write_cell() and related functions, - * and to receive incoming cells with the channel_queue_cell() and related - * functions. See the channel_t documentation for more information. - * - * When new cells arrive on a channel, they are passed to cell handler - * functions, which can be set by channel_set_cell_handlers() - * functions. (Tor's cell handlers are in command.c.) - * - * Tor flushes cells to channels from relay.c in - * channel_flush_from_first_active_circuit(). + * cells that are passed to it + * + * For *inbound* cells, the entry point is: channel_process_cell(). It takes a + * cell and will pass it to the cell handler set by + * channel_set_cell_handlers(). Currently, this is passed back to the command + * subsystem which is command_process_cell(). + * + * NOTE: For now, the seperation between channels and specialized channels + * (like channeltls) is not that well defined. So the channeltls layer calls + * channel_process_cell() which originally comes from the connection subsytem. + * This should be hopefully be fixed with #23993. + * + * For *outbound* cells, the entry point is: channel_write_packed_cell(). + * Only packed cells are dequeued from the circuit queue by the scheduler + * which uses channel_flush_from_first_active_circuit() to decide which cells + * to flush from which circuit on the channel. They are then passed down to + * the channel subsystem. This calls the low layer with the function pointer + * .write_packed_cell(). + * + * Each specialized channel (currently only channeltls_t) MUST implement a + * series of function found in channel_t. See channel.h for more + * documentation. **/ /* @@ -112,59 +124,6 @@ HANDLE_IMPL(channel, channel_s,) /* Counter for ID numbers */ static uint64_t n_channels_allocated = 0; -/* - * Channel global byte/cell counters, for statistics and for scheduler high - * /low-water marks. - */ - -/* - * Total number of cells ever given to any channel with the - * channel_write_*_cell() functions. - */ - -static uint64_t n_channel_cells_queued = 0; - -/* - * Total number of cells ever passed to a channel lower layer with the - * write_*_cell() methods. - */ - -static uint64_t n_channel_cells_passed_to_lower_layer = 0; - -/* - * Current number of cells in all channel queues; should be - * n_channel_cells_queued - n_channel_cells_passed_to_lower_layer. - */ - -static uint64_t n_channel_cells_in_queues = 0; - -/* - * Total number of bytes for all cells ever queued to a channel and - * counted in n_channel_cells_queued. - */ - -static uint64_t n_channel_bytes_queued = 0; - -/* - * Total number of bytes for all cells ever passed to a channel lower layer - * and counted in n_channel_cells_passed_to_lower_layer. - */ - -static uint64_t n_channel_bytes_passed_to_lower_layer = 0; - -/* - * Current number of bytes in all channel queues; should be - * n_channel_bytes_queued - n_channel_bytes_passed_to_lower_layer. - */ - -static uint64_t n_channel_bytes_in_queues = 0; - -/* - * Current total estimated queue size *including lower layer queues and - * transmit overhead* - */ - -STATIC uint64_t estimated_total_queue_size = 0; /* Digest->channel map * @@ -201,40 +160,15 @@ HT_PROTOTYPE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, HT_GENERATE2(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_) -static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q); -#if 0 -static int cell_queue_entry_is_padding(cell_queue_entry_t *q); -#endif -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell); -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell); -static int is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out); - -static void channel_assert_counter_consistency(void); - /* Functions to maintain the digest map */ -static void channel_add_to_digest_map(channel_t *chan); static void channel_remove_from_digest_map(channel_t *chan); -/* - * Flush cells from just the outgoing queue without trying to get them - * from circuits; used internall by channel_flush_some_cells(). - */ -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells); -static void channel_force_free(channel_t *chan); -static void -channel_free_list(smartlist_t *channels, int mark_for_close); -static void -channel_listener_free_list(smartlist_t *channels, int mark_for_close); -static void channel_listener_force_free(channel_listener_t *chan_l); -static size_t channel_get_cell_queue_entry_size(channel_t *chan, - cell_queue_entry_t *q); -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); +static void channel_force_xfree(channel_t *chan); +static void channel_free_list(smartlist_t *channels, + int mark_for_close); +static void channel_listener_free_list(smartlist_t *channels, + int mark_for_close); +static void channel_listener_force_xfree(channel_listener_t *chan_l); /*********************************** * Channel state utility functions * @@ -628,7 +562,7 @@ channel_listener_unregister(channel_listener_t *chan_l) * already exist. */ -static void +STATIC void channel_add_to_digest_map(channel_t *chan) { channel_idmap_entry_t *ent, search; @@ -676,33 +610,6 @@ channel_remove_from_digest_map(channel_t *chan) /* Assert that there is a digest */ tor_assert(!tor_digest_is_zero(chan->identity_digest)); -#if 0 - /* Make sure we have a map */ - if (!channel_identity_map) { - /* - * No identity map, so we can't find it by definition. This - * case is similar to digestmap_get() failing below. - */ - log_warn(LD_BUG, - "Trying to remove channel %p (global ID " U64_FORMAT ") " - "with digest %s from identity map, but didn't have any identity " - "map", - chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - /* Clear out its next/prev pointers */ - if (chan->next_with_same_id) { - chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; - } - if (chan->prev_with_same_id) { - chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; - } - chan->next_with_same_id = NULL; - chan->prev_with_same_id = NULL; - - return; - } -#endif /* 0 */ - /* Pull it out of its list, wherever that list is */ TOR_LIST_REMOVE(chan, next_with_same_id); @@ -936,10 +843,6 @@ channel_init(channel_t *chan) /* Warn about exhausted circuit IDs no more than hourly. */ chan->last_warned_circ_ids_exhausted.rate = 3600; - /* Initialize queues. */ - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - /* Initialize list entries. */ memset(&chan->next_with_same_id, 0, sizeof(chan->next_with_same_id)); @@ -979,7 +882,7 @@ channel_init_listener(channel_listener_t *chan_l) */ void -channel_free(channel_t *chan) +channel_free_(channel_t *chan) { if (!chan) return; @@ -1022,8 +925,6 @@ channel_free(channel_t *chan) chan->cmux = NULL; } - /* We're in CLOSED or ERROR, so the cell queue is already empty */ - tor_free(chan); } @@ -1034,7 +935,7 @@ channel_free(channel_t *chan) */ void -channel_listener_free(channel_listener_t *chan_l) +channel_listener_free_(channel_listener_t *chan_l) { if (!chan_l) return; @@ -1052,11 +953,6 @@ channel_listener_free(channel_listener_t *chan_l) /* Call a free method if there is one */ if (chan_l->free_fn) chan_l->free_fn(chan_l); - /* - * We're in CLOSED or ERROR, so the incoming channel queue is already - * empty. - */ - tor_free(chan_l); } @@ -1067,9 +963,8 @@ channel_listener_free(channel_listener_t *chan_l) */ static void -channel_force_free(channel_t *chan) +channel_force_xfree(channel_t *chan) { - cell_queue_entry_t *cell, *cell_tmp; tor_assert(chan); log_debug(LD_CHANNEL, @@ -1103,18 +998,6 @@ channel_force_free(channel_t *chan) chan->cmux = NULL; } - /* We might still have a cell queue; kill it */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - - /* Outgoing cell queue is similar, but we can have to free packed cells */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - tor_free(chan); } @@ -1125,7 +1008,7 @@ channel_force_free(channel_t *chan) */ static void -channel_listener_force_free(channel_listener_t *chan_l) +channel_listener_force_xfree(channel_listener_t *chan_l) { tor_assert(chan_l); @@ -1156,24 +1039,6 @@ channel_listener_force_free(channel_listener_t *chan_l) } /** - * Return the current registered listener for a channel listener - * - * This function returns a function pointer to the current registered - * handler for new incoming channels on a channel listener. - */ - -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING) - return chan_l->listener; - - return NULL; -} - -/** * Set the listener for a channel listener * * This function sets the handler for new incoming channels on a channel @@ -1237,8 +1102,7 @@ channel_get_var_cell_handler(channel_t *chan) * Set both cell handlers for a channel * * This function sets both the fixed-length and variable length cell handlers - * for a channel and processes any incoming cells that had been blocked in the - * queue because none were available. + * for a channel. */ void @@ -1247,8 +1111,6 @@ channel_set_cell_handlers(channel_t *chan, channel_var_cell_handler_fn_ptr var_cell_handler) { - int try_again = 0; - tor_assert(chan); tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan)); @@ -1259,21 +1121,9 @@ channel_set_cell_handlers(channel_t *chan, "Setting var_cell_handler callback for channel %p to %p", chan, var_cell_handler); - /* Should we try the queue? */ - if (cell_handler && - cell_handler != chan->cell_handler) try_again = 1; - if (var_cell_handler && - var_cell_handler != chan->var_cell_handler) try_again = 1; - /* Change them */ chan->cell_handler = cell_handler; chan->var_cell_handler = var_cell_handler; - - /* Re-run the queue if we have one and there's any reason to */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue) && - try_again && - (chan->cell_handler || - chan->var_cell_handler)) channel_process_cells(chan); } /* @@ -1400,36 +1250,6 @@ channel_close_from_lower_layer(channel_t *chan) } /** - * Close a channel listener from the lower layer - * - * Notify the channel code that the channel listener is being closed due to a - * non-error condition in the lower layer. This does not call the close() - * method, since the lower layer already knows. - */ - -void -channel_listener_close_from_lower_layer(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer event", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** * Notify that the channel is being closed due to an error condition * * This function is called by the lower layer implementing the transport @@ -1458,37 +1278,6 @@ channel_close_for_error(channel_t *chan) } /** - * Notify that the channel listener is being closed due to an error condition - * - * This function is called by the lower layer implementing the transport - * when a channel listener must be closed due to an error condition. This - * does not call the channel listener's close method, since the lower layer - * already knows. - */ - -void -channel_listener_close_for_error(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer error", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** * Notify that the lower layer is finished closing the channel * * This function should be called by the lower layer when a channel @@ -1522,33 +1311,6 @@ channel_closed(channel_t *chan) } /** - * Notify that the lower layer is finished closing the channel listener - * - * This function should be called by the lower layer when a channel listener - * is finished closing and it should be regarded as inactive and - * freed by the channel code. - */ - -void -channel_listener_closed(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR); - - /* No-op if already inactive */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); - } else { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR); - } -} - -/** * Clear the identity_digest of a channel * * This function clears the identity digest of the remote endpoint for a @@ -1638,7 +1400,7 @@ channel_set_identity_digest(channel_t *chan, } /** - * Clear the remote end metadata (identity_digest/nickname) of a channel + * Clear the remote end metadata (identity_digest) of a channel * * This function clears all the remote end info from a channel; this is * intended for use by the lower layer. @@ -1665,419 +1427,95 @@ channel_clear_remote_end(channel_t *chan) memset(chan->identity_digest, 0, sizeof(chan->identity_digest)); - tor_free(chan->nickname); } /** - * Set the remote end metadata (identity_digest/nickname) of a channel + * Write to a channel the given packed cell. * - * This function sets new remote end info on a channel; this is intended - * for use by the lower layer. - */ - -void -channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname) -{ - int was_in_digest_map, should_be_in_digest_map, state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Setting remote endpoint identity on channel %p with " - "global ID " U64_FORMAT " to nickname %s, digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - nickname ? nickname : "(null)", - identity_digest ? - hex_str(identity_digest, DIGEST_LEN) : "(null)"); - - state_not_in_map = CHANNEL_CONDEMNED(chan); - - was_in_digest_map = - !state_not_in_map && - chan->registered && - !tor_digest_is_zero(chan->identity_digest); - should_be_in_digest_map = - !state_not_in_map && - chan->registered && - (identity_digest && - !tor_digest_is_zero(identity_digest)); - - if (was_in_digest_map) - /* We should always remove it; we'll add it back if we're writing - * in a new digest. - */ - channel_remove_from_digest_map(chan); - - if (identity_digest) { - memcpy(chan->identity_digest, - identity_digest, - sizeof(chan->identity_digest)); - - } else { - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); - } - - tor_free(chan->nickname); - if (nickname) - chan->nickname = tor_strdup(nickname); - - /* Put it in the digest map if we should */ - if (should_be_in_digest_map) - channel_add_to_digest_map(chan); -} - -/** - * Duplicate a cell queue entry; this is a shallow copy intended for use - * in channel_write_cell_queue_entry(). - */ - -static cell_queue_entry_t * -cell_queue_entry_dup(cell_queue_entry_t *q) -{ - cell_queue_entry_t *rv = NULL; - - tor_assert(q); - - rv = tor_malloc(sizeof(*rv)); - memcpy(rv, q, sizeof(*rv)); - - return rv; -} - -/** - * Free a cell_queue_entry_t; the handed_off parameter indicates whether - * the contents were passed to the lower layer (it is responsible for - * them) or not (we should free). - */ - -STATIC void -cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) -{ - if (!q) return; - - if (!handed_off) { - /* - * If we handed it off, the recipient becomes responsible (or - * with packed cells the channel_t subclass calls packed_cell - * free after writing out its contents; see, e.g., - * channel_tls_write_packed_cell_method(). Otherwise, we have - * to take care of it here if possible. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - /* - * There doesn't seem to be a cell_free() function anywhere in the - * pre-channel code; just use tor_free() - */ - tor_free(q->u.fixed.cell); - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - packed_cell_free(q->u.packed.packed_cell); - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - /* - * This one's in connection_or.c; it'd be nice to figure out the - * whole flow of cells from one end to the other and factor the - * cell memory management functions like this out of the specific - * TLS lower layer. - */ - var_cell_free(q->u.var.var_cell); - } - break; - default: - /* - * Nothing we can do if we don't know the type; this will - * have been warned about elsewhere. - */ - break; - } - } - tor_free(q); -} - -#if 0 -/** - * Check whether a cell queue entry is padding; this is a helper function - * for channel_write_cell_queue_entry() - */ - -static int -cell_queue_entry_is_padding(cell_queue_entry_t *q) -{ - tor_assert(q); - - if (q->type == CELL_QUEUE_FIXED) { - if (q->u.fixed.cell) { - if (q->u.fixed.cell->command == CELL_PADDING || - q->u.fixed.cell->command == CELL_VPADDING) { - return 1; - } - } - } else if (q->type == CELL_QUEUE_VAR) { - if (q->u.var.var_cell) { - if (q->u.var.var_cell->command == CELL_PADDING || - q->u.var.var_cell->command == CELL_VPADDING) { - return 1; - } - } - } - - return 0; -} -#endif /* 0 */ - -/** - * Allocate a new cell queue entry for a fixed-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_FIXED; - q->u.fixed.cell = cell; - - return q; -} - -/** - * Allocate a new cell queue entry for a variable-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(var_cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_VAR; - q->u.var.var_cell = var_cell; - - return q; -} - -/** - * Ask how big the cell contained in a cell_queue_entry_t is - */ - -static size_t -channel_get_cell_queue_entry_size(channel_t *chan, cell_queue_entry_t *q) -{ - size_t rv = 0; - - tor_assert(chan); - tor_assert(q); - - switch (q->type) { - case CELL_QUEUE_FIXED: - rv = get_cell_network_size(chan->wide_circ_ids); - break; - case CELL_QUEUE_VAR: - rv = get_var_cell_header_size(chan->wide_circ_ids) + - (q->u.var.var_cell ? q->u.var.var_cell->payload_len : 0); - break; - case CELL_QUEUE_PACKED: - rv = get_cell_network_size(chan->wide_circ_ids); - break; - default: - tor_assert_nonfatal_unreached_once(); - } - - return rv; -} - -/** - * Write to a channel based on a cell_queue_entry_t * - * Given a cell_queue_entry_t filled out by the caller, try to send the cell - * and queue it if we can't. + * Two possible errors can happen. Either the channel is not opened or the + * lower layer (specialized channel) failed to write it. In both cases, it is + * the caller responsability to free the cell. */ - -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) +static int +write_packed_cell(channel_t *chan, packed_cell_t *cell) { - int result = 0, sent = 0; - cell_queue_entry_t *tmp = NULL; + int ret = -1; size_t cell_bytes; tor_assert(chan); - tor_assert(q); + tor_assert(cell); /* Assert that the state makes sense for a cell write */ tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan)); { circid_t circ_id; - if (is_destroy_cell(chan, q, &circ_id)) { + if (packed_cell_is_destroy(chan, cell, &circ_id)) { channel_note_destroy_not_pending(chan, circ_id); } } /* For statistical purposes, figure out how big this cell is */ - cell_bytes = channel_get_cell_queue_entry_size(chan, q); + cell_bytes = get_cell_network_size(chan->wide_circ_ids); /* Can we send it right out? If so, try */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) && - CHANNEL_IS_OPEN(chan)) { - /* Pick the right write function for this cell type and save the result */ - switch (q->type) { - case CELL_QUEUE_FIXED: - tor_assert(chan->write_cell); - tor_assert(q->u.fixed.cell); - result = chan->write_cell(chan, q->u.fixed.cell); - break; - case CELL_QUEUE_PACKED: - tor_assert(chan->write_packed_cell); - tor_assert(q->u.packed.packed_cell); - result = chan->write_packed_cell(chan, q->u.packed.packed_cell); - break; - case CELL_QUEUE_VAR: - tor_assert(chan->write_var_cell); - tor_assert(q->u.var.var_cell); - result = chan->write_var_cell(chan, q->u.var.var_cell); - break; - default: - tor_assert(1); - } - - /* Check if we got it out */ - if (result > 0) { - sent = 1; - /* Timestamp for transmission */ - channel_timestamp_xmit(chan); - /* If we're here the queue is empty, so it's drained too */ - channel_timestamp_drained(chan); - /* Update the counter */ - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_bytes; - /* Update global counters */ - ++n_channel_cells_queued; - ++n_channel_cells_passed_to_lower_layer; - n_channel_bytes_queued += cell_bytes; - n_channel_bytes_passed_to_lower_layer += cell_bytes; - channel_assert_counter_consistency(); - } + if (!CHANNEL_IS_OPEN(chan)) { + goto done; } - if (!sent) { - /* Not sent, queue it */ - /* - * We have to copy the queue entry passed in, since the caller probably - * used the stack. - */ - tmp = cell_queue_entry_dup(q); - TOR_SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next); - /* Update global counters */ - ++n_channel_cells_queued; - ++n_channel_cells_in_queues; - n_channel_bytes_queued += cell_bytes; - n_channel_bytes_in_queues += cell_bytes; - channel_assert_counter_consistency(); - /* Update channel queue size */ - chan->bytes_in_queue += cell_bytes; - /* Try to process the queue? */ - if (CHANNEL_IS_OPEN(chan)) channel_flush_cells(chan); + /* Write the cell on the connection's outbuf. */ + if (chan->write_packed_cell(chan, cell) < 0) { + goto done; } + /* Timestamp for transmission */ + channel_timestamp_xmit(chan); + /* Update the counter */ + ++(chan->n_cells_xmitted); + chan->n_bytes_xmitted += cell_bytes; + /* Successfully sent the cell. */ + ret = 0; + + done: + return ret; } -/** Write a generic cell type to a channel +/** + * Write a packed cell to a channel + * + * Write a packed cell to a channel using the write_cell() method. This is + * called by the transport-independent code to deliver a packed cell to a + * channel for transmission. * - * Write a generic cell to a channel. It is called by channel_write_cell(), - * channel_write_var_cell() and channel_write_packed_cell() in order to reduce - * code duplication. Notice that it takes cell as pointer of type void, - * this can be dangerous because no type check is performed. + * Return 0 on success else a negative value. In both cases, the caller should + * not access the cell anymore, it is freed both on success and error. */ - -void -channel_write_cell_generic_(channel_t *chan, const char *cell_type, - void *cell, cell_queue_entry_t *q) +int +channel_write_packed_cell(channel_t *chan, packed_cell_t *cell) { + int ret = -1; tor_assert(chan); tor_assert(cell); if (CHANNEL_IS_CLOSING(chan)) { - log_debug(LD_CHANNEL, "Discarding %c %p on closing channel %p with " - "global ID "U64_FORMAT, *cell_type, cell, chan, + log_debug(LD_CHANNEL, "Discarding %p on closing channel %p with " + "global ID "U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - tor_free(cell); - return; + goto end; } log_debug(LD_CHANNEL, - "Writing %c %p to channel %p with global ID " - U64_FORMAT, *cell_type, - cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - - channel_write_cell_queue_entry(chan, q); - /* Update the queue size estimate */ - channel_update_xmit_queue_size(chan); -} - -/** - * Write a cell to a channel - * - * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); - * it is called by the transport-independent code to deliver a cell to a - * channel for transmission. - */ + "Writing %p to channel %p with global ID " + U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); -void -channel_write_cell(channel_t *chan, cell_t *cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_FIXED; - q.u.fixed.cell = cell; - channel_write_cell_generic_(chan, "cell_t", cell, &q); -} + ret = write_packed_cell(chan, cell); -/** - * Write a packed cell to a channel - * - * Write a packed cell to a channel using the write_cell() method. This is - * called by the transport-independent code to deliver a packed cell to a - * channel for transmission. - */ - -void -channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_PACKED; - q.u.packed.packed_cell = packed_cell; - channel_write_cell_generic_(chan, "packed_cell_t", packed_cell, &q); -} - -/** - * Write a variable-length cell to a channel - * - * Write a variable-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels - * connection_or_write_var_cell_to_buf(); it's called by the transport- - * independent code to deliver a var_cell to a channel for transmission. - */ - -void -channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_VAR; - q.u.var.var_cell = var_cell; - channel_write_cell_generic_(chan, "var_cell_t", var_cell, &q); + end: + /* Whatever happens, we free the cell. Either an error occured or the cell + * was put on the connection outbuf, both cases we have ownership of the + * cell and we free it. */ + packed_cell_free(cell); + return ret; } /** @@ -2119,15 +1557,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) tor_assert(chan->reason_for_closing != CHANNEL_NOT_CLOSING); } - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - log_debug(LD_CHANNEL, "Changing state of channel %p (global ID " U64_FORMAT ") from \"%s\" to \"%s\"", @@ -2184,36 +1613,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) } else if (to_state == CHANNEL_STATE_MAINT) { scheduler_channel_doesnt_want_writes(chan); } - - /* - * If we're closing, this channel no longer counts toward the global - * estimated queue size; if we're open, it now does. - */ - if ((to_state == CHANNEL_STATE_CLOSING || - to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) && - (from_state == CHANNEL_STATE_OPEN || - from_state == CHANNEL_STATE_MAINT)) { - estimated_total_queue_size -= chan->bytes_in_queue; - } - - /* - * If we're opening, this channel now does count toward the global - * estimated queue size. - */ - if ((to_state == CHANNEL_STATE_OPEN || - to_state == CHANNEL_STATE_MAINT) && - !(from_state == CHANNEL_STATE_OPEN || - from_state == CHANNEL_STATE_MAINT)) { - estimated_total_queue_size += chan->bytes_in_queue; - } - - if (to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) { - /* Assert that all queues are empty */ - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)); - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)); - } } /** @@ -2237,12 +1636,6 @@ channel_change_state_open(channel_t *chan) /* Tell circuits if we opened and stuff */ channel_do_open_actions(chan); chan->has_been_open = 1; - - /* Check for queued cells to process */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - channel_process_cells(chan); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) - channel_flush_cells(chan); } /** @@ -2284,15 +1677,6 @@ channel_listener_change_state(channel_listener_t *chan_l, tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING); } - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - log_debug(LD_CHANNEL, "Changing state of channel listener %p (global ID " U64_FORMAT "from \"%s\" to \"%s\"", @@ -2325,30 +1709,38 @@ channel_listener_change_state(channel_listener_t *chan_l, if (to_state == CHANNEL_LISTENER_STATE_CLOSED || to_state == CHANNEL_LISTENER_STATE_ERROR) { - /* Assert that the queue is empty */ tor_assert(!(chan_l->incoming_list) || smartlist_len(chan_l->incoming_list) == 0); } } -/** - * Try to flush cells to the lower layer - * - * this is called by the lower layer to indicate that it wants more cells; - * it will try to write up to num_cells cells from the channel's cell queue or - * from circuits active on that channel, or as many as it has available if - * num_cells == -1. - */ - +/* Maximum number of cells that is allowed to flush at once withing + * channel_flush_some_cells(). */ #define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256 +/* Try to flush cells of the given channel chan up to a maximum of num_cells. + * + * This is called by the scheduler when it wants to flush cells from the + * channel's circuit queue(s) to the connection outbuf (not yet on the wire). + * + * If the channel is not in state CHANNEL_STATE_OPEN, this does nothing and + * will return 0 meaning no cells were flushed. + * + * If num_cells is -1, we'll try to flush up to the maximum cells allowed + * defined in MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED. + * + * On success, the number of flushed cells are returned and it can never be + * above num_cells. If 0 is returned, no cells were flushed either because the + * channel was not opened or we had no cells on the channel. A negative number + * can NOT be sent back. + * + * This function is part of the fast path. */ MOCK_IMPL(ssize_t, channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) { unsigned int unlimited = 0; ssize_t flushed = 0; - int num_cells_from_circs, clamped_num_cells; - int q_len_before, q_len_after; + int clamped_num_cells; tor_assert(chan); @@ -2357,11 +1749,6 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ if (CHANNEL_IS_OPEN(chan)) { - /* Try to flush as much as we can that's already queued */ - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - if (!unlimited && num_cells <= flushed) goto done; - if (circuitmux_num_cells(chan->cmux) > 0) { /* Calculate number of cells, including clamp */ if (unlimited) { @@ -2375,45 +1762,9 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) } } - /* - * Keep track of the change in queue size; we have to count cells - * channel_flush_from_first_active_circuit() writes out directly, - * but not double-count ones we might get later in - * channel_flush_some_cells_from_outgoing_queue() - */ - q_len_before = chan_cell_queue_len(&(chan->outgoing_queue)); - /* Try to get more cells from any active circuits */ - num_cells_from_circs = channel_flush_from_first_active_circuit( + flushed = channel_flush_from_first_active_circuit( chan, clamped_num_cells); - - q_len_after = chan_cell_queue_len(&(chan->outgoing_queue)); - - /* - * If it claims we got some, adjust the flushed counter and consider - * processing the queue again - */ - if (num_cells_from_circs > 0) { - /* - * Adjust flushed by the number of cells counted in - * num_cells_from_circs that didn't go to the cell queue. - */ - - if (q_len_after > q_len_before) { - num_cells_from_circs -= (q_len_after - q_len_before); - if (num_cells_from_circs < 0) num_cells_from_circs = 0; - } - - flushed += num_cells_from_circs; - - /* Now process the queue if necessary */ - - if ((q_len_after > q_len_before) && - (unlimited || (flushed < num_cells))) { - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - } - } } } @@ -2422,197 +1773,16 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) } /** - * Flush cells from just the channel's outgoing cell queue - * - * This gets called from channel_flush_some_cells() above to flush cells - * just from the queue without trying for active_circuits. - */ - -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells) -{ - unsigned int unlimited = 0; - ssize_t flushed = 0; - cell_queue_entry_t *q = NULL; - size_t cell_size; - int free_q = 0, handed_off = 0; - - tor_assert(chan); - tor_assert(chan->write_cell); - tor_assert(chan->write_packed_cell); - tor_assert(chan->write_var_cell); - - if (num_cells < 0) unlimited = 1; - if (!unlimited && num_cells <= flushed) return 0; - - /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ - if (CHANNEL_IS_OPEN(chan)) { - while ((unlimited || num_cells > flushed) && - NULL != (q = TOR_SIMPLEQ_FIRST(&chan->outgoing_queue))) { - free_q = 0; - handed_off = 0; - - /* Figure out how big it is for statistical purposes */ - cell_size = channel_get_cell_queue_entry_size(chan, q); - /* - * Okay, we have a good queue entry, try to give it to the lower - * layer. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - if (chan->write_cell(chan, - q->u.fixed.cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_FIXED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - if (chan->write_packed_cell(chan, - q->u.packed.packed_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_PACKED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - if (chan->write_var_cell(chan, - q->u.var.var_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_VAR " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - default: - /* Unknown type, log and free it */ - log_info(LD_CHANNEL, - "Saw an unknown cell queue entry type %d on channel %p " - "(global ID " U64_FORMAT "; ignoring it." - " Someone should fix this.", - q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); - free_q = 1; - handed_off = 0; - } - - /* - * if free_q is set, we used it and should remove the queue entry; - * we have to do the free down here so TOR_SIMPLEQ_REMOVE_HEAD isn't - * accessing freed memory - */ - if (free_q) { - TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next); - /* - * ...and we handed a cell off to the lower layer, so we should - * update the counters. - */ - ++n_channel_cells_passed_to_lower_layer; - --n_channel_cells_in_queues; - n_channel_bytes_passed_to_lower_layer += cell_size; - n_channel_bytes_in_queues -= cell_size; - channel_assert_counter_consistency(); - /* Update the channel's queue size too */ - chan->bytes_in_queue -= cell_size; - /* Finally, free q */ - cell_queue_entry_free(q, handed_off); - q = NULL; - } else { - /* No cell removed from list, so we can't go on any further */ - break; - } - } - } - - /* Did we drain the queue? */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - channel_timestamp_drained(chan); - } - - /* Update the estimate queue size */ - channel_update_xmit_queue_size(chan); - - return flushed; -} - -/** - * Flush as many cells as we possibly can from the queue - * - * This tries to flush as many cells from the queue as the lower layer - * will take. It just calls channel_flush_some_cells_from_outgoing_queue() - * in unlimited mode. - */ - -void -channel_flush_cells(channel_t *chan) -{ - channel_flush_some_cells_from_outgoing_queue(chan, -1); -} - -/** * Check if any cells are available * - * This gets used from the lower layer to check if any more cells are - * available. + * This is used by the scheduler to know if the channel has more to flush + * after a scheduling round. */ - MOCK_IMPL(int, channel_more_to_flush, (channel_t *chan)) { tor_assert(chan); - /* Check if we have any queued */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - return 1; - - /* Check if any circuits would like to queue some */ if (circuitmux_num_cells(chan->cmux) > 0) return 1; /* Else no */ @@ -2747,7 +1917,7 @@ channel_do_open_actions(channel_t *chan) CHANNELPADDING_SOS_PARAM, CHANNELPADDING_SOS_DEFAULT, 0, 1)) { /* Disable if we're using RSOS and the consensus disabled padding - * for RSOS*/ + * for RSOS */ channelpadding_disable_padding_on_channel(chan); } else if (get_options()->ReducedConnectionPadding) { /* Padding can be forced and/or reduced by clients, regardless of if @@ -2816,207 +1986,31 @@ channel_listener_queue_incoming(channel_listener_t *listener, } /** - * Process queued incoming cells - * - * Process as many queued cells as we can from the incoming - * cell queue. + * Process a cell from the given channel. */ - void -channel_process_cells(channel_t *chan) +channel_process_cell(channel_t *chan, cell_t *cell) { - cell_queue_entry_t *q; tor_assert(chan); tor_assert(CHANNEL_IS_CLOSING(chan) || CHANNEL_IS_MAINT(chan) || CHANNEL_IS_OPEN(chan)); - - log_debug(LD_CHANNEL, - "Processing as many incoming cells as we can for channel %p", - chan); - - /* Nothing we can do if we have no registered cell handlers */ - if (!(chan->cell_handler || - chan->var_cell_handler)) return; - /* Nothing we can do if we have no cells */ - if (TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) return; - - /* - * Process cells until we're done or find one we have no current handler - * for. - * - * We must free the cells here after calling the handler, since custody - * of the buffer was given to the channel layer when they were queued; - * see comments on memory management in channel_queue_cell() and in - * channel_queue_var_cell() below. - */ - while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) { - tor_assert(q); - tor_assert(q->type == CELL_QUEUE_FIXED || - q->type == CELL_QUEUE_VAR); - - if (q->type == CELL_QUEUE_FIXED && - chan->cell_handler) { - /* Handle a fixed-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.fixed.cell); - log_debug(LD_CHANNEL, - "Processing incoming cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.fixed.cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, q->u.fixed.cell); - tor_free(q->u.fixed.cell); - tor_free(q); - } else if (q->type == CELL_QUEUE_VAR && - chan->var_cell_handler) { - /* Handle a variable-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.var.var_cell); - log_debug(LD_CHANNEL, - "Processing incoming var_cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.var.var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, q->u.var.var_cell); - tor_free(q->u.var.var_cell); - tor_free(q); - } else { - /* Can't handle this one */ - break; - } - } -} - -/** - * Queue incoming cell - * - * This should be called by a channel_t subclass to queue an incoming fixed- - * length cell for processing, and process it if possible. - */ - -void -channel_queue_cell(channel_t *chan, cell_t *cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - cell_t *cell_copy = NULL; - - tor_assert(chan); tor_assert(cell); - tor_assert(CHANNEL_IS_OPEN(chan)); - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; + /* Nothing we can do if we have no registered cell handlers */ + if (!chan->cell_handler) + return; /* Timestamp for receiving */ channel_timestamp_recv(chan); - - /* Update the counters */ + /* Update received counter. */ ++(chan->n_cells_recved); chan->n_bytes_recved += get_cell_network_size(chan->wide_circ_ids); - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, cell); - } else { - /* - * Otherwise queue it and then process the queue if possible. - * - * We queue a copy, not the original pointer - it might have been on the - * stack in connection_or_process_cells_from_inbuf() (or another caller - * if we ever have a subclass other than channel_tls_t), or be freed - * there after we return. This is the uncommon case; the non-copying - * fast path occurs in the if (!need_to_queue) case above when the - * upper layer has installed cell handlers. - */ - cell_copy = tor_malloc_zero(sizeof(cell_t)); - memcpy(cell_copy, cell, sizeof(cell_t)); - q = cell_queue_entry_new_fixed(cell_copy); - log_debug(LD_CHANNEL, - "Queueing incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } -} - -/** - * Queue incoming variable-length cell - * - * This should be called by a channel_t subclass to queue an incoming - * variable-length cell for processing, and process it if possible. - */ - -void -channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - var_cell_t *cell_copy = NULL; - - tor_assert(chan); - tor_assert(var_cell); - tor_assert(CHANNEL_IS_OPEN(chan)); - - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->var_cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; - - /* Timestamp for receiving */ - channel_timestamp_recv(chan); - - /* Update the counter */ - ++(chan->n_cells_recved); - chan->n_bytes_recved += get_var_cell_header_size(chan->wide_circ_ids) + - var_cell->payload_len; - - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->var_cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, var_cell); - } else { - /* - * Otherwise queue it and then process the queue if possible. - * - * We queue a copy, not the original pointer - it might have been on the - * stack in connection_or_process_cells_from_inbuf() (or another caller - * if we ever have a subclass other than channel_tls_t), or be freed - * there after we return. This is the uncommon case; the non-copying - * fast path occurs in the if (!need_to_queue) case above when the - * upper layer has installed cell handlers. - */ - cell_copy = var_cell_copy(var_cell); - q = cell_queue_entry_new_var(cell_copy); - log_debug(LD_CHANNEL, - "Queueing incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } + log_debug(LD_CHANNEL, + "Processing incoming cell_t %p for channel %p (global ID " + U64_FORMAT ")", cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->cell_handler(chan, cell); } /** If <b>packed_cell</b> on <b>chan</b> is a destroy cell, then set @@ -3043,44 +2037,6 @@ packed_cell_is_destroy(channel_t *chan, } /** - * Assert that the global channel stats counters are internally consistent - */ - -static void -channel_assert_counter_consistency(void) -{ - tor_assert(n_channel_cells_queued == - (n_channel_cells_in_queues + n_channel_cells_passed_to_lower_layer)); - tor_assert(n_channel_bytes_queued == - (n_channel_bytes_in_queues + n_channel_bytes_passed_to_lower_layer)); -} - -/* DOCDOC */ -static int -is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out) -{ - *circid_out = 0; - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell->command == CELL_DESTROY) { - *circid_out = q->u.fixed.cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell->command == CELL_DESTROY) { - *circid_out = q->u.var.var_cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_PACKED: - return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out); - } - return 0; -} - -/** * Send destroy cell on a channel * * Write a destroy cell with circ ID <b>circ_id</b> and reason <b>reason</b> @@ -3134,19 +2090,6 @@ channel_dumpstats(int severity) { if (all_channels && smartlist_len(all_channels) > 0) { tor_log(severity, LD_GENERAL, - "Channels have queued " U64_FORMAT " bytes in " U64_FORMAT " cells, " - "and handed " U64_FORMAT " bytes in " U64_FORMAT " cells to the lower" - " layer.", - U64_PRINTF_ARG(n_channel_bytes_queued), - U64_PRINTF_ARG(n_channel_cells_queued), - U64_PRINTF_ARG(n_channel_bytes_passed_to_lower_layer), - U64_PRINTF_ARG(n_channel_cells_passed_to_lower_layer)); - tor_log(severity, LD_GENERAL, - "There are currently " U64_FORMAT " bytes in " U64_FORMAT " cells " - "in channel queues.", - U64_PRINTF_ARG(n_channel_bytes_in_queues), - U64_PRINTF_ARG(n_channel_cells_in_queues)); - tor_log(severity, LD_GENERAL, "Dumping statistics about %d channels:", smartlist_len(all_channels)); tor_log(severity, LD_GENERAL, @@ -3296,7 +2239,7 @@ channel_free_list(smartlist_t *channels, int mark_for_close) if (!CHANNEL_CONDEMNED(curr)) { channel_mark_for_close(curr); } - channel_force_free(curr); + channel_force_xfree(curr); } else channel_free(curr); } SMARTLIST_FOREACH_END(curr); } @@ -3325,7 +2268,7 @@ channel_listener_free_list(smartlist_t *listeners, int mark_for_close) curr->state == CHANNEL_LISTENER_STATE_ERROR)) { channel_listener_mark_for_close(curr); } - channel_listener_force_free(curr); + channel_listener_force_xfree(curr); } else channel_listener_free(curr); } SMARTLIST_FOREACH_END(curr); } @@ -3629,19 +2572,6 @@ channel_listener_describe_transport(channel_listener_t *chan_l) } /** - * Return the number of entries in <b>queue</b> - */ -STATIC int -chan_cell_queue_len(const chan_cell_queue_t *queue) -{ - int r = 0; - cell_queue_entry_t *cell; - TOR_SIMPLEQ_FOREACH(cell, queue, next) - ++r; - return r; -} - -/** * Dump channel statistics * * Dump statistics for one channel to the log @@ -3676,35 +2606,18 @@ channel_dump_statistics, (channel_t *chan, int severity)) U64_PRINTF_ARG(chan->timestamp_active), U64_PRINTF_ARG(now - chan->timestamp_active)); - /* Handle digest and nickname */ + /* Handle digest. */ if (!tor_digest_is_zero(chan->identity_digest)) { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and nickname %s", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and no known nickname", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - } + tor_log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " says it is connected " + "to an OR with digest %s", + U64_PRINTF_ARG(chan->global_identifier), + hex_str(chan->identity_digest, DIGEST_LEN)); } else { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " of the OR it is connected to, but reports its nickname is %s", - U64_PRINTF_ARG(chan->global_identifier), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " or the nickname of the OR it is connected to", - U64_PRINTF_ARG(chan->global_identifier)); - } + tor_log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " does not know the digest" + " of the OR it is connected to", + U64_PRINTF_ARG(chan->global_identifier)); } /* Handle remote address and descriptions */ @@ -3753,14 +2666,6 @@ channel_dump_statistics, (channel_t *chan, int severity)) channel_is_incoming(chan) ? "incoming" : "outgoing"); - /* Describe queues */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has %d queued incoming cells" - " and %d queued outgoing cells", - U64_PRINTF_ARG(chan->global_identifier), - chan_cell_queue_len(&chan->incoming_queue), - chan_cell_queue_len(&chan->outgoing_queue)); - /* Describe circuits */ tor_log(severity, LD_GENERAL, " * Channel " U64_FORMAT " has %d active circuits out of" @@ -3779,12 +2684,6 @@ channel_dump_statistics, (channel_t *chan, int severity)) U64_PRINTF_ARG(chan->timestamp_client), U64_PRINTF_ARG(now - chan->timestamp_client)); tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " was last drained at " - U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_drained), - U64_PRINTF_ARG(now - chan->timestamp_drained)); - tor_log(severity, LD_GENERAL, " * Channel " U64_FORMAT " last received a cell " "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", U64_PRINTF_ARG(chan->global_identifier), @@ -4027,29 +2926,18 @@ channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) else return 0; } -/** - * Check if there are outgoing queue writes on this channel - * - * Indicate if either we have queued cells, or if not, whether the underlying - * lower-layer transport thinks it has an output queue. +/* + * Return true iff the channel has any cells on the connection outbuf waiting + * to be sent onto the network. */ - int channel_has_queued_writes(channel_t *chan) { - int has_writes = 0; - tor_assert(chan); tor_assert(chan->has_queued_writes); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - has_writes = 1; - } else { - /* Check with the lower layer */ - has_writes = chan->has_queued_writes(chan); - } - - return has_writes; + /* Check with the lower layer */ + return chan->has_queued_writes(chan); } /** @@ -4274,23 +3162,10 @@ channel_mark_outgoing(channel_t *chan) ***********************/ /* - * Get the latest estimate for the total queue size of all open channels - */ - -uint64_t -channel_get_global_queue_estimate(void) -{ - return estimated_total_queue_size; -} - -/* * Estimate the number of writeable cells * - * Ask the lower layer for an estimate of how many cells it can accept, and - * then subtract the length of our outgoing_queue, if any, to produce an - * estimate of the number of cells this channel can accept for writes. + * Ask the lower layer for an estimate of how many cells it can accept. */ - int channel_num_cells_writeable(channel_t *chan) { @@ -4302,8 +3177,6 @@ channel_num_cells_writeable(channel_t *chan) if (chan->state == CHANNEL_STATE_OPEN) { /* Query lower layer */ result = chan->num_cells_writeable(chan); - /* Subtract cell queue length, if any */ - result -= chan_cell_queue_len(&chan->outgoing_queue); if (result < 0) result = 0; } else { /* No cells are writeable in any other state */ @@ -4368,12 +3241,12 @@ channel_timestamp_active(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /** @@ -4427,25 +3300,6 @@ channel_timestamp_client(channel_t *chan) } /** - * Update the last drained timestamp - * - * This is called whenever we transmit a cell which leaves the outgoing cell - * queue completely empty. It also updates the xmit time and the active time. - */ - -void -channel_timestamp_drained(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; - chan->timestamp_drained = now; - chan->timestamp_xmit = now; -} - -/** * Update the recv timestamp * * This is called whenever we get an incoming cell from the lower layer. @@ -4457,13 +3311,13 @@ channel_timestamp_recv(channel_t *chan) { time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; chan->timestamp_recv = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /** @@ -4478,13 +3332,13 @@ channel_timestamp_xmit(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; chan->timestamp_xmit = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /*************************************************************** @@ -4504,54 +3358,6 @@ channel_when_created(channel_t *chan) } /** - * Query created timestamp for a channel listener - */ - -time_t -channel_listener_when_created(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_created; -} - -/** - * Query last active timestamp for a channel - */ - -time_t -channel_when_last_active(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_active; -} - -/** - * Query last active timestamp for a channel listener - */ - -time_t -channel_listener_when_last_active(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_active; -} - -/** - * Query last accepted timestamp for a channel listener - */ - -time_t -channel_listener_when_last_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_accepted; -} - -/** * Query client timestamp */ @@ -4564,30 +3370,6 @@ channel_when_last_client(channel_t *chan) } /** - * Query drained timestamp - */ - -time_t -channel_when_last_drained(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_drained; -} - -/** - * Query recv timestamp - */ - -time_t -channel_when_last_recv(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_recv; -} - -/** * Query xmit timestamp */ @@ -4600,42 +3382,6 @@ channel_when_last_xmit(channel_t *chan) } /** - * Query accepted counter - */ - -uint64_t -channel_listener_count_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->n_accepted; -} - -/** - * Query received cell counter - */ - -uint64_t -channel_count_recved(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_recved; -} - -/** - * Query transmitted cell counter - */ - -uint64_t -channel_count_xmitted(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_xmitted; -} - -/** * Check if a channel matches an extend_info_t * * This function calls the lower layer and asks if this channel matches a @@ -4726,6 +3472,16 @@ channel_set_circid_type,(channel_t *chan, } } +static int +channel_sort_by_ed25519_identity(const void **a_, const void **b_) +{ + const channel_t *a = *a_, + *b = *b_; + return fast_memcmp(&a->ed25519_identity.pubkey, + &b->ed25519_identity.pubkey, + sizeof(a->ed25519_identity.pubkey)); +} + /** Helper for channel_update_bad_for_new_circs(): Perform the * channel_update_bad_for_new_circs operation on all channels in <b>lst</b>, * all of which MUST have the same RSA ID. (They MAY have different @@ -4734,44 +3490,52 @@ static void channel_rsa_id_group_set_badness(struct channel_list_s *lst, int force) { /*XXXX This function should really be about channels. 15056 */ - channel_t *chan; + channel_t *chan = TOR_LIST_FIRST(lst); + + if (!chan) + return; + + /* if there is only one channel, don't bother looping */ + if (PREDICT_LIKELY(!TOR_LIST_NEXT(chan, next_with_same_id))) { + connection_or_single_set_badness_( + time(NULL), BASE_CHAN_TO_TLS(chan)->conn, force); + return; + } + + smartlist_t *channels = smartlist_new(); - /* First, get a minimal list of the ed25519 identites */ - smartlist_t *ed_identities = smartlist_new(); TOR_LIST_FOREACH(chan, lst, next_with_same_id) { - uint8_t *id_copy = - tor_memdup(&chan->ed25519_identity.pubkey, DIGEST256_LEN); - smartlist_add(ed_identities, id_copy); + if (BASE_CHAN_TO_TLS(chan)->conn) { + smartlist_add(channels, chan); + } } - smartlist_sort_digests256(ed_identities); - smartlist_uniq_digests256(ed_identities); - /* Now, for each Ed identity, build a smartlist and find the best entry on - * it. */ + smartlist_sort(channels, channel_sort_by_ed25519_identity); + + const ed25519_public_key_t *common_ed25519_identity = NULL; + /* it would be more efficient to do a slice, but this case is rare */ smartlist_t *or_conns = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(ed_identities, const uint8_t *, ed_id) { - TOR_LIST_FOREACH(chan, lst, next_with_same_id) { - channel_tls_t *chantls = BASE_CHAN_TO_TLS(chan); - if (tor_memneq(ed_id, &chan->ed25519_identity.pubkey, DIGEST256_LEN)) - continue; - or_connection_t *orconn = chantls->conn; - if (orconn) { - tor_assert(orconn->chan == chantls); - smartlist_add(or_conns, orconn); - } + SMARTLIST_FOREACH_BEGIN(channels, channel_t *, channel) { + if (!common_ed25519_identity) + common_ed25519_identity = &channel->ed25519_identity; + + if (! ed25519_pubkey_eq(&channel->ed25519_identity, + common_ed25519_identity)) { + connection_or_group_set_badness_(or_conns, force); + smartlist_clear(or_conns); + common_ed25519_identity = &channel->ed25519_identity; } - connection_or_group_set_badness_(or_conns, force); - smartlist_clear(or_conns); - } SMARTLIST_FOREACH_END(ed_id); + smartlist_add(or_conns, BASE_CHAN_TO_TLS(channel)->conn); + } SMARTLIST_FOREACH_END(channel); + + connection_or_group_set_badness_(or_conns, force); /* XXXX 15056 we may want to do something special with connections that have * no set Ed25519 identity! */ smartlist_free(or_conns); - - SMARTLIST_FOREACH(ed_identities, uint8_t *, ed_id, tor_free(ed_id)); - smartlist_free(ed_identities); + smartlist_free(channels); } /** Go through all the channels (or if <b>digest</b> is non-NULL, just @@ -4801,83 +3565,3 @@ channel_update_bad_for_new_circs(const char *digest, int force) } } -/** - * Update the estimated number of bytes queued to transmit for this channel, - * and notify the scheduler. The estimate includes both the channel queue and - * the queue size reported by the lower layer, and an overhead estimate - * optionally provided by the lower layer. - */ - -void -channel_update_xmit_queue_size(channel_t *chan) -{ - uint64_t queued, adj; - double overhead; - - tor_assert(chan); - tor_assert(chan->num_bytes_queued); - - /* - * First, get the number of bytes we have queued without factoring in - * lower-layer overhead. - */ - queued = chan->num_bytes_queued(chan) + chan->bytes_in_queue; - /* Next, adjust by the overhead factor, if any is available */ - if (chan->get_overhead_estimate) { - overhead = chan->get_overhead_estimate(chan); - if (overhead >= 1.0) { - queued = (uint64_t)(queued * overhead); - } else { - /* Ignore silly overhead factors */ - log_notice(LD_CHANNEL, "Ignoring silly overhead factor %f", overhead); - } - } - - /* Now, compare to the previous estimate */ - if (queued > chan->bytes_queued_for_xmit) { - adj = queued - chan->bytes_queued_for_xmit; - log_debug(LD_CHANNEL, - "Increasing queue size for channel " U64_FORMAT " by " U64_FORMAT - " from " U64_FORMAT " to " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(adj), - U64_PRINTF_ARG(chan->bytes_queued_for_xmit), - U64_PRINTF_ARG(queued)); - /* Update the channel's estimate */ - chan->bytes_queued_for_xmit = queued; - - /* Update the global queue size estimate if appropriate */ - if (chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) { - estimated_total_queue_size += adj; - log_debug(LD_CHANNEL, - "Increasing global queue size by " U64_FORMAT " for channel " - U64_FORMAT ", new size is " U64_FORMAT, - U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(estimated_total_queue_size)); - } - } else if (queued < chan->bytes_queued_for_xmit) { - adj = chan->bytes_queued_for_xmit - queued; - log_debug(LD_CHANNEL, - "Decreasing queue size for channel " U64_FORMAT " by " U64_FORMAT - " from " U64_FORMAT " to " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(adj), - U64_PRINTF_ARG(chan->bytes_queued_for_xmit), - U64_PRINTF_ARG(queued)); - /* Update the channel's estimate */ - chan->bytes_queued_for_xmit = queued; - - /* Update the global queue size estimate if appropriate */ - if (chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) { - estimated_total_queue_size -= adj; - log_debug(LD_CHANNEL, - "Decreasing global queue size by " U64_FORMAT " for channel " - U64_FORMAT ", new size is " U64_FORMAT, - U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(estimated_total_queue_size)); - } - } -} - diff --git a/src/or/channel.h b/src/or/channel.h index 32336fe1d2..4b60b7a7d6 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -19,10 +19,6 @@ typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *); typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *); typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); -struct cell_queue_entry_s; -TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s); -typedef struct chan_cell_queue chan_cell_queue_t; - /** * This enum is used by channelpadding to decide when to pad channels. * Don't add values to it without updating the checks in @@ -92,10 +88,9 @@ struct channel_s { * Used to decide what channels to pad, and when. */ channel_usage_info_t channel_usage; - /** When should we send a cell for netflow padding, in absolute - * milliseconds since monotime system start. 0 means no padding - * is scheduled. */ - uint64_t next_padding_time_ms; + /** When should we send a cell for netflow padding? 0 means no padding is + * scheduled. */ + monotime_coarse_t next_padding_time; /** The callback pointer for the padding callbacks */ tor_timer_t *padding_timer; @@ -162,7 +157,7 @@ struct channel_s { time_t timestamp_active; /* Any activity */ /** - * This is a high-resolution monotonic timestamp that marks when we + * This is a monotonic timestamp that marks when we * believe the channel has actually sent or received data to/from * the wire. Right now, it is used to determine when we should send * a padding cell for channelpadding. @@ -171,7 +166,7 @@ struct channel_s { * accurately reflect actual network data transfer? Or might this be * very wrong wrt when bytes actually go on the wire? */ - uint64_t timestamp_xfer_ms; + monotime_coarse_t timestamp_xfer; /* Methods implemented by the lower layer */ @@ -259,21 +254,12 @@ struct channel_s { */ ed25519_public_key_t ed25519_identity; - /** Nickname of the OR on the other side, or NULL if none. */ - char *nickname; - /** * Linked list of channels with the same RSA identity digest, for use with * the digest->channel map */ TOR_LIST_ENTRY(channel_s) next_with_same_id; - /** List of incoming cells to handle */ - chan_cell_queue_t incoming_queue; - - /** List of queued outgoing cells */ - chan_cell_queue_t outgoing_queue; - /** Circuit mux for circuits sending on this channel */ circuitmux_t *cmux; @@ -320,7 +306,6 @@ struct channel_s { /** Channel timestamps for cell channels */ time_t timestamp_client; /* Client used this, according to relay.c */ - time_t timestamp_drained; /* Output queue empty */ time_t timestamp_recv; /* Cell received from lower layer */ time_t timestamp_xmit; /* Cell sent to lower layer */ @@ -337,14 +322,6 @@ struct channel_s { /** Channel counters for cell channels */ uint64_t n_cells_recved, n_bytes_recved; uint64_t n_cells_xmitted, n_bytes_xmitted; - - /** Our current contribution to the scheduler's total xmit queue */ - uint64_t bytes_queued_for_xmit; - - /** Number of bytes in this channel's cell queue; does not include - * lower-layer queueing. - */ - uint64_t bytes_in_queue; }; struct channel_listener_s { @@ -412,18 +389,13 @@ channel_listener_state_to_string(channel_listener_state_t state); /* Abstract channel operations */ void channel_mark_for_close(channel_t *chan); -void channel_write_cell(channel_t *chan, cell_t *cell); -void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); -void channel_write_var_cell(channel_t *chan, var_cell_t *cell); +int channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); void channel_listener_mark_for_close(channel_listener_t *chan_l); /* Channel callback registrations */ /* Listener callback */ -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan); - void channel_listener_set_listener_fn(channel_listener_t *chan, channel_listener_fn_ptr listener); @@ -457,36 +429,9 @@ void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol); #ifdef TOR_CHANNEL_INTERNAL_ #ifdef CHANNEL_PRIVATE_ -/* Cell queue structure (here rather than channel.c for test suite use) */ - -typedef struct cell_queue_entry_s cell_queue_entry_t; -struct cell_queue_entry_s { - TOR_SIMPLEQ_ENTRY(cell_queue_entry_s) next; - enum { - CELL_QUEUE_FIXED, - CELL_QUEUE_VAR, - CELL_QUEUE_PACKED - } type; - union { - struct { - cell_t *cell; - } fixed; - struct { - var_cell_t *var_cell; - } var; - struct { - packed_cell_t *packed_cell; - } packed; - } u; -}; - -/* Cell queue functions for benefit of test suite */ -STATIC int chan_cell_queue_len(const chan_cell_queue_t *queue); -STATIC void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off); +STATIC void channel_add_to_digest_map(channel_t *chan); -void channel_write_cell_generic_(channel_t *chan, const char *cell_type, - void *cell, cell_queue_entry_t *q); #endif /* defined(CHANNEL_PRIVATE_) */ /* Channel operations for subclasses and internal use only */ @@ -511,13 +456,12 @@ void channel_close_from_lower_layer(channel_t *chan); void channel_close_for_error(channel_t *chan); void channel_closed(channel_t *chan); -void channel_listener_close_from_lower_layer(channel_listener_t *chan_l); -void channel_listener_close_for_error(channel_listener_t *chan_l); -void channel_listener_closed(channel_listener_t *chan_l); - /* Free a channel */ -void channel_free(channel_t *chan); -void channel_listener_free(channel_listener_t *chan_l); +void channel_free_(channel_t *chan); +#define channel_free(chan) FREE_AND_NULL(channel_t, channel_free_, (chan)) +void channel_listener_free_(channel_listener_t *chan_l); +#define channel_listener_free(chan_l) \ + FREE_AND_NULL(channel_listener_t, channel_listener_free_, (chan_l)) /* State/metadata setters */ @@ -532,9 +476,6 @@ void channel_mark_remote(channel_t *chan); void channel_set_identity_digest(channel_t *chan, const char *identity_digest, const ed25519_public_key_t *ed_identity); -void channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname); void channel_listener_change_state(channel_listener_t *chan_l, channel_listener_state_t to_state); @@ -542,7 +483,6 @@ void channel_listener_change_state(channel_listener_t *chan_l, /* Timestamp updates */ void channel_timestamp_created(channel_t *chan); void channel_timestamp_active(channel_t *chan); -void channel_timestamp_drained(channel_t *chan); void channel_timestamp_recv(channel_t *chan); void channel_timestamp_xmit(channel_t *chan); @@ -556,12 +496,7 @@ void channel_listener_queue_incoming(channel_listener_t *listener, channel_t *incoming); /* Incoming cell handling */ -void channel_process_cells(channel_t *chan); -void channel_queue_cell(channel_t *chan, cell_t *cell); -void channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell); - -/* Outgoing cell handling */ -void channel_flush_cells(channel_t *chan); +void channel_process_cell(channel_t *chan, cell_t *cell); /* Request from lower layer for more cells if available */ MOCK_DECL(ssize_t, channel_flush_some_cells, @@ -576,10 +511,6 @@ void channel_notify_flushed(channel_t *chan); /* Handle stuff we need to do on open like notifying circuits */ void channel_do_open_actions(channel_t *chan); -#ifdef TOR_UNIT_TESTS -extern uint64_t estimated_total_queue_size; -#endif - #endif /* defined(TOR_CHANNEL_INTERNAL_) */ /* Helper functions to perform operations on channels */ @@ -680,7 +611,6 @@ MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, crypto_pk_t *identity_rcvd, int consider_identity)); void channel_timestamp_client(channel_t *chan); -void channel_update_xmit_queue_size(channel_t *chan); const char * channel_listener_describe_transport(channel_listener_t *chan_l); void channel_listener_dump_statistics(channel_listener_t *chan_l, @@ -692,33 +622,22 @@ void channel_check_for_duplicates(void); void channel_update_bad_for_new_circs(const char *digest, int force); /* Flow control queries */ -uint64_t channel_get_global_queue_estimate(void); int channel_num_cells_writeable(channel_t *chan); /* Timestamp queries */ time_t channel_when_created(channel_t *chan); -time_t channel_when_last_active(channel_t *chan); time_t channel_when_last_client(channel_t *chan); -time_t channel_when_last_drained(channel_t *chan); -time_t channel_when_last_recv(channel_t *chan); time_t channel_when_last_xmit(channel_t *chan); -time_t channel_listener_when_created(channel_listener_t *chan_l); -time_t channel_listener_when_last_active(channel_listener_t *chan_l); -time_t channel_listener_when_last_accepted(channel_listener_t *chan_l); - /* Counter queries */ -uint64_t channel_count_recved(channel_t *chan); -uint64_t channel_count_xmitted(channel_t *chan); - -uint64_t channel_listener_count_accepted(channel_listener_t *chan_l); - int packed_cell_is_destroy(channel_t *chan, const packed_cell_t *packed_cell, circid_t *circid_out); /* Declare the handle helpers */ HANDLE_DECL(channel, channel_s,) +#define channel_handle_free(h) \ + FREE_AND_NULL(channel_handle_t, channel_handle_free_, (h)) #endif /* !defined(TOR_CHANNEL_H) */ diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c index 435436c45c..70a906d232 100644 --- a/src/or/channelpadding.c +++ b/src/or/channelpadding.c @@ -23,7 +23,8 @@ #include <event2/event.h> #include "rendservice.h" -STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *); +STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms( + const channel_t *); STATIC int channelpadding_send_disable_command(channel_t *); STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *); @@ -165,7 +166,7 @@ channelpadding_new_consensus_params(networkstatus_t *ns) * Returns the next timeout period (in milliseconds) after which we should * send a padding packet, or 0 if padding is disabled. */ -STATIC int +STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan) { int low_timeout = consensus_nf_ito_low; @@ -377,15 +378,16 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) chan->pending_padding_callback = 0; - if (!chan->next_padding_time_ms || + if (monotime_coarse_is_zero(&chan->next_padding_time) || chan->has_queued_writes(chan)) { /* We must have been active before the timer fired */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); return; } { - uint64_t now = monotime_coarse_absolute_msec(); + monotime_coarse_t now; + monotime_coarse_get(&now); log_fn(LOG_INFO,LD_OR, "Sending netflow keepalive on "U64_FORMAT" to %s (%s) after " @@ -393,12 +395,13 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) U64_PRINTF_ARG(chan->global_identifier), safe_str_client(chan->get_remote_descr(chan, 0)), safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)), - U64_PRINTF_ARG(now - chan->timestamp_xfer_ms), - U64_PRINTF_ARG(now - chan->next_padding_time_ms)); + I64_PRINTF_ARG(monotime_coarse_diff_msec(&chan->timestamp_xfer,&now)), + I64_PRINTF_ARG( + monotime_coarse_diff_msec(&chan->next_padding_time,&now))); } /* Clear the timer */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); /* Send the padding cell. This will cause the channel to get a * fresh timestamp_active */ @@ -500,38 +503,42 @@ channelpadding_schedule_padding(channel_t *chan, int in_ms) STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) { - uint64_t long_now = monotime_coarse_absolute_msec(); + monotime_coarse_t now; + monotime_coarse_get(&now); - if (!chan->next_padding_time_ms) { + if (monotime_coarse_is_zero(&chan->next_padding_time)) { /* If the below line or crypto_rand_int() shows up on a profile, * we can avoid getting a timeout until we're at least nf_ito_lo * from a timeout window. That will prevent us from setting timers * on connections that were active up to 1.5 seconds ago. * Idle connections should only call this once every 5.5s on average * though, so that might be a micro-optimization for little gain. */ - int64_t padding_timeout = + int32_t padding_timeout = channelpadding_get_netflow_inactive_timeout_ms(chan); if (!padding_timeout) return CHANNELPADDING_TIME_DISABLED; - chan->next_padding_time_ms = padding_timeout - + chan->timestamp_xfer_ms; + monotime_coarse_add_msec(&chan->next_padding_time, + &chan->timestamp_xfer, + padding_timeout); } + const int64_t ms_till_pad = + monotime_coarse_diff_msec(&now, &chan->next_padding_time); + /* If the next padding time is beyond the maximum possible consensus value, * then this indicates a clock jump, so just send padding now. This is * better than using monotonic time because we want to avoid the situation * where we wait around forever for monotonic time to move forward after * a clock jump far into the past. */ - if (chan->next_padding_time_ms > long_now + - DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) { + if (ms_till_pad > DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) { tor_fragile_assert(); log_warn(LD_BUG, "Channel padding timeout scheduled "I64_FORMAT"ms in the future. " "Did the monotonic clock just jump?", - I64_PRINTF_ARG(chan->next_padding_time_ms - long_now)); + I64_PRINTF_ARG(ms_till_pad)); return 0; /* Clock jumped: Send padding now */ } @@ -540,11 +547,8 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) from now which we should send padding, so we can schedule a callback then. */ - if (long_now + - (TOR_HOUSEKEEPING_CALLBACK_MSEC + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC) - >= chan->next_padding_time_ms) { - int64_t ms_until_pad_for_netflow = chan->next_padding_time_ms - - long_now; + if (ms_till_pad < (TOR_HOUSEKEEPING_CALLBACK_MSEC + + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)) { /* If the padding time is in the past, that means that libevent delayed * calling the once-per-second callback due to other work taking too long. * See https://bugs.torproject.org/22212 and @@ -554,16 +558,16 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) * and allowed a router to emit a netflow frame, just so we don't forget * about it entirely.. */ #define NETFLOW_MISSED_WINDOW (150000 - DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH) - if (ms_until_pad_for_netflow < 0) { - int severity = (ms_until_pad_for_netflow < -NETFLOW_MISSED_WINDOW) + if (ms_till_pad < 0) { + int severity = (ms_till_pad < -NETFLOW_MISSED_WINDOW) ? LOG_NOTICE : LOG_INFO; log_fn(severity, LD_OR, "Channel padding timeout scheduled "I64_FORMAT"ms in the past. ", - I64_PRINTF_ARG(-ms_until_pad_for_netflow)); + I64_PRINTF_ARG(-ms_till_pad)); return 0; /* Clock jumped: Send padding now */ } - return ms_until_pad_for_netflow; + return ms_till_pad; } return CHANNELPADDING_TIME_LATER; } diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 8277813186..023ccdefd3 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -832,6 +832,9 @@ channel_tls_write_cell_method(channel_t *chan, cell_t *cell) * * This implements the write_packed_cell method for channel_tls_t; given a * channel_tls_t and a packed_cell_t, transmit the packed_cell_t. + * + * Return 0 on success or negative value on error. The caller must free the + * packed cell. */ static int @@ -841,7 +844,6 @@ channel_tls_write_packed_cell_method(channel_t *chan, tor_assert(chan); channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids); - int written = 0; tor_assert(tlschan); tor_assert(packed_cell); @@ -849,18 +851,15 @@ channel_tls_write_packed_cell_method(channel_t *chan, if (tlschan->conn) { connection_buf_add(packed_cell->body, cell_network_size, TO_CONN(tlschan->conn)); - - /* This is where the cell is finished; used to be done from relay.c */ - packed_cell_free(packed_cell); - ++written; } else { log_info(LD_CHANNEL, "something called write_packed_cell on a tlschan " "(%p with ID " U64_FORMAT " but no conn", chan, U64_PRINTF_ARG(chan->global_identifier)); + return -1; } - return written; + return 0; } /** @@ -1149,7 +1148,7 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) * These are all transport independent and we pass them up through the * channel_t mechanism. They are ultimately handled in command.c. */ - channel_queue_cell(TLS_CHAN_TO_BASE(chan), cell); + channel_process_cell(TLS_CHAN_TO_BASE(chan), cell); break; default: log_fn(LOG_INFO, LD_PROTOCOL, diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b36fed63b3..a350f6c142 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -75,7 +75,7 @@ static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit, int is_hs_v3_rp_circuit); static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); -static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); +STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ, @@ -631,8 +631,7 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits) tor_assert(chan); - log_debug(LD_CIRC,"chan to %s/%s, status=%d", - chan->nickname ? chan->nickname : "NULL", + log_debug(LD_CIRC,"chan to %s, status=%d", channel_get_canonical_remote_descr(chan), status); pending_circs = smartlist_new(); @@ -825,16 +824,25 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) return networkstatus_get_param(NULL, "usecreatefast", 0, 0, 1); } -/** Return true if <b>circ</b> is the type of circuit we want to count - * timeouts from. In particular, we want it to have not completed yet - * (already completing indicates we cannibalized it), and we want it to - * have exactly three hops. +/** + * Return true if <b>circ</b> is the type of circuit we want to count + * timeouts from. + * + * In particular, we want to consider any circuit that plans to build + * at least 3 hops (but maybe more), but has 3 or fewer hops built + * so far. + * + * We still want to consider circuits before 3 hops, because we need + * to decide if we should convert them to a measurement circuit in + * circuit_build_times_handle_completed_hop(), rather than letting + * slow circuits get killed right away. */ int -circuit_timeout_want_to_count_circ(origin_circuit_t *circ) +circuit_timeout_want_to_count_circ(const origin_circuit_t *circ) { return !circ->has_opened - && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; + && circ->build_state->desired_path_len >= DEFAULT_ROUTE_LEN + && circuit_get_cpath_opened_len(circ) <= DEFAULT_ROUTE_LEN; } /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> @@ -938,7 +946,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) tor_assert(circ->cpath->state == CPATH_STATE_OPEN); tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING); + crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath); + circuit_build_times_handle_completed_hop(circ); if (hop) { /* Case two: we're on a hop after the first. */ @@ -1053,38 +1063,6 @@ circuit_build_no_more_hops(origin_circuit_t *circ) * I think I got them right, but more checking would be wise. -NM */ - if (circuit_timeout_want_to_count_circ(circ)) { - struct timeval end; - long timediff; - tor_gettimeofday(&end); - timediff = tv_mdiff(&circ->base_.timestamp_began, &end); - - /* - * If the circuit build time is much greater than we would have cut - * it off at, we probably had a suspend event along this codepath, - * and we should discard the value. - */ - if (timediff < 0 || - timediff > 2*get_circuit_build_close_time_ms()+1000) { - log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " - "Assuming clock jump. Purpose %d (%s)", timediff, - circ->base_.purpose, - circuit_purpose_to_string(circ->base_.purpose)); - } else if (!circuit_build_times_disabled(get_options())) { - /* Only count circuit times if the network is live */ - if (circuit_build_times_network_check_live( - get_circuit_build_times())) { - circuit_build_times_add_time(get_circuit_build_times_mutable(), - (build_time_t)timediff); - circuit_build_times_set_timeout(get_circuit_build_times_mutable()); - } - - if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - circuit_build_times_network_circ_success( - get_circuit_build_times_mutable()); - } - } - } log_info(LD_CIRC,"circuit built!"); circuit_reset_failure_count(0); @@ -1290,7 +1268,7 @@ circuit_extend(cell_t *cell, circuit_t *circ) const node_t *node = node_get_by_id((const char*)ec.node_id); const ed25519_public_key_t *node_ed_id = NULL; if (node && - node_supports_ed25519_link_authentication(node) && + node_supports_ed25519_link_authentication(node, 1) && (node_ed_id = node_get_ed25519_id(node))) { ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id); } @@ -2607,7 +2585,7 @@ onion_extend_cpath(origin_circuit_t *circ) /** Create a new hop, annotate it with information about its * corresponding router <b>choice</b>, and append it to the * end of the cpath <b>head_ptr</b>. */ -static int +STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) { crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t)); @@ -2698,7 +2676,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) /* Don't send the ed25519 pubkey unless the target node actually supports * authenticating with it. */ - if (node_supports_ed25519_link_authentication(node)) { + if (node_supports_ed25519_link_authentication(node, 0)) { log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); ed_pubkey = node_get_ed25519_id(node); } else if (node_get_ed25519_id(node)) { @@ -2707,12 +2685,16 @@ extend_info_from_node(const node_t *node, int for_direct_connect) node_describe(node)); } + /* Retrieve the curve25519 pubkey. */ + const curve25519_public_key_t *curve_pubkey = + node_get_curve25519_onion_key(node); + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, ed_pubkey, node->ri->onion_pkey, - node->ri->onion_curve25519_pkey, + curve_pubkey, &ap.addr, ap.port); else if (valid_addr && node->rs && node->md) @@ -2720,7 +2702,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) node->identity, ed_pubkey, node->md->onion_pkey, - node->md->onion_curve25519_pkey, + curve_pubkey, &ap.addr, ap.port); else @@ -2729,7 +2711,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) /** Release storage held by an extend_info_t struct. */ void -extend_info_free(extend_info_t *info) +extend_info_free_(extend_info_t *info) { if (!info) return; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index b8a651e055..a85ef672f9 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -27,7 +27,7 @@ int circuit_handle_first_hop(origin_circuit_t *circ); void circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits); int inform_testing_reachability(void); -int circuit_timeout_want_to_count_circ(origin_circuit_t *circ); +int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ); void circuit_note_clock_jumped(int seconds_elapsed); int circuit_extend(cell_t *cell, circuit_t *circ); @@ -58,7 +58,9 @@ extend_info_t *extend_info_new(const char *nickname, const tor_addr_t *addr, uint16_t port); extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); -void extend_info_free(extend_info_t *info); +void extend_info_free_(extend_info_t *info); +#define extend_info_free(info) \ + FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) int extend_info_addr_is_allowed(const tor_addr_t *addr); int extend_info_supports_tap(const extend_info_t* ei); int extend_info_supports_ntor(const extend_info_t* ei); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d442887c9e..028fdbfc2f 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -51,6 +51,8 @@ * logic, which was originally circuit-focused. **/ #define CIRCUITLIST_PRIVATE +#include "torint.h" /* TOR_PRIuSZ */ + #include "or.h" #include "channel.h" #include "circpathbias.h" @@ -108,6 +110,14 @@ static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); static void circuit_about_to_free_atexit(circuit_t *circ); static void circuit_about_to_free(circuit_t *circ); +/** + * A cached value of the current state of the origin circuit list. Has the + * value 1 if we saw any opened circuits recently (since the last call to + * circuit_any_opened_circuits(), which gets called around once a second by + * circuit_expire_building). 0 otherwise. + */ +static int any_opened_circs_cached_val = 0; + /********* END VARIABLES ************/ /** A map from channel and circuit ID to circuit. (Lookup performance is @@ -504,8 +514,7 @@ circuit_count_pending_on_channel(channel_t *chan) circuit_get_all_pending_on_channel(sl, chan); cnt = smartlist_len(sl); smartlist_free(sl); - log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - chan->nickname ? chan->nickname : "NULL", + log_debug(LD_CIRC,"or_conn to %s, %d pending circs", channel_get_canonical_remote_descr(chan), cnt); return cnt; @@ -596,6 +605,56 @@ circuit_get_global_origin_circuit_list(void) return global_origin_circuit_list; } +/** + * Return true if we have any opened general-purpose 3 hop + * origin circuits. + * + * The result from this function is cached for use by + * circuit_any_opened_circuits_cached(). + */ +int +circuit_any_opened_circuits(void) +{ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_origin_circuit_list(), + const origin_circuit_t *, next_circ) { + if (!TO_CIRCUIT(next_circ)->marked_for_close && + next_circ->has_opened && + TO_CIRCUIT(next_circ)->state == CIRCUIT_STATE_OPEN && + TO_CIRCUIT(next_circ)->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT && + next_circ->build_state && + next_circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN) { + circuit_cache_opened_circuit_state(1); + return 1; + } + } SMARTLIST_FOREACH_END(next_circ); + + circuit_cache_opened_circuit_state(0); + return 0; +} + +/** + * Cache the "any circuits opened" state, as specified in param + * circuits_are_opened. This is a helper function to update + * the circuit opened status whenever we happen to look at the + * circuit list. + */ +void +circuit_cache_opened_circuit_state(int circuits_are_opened) +{ + any_opened_circs_cached_val = circuits_are_opened; +} + +/** + * Return true if there were any opened circuits since the last call to + * circuit_any_opened_circuits(), or since circuit_expire_building() last + * ran (it runs roughly once per second). + */ +int +circuit_any_opened_circuits_cached(void) +{ + return any_opened_circs_cached_val; +} + /** Function to make circ-\>state human-readable */ const char * circuit_state_to_string(int state) @@ -923,7 +982,7 @@ circuit_clear_testing_cell_stats(circuit_t *circ) /** Deallocate space associated with circ. */ STATIC void -circuit_free(circuit_t *circ) +circuit_free_(circuit_t *circ) { circid_t n_circ_id = 0; void *mem; @@ -1091,7 +1150,7 @@ circuit_free_all(void) while (or_circ->resolving_streams) { edge_connection_t *next_conn; next_conn = or_circ->resolving_streams->next_stream; - connection_free(TO_CONN(or_circ->resolving_streams)); + connection_free_(TO_CONN(or_circ->resolving_streams)); or_circ->resolving_streams = next_conn; } } @@ -1793,6 +1852,25 @@ circuit_get_cpath_len(origin_circuit_t *circ) return n; } +/** Return the number of opened hops in circuit's path. + * If circ has no entries, or is NULL, returns 0. */ +int +circuit_get_cpath_opened_len(const origin_circuit_t *circ) +{ + int n = 0; + if (circ && circ->cpath) { + crypt_path_t *cpath, *cpath_next = NULL; + for (cpath = circ->cpath; + cpath->state == CPATH_STATE_OPEN + && cpath_next != circ->cpath; + cpath = cpath_next) { + cpath_next = cpath->next; + ++n; + } + } + return n; +} + /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there * aren't that many hops in the list. <b>hopnum</b> starts at 1. * Returns NULL if <b>hopnum</b> is 0 or negative. */ @@ -2176,12 +2254,12 @@ n_cells_in_circ_queues(const circuit_t *c) } /** - * Return the age of the oldest cell queued on <b>c</b>, in milliseconds. + * Return the age of the oldest cell queued on <b>c</b>, in timestamp units. * Return 0 if there are no cells queued on c. Requires that <b>now</b> be - * the current time in milliseconds since the epoch, truncated. + * the current coarse timestamp. * * This function will return incorrect results if the oldest cell queued on - * the circuit is older than 2**32 msec (about 49 days) old. + * the circuit is older than about 2**32 msec (about 49 days) old. */ STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) @@ -2190,12 +2268,12 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) packed_cell_t *cell; if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head))) - age = now - cell->inserted_time; + age = now - cell->inserted_timestamp; if (! CIRCUIT_IS_ORIGIN(c)) { const or_circuit_t *orcirc = CONST_TO_OR_CIRCUIT(c); if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) { - uint32_t age2 = now - cell->inserted_time; + uint32_t age2 = now - cell->inserted_timestamp; if (age2 > age) return age2; } @@ -2203,31 +2281,30 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) return age; } -/** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>, - * where age is taken in milliseconds before the time <b>now</b> (in truncated - * absolute monotonic msec). If the connection has no data, treat - * it as having age zero. +/** Return the age of the oldest buffer chunk on <b>conn</b>, where age is + * taken in timestamp units before the time <b>now</b>. If the connection has + * no data, treat it as having age zero. **/ static uint32_t -conn_get_buffer_age(const connection_t *conn, uint32_t now) +conn_get_buffer_age(const connection_t *conn, uint32_t now_ts) { uint32_t age = 0, age2; if (conn->outbuf) { - age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now); + age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now_ts); if (age2 > age) age = age2; } if (conn->inbuf) { - age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now); + age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now_ts); if (age2 > age) age = age2; } return age; } -/** Return the age in milliseconds of the oldest buffer chunk on any stream in - * the linked list <b>stream</b>, where age is taken in milliseconds before - * the time <b>now</b> (in truncated milliseconds since the epoch). */ +/** Return the age in timestamp units of the oldest buffer chunk on any stream + * in the linked list <b>stream</b>, where age is taken in timestamp units + * before the timestamp <b>now</b>. */ static uint32_t circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) { @@ -2246,9 +2323,9 @@ circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) return age; } -/** Return the age in milliseconds of the oldest buffer chunk on any stream - * attached to the circuit <b>c</b>, where age is taken in milliseconds before - * the time <b>now</b> (in truncated milliseconds since the epoch). */ +/** Return the age in timestamp units of the oldest buffer chunk on any stream + * attached to the circuit <b>c</b>, where age is taken before the timestamp + * <b>now</b>. */ STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now) { @@ -2262,8 +2339,8 @@ circuit_max_queued_data_age(const circuit_t *c, uint32_t now) } /** Return the age of the oldest cell or stream buffer chunk on the circuit - * <b>c</b>, where age is taken in milliseconds before the time <b>now</b> (in - * truncated milliseconds since the epoch). */ + * <b>c</b>, where age is taken in timestamp units before the timestamp + * <b>now</b> */ STATIC uint32_t circuit_max_queued_item_age(const circuit_t *c, uint32_t now) { @@ -2293,7 +2370,7 @@ circuits_compare_by_oldest_queued_item_(const void **a_, const void **b_) return -1; } -static uint32_t now_ms_for_buf_cmp; +static uint32_t now_ts_for_buf_cmp; /** Helper to sort a list of circuit_t by age of oldest item, in descending * order. */ @@ -2302,8 +2379,8 @@ conns_compare_by_buffer_age_(const void **a_, const void **b_) { const connection_t *a = *a_; const connection_t *b = *b_; - time_t age_a = conn_get_buffer_age(a, now_ms_for_buf_cmp); - time_t age_b = conn_get_buffer_age(b, now_ms_for_buf_cmp); + time_t age_a = conn_get_buffer_age(a, now_ts_for_buf_cmp); + time_t age_b = conn_get_buffer_age(b, now_ts_for_buf_cmp); if (age_a < age_b) return 1; @@ -2328,10 +2405,17 @@ circuits_handle_oom(size_t current_allocation) size_t mem_recovered=0; int n_circuits_killed=0; int n_dirconns_killed=0; - uint32_t now_ms; - log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " - "over-long queues. (This behavior is controlled by " - "MaxMemInQueues.)"); + uint32_t now_ts; + log_notice(LD_GENERAL, "We're low on memory (cell queues total alloc:" + " %"TOR_PRIuSZ" buffer total alloc: %" TOR_PRIuSZ "," + " tor compress total alloc: %" TOR_PRIuSZ + " rendezvous cache total alloc: %" TOR_PRIuSZ "). Killing" + " circuits withover-long queues. (This behavior is controlled by" + " MaxMemInQueues.)", + cell_queues_get_total_allocation(), + buf_get_total_allocation(), + tor_compress_get_total_allocation(), + rend_cache_get_total_allocation()); { size_t mem_target = (size_t)(get_options()->MaxMemInQueues * @@ -2341,11 +2425,11 @@ circuits_handle_oom(size_t current_allocation) mem_to_recover = current_allocation - mem_target; } - now_ms = (uint32_t)monotime_coarse_absolute_msec(); + now_ts = monotime_coarse_get_stamp(); circlist = circuit_get_global_list(); SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { - circ->age_tmp = circuit_max_queued_item_age(circ, now_ms); + circ->age_tmp = circuit_max_queued_item_age(circ, now_ts); } SMARTLIST_FOREACH_END(circ); /* This is O(n log n); there are faster algorithms we could use instead. @@ -2358,9 +2442,9 @@ circuits_handle_oom(size_t current_allocation) } SMARTLIST_FOREACH_END(circ); /* Now sort the connection array ... */ - now_ms_for_buf_cmp = now_ms; + now_ts_for_buf_cmp = now_ts; smartlist_sort(connection_array, conns_compare_by_buffer_age_); - now_ms_for_buf_cmp = 0; + now_ts_for_buf_cmp = 0; /* Fix up the connection array to its new order. */ SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) { @@ -2379,7 +2463,7 @@ circuits_handle_oom(size_t current_allocation) * data older than this circuit. */ while (conn_idx < smartlist_len(connection_array)) { connection_t *conn = smartlist_get(connection_array, conn_idx); - uint32_t conn_age = conn_get_buffer_age(conn, now_ms); + uint32_t conn_age = conn_get_buffer_age(conn, now_ts); if (conn_age < circ->age_tmp) { break; } diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 4190c1f82e..246f0c8815 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -17,6 +17,10 @@ MOCK_DECL(smartlist_t *, circuit_get_global_list, (void)); smartlist_t *circuit_get_global_origin_circuit_list(void); +int circuit_any_opened_circuits(void); +int circuit_any_opened_circuits_cached(void); +void circuit_cache_opened_circuit_state(int circuits_are_opened); + const char *circuit_state_to_string(int state); const char *circuit_purpose_to_controller_string(uint8_t purpose); const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); @@ -58,6 +62,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void); MOCK_DECL(void, circuit_mark_for_close_, (circuit_t *circ, int reason, int line, const char *file)); int circuit_get_cpath_len(origin_circuit_t *circ); +int circuit_get_cpath_opened_len(const origin_circuit_t *); void circuit_clear_cpath(origin_circuit_t *circ); crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum); void circuit_get_all_pending_on_channel(smartlist_t *out, @@ -81,7 +86,8 @@ MOCK_DECL(void, channel_note_destroy_not_pending, smartlist_t *circuit_find_circuits_to_upgrade_from_guard_wait(void); #ifdef CIRCUITLIST_PRIVATE -STATIC void circuit_free(circuit_t *circ); +STATIC void circuit_free_(circuit_t *circ); +#define circuit_free(circ) FREE_AND_NULL(circuit_t, circuit_free_, (circ)) STATIC size_t n_cells_in_circ_queues(const circuit_t *c); STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now); STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now); diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 2cc0e8fc44..fe3d8f1332 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -537,7 +537,7 @@ circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan) */ void -circuitmux_free(circuitmux_t *cmux) +circuitmux_free_(circuitmux_t *cmux) { if (!cmux) return; diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index a95edb99d8..336e128c76 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -104,7 +104,9 @@ void circuitmux_assert_okay(circuitmux_t *cmux); circuitmux_t * circuitmux_alloc(void); void circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out); -void circuitmux_free(circuitmux_t *cmux); +void circuitmux_free_(circuitmux_t *cmux); +#define circuitmux_free(cmux) \ + FREE_AND_NULL(circuitmux_t, circuitmux_free_, (cmux)) /* Policy control */ void circuitmux_clear_policy(circuitmux_t *cmux); diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index b8421a3c7e..f1df19eb25 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -36,6 +36,8 @@ #include "rendclient.h" #include "rendservice.h" #include "statefile.h" +#include "circuitlist.h" +#include "circuituse.h" #undef log #include <math.h> @@ -43,6 +45,7 @@ static void cbt_control_event_buildtimeout_set( const circuit_build_times_t *cbt, buildtimeout_set_event_t type); +static void circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt); #define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2)) @@ -540,6 +543,11 @@ circuit_build_times_reset(circuit_build_times_t *cbt) cbt->total_build_times = 0; cbt->build_times_idx = 0; cbt->have_computed_timeout = 0; + + // Reset timeout and close counts + cbt->num_circ_succeeded = 0; + cbt->num_circ_closed = 0; + cbt->num_circ_timeouts = 0; } /** @@ -616,6 +624,123 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n) #endif /* 0 */ /** + * Mark this circuit as timed out, but change its purpose + * so that it continues to build, allowing us to measure + * its full build time. + */ +void +circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ) +{ + control_event_circuit_status(circ, + CIRC_EVENT_FAILED, + END_CIRC_REASON_TIMEOUT); + circuit_change_purpose(TO_CIRCUIT(circ), + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + /* Record this event to check for too many timeouts + * in a row. This function does not record a time value yet + * (we do that later); it only counts the fact that we did + * have a timeout. We also want to avoid double-counting + * already "relaxed" circuits, which are counted in + * circuit_expire_building(). */ + if (!circ->relaxed_timeout) { + int first_hop_succeeded = circ->cpath && + circ->cpath->state == CPATH_STATE_OPEN; + + circuit_build_times_count_timeout( + get_circuit_build_times_mutable(), + first_hop_succeeded); + } +} + +/** + * Perform the build time work that needs to be done when a circuit + * completes a hop. + * + * This function decides if we should record a circuit's build time + * in our histogram data and other statistics, and if so, records it. + * It also will mark circuits that have already timed out as + * measurement-only circuits, so they can continue to build but + * not get used. + * + * For this, we want to consider circuits that will eventually make + * it to the third hop. For circuits longer than 3 hops, we want to + * record their build time when they reach the third hop, but let + * them continue (and not count them later). For circuits that are + * exactly 3 hops, this will count them when they are completed. We + * do this so that CBT is always gathering statistics on circuits + * of the same length, regardless of their type. + */ +void +circuit_build_times_handle_completed_hop(origin_circuit_t *circ) +{ + struct timeval end; + long timediff; + + /* If circuit build times are disabled, let circuit_expire_building() + * handle it.. */ + if (circuit_build_times_disabled(get_options())) { + return; + } + + /* Is this a circuit for which the timeout applies in a straight-forward + * way? If so, handle it below. If not, just return (and let + * circuit_expire_building() eventually take care of it). + */ + if (!circuit_timeout_want_to_count_circ(circ)) { + return; + } + + tor_gettimeofday(&end); + timediff = tv_mdiff(&circ->base_.timestamp_began, &end); + + /* Check if we would have timed out already. If so, change the + * purpose here. But don't do any timeout handling here if there + * are no circuits opened yet. Save it for circuit_expire_building() + * (to allow it to handle timeout "relaxing" over there). */ + if (timediff > get_circuit_build_timeout_ms() && + circuit_any_opened_circuits_cached()) { + + /* Circuits are allowed to last longer for measurement. + * Switch their purpose and wait. */ + if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + log_info(LD_CIRC, + "Deciding to timeout circuit "U64_FORMAT"\n", + U64_PRINTF_ARG(circ->global_identifier)); + circuit_build_times_mark_circ_as_measurement_only(circ); + } + } + + /* If the circuit is built to exactly the DEFAULT_ROUTE_LEN, + * add it to our buildtimes. */ + if (circuit_get_cpath_opened_len(circ) == DEFAULT_ROUTE_LEN) { + /* If the circuit build time is much greater than we would have cut + * it off at, we probably had a suspend event along this codepath, + * and we should discard the value. + */ + if (timediff < 0 || + timediff > 2*get_circuit_build_close_time_ms()+1000) { + log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " + "Assuming clock jump. Purpose %d (%s)", timediff, + circ->base_.purpose, + circuit_purpose_to_string(circ->base_.purpose)); + } else { + /* Only count circuit times if the network is live */ + if (circuit_build_times_network_check_live( + get_circuit_build_times())) { + circuit_build_times_add_time(get_circuit_build_times_mutable(), + (build_time_t)timediff); + circuit_build_times_set_timeout(get_circuit_build_times_mutable()); + } + + if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + circuit_build_times_network_circ_success( + get_circuit_build_times_mutable()); + } + } + } +} + +/** * Add a new build time value <b>time</b> to the set of build times. Time * units are milliseconds. * @@ -1290,9 +1415,32 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) } /** - * Called to indicate that we completed a circuit. Because this circuit + * Non-destructively scale all of our circuit success, timeout, and close + * counts down by a factor of two. Scaling in this way preserves the + * ratios between succeeded vs timed out vs closed circuits, so that + * our statistics don't change when we scale. + * + * This is used only in the rare event that we build more than + * INT32_MAX circuits. Since the num_circ_* variables are + * uint32_t, we won't even be close to overflowing them. + */ +void +circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt) +{ + cbt->num_circ_succeeded /= 2; + cbt->num_circ_timeouts /= 2; + cbt->num_circ_closed /= 2; +} + +/** + * Called to indicate that we "completed" a circuit. Because this circuit * succeeded, it doesn't count as a timeout-after-the-first-hop. * + * (For the purposes of the cbt code, we consider a circuit "completed" if + * it has 3 hops, regardless of its final hop count. We do this because + * we're trying to answer the question, "how long should a circuit take to + * reach the 3-hop count".) + * * This is used by circuit_build_times_network_check_changed() to determine * if we had too many recent timeouts and need to reset our learned timeout * to something higher. @@ -1300,6 +1448,14 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) void circuit_build_times_network_circ_success(circuit_build_times_t *cbt) { + // Count circuit success + cbt->num_circ_succeeded++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_succeeded >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* Check for NULLness because we might not be using adaptive timeouts */ if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { @@ -1322,6 +1478,14 @@ static void circuit_build_times_network_timeout(circuit_build_times_t *cbt, int did_onehop) { + // Count circuit timeout + cbt->num_circ_timeouts++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_timeouts >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* Check for NULLness because we might not be using adaptive timeouts */ if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { @@ -1347,6 +1511,15 @@ circuit_build_times_network_close(circuit_build_times_t *cbt, int did_onehop, time_t start_time) { time_t now = time(NULL); + + // Count circuit close + cbt->num_circ_closed++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_closed >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* * Check if this is a timeout that was for a circuit that spent its * entire existence during a time where we have had no network activity. @@ -1701,6 +1874,8 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, { char *args = NULL; double qnt; + double timeout_rate = 0.0; + double close_rate = 0.0; switch (type) { case BUILDTIMEOUT_SET_EVENT_RESET: @@ -1715,15 +1890,29 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, break; } + /* The timeout rate is the ratio of the timeout count over + * the total number of circuits attempted. The total number of + * circuits is (timeouts+succeeded+closed), since a circuit can + * either timeout, close, or succeed. We cast the denominator + * to promote it to double before the addition, to avoid int32 + * overflow. */ + const double total_circuits = + ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded + + cbt->num_circ_closed; + if (total_circuits >= 1.0) { + timeout_rate = cbt->num_circ_timeouts / total_circuits; + close_rate = cbt->num_circ_closed / total_circuits; + } + tor_asprintf(&args, "TOTAL_TIMES=%lu " "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", (unsigned long)cbt->total_build_times, (unsigned long)cbt->timeout_ms, (unsigned long)cbt->Xm, cbt->alpha, qnt, - circuit_build_times_timeout_rate(cbt), + timeout_rate, (unsigned long)cbt->close_ms, - circuit_build_times_close_rate(cbt)); + close_rate); control_event_buildtimeout_set(type, args); diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h index 92dc6405ba..86116cb7f8 100644 --- a/src/or/circuitstats.h +++ b/src/or/circuitstats.h @@ -34,6 +34,7 @@ void circuit_build_times_set_timeout(circuit_build_times_t *cbt); int circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time); int circuit_build_times_needs_circuits(const circuit_build_times_t *cbt); +void circuit_build_times_handle_completed_hop(origin_circuit_t *circ); int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); void circuit_build_times_init(circuit_build_times_t *cbt); @@ -44,6 +45,7 @@ double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); void circuit_build_times_update_last_circ(circuit_build_times_t *cbt); +void circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ); #ifdef CIRCUITSTATS_PRIVATE STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, @@ -94,6 +96,16 @@ struct circuit_build_times_s { double timeout_ms; /** How long we wait before actually closing the circuit. */ double close_ms; + /** Total succeeded counts. Old measurements may be scaled downward if + * we've seen a lot of circuits. */ + uint32_t num_circ_succeeded; + /** Total timeout counts. Old measurements may be scaled downward if + * we've seen a lot of circuits. */ + uint32_t num_circ_timeouts; + /** Total closed counts. Old measurements may be scaled downward if + * we've seen a lot of circuits.*/ + uint32_t num_circ_closed; + }; #endif /* defined(CIRCUITSTATS_PRIVATE) */ diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 7baa035696..f04448ffce 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -412,8 +412,17 @@ circuit_conforms_to_options(const origin_circuit_t *circ, } #endif /* 0 */ -/** Close all circuits that start at us, aren't open, and were born +/** + * Close all circuits that start at us, aren't open, and were born * at least CircuitBuildTimeout seconds ago. + * + * TODO: This function is now partially redundant to + * circuit_build_times_handle_completed_hop(), but that function only + * covers circuits up to and including 3 hops that are still actually + * completing hops. However, circuit_expire_building() also handles longer + * circuits, as well as circuits that are completely stalled. + * In the future (after prop247/other path selection revamping), we probably + * want to eliminate this rats nest in favor of a simpler approach. */ void circuit_expire_building(void) @@ -435,21 +444,7 @@ circuit_expire_building(void) * we want to be more lenient with timeouts, in case the * user has relocated and/or changed network connections. * See bug #3443. */ - SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, next_circ) { - if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */ - next_circ->marked_for_close) { /* don't mess with marked circs */ - continue; - } - - if (TO_ORIGIN_CIRCUIT(next_circ)->has_opened && - next_circ->state == CIRCUIT_STATE_OPEN && - TO_ORIGIN_CIRCUIT(next_circ)->build_state && - TO_ORIGIN_CIRCUIT(next_circ)->build_state->desired_path_len - == DEFAULT_ROUTE_LEN) { - any_opened_circs = 1; - break; - } - } SMARTLIST_FOREACH_END(next_circ); + any_opened_circs = circuit_any_opened_circuits(); #define SET_CUTOFF(target, msec) do { \ long ms = tor_lround(msec); \ @@ -493,6 +488,10 @@ circuit_expire_building(void) SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms()); SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms()); + // TODO: We should probably use route_len_for_purpose() here instead, + // except that does not count the extra round trip for things like server + // intros and rends. + /* > 3hop circs seem to have a 1.0 second delay on their cannibalized * 4th hop. */ SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000); @@ -585,7 +584,8 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state)); + victim->n_chan ? + channel_state_to_string(victim->n_chan->state) : "none"); /* We count the timeout here for CBT, because technically this * was a timeout, and the timeout value needs to reset if we @@ -610,7 +610,8 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state), + victim->n_chan ? + channel_state_to_string(victim->n_chan->state) : "none", (long)build_close_ms); } } @@ -694,23 +695,17 @@ circuit_expire_building(void) if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) && circuit_build_times_enough_to_compute(get_circuit_build_times())) { + + log_info(LD_CIRC, + "Deciding to count the timeout for circuit "U64_FORMAT"\n", + U64_PRINTF_ARG( + TO_ORIGIN_CIRCUIT(victim)->global_identifier)); + /* Circuits are allowed to last longer for measurement. * Switch their purpose and wait. */ if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - control_event_circuit_status(TO_ORIGIN_CIRCUIT(victim), - CIRC_EVENT_FAILED, - END_CIRC_REASON_TIMEOUT); - circuit_change_purpose(victim, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); - /* Record this failure to check for too many timeouts - * in a row. This function does not record a time value yet - * (we do that later); it only counts the fact that we did - * have a timeout. We also want to avoid double-counting - * already "relaxed" circuits, which are counted above. */ - if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) { - circuit_build_times_count_timeout( - get_circuit_build_times_mutable(), - first_hop_succeeded); - } + circuit_build_times_mark_circ_as_measurement_only(TO_ORIGIN_CIRCUIT( + victim)); continue; } diff --git a/src/or/config.c b/src/or/config.c index 1c79c148e3..e5c10498c4 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -18,7 +18,7 @@ * inspecting values that are calculated as a result of the * configured options. * - * <h3>How to add new options</h3> + * <h3>How to add new options</h3> * * To add new items to the torrc, there are a minimum of three places to edit: * <ul> @@ -82,6 +82,7 @@ #include "dirvote.h" #include "dns.h" #include "entrynodes.h" +#include "git_revision.h" #include "geoip.h" #include "hibernate.h" #include "main.h" @@ -252,6 +253,8 @@ static config_var_t option_vars_[] = { V(BridgeRecordUsageByCountry, BOOL, "1"), V(BridgeRelay, BOOL, "0"), V(BridgeDistribution, STRING, NULL), + VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL), + V(CacheDirectoryGroupReadable, BOOL, "0"), V(CellStatistics, BOOL, "0"), V(PaddingStatistics, BOOL, "1"), V(LearnCircuitBuildTimeout, BOOL, "1"), @@ -285,7 +288,7 @@ static config_var_t option_vars_[] = { V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), V(CountPrivateBandwidth, BOOL, "0"), - V(DataDirectory, FILENAME, NULL), + VAR("DataDirectory", FILENAME, DataDirectory_option, NULL), V(DataDirectoryGroupReadable, BOOL, "0"), V(DisableOOSCheck, BOOL, "1"), V(DisableNetwork, BOOL, "0"), @@ -364,6 +367,7 @@ static config_var_t option_vars_[] = { V(GuardLifetime, INTERVAL, "0 minutes"), V(HardwareAccel, BOOL, "0"), V(HeartbeatPeriod, INTERVAL, "6 hours"), + V(MainloopStats, BOOL, "0"), V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), @@ -398,6 +402,8 @@ static config_var_t option_vars_[] = { V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), V(Socks5ProxyPassword, STRING, NULL), + VAR("KeyDirectory", FILENAME, KeyDirectory_option, NULL), + V(KeyDirectoryGroupReadable, BOOL, "0"), V(KeepalivePeriod, INTERVAL, "5 minutes"), V(KeepBindCapabilities, AUTOBOOL, "auto"), VAR("Log", LINELIST, Logs, NULL), @@ -405,6 +411,7 @@ static config_var_t option_vars_[] = { V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(TruncateLogFile, BOOL, "0"), V(SyslogIdentityTag, STRING, NULL), + V(AndroidIdentityTag, STRING, NULL), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), @@ -490,6 +497,7 @@ static config_var_t option_vars_[] = { V(RendPostPeriod, INTERVAL, "1 hour"), V(RephistTrackTime, INTERVAL, "24 hours"), V(RunAsDaemon, BOOL, "0"), + V(ReducedExitPolicy, BOOL, "0"), OBSOLETE("RunTesting"), // currently unused V(Sandbox, BOOL, "0"), V(SafeLogging, STRING, "1"), @@ -568,6 +576,7 @@ static config_var_t option_vars_[] = { VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), + VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " "300, 900, 2147483647"), @@ -737,7 +746,7 @@ static int parse_ports(or_options_t *options, int validate_only, static int check_server_ports(const smartlist_t *ports, const or_options_t *options, int *num_low_ports_out); -static int validate_data_directory(or_options_t *options); +static int validate_data_directories(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); static int options_init_logs(const or_options_t *old_options, @@ -782,7 +791,7 @@ static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; /** Name of the most recently read torrc-defaults file.*/ -static char *torrc_defaults_fname; +static char *torrc_defaults_fname = NULL; /** Configuration options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** Non-configuration options set by the command line */ @@ -796,6 +805,8 @@ static smartlist_t *configured_ports = NULL; /** True iff we're currently validating options, and any calls to * get_options() are likely to be bugs. */ static int in_option_validation = 0; +/* True iff we've initialized libevent */ +static int libevent_initialized = 0; /** Return the contents of our frontpage string, or NULL if not configured. */ MOCK_IMPL(const char*, @@ -840,9 +851,12 @@ set_options(or_options_t *new_val, char **msg) return -1; } if (options_act(old_options) < 0) { /* acting on the options failed. die. */ - log_err(LD_BUG, - "Acting on config options left us in a broken state. Dying."); - exit(1); + if (! tor_event_loop_shutdown_is_pending()) { + log_err(LD_BUG, + "Acting on config options left us in a broken state. Dying."); + tor_shutdown_event_loop_and_exit(1); + } + return -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. */ @@ -884,8 +898,6 @@ set_options(or_options_t *new_val, char **msg) return 0; } -extern const char tor_git_revision[]; /* from tor_main.c */ - /** The version of this Tor process, as parsed. */ static char *the_tor_version = NULL; /** A shorter version of this Tor process's version, for export in our router @@ -925,7 +937,7 @@ get_short_version(void) /** Release additional memory allocated in options */ STATIC void -or_options_free(or_options_t *options) +or_options_free_(or_options_t *options) { if (!options) return; @@ -940,6 +952,13 @@ or_options_free(or_options_t *options) SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i)); smartlist_free(options->SchedulerTypes_); } + if (options->FilesOpenedByIncludes) { + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f)); + smartlist_free(options->FilesOpenedByIncludes); + } + tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); + tor_free(options->KeyDirectory); tor_free(options->BridgePassword_AuthDigest_); tor_free(options->command_arg); tor_free(options->master_key_fname); @@ -976,6 +995,9 @@ config_free_all(void) tor_free(the_short_tor_version); tor_free(the_tor_version); + + have_parsed_cmdline = 0; + libevent_initialized = 0; } /** Make <b>address</b> -- a piece of information related to our operation as @@ -1249,6 +1271,69 @@ consider_adding_dir_servers(const or_options_t *options, return 0; } +/** + * Make sure that <b>directory</b> exists, with appropriate ownership and + * permissions (as modified by <b>group_readable</b>). If <b>create</b>, + * create the directory if it is missing. Return 0 on success. + * On failure, return -1 and set *<b>msg_out</b>. + */ +static int +check_and_create_data_directory(int create, + const char *directory, + int group_readable, + const char *owner, + char **msg_out) +{ + cpd_check_t cpd_opts = create ? CPD_CREATE : CPD_CHECK; + if (group_readable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(directory, + cpd_opts, + owner) < 0) { + tor_asprintf(msg_out, + "Couldn't %s private data directory \"%s\"", + create ? "create" : "access", + directory); + return -1; + } + +#ifndef _WIN32 + if (group_readable) { + /* Only new dirs created get new opts, also enforce group read. */ + if (chmod(directory, 0750)) { + log_warn(LD_FS,"Unable to make %s group-readable: %s", + directory, strerror(errno)); + } + } +#endif /* !defined(_WIN32) */ + + return 0; +} + +/** + * Ensure that our keys directory exists, with appropriate permissions. + * Return 0 on success, -1 on failure. + */ +int +create_keys_directory(const or_options_t *options) +{ + /* Make sure DataDirectory exists, and is private. */ + cpd_check_t cpd_opts = CPD_CREATE; + if (options->DataDirectoryGroupReadable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { + log_err(LD_OR, "Can't create/check datadirectory %s", + options->DataDirectory); + return -1; + } + + /* Check the key directory. */ + if (check_private_dir(options->KeyDirectory, CPD_CREATE, options->User)) { + return -1; + } + return 0; +} + /* Helps determine flags to pass to switch_id. */ static int have_low_ports = -1; @@ -1263,7 +1348,6 @@ options_act_reversible(const or_options_t *old_options, char **msg) { smartlist_t *new_listeners = smartlist_new(); smartlist_t *replaced_listeners = smartlist_new(); - static int libevent_initialized = 0; or_options_t *options = get_options_mutable(); int running_tor = options->command == CMD_RUN_TOR; int set_conn_limit = 0; @@ -1403,29 +1487,30 @@ options_act_reversible(const or_options_t *old_options, char **msg) } /* Ensure data directory is private; create if possible. */ - cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, - cpd_opts, - options->User)<0) { - tor_asprintf(msg, - "Couldn't access/create private data directory \"%s\"", - options->DataDirectory); - + /* It's okay to do this in "options_act_reversible()" even though it isn't + * actually reversible, since you can't change the DataDirectory while + * Tor is running. */ + if (check_and_create_data_directory(running_tor /* create */, + options->DataDirectory, + options->DataDirectoryGroupReadable, + options->User, + msg) < 0) { goto done; - /* No need to roll back, since you can't change the value. */ } - -#ifndef _WIN32 - if (options->DataDirectoryGroupReadable) { - /* Only new dirs created get new opts, also enforce group read. */ - if (chmod(options->DataDirectory, 0750)) { - log_warn(LD_FS,"Unable to make %s group-readable: %s", - options->DataDirectory, strerror(errno)); - } + if (check_and_create_data_directory(running_tor /* create */, + options->KeyDirectory, + options->KeyDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; + } + if (check_and_create_data_directory(running_tor /* create */, + options->CacheDirectory, + options->CacheDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; } -#endif /* !defined(_WIN32) */ /* Bail out at this point if we're not going to be a client or server: * we don't run Tor itself. */ @@ -1602,32 +1687,46 @@ get_effective_bwburst(const or_options_t *options) return (uint32_t)bw; } +/* Used in the various options_transition_affects* functions. */ +#define YES_IF_CHANGED_BOOL(opt) \ + if (!CFG_EQ_BOOL(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_INT(opt) \ + if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_STRING(opt) \ + if (!CFG_EQ_STRING(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_LINELIST(opt) \ + if (!CFG_EQ_LINELIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_SMARTLIST(opt) \ + if (!CFG_EQ_SMARTLIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_ROUTERSET(opt) \ + if (!CFG_EQ_ROUTERSET(old_options, new_options, opt)) return 1; + /** * Return true if changing the configuration from <b>old</b> to <b>new</b> - * affects the guard susbsystem. + * affects the guard subsystem. */ static int -options_transition_affects_guards(const or_options_t *old, - const or_options_t *new) +options_transition_affects_guards(const or_options_t *old_options, + const or_options_t *new_options) { /* NOTE: Make sure this function stays in sync with - * entry_guards_set_filtered_flags */ - - tor_assert(old); - tor_assert(new); - - return - (old->UseEntryGuards != new->UseEntryGuards || - old->UseBridges != new->UseBridges || - old->ClientUseIPv4 != new->ClientUseIPv4 || - old->ClientUseIPv6 != new->ClientUseIPv6 || - old->FascistFirewall != new->FascistFirewall || - !routerset_equal(old->ExcludeNodes, new->ExcludeNodes) || - !routerset_equal(old->EntryNodes, new->EntryNodes) || - !smartlist_strings_eq(old->FirewallPorts, new->FirewallPorts) || - !config_lines_eq(old->Bridges, new->Bridges) || - !config_lines_eq(old->ReachableORAddresses, new->ReachableORAddresses) || - !config_lines_eq(old->ReachableDirAddresses, new->ReachableDirAddresses)); + * node_passes_guard_filter */ + tor_assert(old_options); + tor_assert(new_options); + + YES_IF_CHANGED_BOOL(UseEntryGuards); + YES_IF_CHANGED_BOOL(UseBridges); + YES_IF_CHANGED_BOOL(ClientUseIPv4); + YES_IF_CHANGED_BOOL(ClientUseIPv6); + YES_IF_CHANGED_BOOL(FascistFirewall); + YES_IF_CHANGED_ROUTERSET(ExcludeNodes); + YES_IF_CHANGED_ROUTERSET(EntryNodes); + YES_IF_CHANGED_SMARTLIST(FirewallPorts); + YES_IF_CHANGED_LINELIST(Bridges); + YES_IF_CHANGED_LINELIST(ReachableORAddresses); + YES_IF_CHANGED_LINELIST(ReachableDirAddresses); + + return 0; } /** Fetch the active option list, and take actions based on it. All of the @@ -1695,8 +1794,11 @@ options_act(const or_options_t *old_options) else protocol_warning_severity_level = LOG_INFO; - if (consider_adding_dir_servers(options, old_options) < 0) + if (consider_adding_dir_servers(options, old_options) < 0) { + // XXXX This should get validated earlier, and committed here, to + // XXXX lower opportunities for reaching an error case. return -1; + } if (rend_non_anonymous_mode_enabled(options)) { log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " @@ -1705,6 +1807,7 @@ options_act(const or_options_t *old_options) #ifdef ENABLE_TOR2WEB_MODE /* LCOV_EXCL_START */ + // XXXX This should move into options_validate() if (!options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was compiled to run in " "'tor2web mode'. It can only be run with the Tor2webMode torrc " @@ -1713,6 +1816,7 @@ options_act(const or_options_t *old_options) } /* LCOV_EXCL_STOP */ #else /* !(defined(ENABLE_TOR2WEB_MODE)) */ + // XXXX This should move into options_validate() if (options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " "'tor2web mode'. It cannot be run with the Tor2webMode torrc " @@ -1740,9 +1844,11 @@ options_act(const or_options_t *old_options) for (cl = options->Bridges; cl; cl = cl->next) { bridge_line_t *bridge_line = parse_bridge_line(cl->value); if (!bridge_line) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated Bridge line could not be added!"); return -1; + // LCOV_EXCL_STOP } bridge_add_from_config(bridge_line); } @@ -1750,15 +1856,37 @@ options_act(const or_options_t *old_options) } if (running_tor && hs_config_service_all(options, 0)<0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated hidden services line could not be added!"); return -1; + // LCOV_EXCL_STOP } if (running_tor && rend_parse_service_authorization(options, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated client authorization for " "hidden services could not be added!"); return -1; + // LCOV_EXCL_STOP + } + + if (running_tor && !old_options && options->OwningControllerFD != -1) { +#ifdef _WIN32 + log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. " + "If you need it, tell the Tor developers."); + return -1; +#else + const unsigned ctrl_flags = + CC_LOCAL_FD_IS_OWNER | + CC_LOCAL_FD_IS_AUTHENTICATED; + tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD; + if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) { + log_warn(LD_CONFIG, "Could not add local controller connection with " + "given FD."); + return -1; + } +#endif /* defined(_WIN32) */ } /* Load state */ @@ -1781,10 +1909,12 @@ options_act(const or_options_t *old_options) if (options->ClientTransportPlugin) { for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ClientTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1792,10 +1922,12 @@ options_act(const or_options_t *old_options) if (options->ServerTransportPlugin && server_mode(options)) { for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 1) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ServerTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1881,8 +2013,10 @@ options_act(const or_options_t *old_options) /* Set up accounting */ if (accounting_parse_options(options, 0)<0) { - log_warn(LD_CONFIG,"Error in accounting options"); + // LCOV_EXCL_START + log_warn(LD_BUG,"Error in previously validated accounting options"); return -1; + // LCOV_EXCL_STOP } if (accounting_is_enabled(options)) configure_accounting(time(NULL)); @@ -1905,6 +2039,7 @@ options_act(const or_options_t *old_options) char *http_authenticator; http_authenticator = alloc_http_authenticator(options->BridgePassword); if (!http_authenticator) { + // XXXX This should get validated in options_validate(). log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting " "BridgePassword."); return -1; @@ -1917,9 +2052,12 @@ options_act(const or_options_t *old_options) } if (parse_outbound_addresses(options, 0, &msg) < 0) { - log_warn(LD_BUG, "Failed parsing outbound bind addresses: %s", msg); + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); tor_free(msg); return -1; + // LCOV_EXCL_STOP } config_maybe_load_geoip_files_(options, old_options); @@ -2043,6 +2181,10 @@ options_act(const or_options_t *old_options) if (options->PerConnBWRate != old_options->PerConnBWRate || options->PerConnBWBurst != old_options->PerConnBWBurst) connection_or_update_token_buckets(get_connection_array(), options); + + if (options->MainloopStats != old_options->MainloopStats) { + reset_main_loop_counters(); + } } /* Only collect directory-request statistics on relays and bridges. */ @@ -3148,12 +3290,12 @@ options_validate(or_options_t *old_options, or_options_t *options, if (parse_outbound_addresses(options, 1, msg) < 0) return -1; - if (validate_data_directory(options)<0) + if (validate_data_directories(options)<0) REJECT("Invalid DataDirectory"); if (options->Nickname == NULL) { if (server_mode(options)) { - options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); + options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); } } else { if (!is_legal_nickname(options->Nickname)) { @@ -3173,9 +3315,9 @@ options_validate(or_options_t *old_options, or_options_t *options, /* Special case on first boot if no Log options are given. */ if (!options->Logs && !options->RunAsDaemon && !from_setconf) { if (quiet_level == 0) - config_line_append(&options->Logs, "Log", "notice stdout"); + config_line_append(&options->Logs, "Log", "notice stdout"); else if (quiet_level == 1) - config_line_append(&options->Logs, "Log", "warn stdout"); + config_line_append(&options->Logs, "Log", "warn stdout"); } /* Validate the tor_log(s) */ @@ -4521,125 +4663,60 @@ options_transition_allowed(const or_options_t *old, if (!old) return 0; - if (!opt_streq(old->PidFile, new_val->PidFile)) { - *msg = tor_strdup("PidFile is not allowed to change."); - return -1; - } - - if (old->RunAsDaemon != new_val->RunAsDaemon) { - *msg = tor_strdup("While Tor is running, changing RunAsDaemon " - "is not allowed."); - return -1; - } - - if (old->Sandbox != new_val->Sandbox) { - *msg = tor_strdup("While Tor is running, changing Sandbox " - "is not allowed."); - return -1; - } - - if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) { - tor_asprintf(msg, - "While Tor is running, changing DataDirectory " - "(\"%s\"->\"%s\") is not allowed.", - old->DataDirectory, new_val->DataDirectory); - return -1; - } - - if (!opt_streq(old->User, new_val->User)) { - *msg = tor_strdup("While Tor is running, changing User is not allowed."); - return -1; - } - - if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) { - *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is " - "not allowed."); - return -1; - } - - if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) { - *msg = tor_strdup("While Tor is running, changing " - "SyslogIdentityTag is not allowed."); - return -1; - } - - if ((old->HardwareAccel != new_val->HardwareAccel) - || !opt_streq(old->AccelName, new_val->AccelName) - || !opt_streq(old->AccelDir, new_val->AccelDir)) { - *msg = tor_strdup("While Tor is running, changing OpenSSL hardware " - "acceleration engine is not allowed."); - return -1; - } - - if (old->TestingTorNetwork != new_val->TestingTorNetwork) { - *msg = tor_strdup("While Tor is running, changing TestingTorNetwork " - "is not allowed."); - return -1; - } - - if (old->DisableAllSwap != new_val->DisableAllSwap) { - *msg = tor_strdup("While Tor is running, changing DisableAllSwap " - "is not allowed."); - return -1; - } - - if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) { - *msg = tor_strdup("While Tor is running, changing TokenBucketRefill" - "Interval is not allowed"); - return -1; - } - - if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) { - *msg = tor_strdup("While Tor is running, changing " - "HiddenServiceSingleHopMode is not allowed."); - return -1; - } - - if (old->HiddenServiceNonAnonymousMode != - new_val->HiddenServiceNonAnonymousMode) { - *msg = tor_strdup("While Tor is running, changing " - "HiddenServiceNonAnonymousMode is not allowed."); - return -1; - } - - if (old->DisableDebuggerAttachment && - !new_val->DisableDebuggerAttachment) { - *msg = tor_strdup("While Tor is running, disabling " - "DisableDebuggerAttachment is not allowed."); - return -1; - } - - if (old->NoExec && !new_val->NoExec) { - *msg = tor_strdup("While Tor is running, disabling " - "NoExec is not allowed."); - return -1; - } +#define BAD_CHANGE_TO(opt, how) do { \ + *msg = tor_strdup("While Tor is running"how", changing " #opt \ + " is not allowed"); \ + return -1; \ + } while (0) + +#define NO_CHANGE_BOOL(opt) \ + if (! CFG_EQ_BOOL(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_STRING(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) BAD_CHANGE_TO(opt,"") + + NO_CHANGE_STRING(PidFile); + NO_CHANGE_BOOL(RunAsDaemon); + NO_CHANGE_BOOL(Sandbox); + NO_CHANGE_STRING(DataDirectory); + NO_CHANGE_STRING(KeyDirectory); + NO_CHANGE_STRING(CacheDirectory); + NO_CHANGE_STRING(User); + NO_CHANGE_BOOL(KeepBindCapabilities); + NO_CHANGE_STRING(SyslogIdentityTag); + NO_CHANGE_STRING(AndroidIdentityTag); + NO_CHANGE_BOOL(HardwareAccel); + NO_CHANGE_STRING(AccelName); + NO_CHANGE_STRING(AccelDir); + NO_CHANGE_BOOL(TestingTorNetwork); + NO_CHANGE_BOOL(DisableAllSwap); + NO_CHANGE_INT(TokenBucketRefillInterval); + NO_CHANGE_BOOL(HiddenServiceSingleHopMode); + NO_CHANGE_BOOL(HiddenServiceNonAnonymousMode); + NO_CHANGE_BOOL(DisableDebuggerAttachment); + NO_CHANGE_BOOL(NoExec); + NO_CHANGE_INT(OwningControllerFD); if (sandbox_is_active()) { -#define SB_NOCHANGE_STR(opt) \ - do { \ - if (! opt_streq(old->opt, new_val->opt)) { \ - *msg = tor_strdup("Can't change " #opt " while Sandbox is active"); \ - return -1; \ - } \ - } while (0) +#define SB_NOCHANGE_STR(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_LINELIST(opt) \ + if (! CFG_EQ_LINELIST(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") SB_NOCHANGE_STR(Address); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); SB_NOCHANGE_STR(CookieAuthFile); SB_NOCHANGE_STR(ExtORPortCookieAuthFile); + SB_NOCHANGE_LINELIST(Logs); + SB_NOCHANGE_INT(ConnLimit); -#undef SB_NOCHANGE_STR - - if (! config_lines_eq(old->Logs, new_val->Logs)) { - *msg = tor_strdup("Can't change Logs while Sandbox is active"); - return -1; - } - if (old->ConnLimit != new_val->ConnLimit) { - *msg = tor_strdup("Can't change ConnLimit while Sandbox is active"); - return -1; - } if (server_mode(old) != server_mode(new_val)) { *msg = tor_strdup("Can't start/stop being a server while " "Sandbox is active"); @@ -4647,6 +4724,13 @@ options_transition_allowed(const or_options_t *old, } } +#undef SB_NOCHANGE_LINELIST +#undef SB_NOCHANGE_STR +#undef SB_NOCHANGE_INT +#undef BAD_CHANGE_TO +#undef NO_CHANGE_BOOL +#undef NO_CHANGE_INT +#undef NO_CHANGE_STRING return 0; } @@ -4656,21 +4740,19 @@ static int options_transition_affects_workers(const or_options_t *old_options, const or_options_t *new_options) { - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - old_options->NumCPUs != new_options->NumCPUs || - !config_lines_eq(old_options->ORPort_lines, new_options->ORPort_lines) || - old_options->ServerDNSSearchDomains != - new_options->ServerDNSSearchDomains || - old_options->SafeLogging_ != new_options->SafeLogging_ || - old_options->ClientOnly != new_options->ClientOnly || - server_mode(old_options) != server_mode(new_options) || - public_server_mode(old_options) != public_server_mode(new_options) || - !config_lines_eq(old_options->Logs, new_options->Logs) || - old_options->LogMessageDomains != new_options->LogMessageDomains) + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_INT(NumCPUs); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_BOOL(ServerDNSSearchDomains); + YES_IF_CHANGED_BOOL(SafeLogging_); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(LogMessageDomains); + YES_IF_CHANGED_LINELIST(Logs); + + if (server_mode(old_options) != server_mode(new_options) || + public_server_mode(old_options) != public_server_mode(new_options)) return 1; - /* Check whether log options match. */ - /* Nothing that changed matters. */ return 0; } @@ -4683,37 +4765,34 @@ options_transition_affects_descriptor(const or_options_t *old_options, { /* XXX We can be smarter here. If your DirPort isn't being * published and you just turned it off, no need to republish. Etc. */ - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - !opt_streq(old_options->Nickname,new_options->Nickname) || - !opt_streq(old_options->Address,new_options->Address) || - !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || - old_options->ExitRelay != new_options->ExitRelay || - old_options->ExitPolicyRejectPrivate != - new_options->ExitPolicyRejectPrivate || - old_options->ExitPolicyRejectLocalInterfaces != - new_options->ExitPolicyRejectLocalInterfaces || - old_options->IPv6Exit != new_options->IPv6Exit || - !config_lines_eq(old_options->ORPort_lines, - new_options->ORPort_lines) || - !config_lines_eq(old_options->DirPort_lines, - new_options->DirPort_lines) || - old_options->ClientOnly != new_options->ClientOnly || - old_options->DisableNetwork != new_options->DisableNetwork || - old_options->PublishServerDescriptor_ != - new_options->PublishServerDescriptor_ || - get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || + + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_STRING(Nickname); + YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(ExitPolicy); + YES_IF_CHANGED_BOOL(ExitRelay); + YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); + YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces); + YES_IF_CHANGED_BOOL(IPv6Exit); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(DisableNetwork); + YES_IF_CHANGED_BOOL(PublishServerDescriptor_); + YES_IF_CHANGED_STRING(ContactInfo); + YES_IF_CHANGED_STRING(BridgeDistribution); + YES_IF_CHANGED_LINELIST(MyFamily); + YES_IF_CHANGED_STRING(AccountingStart); + YES_IF_CHANGED_INT(AccountingMax); + YES_IF_CHANGED_INT(AccountingRule); + YES_IF_CHANGED_BOOL(DirCache); + YES_IF_CHANGED_BOOL(AssumeReachable); + + if (get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || get_effective_bwburst(old_options) != get_effective_bwburst(new_options) || - !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || - !opt_streq(old_options->BridgeDistribution, - new_options->BridgeDistribution) || - !config_lines_eq(old_options->MyFamily, new_options->MyFamily) || - !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || - old_options->AccountingMax != new_options->AccountingMax || - old_options->AccountingRule != new_options->AccountingRule || - public_server_mode(old_options) != public_server_mode(new_options) || - old_options->DirCache != new_options->DirCache || - old_options->AssumeReachable != new_options->AssumeReachable) + public_server_mode(old_options) != public_server_mode(new_options)) return 1; return 0; @@ -5027,7 +5106,8 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) /** Read a configuration file into <b>options</b>, finding the configuration * file location based on the command line. After loading the file * call options_init_from_string() to load the config. - * Return 0 if success, -1 if failure. */ + * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit + * anyway. */ int options_init_from_torrc(int argc, char **argv) { @@ -5054,22 +5134,22 @@ options_init_from_torrc(int argc, char **argv) if (config_line_find(cmdline_only_options, "-h") || config_line_find(cmdline_only_options, "--help")) { print_usage(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-torrc-options")) { /* For validating whether we've documented everything. */ list_torrc_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-deprecated-options")) { /* For validating whether what we have deprecated really exists. */ list_deprecated_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--library-versions")) { @@ -5097,7 +5177,7 @@ options_init_from_torrc(int argc, char **argv) tor_compress_header_version_str(ZSTD_METHOD)); } //TODO: Hex versions? - exit(0); + return 1; } command = CMD_RUN_TOR; @@ -5158,7 +5238,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; } else { log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -5167,7 +5248,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->change_key_passphrase = 1; } else { log_err(LD_CONFIG, "--newpass specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -5177,17 +5259,20 @@ options_init_from_torrc(int argc, char **argv) if (fd_line) { if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); - exit(1); + retval = -1; + goto err; } else if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); - exit(1); + retval = -1; + goto err; } else { const char *v = fd_line->value; int ok = 1; long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); if (fd < 0 || ok == 0) { log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); - exit(1); + retval = -1; + goto err; } get_options_mutable()->keygen_passphrase_fd = (int)fd; get_options_mutable()->use_keygen_passphrase_fd = 1; @@ -5202,7 +5287,8 @@ options_init_from_torrc(int argc, char **argv) if (key_line) { if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--master-key without --keygen!"); - exit(1); + retval = -1; + goto err; } else { get_options_mutable()->master_key_fname = tor_strdup(key_line->value); } @@ -5250,13 +5336,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command = command; newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; + smartlist_t *opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ retval = config_get_lines_include(body, &cl, 1, - body == cf ? &cf_has_include : NULL); + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5285,6 +5374,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, } newoptions->IncludeUsed = cf_has_include; + newoptions->FilesOpenedByIncludes = opened_files; /* If this is a testing network configuration, change defaults * for a list of dependent config options, re-initialize newoptions @@ -5324,13 +5414,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; /* Assign all options a second time. */ + opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ retval = config_get_lines_include(body, &cl, 1, - body == cf ? &cf_has_include : NULL); + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5355,6 +5448,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->IncludeUsed = cf_has_include; in_option_validation = 1; + newoptions->FilesOpenedByIncludes = opened_files; /* Validate newoptions */ if (options_validate(oldoptions, newoptions, newdefaultoptions, @@ -5381,6 +5475,12 @@ options_init_from_string(const char *cf_defaults, const char *cf, err: in_option_validation = 0; + if (opened_files) { + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + } + // may have been set to opened_files, avoid double free + newoptions->FilesOpenedByIncludes = NULL; or_options_free(newoptions); or_options_free(newdefaultoptions); if (*msg) { @@ -5576,16 +5676,29 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, } goto cleanup; } - if (smartlist_len(elts) == 1 && - !strcasecmp(smartlist_get(elts,0), "syslog")) { + if (smartlist_len(elts) == 1) { + if (!strcasecmp(smartlist_get(elts,0), "syslog")) { #ifdef HAVE_SYSLOG_H - if (!validate_only) { - add_syslog_log(severity, options->SyslogIdentityTag); - } + if (!validate_only) { + add_syslog_log(severity, options->SyslogIdentityTag); + } #else - log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); + log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); #endif /* defined(HAVE_SYSLOG_H) */ - goto cleanup; + goto cleanup; + } + + if (!strcasecmp(smartlist_get(elts, 0), "android")) { +#ifdef HAVE_ANDROID_LOG_H + if (!validate_only) { + add_android_log(severity, options->AndroidIdentityTag); + } +#else + log_warn(LD_CONFIG, "Android logging is not supported" + " on this system. Sorry."); +#endif // HAVE_ANDROID_LOG_H. + goto cleanup; + } } if (smartlist_len(elts) == 2 && @@ -5671,7 +5784,7 @@ validate_transport_socks_arguments(const smartlist_t *args) /** Deallocate a bridge_line_t structure. */ /* private */ void -bridge_line_free(bridge_line_t *bridge_line) +bridge_line_free_(bridge_line_t *bridge_line) { if (!bridge_line) return; @@ -6437,7 +6550,7 @@ port_cfg_new(size_t namelen) /** Free all storage held in <b>port</b> */ STATIC void -port_cfg_free(port_cfg_t *port) +port_cfg_free_(port_cfg_t *port) { tor_free(port); } @@ -7615,60 +7728,81 @@ port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h, check_wildcard); } -/** Adjust the value of options->DataDirectory, or fill it in if it's - * absent. Return 0 on success, -1 on failure. */ -static int -normalize_data_directory(or_options_t *options) +/** Allocate and return a good value for the DataDirectory based on + * <b>val</b>, which may be NULL. Return NULL on failure. */ +static char * +get_data_directory(const char *val) { #ifdef _WIN32 - char *p; - if (options->DataDirectory) - return 0; /* all set */ - p = tor_malloc(MAX_PATH); - strlcpy(p,get_windows_conf_root(),MAX_PATH); - options->DataDirectory = p; - return 0; + if (val) { + return tor_strdup(val); + } else { + return tor_strdup(get_windows_conf_root()); + } #else /* !(defined(_WIN32)) */ - const char *d = options->DataDirectory; + const char *d = val; if (!d) d = "~/.tor"; - if (strncmp(d,"~/",2) == 0) { - char *fn = expand_filename(d); - if (!fn) { - log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); - return -1; - } - if (!options->DataDirectory && !strcmp(fn,"/.tor")) { - /* If our homedir is /, we probably don't want to use it. */ - /* Default to LOCALSTATEDIR/tor which is probably closer to what we - * want. */ - log_warn(LD_CONFIG, - "Default DataDirectory is \"~/.tor\". This expands to " - "\"%s\", which is probably not what you want. Using " - "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); - tor_free(fn); - fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); - } - tor_free(options->DataDirectory); - options->DataDirectory = fn; - } - return 0; + if (!strcmpstart(d, "~/")) { + char *fn = expand_filename(d); + if (!fn) { + log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); + return NULL; + } + if (!val && !strcmp(fn,"/.tor")) { + /* If our homedir is /, we probably don't want to use it. */ + /* Default to LOCALSTATEDIR/tor which is probably closer to what we + * want. */ + log_warn(LD_CONFIG, + "Default DataDirectory is \"~/.tor\". This expands to " + "\"%s\", which is probably not what you want. Using " + "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); + tor_free(fn); + fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); + } + return fn; + } + return tor_strdup(d); #endif /* defined(_WIN32) */ } -/** Check and normalize the value of options->DataDirectory; return 0 if it - * is sane, -1 otherwise. */ +/** Check and normalize the values of options->{Key,Data,Cache}Directory; + * return 0 if it is sane, -1 otherwise. */ static int -validate_data_directory(or_options_t *options) +validate_data_directories(or_options_t *options) { - if (normalize_data_directory(options) < 0) + tor_free(options->DataDirectory); + options->DataDirectory = get_data_directory(options->DataDirectory_option); + if (!options->DataDirectory) return -1; - tor_assert(options->DataDirectory); if (strlen(options->DataDirectory) > (512-128)) { log_warn(LD_CONFIG, "DataDirectory is too long."); return -1; } + + tor_free(options->KeyDirectory); + if (options->KeyDirectory_option) { + options->KeyDirectory = get_data_directory(options->KeyDirectory_option); + if (!options->KeyDirectory) + return -1; + } else { + /* Default to the data directory's keys subdir */ + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + } + + tor_free(options->CacheDirectory); + if (options->CacheDirectory_option) { + options->CacheDirectory = get_data_directory( + options->CacheDirectory_option); + if (!options->CacheDirectory) + return -1; + } else { + /* Default to the data directory. */ + options->CacheDirectory = tor_strdup(options->DataDirectory); + } + return 0; } @@ -7809,53 +7943,56 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); } -/** Return a newly allocated string holding a filename relative to the data - * directory. If <b>sub1</b> is present, it is the first path component after +/** Return a newly allocated string holding a filename relative to the + * directory in <b>options</b> specified by <b>roottype</b>. + * If <b>sub1</b> is present, it is the first path component after * the data directory. If <b>sub2</b> is also present, it is the second path * component after the data directory. If <b>suffix</b> is present, it * is appended to the filename. * - * Examples: - * get_datadir_fname2_suffix("a", NULL, NULL) -> $DATADIR/a - * get_datadir_fname2_suffix("a", NULL, ".tmp") -> $DATADIR/a.tmp - * get_datadir_fname2_suffix("a", "b", ".tmp") -> $DATADIR/a/b/.tmp - * get_datadir_fname2_suffix("a", "b", NULL) -> $DATADIR/a/b - * - * Note: Consider using the get_datadir_fname* macros in or.h. + * Note: Consider using macros in config.h that wrap this function; + * you should probably never need to call it as-is. */ MOCK_IMPL(char *, -options_get_datadir_fname2_suffix,(const or_options_t *options, - const char *sub1, const char *sub2, - const char *suffix)) +options_get_dir_fname2_suffix,(const or_options_t *options, + directory_root_t roottype, + const char *sub1, const char *sub2, + const char *suffix)) { - char *fname = NULL; - size_t len; tor_assert(options); - tor_assert(options->DataDirectory); - tor_assert(sub1 || !sub2); /* If sub2 is present, sub1 must be present. */ - len = strlen(options->DataDirectory); - if (sub1) { - len += strlen(sub1)+1; - if (sub2) - len += strlen(sub2)+1; - } - if (suffix) - len += strlen(suffix); - len++; - fname = tor_malloc(len); - if (sub1) { - if (sub2) { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1, sub2); - } else { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1); - } + + const char *rootdir = NULL; + switch (roottype) { + case DIRROOT_DATADIR: + rootdir = options->DataDirectory; + break; + case DIRROOT_CACHEDIR: + rootdir = options->CacheDirectory; + break; + case DIRROOT_KEYDIR: + rootdir = options->KeyDirectory; + break; + default: + tor_assert_unreached(); + break; + } + tor_assert(rootdir); + + if (!suffix) + suffix = ""; + + char *fname = NULL; + + if (sub1 == NULL) { + tor_asprintf(&fname, "%s%s", rootdir, suffix); + tor_assert(!sub2); /* If sub2 is present, sub1 must be present. */ + } else if (sub2 == NULL) { + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s%s", rootdir, sub1, suffix); } else { - strlcpy(fname, options->DataDirectory, len); + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s%s", + rootdir, sub1, sub2, suffix); } - if (suffix) - strlcat(fname, suffix, len); + return fname; } @@ -7896,28 +8033,6 @@ write_to_data_subdir(const char* subdir, const char* fname, return return_val; } -/** Given a file name check to see whether the file exists but has not been - * modified for a very long time. If so, remove it. */ -void -remove_file_if_very_old(const char *fname, time_t now) -{ -#define VERY_OLD_FILE_AGE (28*24*60*60) - struct stat st; - - log_debug(LD_FS, "stat()ing %s", fname); - if (stat(sandbox_intern_string(fname), &st)==0 && - st.st_mtime < now-VERY_OLD_FILE_AGE) { - char buf[ISO_TIME_LEN+1]; - format_local_iso_time(buf, st.st_mtime); - log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. " - "Removing it.", fname, buf); - if (unlink(fname) != 0) { - log_warn(LD_FS, "Failed to unlink %s: %s", - fname, strerror(errno)); - } - } -} - /** Return a smartlist of ports that must be forwarded by * tor-fw-helper. The smartlist contains the ports in a string format * that is understandable by tor-fw-helper. */ diff --git a/src/or/config.h b/src/or/config.h index efdd8c59b0..7c7ef1825a 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -58,31 +58,77 @@ config_line_t *option_get_assignment(const or_options_t *options, const char *key); int options_save_current(void); const char *get_torrc_fname(int defaults_fname); +typedef enum { + DIRROOT_DATADIR, + DIRROOT_CACHEDIR, + DIRROOT_KEYDIR +} directory_root_t; + MOCK_DECL(char *, - options_get_datadir_fname2_suffix, + options_get_dir_fname2_suffix, (const or_options_t *options, + directory_root_t roottype, const char *sub1, const char *sub2, const char *suffix)); + +/* These macros wrap options_get_dir_fname2_suffix to provide a more + * convenient API for finding filenames that Tor uses inside its storage + * They are named according to a pattern: + * (options_)?get_(cache|key|data)dir_fname(2)?(_suffix)? + * + * Macros that begin with options_ take an options argument; the others + * work with respect to the global options. + * + * Each macro works relative to the data directory, the key directory, + * or the cache directory, as determined by which one is mentioned. + * + * Macro variants with "2" in their name take two path components; others + * take one. + * + * Macro variants with "_suffix" at the end take an additional suffix + * that gets appended to the end of the file + */ +#define options_get_datadir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_DATADIR, \ + (sub1), (sub2), (suffix)) +#define options_get_cachedir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_CACHEDIR, \ + (sub1), (sub2), (suffix)) +#define options_get_keydir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_KEYDIR, \ + (sub1), (sub2), (suffix)) + +#define options_get_datadir_fname(opts,sub1) \ + options_get_datadir_fname2_suffix((opts),(sub1), NULL, NULL) +#define options_get_datadir_fname2(opts,sub1,sub2) \ + options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) + #define get_datadir_fname2_suffix(sub1, sub2, suffix) \ options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix)) -/** Return a newly allocated string containing datadir/sub1. See - * get_datadir_fname2_suffix. */ -#define get_datadir_fname(sub1) get_datadir_fname2_suffix((sub1), NULL, NULL) -/** Return a newly allocated string containing datadir/sub1/sub2. See - * get_datadir_fname2_suffix. */ +#define get_datadir_fname(sub1) \ + get_datadir_fname2_suffix((sub1), NULL, NULL) #define get_datadir_fname2(sub1,sub2) \ get_datadir_fname2_suffix((sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1/sub2 relative to - * opts. See get_datadir_fname2_suffix. */ -#define options_get_datadir_fname2(opts,sub1,sub2) \ - options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1suffix. See - * get_datadir_fname2_suffix. */ #define get_datadir_fname_suffix(sub1, suffix) \ get_datadir_fname2_suffix((sub1), NULL, (suffix)) +/** DOCDOC */ +#define options_get_keydir_fname(options, sub1) \ + options_get_keydir_fname2_suffix((options), (sub1), NULL, NULL) +#define get_keydir_fname_suffix(sub1, suffix) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, suffix) +#define get_keydir_fname(sub1) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, NULL) + +#define get_cachedir_fname(sub1) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, NULL) +#define get_cachedir_fname_suffix(sub1, suffix) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, (suffix)) + int using_default_dir_authorities(const or_options_t *options); +int create_keys_directory(const or_options_t *options); + int check_or_create_data_subdir(const char *subdir); int write_to_data_subdir(const char* subdir, const char* fname, const char* str, const char* descr); @@ -152,7 +198,9 @@ typedef struct bridge_line_t { transport proxy. */ } bridge_line_t; -void bridge_line_free(bridge_line_t *bridge_line); +void bridge_line_free_(bridge_line_t *bridge_line); +#define bridge_line_free(line) \ + FREE_AND_NULL(bridge_line_t, bridge_line_free_, (line)) bridge_line_t *parse_bridge_line(const char *line); smartlist_t *get_options_from_transport_options_line(const char *line, const char *transport); @@ -175,8 +223,12 @@ extern struct config_format_t options_format; #endif STATIC port_cfg_t *port_cfg_new(size_t namelen); -STATIC void port_cfg_free(port_cfg_t *port); -STATIC void or_options_free(or_options_t *options); +#define port_cfg_free(port) \ + FREE_AND_NULL(port_cfg_t, port_cfg_free_, (port)) +STATIC void port_cfg_free_(port_cfg_t *port); +#define or_options_free(opt) \ + FREE_AND_NULL(or_options_t, or_options_free_, (opt)) +STATIC void or_options_free_(or_options_t *options); STATIC int options_validate_single_onion(or_options_t *options, char **msg); STATIC int options_validate(or_options_t *old_options, diff --git a/src/or/confparse.c b/src/or/confparse.c index abae7e33dc..64ed9ee6bb 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -863,7 +863,7 @@ config_reset(const config_format_t *fmt, void *options, /** Release storage held by <b>options</b>. */ void -config_free(const config_format_t *fmt, void *options) +config_free_(const config_format_t *fmt, void *options) { int i; diff --git a/src/or/confparse.h b/src/or/confparse.h index 6f0b3b325c..f1f2030343 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -68,7 +68,7 @@ typedef union { config_line_t **LINELIST_V; routerset_t **ROUTERSET; } confparse_dummy_values_t; -#endif +#endif /* defined(TOR_UNIT_TESTS) */ /** An abbreviation for a configuration option allowed on the command line. */ typedef struct config_abbrev_t { @@ -132,13 +132,13 @@ typedef struct config_var_t { { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } } #define DUMMY_TYPECHECK_INSTANCE(tp) \ static tp tp ## _dummy -#else +#else /* !(defined(TOR_UNIT_TESTS)) */ #define CONF_TEST_MEMBERS(tp, conftype, member) #define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } /* Repeatedly declarable incomplete struct to absorb redundant semicolons */ #define DUMMY_TYPECHECK_INSTANCE(tp) \ struct tor_semicolon_eater -#endif +#endif /* defined(TOR_UNIT_TESTS) */ /** Type of a callback to validate whether a given configuration is * well-formed and consistent. See options_trial_assign() for documentation @@ -177,7 +177,12 @@ typedef struct config_format_t { #define CAL_WARN_DEPRECATIONS (1u<<2) void *config_new(const config_format_t *fmt); -void config_free(const config_format_t *fmt, void *options); +void config_free_(const config_format_t *fmt, void *options); +#define config_free(fmt, options) do { \ + config_free_((fmt), (options)); \ + (options) = NULL; \ + } while (0) + config_line_t *config_get_assigned_option(const config_format_t *fmt, const void *options, const char *key, int escape_val); @@ -203,5 +208,13 @@ const char *config_expand_abbrev(const config_format_t *fmt, int command_line, int warn_obsolete); void warn_deprecated_option(const char *what, const char *why); +/* Helper macros to compare an option across two configuration objects */ +#define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt) +#define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt) +#define CFG_EQ_STRING(a,b,opt) (!strcmp_opt((a)->opt, (b)->opt)) +#define CFG_EQ_SMARTLIST(a,b,opt) smartlist_strings_eq((a)->opt, (b)->opt) +#define CFG_EQ_LINELIST(a,b,opt) config_lines_eq((a)->opt, (b)->opt) +#define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt) + #endif /* !defined(TOR_CONFPARSE_H) */ diff --git a/src/or/connection.c b/src/or/connection.c index d2cf4fb416..a020bef775 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2001 Matej Pfajfar. + /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2017, The Tor Project, Inc. */ @@ -118,8 +118,6 @@ static connection_t *connection_listener_new( const port_cfg_t *portcfg); static void connection_init(time_t now, connection_t *conn, int type, int socket_family); -static int connection_init_accepted_conn(connection_t *conn, - const listener_connection_t *listener); static int connection_handle_listener_read(connection_t *conn, int new_type); static int connection_bucket_should_increase(int bucket, or_connection_t *conn); @@ -501,7 +499,7 @@ conn_listener_type_supports_af_unix(int type) * if <b>conn</b> is an OR or OP connection. */ STATIC void -connection_free_(connection_t *conn) +connection_free_minimal(connection_t *conn) { void *mem; size_t memlen; @@ -677,7 +675,7 @@ connection_free_(connection_t *conn) /** Make sure <b>conn</b> isn't in any of the global conn lists; then free it. */ MOCK_IMPL(void, -connection_free,(connection_t *conn)) +connection_free_,(connection_t *conn)) { if (!conn) return; @@ -706,7 +704,7 @@ connection_free,(connection_t *conn)) } #endif /* 1 */ connection_unregister_events(conn); - connection_free_(conn); + connection_free_minimal(conn); } /** @@ -1666,11 +1664,15 @@ connection_handle_listener_read(connection_t *conn, int new_type) } /** Initialize states for newly accepted connection <b>conn</b>. + * * If conn is an OR, start the TLS handshake. + * * If conn is a transparent AP, get its original destination * and place it in circuit_wait. + * + * The <b>listener</b> parameter is only used for AP connections. */ -static int +int connection_init_accepted_conn(connection_t *conn, const listener_connection_t *listener) { @@ -1750,7 +1752,11 @@ connection_connect_sockaddr,(connection_t *conn, if (get_options()->DisableNetwork) { /* We should never even try to connect anyplace if DisableNetwork is set. - * Warn if we do, and refuse to make the connection. */ + * Warn if we do, and refuse to make the connection. + * + * We only check DisableNetwork here, not we_are_hibernating(), since + * we'll still try to fulfill client requests sometimes in the latter case + * (if it is soft hibernation) */ static ratelim_t disablenet_violated = RATELIM_INIT(30*60); *socket_error = SOCK_ERRNO(ENETUNREACH); log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG, @@ -3755,6 +3761,44 @@ connection_outbuf_too_full(connection_t *conn) return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE); } +/** + * On Windows Vista and Windows 7, tune the send buffer size according to a + * hint from the OS. + * + * This should help fix slow upload rates. + */ +static void + +update_send_buffer_size(tor_socket_t sock) +{ +#ifdef _WIN32 + /* We only do this on Vista and 7, because earlier versions of Windows + * don't have the SIO_IDEAL_SEND_BACKLOG_QUERY functionality, and on + * later versions it isn't necessary. */ + static int isVistaOr7 = -1; + if (isVistaOr7 == -1) { + isVistaOr7 = 0; + OSVERSIONINFO osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion < 2) + isVistaOr7 = 1; + } + if (!isVistaOr7) + return; + if (get_options()->ConstrainedSockets) + return; + ULONG isb = 0; + DWORD bytesReturned = 0; + if (!WSAIoctl(sock, SIO_IDEAL_SEND_BACKLOG_QUERY, NULL, 0, + &isb, sizeof(isb), &bytesReturned, NULL, NULL)) { + setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&isb, sizeof(isb)); + } +#else + (void) sock; +#endif +} + /** Try to flush more bytes onto <b>conn</b>-\>s. * * This function gets called either from conn_write_callback() in main.c @@ -3873,6 +3917,9 @@ connection_handle_write_impl(connection_t *conn, int force) result = buf_flush_to_tls(conn->outbuf, or_conn->tls, max_to_write, &conn->outbuf_flushlen); + if (result >= 0) + update_send_buffer_size(conn->s); + /* If we just flushed the last bytes, tell the channel on the * or_conn to check if it needs to geoip_change_dirreq_state() */ /* XXXX move this to flushed_some or finished_flushing -NM */ @@ -3947,6 +3994,7 @@ connection_handle_write_impl(connection_t *conn, int force) connection_mark_for_close(conn); return -1; } + update_send_buffer_size(conn->s); n_written = (size_t) result; } @@ -4049,6 +4097,68 @@ connection_flush(connection_t *conn) return connection_handle_write(conn, 1); } +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Return true iff it is okay to queue bytes on <b>conn</b>'s outbuf for + * writing. + */ +static int +connection_may_write_to_buf(connection_t *conn) +{ + /* if it's marked for close, only allow write if we mean to flush it */ + if (conn->marked_for_close && !conn->hold_open_until_flushed) + return 0; + + return 1; +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has failed; + * mark the connection and warn as appropriate. + */ +static void +connection_write_to_buf_failed(connection_t *conn) +{ + if (CONN_IS_EDGE(conn)) { + /* if it failed, it means we have our package/delivery windows set + wrong compared to our max outbuf size. close the whole circuit. */ + log_warn(LD_NET, + "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); + circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), + END_CIRC_REASON_INTERNAL); + } else if (conn->type == CONN_TYPE_OR) { + or_connection_t *orconn = TO_OR_CONN(conn); + log_warn(LD_NET, + "write_to_buf failed on an orconn; notifying of error " + "(fd %d)", (int)(conn->s)); + connection_or_close_for_error(orconn, 0); + } else { + log_warn(LD_NET, + "write_to_buf failed. Closing connection (fd %d).", + (int)conn->s); + connection_mark_for_close(conn); + } +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded: + * record the number of bytes added. + */ +static void +connection_write_to_buf_commit(connection_t *conn, size_t len) +{ + /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING + * state, we don't want to try to write it right away, since + * conn->write_event won't be set yet. Otherwise, write data from + * this conn as the socket is available. */ + if (conn->write_event) { + connection_start_writing(conn); + } + conn->outbuf_flushlen += len; +} + /** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s * outbuf, and ask it to start writing. * @@ -4063,58 +4173,52 @@ connection_write_to_buf_impl_,(const char *string, size_t len, { /* XXXX This function really needs to return -1 on failure. */ int r; - size_t old_datalen; if (!len && !(zlib<0)) return; - /* if it's marked for close, only allow write if we mean to flush it */ - if (conn->marked_for_close && !conn->hold_open_until_flushed) + + if (!connection_may_write_to_buf(conn)) return; - old_datalen = buf_datalen(conn->outbuf); + size_t written; + if (zlib) { + size_t old_datalen = buf_datalen(conn->outbuf); dir_connection_t *dir_conn = TO_DIR_CONN(conn); int done = zlib < 0; CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf, - dir_conn->compress_state, - string, len, done)); + dir_conn->compress_state, + string, len, done)); + written = buf_datalen(conn->outbuf) - old_datalen; } else { CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len)); + written = len; } if (r < 0) { - if (CONN_IS_EDGE(conn)) { - /* if it failed, it means we have our package/delivery windows set - wrong compared to our max outbuf size. close the whole circuit. */ - log_warn(LD_NET, - "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); - circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), - END_CIRC_REASON_INTERNAL); - } else if (conn->type == CONN_TYPE_OR) { - or_connection_t *orconn = TO_OR_CONN(conn); - log_warn(LD_NET, - "write_to_buf failed on an orconn; notifying of error " - "(fd %d)", (int)(conn->s)); - connection_or_close_for_error(orconn, 0); - } else { - log_warn(LD_NET, - "write_to_buf failed. Closing connection (fd %d).", - (int)conn->s); - connection_mark_for_close(conn); - } + connection_write_to_buf_failed(conn); return; } + connection_write_to_buf_commit(conn, written); +} - /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING - * state, we don't want to try to write it right away, since - * conn->write_event won't be set yet. Otherwise, write data from - * this conn as the socket is available. */ - if (conn->write_event) { - connection_start_writing(conn); - } - if (zlib) { - conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen; - } else { - conn->outbuf_flushlen += len; - } +/** + * Add all bytes from <b>buf</b> to <b>conn</b>'s outbuf, draining them + * from <b>buf</b>. (If the connection is marked and will soon be closed, + * nothing is drained.) + */ +void +connection_buf_add_buf(connection_t *conn, buf_t *buf) +{ + tor_assert(conn); + tor_assert(buf); + size_t len = buf_datalen(buf); + if (len == 0) + return; + + if (!connection_may_write_to_buf(conn)) + return; + + buf_move_all(conn->outbuf, buf); + connection_write_to_buf_commit(conn, len); } #define CONN_GET_ALL_TEMPLATE(var, test) \ @@ -5177,8 +5281,8 @@ 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. +/** Call connection_free_minimal() on every connection in our array, and + * release all storage held by connection.c. * * Don't do the checks in connection_free(), because they will * fail. @@ -5202,7 +5306,8 @@ connection_free_all(void) /* Clear out our list of broken connections */ clear_broken_connection_map(0); - SMARTLIST_FOREACH(conns, connection_t *, conn, connection_free_(conn)); + SMARTLIST_FOREACH(conns, connection_t *, conn, + connection_free_minimal(conn)); if (outgoing_addrs) { SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t *, addr, tor_free(addr)); diff --git a/src/or/connection.h b/src/or/connection.h index 4a5bd6971b..6bc5a7cfd0 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -26,9 +26,12 @@ entry_connection_t *entry_connection_new(int type, int socket_family); control_connection_t *control_connection_new(int socket_family); listener_connection_t *listener_connection_new(int type, int socket_family); connection_t *connection_new(int type, int socket_family); - +int connection_init_accepted_conn(connection_t *conn, + const listener_connection_t *listener); void connection_link_connections(connection_t *conn_a, connection_t *conn_b); -MOCK_DECL(void,connection_free,(connection_t *conn)); +MOCK_DECL(void,connection_free_,(connection_t *conn)); +#define connection_free(conn) \ + FREE_AND_NULL(connection_t, connection_free_, (conn)) void connection_free_all(void); void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); @@ -155,6 +158,7 @@ connection_buf_add_compress(const char *string, size_t len, { connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1); } +void connection_buf_add_buf(connection_t *conn, buf_t *buf); /* DOCDOC connection_get_inbuf_len */ static size_t connection_get_inbuf_len(connection_t *conn); @@ -243,7 +247,6 @@ char *alloc_http_authenticator(const char *authenticator); void assert_connection_ok(connection_t *conn, time_t now); int connection_or_nonopen_was_started_here(or_connection_t *conn); void connection_dump_buffer_mem_stats(int severity); -void remove_file_if_very_old(const char *fname, time_t now); void clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted, log_domain_mask_t domain, @@ -266,7 +269,7 @@ connection_is_moribund(connection_t *conn) void connection_check_oos(int n_socks, int failed); #ifdef CONNECTION_PRIVATE -STATIC void connection_free_(connection_t *conn); +STATIC void connection_free_minimal(connection_t *conn); /* Used only by connection.c and test*.c */ uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index f178917f0b..0c4352ea13 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -339,6 +339,10 @@ relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ, payload[0] = (char) reason; + /* Note: we have to use relay_send_command_from_edge here, not + * connection_edge_end or connection_edge_send_command, since those require + * that we have a stream connected to a circuit, and we don't connect to a + * circuit until we have a pending/successful resolve. */ return relay_send_command_from_edge(stream_id, circ, RELAY_COMMAND_END, payload, 1, cpath_layer); } @@ -999,7 +1003,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, * So the fix is to tell it right now that it ought to finish its loop at * its next available opportunity. */ - tell_event_loop_to_finish(); + tell_event_loop_to_run_external_code(); } /** Mark <b>entry_conn</b> as no longer waiting for a circuit. */ @@ -3327,7 +3331,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) relay_send_end_cell_from_edge(conn->stream_id, circ, END_STREAM_REASON_DONE, origin_circ->cpath->prev); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); /* Drop the circuit here since it might be someone deliberately * scanning the hidden service ports. Note that this mitigates port @@ -3405,11 +3409,6 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (rh.length > RELAY_PAYLOAD_SIZE) return -END_CIRC_REASON_TORPROTOCOL; - /* Note: we have to use relay_send_command_from_edge here, not - * connection_edge_end or connection_edge_send_command, since those require - * that we have a stream connected to a circuit, and we don't connect to a - * circuit until we have a pending/successful resolve. */ - if (!server_mode(options) && circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, @@ -3524,7 +3523,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (we_are_hibernating()) { relay_send_end_cell_from_edge(rh.stream_id, circ, END_STREAM_REASON_HIBERNATING, NULL); - connection_free(TO_CONN(n_stream)); + connection_free_(TO_CONN(n_stream)); return 0; } @@ -3602,7 +3601,7 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) return 0; case 1: /* The result was cached; a resolved cell was sent. */ if (!dummy_conn->base_.marked_for_close) - connection_free(TO_CONN(dummy_conn)); + connection_free_(TO_CONN(dummy_conn)); return 0; case 0: /* resolve added to pending list */ assert_circuit_ok(TO_CIRCUIT(circ)); @@ -3775,8 +3774,8 @@ connection_exit_connect_dir(edge_connection_t *exitconn) if (connection_add(TO_CONN(exitconn))<0) { connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); - connection_free(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); + connection_free_(TO_CONN(exitconn)); + connection_free_(TO_CONN(dirconn)); return 0; } @@ -3788,7 +3787,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); connection_close_immediate(TO_CONN(exitconn)); connection_mark_for_close(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); + connection_free_(TO_CONN(dirconn)); return 0; } diff --git a/src/or/connection_or.c b/src/or/connection_or.c index fd8c5fc7f2..8379f58030 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -505,7 +505,7 @@ var_cell_copy(const var_cell_t *src) /** Release all space held by <b>cell</b>. */ void -var_cell_free(var_cell_t *cell) +var_cell_free_(var_cell_t *cell) { tor_free(cell); } @@ -592,9 +592,6 @@ connection_or_flushed_some(or_connection_t *conn) { size_t datalen; - /* The channel will want to update its estimated queue size */ - channel_update_xmit_queue_size(TLS_CHAN_TO_BASE(conn->chan)); - /* If we're under the low water mark, add cells until we're just over the * high water mark. */ datalen = connection_get_outbuf_len(TO_CONN(conn)); @@ -886,7 +883,7 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here) const node_t *r = node_get_by_id(id_digest); if (r && - node_supports_ed25519_link_authentication(r) && + node_supports_ed25519_link_authentication(r, 1) && ! node_ed25519_id_matches(r, ed_id)) { /* If this node is capable of proving an ed25519 ID, * we can't call this a canonical connection unless both IDs match. */ @@ -965,6 +962,36 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) * too old for new circuits? */ #define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) +/** Expire an or_connection if it is too old. Helper for + * connection_or_group_set_badness_ and fast path for + * channel_rsa_id_group_set_badness. + * + * Returns 1 if the connection was already expired, else 0. + */ +int +connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force) +{ + /* XXXX this function should also be about channels? */ + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn)) + return 1; + + if (force || + or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD + < now) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } + + return 0; +} + /** Given a list of all the or_connections with a given * identity, set elements of that list as is_bad_for_new_circs as * appropriate. Helper for connection_or_set_bad_connections(). @@ -995,19 +1022,8 @@ connection_or_group_set_badness_(smartlist_t *group, int force) /* Pass 1: expire everything that's old, and see what the status of * everything else is. */ SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn)) + if (connection_or_single_set_badness_(now, or_conn, force)) continue; - if (force || - or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD - < now) { - log_info(LD_OR, - "Marking OR conn to %s:%d as too old for new circuits " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } if (connection_or_is_bad_for_new_circs(or_conn)) { ++n_old; @@ -1247,7 +1263,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); } - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; } @@ -1260,7 +1276,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, connection_or_connect_failed(conn, errno_to_orconn_end_reason(socket_error), tor_socket_strerror(socket_error)); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; case 0: connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); @@ -1854,7 +1870,7 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) /** Free all storage held by <b>state</b>. */ void -or_handshake_state_free(or_handshake_state_t *state) +or_handshake_state_free_(or_handshake_state_t *state) { if (!state) return; diff --git a/src/or/connection_or.h b/src/or/connection_or.h index ee66b7c528..7c1dced631 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -68,7 +68,9 @@ int connection_or_client_learned_peer_id(or_connection_t *conn, const ed25519_public_key_t *ed_peer_id); time_t connection_or_client_used(or_connection_t *conn); MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn)); -void or_handshake_state_free(or_handshake_state_t *state); +void or_handshake_state_free_(or_handshake_state_t *state); +#define or_handshake_state_free(state) \ + FREE_AND_NULL(or_handshake_state_t, or_handshake_state_free_, (state)) void or_handshake_state_record_cell(or_connection_t *conn, or_handshake_state_t *state, const cell_t *cell, @@ -105,13 +107,17 @@ int var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids); var_cell_t *var_cell_new(uint16_t payload_len); var_cell_t *var_cell_copy(const var_cell_t *src); -void var_cell_free(var_cell_t *cell); +void var_cell_free_(var_cell_t *cell); +#define var_cell_free(cell) FREE_AND_NULL(var_cell_t, var_cell_free_, (cell)) /* DOCDOC */ #define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4 #define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5 #define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING +int connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force); void connection_or_group_set_badness_(smartlist_t *group, int force); #ifdef TOR_UNIT_TESTS diff --git a/src/or/conscache.c b/src/or/conscache.c index 1a15126f97..e25ac5f40b 100644 --- a/src/or/conscache.c +++ b/src/or/conscache.c @@ -79,7 +79,7 @@ consensus_cache_open(const char *subdir, int max_entries) { int storagedir_max_entries; consensus_cache_t *cache = tor_malloc_zero(sizeof(consensus_cache_t)); - char *directory = get_datadir_fname(subdir); + char *directory = get_cachedir_fname(subdir); cache->max_entries = max_entries; #ifdef MUST_UNMAP_TO_UNLINK @@ -170,7 +170,7 @@ consensus_cache_clear(consensus_cache_t *cache) * Drop all storage held by <b>cache</b>. */ void -consensus_cache_free(consensus_cache_t *cache) +consensus_cache_free_(consensus_cache_t *cache) { if (! cache) return; diff --git a/src/or/conscache.h b/src/or/conscache.h index 3c89dedf43..08a5c5a37b 100644 --- a/src/or/conscache.h +++ b/src/or/conscache.h @@ -10,9 +10,14 @@ typedef struct consensus_cache_entry_t consensus_cache_entry_t; typedef struct consensus_cache_t consensus_cache_t; HANDLE_DECL(consensus_cache_entry, consensus_cache_entry_t, ) +#define consensus_cache_entry_handle_free(h) \ + FREE_AND_NULL(consensus_cache_entry_handle_t, \ + consensus_cache_entry_handle_free_, (h)) consensus_cache_t *consensus_cache_open(const char *subdir, int max_entries); -void consensus_cache_free(consensus_cache_t *cache); +void consensus_cache_free_(consensus_cache_t *cache); +#define consensus_cache_free(cache) \ + FREE_AND_NULL(consensus_cache_t, consensus_cache_free_, (cache)) struct sandbox_cfg_elem; int consensus_cache_may_overallocate(consensus_cache_t *cache); int consensus_cache_register_with_sandbox(consensus_cache_t *cache, diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c index e539b61484..38d901a3ae 100644 --- a/src/or/consdiffmgr.c +++ b/src/or/consdiffmgr.c @@ -207,9 +207,12 @@ HT_PROTOTYPE(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq) HT_GENERATE2(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq, 0.6, tor_reallocarray, tor_free_) +#define cdm_diff_free(diff) \ + FREE_AND_NULL(cdm_diff_t, cdm_diff_free_, (diff)) + /** Release all storage held in <b>diff</b>. */ static void -cdm_diff_free(cdm_diff_t *diff) +cdm_diff_free_(cdm_diff_t *diff) { if (!diff) return; @@ -1506,11 +1509,15 @@ consensus_diff_worker_threadfn(void *state_, void *work_) return WQ_RPL_REPLY; } +#define consensus_diff_worker_job_free(job) \ + FREE_AND_NULL(consensus_diff_worker_job_t, \ + consensus_diff_worker_job_free_, (job)) + /** * Helper: release all storage held in <b>job</b>. */ static void -consensus_diff_worker_job_free(consensus_diff_worker_job_t *job) +consensus_diff_worker_job_free_(consensus_diff_worker_job_t *job) { if (!job) return; @@ -1658,11 +1665,15 @@ typedef struct consensus_compress_worker_job_t { compressed_result_t out[ARRAY_LENGTH(compress_consensus_with)]; } consensus_compress_worker_job_t; +#define consensus_compress_worker_job_free(job) \ + FREE_AND_NULL(consensus_compress_worker_job_t, \ + consensus_compress_worker_job_free_, (job)) + /** * Free all resources held in <b>job</b> */ static void -consensus_compress_worker_job_free(consensus_compress_worker_job_t *job) +consensus_compress_worker_job_free_(consensus_compress_worker_job_t *job) { if (!job) return; diff --git a/src/or/control.c b/src/or/control.c index 202366aaec..cce5c7953b 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -58,7 +58,9 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -549,6 +551,49 @@ decode_escaped_string(const char *start, size_t in_len_max, return end+1; } +/** Create and add a new controller connection on <b>sock</b>. If + * <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should + * exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b> + * is set, then the connection does not need to authenticate. + */ +int +control_connection_add_local_fd(tor_socket_t sock, unsigned flags) +{ + if (BUG(! SOCKET_OK(sock))) + return -1; + const int is_owner = !!(flags & CC_LOCAL_FD_IS_OWNER); + const int is_authenticated = !!(flags & CC_LOCAL_FD_IS_AUTHENTICATED); + control_connection_t *control_conn = control_connection_new(AF_UNSPEC); + connection_t *conn = TO_CONN(control_conn); + conn->s = sock; + tor_addr_make_unspec(&conn->addr); + conn->port = 1; + conn->address = tor_strdup("<local socket>"); + + /* We take ownership of this socket so that later, when we close it, + * we don't freak out. */ + tor_take_socket_ownership(sock); + + if (set_socket_nonblocking(sock) < 0 || + connection_add(conn) < 0) { + connection_free(conn); + return -1; + } + + control_conn->is_owning_control_connection = is_owner; + + if (connection_init_accepted_conn(conn, NULL) < 0) { + connection_mark_for_close(conn); + return -1; + } + + if (is_authenticated) { + conn->state = CONTROL_CONN_STATE_OPEN; + } + + return 0; +} + /** Acts like sprintf, but writes its formatted string to the end of * <b>conn</b>-\>outbuf. */ static void @@ -740,9 +785,12 @@ queue_control_event_string,(uint16_t event, char *msg)) } } +#define queued_event_free(ev) \ + FREE_AND_NULL(queued_event_t, queued_event_free_, (ev)) + /** Release all storage held by <b>ev</b>. */ static void -queued_event_free(queued_event_t *ev) +queued_event_free_(queued_event_t *ev) { if (ev == NULL) return; @@ -1971,36 +2019,89 @@ getinfo_helper_dir(control_connection_t *control_conn, SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); } else if (!strcmpstart(question, "hs/client/desc/id/")) { - rend_cache_entry_t *e = NULL; + hostname_type_t addr_type; question += strlen("hs/client/desc/id/"); - if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + if (rend_valid_v2_service_id(question)) { + addr_type = ONION_V2_HOSTNAME; + } else if (hs_address_is_valid(question)) { + addr_type = ONION_V3_HOSTNAME; + } else { *errmsg = "Invalid address"; return -1; } - if (!rend_cache_lookup_entry(question, -1, &e)) { - /* Descriptor found in cache */ - *answer = tor_strdup(e->desc); + if (addr_type == ONION_V2_HOSTNAME) { + rend_cache_entry_t *e = NULL; + if (!rend_cache_lookup_entry(question, -1, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else { - *errmsg = "Not found in cache"; - return -1; + ed25519_public_key_t service_pk; + const char *desc; + + /* The check before this if/else makes sure of this. */ + tor_assert(addr_type == ONION_V3_HOSTNAME); + + if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) { + *errmsg = "Invalid v3 address"; + return -1; + } + + desc = hs_cache_lookup_encoded_as_client(&service_pk); + if (desc) { + *answer = tor_strdup(desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } } else if (!strcmpstart(question, "hs/service/desc/id/")) { - rend_cache_entry_t *e = NULL; + hostname_type_t addr_type; question += strlen("hs/service/desc/id/"); - if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + if (rend_valid_v2_service_id(question)) { + addr_type = ONION_V2_HOSTNAME; + } else if (hs_address_is_valid(question)) { + addr_type = ONION_V3_HOSTNAME; + } else { *errmsg = "Invalid address"; return -1; } + rend_cache_entry_t *e = NULL; - if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { - /* Descriptor found in cache */ - *answer = tor_strdup(e->desc); + if (addr_type == ONION_V2_HOSTNAME) { + if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else { - *errmsg = "Not found in cache"; - return -1; + ed25519_public_key_t service_pk; + char *desc; + + /* The check before this if/else makes sure of this. */ + tor_assert(addr_type == ONION_V3_HOSTNAME); + + if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) { + *errmsg = "Invalid v3 address"; + return -1; + } + + desc = hs_service_lookup_current_desc(&service_pk); + if (desc) { + /* Newly allocated string, we have ownership. */ + *answer = desc; + } else { + *errmsg = "Not found in cache"; + return -1; + } } } else if (!strcmpstart(question, "md/id/")) { const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0); @@ -2072,7 +2173,7 @@ getinfo_helper_dir(control_connection_t *control_conn, *answer = tor_strdup(consensus->dir); } if (!*answer) { /* try loading it from disk */ - char *filename = get_datadir_fname("cached-consensus"); + char *filename = get_cachedir_fname("cached-consensus"); *answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); if (!*answer) { /* generate an error */ @@ -2581,9 +2682,16 @@ circuit_describe_status_for_controller(origin_circuit_t *circ) } } - if (circ->rend_data != NULL) { - smartlist_add_asprintf(descparts, "REND_QUERY=%s", - rend_data_get_address(circ->rend_data)); + if (circ->rend_data != NULL || circ->hs_ident != NULL) { + char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + const char *onion_address; + if (circ->rend_data) { + onion_address = rend_data_get_address(circ->rend_data); + } else { + hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr); + onion_address = addr; + } + smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address); } { @@ -4234,9 +4342,11 @@ handle_control_hspost(control_connection_t *conn, const char *body) { static const char *opt_server = "SERVER="; + static const char *opt_hsaddress = "HSADDRESS="; smartlist_t *hs_dirs = NULL; const char *encoded_desc = body; size_t encoded_desc_len = len; + const char *onion_address = NULL; char *cp = memchr(body, '\n', len); if (cp == NULL) { @@ -4266,15 +4376,16 @@ handle_control_hspost(control_connection_t *conn, server); goto done; } - if (!node->rs->is_hs_dir) { - connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir" - "\r\n", server); - goto done; - } /* Valid server, add it to our local list. */ if (!hs_dirs) hs_dirs = smartlist_new(); smartlist_add(hs_dirs, node->rs); + } else if (!strcasecmpstart(arg, opt_hsaddress)) { + if (!hs_address_is_valid(arg)) { + connection_printf_to_buf(conn, "512 Malformed onion address\r\n"); + goto done; + } + onion_address = arg; } else { connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n", arg); @@ -4283,6 +4394,19 @@ handle_control_hspost(control_connection_t *conn, } SMARTLIST_FOREACH_END(arg); } + /* Handle the v3 case. */ + if (onion_address) { + char *desc_str = NULL; + read_escaped_data(encoded_desc, encoded_desc_len, &desc_str); + if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) { + connection_printf_to_buf(conn, "554 Invalid descriptor\r\n"); + } + tor_free(desc_str); + goto done; + } + + /* From this point on, it is only v2. */ + /* Read the dot encoded descriptor, and parse it. */ rend_encoded_v2_service_descriptor_t *desc = tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t)); @@ -4327,6 +4451,52 @@ handle_control_hspost(control_connection_t *conn, return 0; } +/* Helper function for ADD_ONION that adds an ephemeral service depending on + * the given hs_version. + * + * The secret key in pk depends on the hs_version. The ownership of the key + * used in pk is given to the HS subsystem so the caller must stop accessing + * it after. + * + * The port_cfgs is a list of service port. Ownership transfered to service. + * The max_streams refers to the MaxStreams= key. + * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key. + * The auth_type is the authentication type of the clients in auth_clients. + * The ownership of that list is transfered to the service. + * + * On success (RSAE_OKAY), the address_out points to a newly allocated string + * containing the onion address without the .onion part. On error, address_out + * is untouched. */ +static hs_service_add_ephemeral_status_t +add_onion_helper_add_service(int hs_version, + add_onion_secret_key_t *pk, + smartlist_t *port_cfgs, int max_streams, + int max_streams_close_circuit, int auth_type, + smartlist_t *auth_clients, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + + tor_assert(pk); + tor_assert(port_cfgs); + tor_assert(address_out); + + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams, + max_streams_close_circuit, auth_type, + auth_clients, address_out); + break; + case HS_VERSION_THREE: + ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams, + max_streams_close_circuit, address_out); + break; + default: + tor_assert_unreached(); + } + + return ret; +} + /** Called when we get a ADD_ONION command; parse the body, and set up * the new ephemeral Onion Service. */ static int @@ -4508,15 +4678,15 @@ handle_control_add_onion(control_connection_t *conn, } /* Parse the "keytype:keyblob" argument. */ - crypto_pk_t *pk = NULL; + int hs_version = 0; + add_onion_secret_key_t pk = { NULL }; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, - &key_new_alg, &key_new_blob, - &err_msg); - if (!pk) { + if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, + &key_new_alg, &key_new_blob, &pk, &hs_version, + &err_msg) < 0) { if (err_msg) { connection_write_str_to_buf(err_msg, conn); tor_free(err_msg); @@ -4525,16 +4695,23 @@ handle_control_add_onion(control_connection_t *conn, } tor_assert(!err_msg); + /* Hidden service version 3 don't have client authentication support so if + * ClientAuth was given, send back an error. */ + if (hs_version == HS_VERSION_THREE && auth_clients) { + connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n"); + goto out; + } + /* Create the HS, using private key pk, client authentication auth_type, * the list of auth_clients, and port config port_cfg. * rend_service_add_ephemeral() will take ownership of pk and port_cfg, * regardless of success/failure. */ char *service_id = NULL; - int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, - max_streams_close_circuit, - auth_type, auth_clients, - &service_id); + int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs, + max_streams, + max_streams_close_circuit, auth_type, + auth_clients, &service_id); port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */ auth_clients = NULL; /* so is auth_clients */ switch (ret) { @@ -4627,9 +4804,10 @@ handle_control_add_onion(control_connection_t *conn, * Note: The error messages returned are deliberately vague to avoid echoing * key material. */ -STATIC crypto_pk_t * +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, int *hs_version, char **err_msg_out) { smartlist_t *key_args = smartlist_new(); @@ -4637,7 +4815,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - int ok = 0; + int ret = -1; smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { @@ -4649,6 +4827,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, static const char *key_type_new = "NEW"; static const char *key_type_best = "BEST"; static const char *key_type_rsa1024 = "RSA1024"; + static const char *key_type_ed25519_v3 = "ED25519-V3"; const char *key_type = smartlist_get(key_args, 0); const char *key_blob = smartlist_get(key_args, 1); @@ -4661,9 +4840,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, goto err; } if (crypto_pk_num_bits(pk) != PK_BYTES*8) { + crypto_pk_free(pk); err_msg = tor_strdup("512 Invalid RSA key size\r\n"); goto err; } + decoded_key->v2 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */ + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, + strlen(key_blob)) != sizeof(sk->seckey)) { + tor_free(sk); + err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n"); + goto err; + } + decoded_key->v3 = sk; + *hs_version = HS_VERSION_THREE; } else if (!strcasecmp(key_type_new, key_type)) { /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */ if (!strcasecmp(key_type_rsa1024, key_blob) || @@ -4677,12 +4870,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } if (!discard_pk) { if (crypto_pk_base64_encode(pk, &key_new_blob)) { + crypto_pk_free(pk); tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", key_type_rsa1024); goto err; } key_new_alg = key_type_rsa1024; } + decoded_key->v2 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) { + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (ed25519_secret_key_generate(sk, 1) < 0) { + tor_free(sk); + tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", + key_type_ed25519_v3); + goto err; + } + if (!discard_pk) { + ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1; + key_new_blob = tor_malloc_zero(len); + if (base64_encode(key_new_blob, len, (const char *) sk->seckey, + sizeof(sk->seckey), 0) != (len - 1)) { + tor_free(sk); + tor_free(key_new_blob); + tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", + key_type_ed25519_v3); + goto err; + } + key_new_alg = key_type_ed25519_v3; + } + decoded_key->v3 = sk; + *hs_version = HS_VERSION_THREE; } else { err_msg = tor_strdup("513 Invalid key type\r\n"); goto err; @@ -4693,8 +4912,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } /* Succeded in loading or generating a private key. */ - tor_assert(pk); - ok = 1; + ret = 0; err: SMARTLIST_FOREACH(key_args, char *, cp, { @@ -4703,10 +4921,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }); smartlist_free(key_args); - if (!ok) { - crypto_pk_free(pk); - pk = NULL; - } if (err_msg_out) { *err_msg_out = err_msg; } else { @@ -4715,7 +4929,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, *key_new_alg_out = key_new_alg; *key_new_blob_out = key_new_blob; - return pk; + return ret; } /** Helper function to handle parsing a ClientAuth argument to the @@ -4784,6 +4998,7 @@ handle_control_del_onion(control_connection_t *conn, uint32_t len, const char *body) { + int hs_version = 0; smartlist_t *args; (void) len; /* body is nul-terminated; it's safe to ignore the length */ args = getargs_helper("DEL_ONION", conn, body, 1, 1); @@ -4791,7 +5006,11 @@ handle_control_del_onion(control_connection_t *conn, return 0; const char *service_id = smartlist_get(args, 0); - if (!rend_valid_v2_service_id(service_id)) { + if (rend_valid_v2_service_id(service_id)) { + hs_version = HS_VERSION_TWO; + } else if (hs_address_is_valid(service_id)) { + hs_version = HS_VERSION_THREE; + } else { connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n"); goto out; } @@ -4818,8 +5037,20 @@ handle_control_del_onion(control_connection_t *conn, if (onion_services == NULL) { connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n"); } else { - int ret = rend_service_del_ephemeral(service_id); - if (ret) { + int ret = -1; + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_del_ephemeral(service_id); + break; + case HS_VERSION_THREE: + ret = hs_service_del_ephemeral(service_id); + break; + default: + /* The ret value will be -1 thus hitting the warning below. This should + * never happen because of the check at the start of the function. */ + break; + } + if (ret < 0) { /* This should *NEVER* fail, since the service is on either the * per-control connection list, or the global one. */ @@ -4889,9 +5120,16 @@ connection_control_closed(control_connection_t *conn) * The list and it's contents are scrubbed/freed in connection_free_. */ if (conn->ephemeral_onion_services) { - SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, { - rend_service_del_ephemeral(cp); - }); + SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) { + if (rend_valid_v2_service_id(cp)) { + rend_service_del_ephemeral(cp); + } else if (hs_address_is_valid(cp)) { + hs_service_del_ephemeral(cp); + } else { + /* An invalid .onion in our list should NEVER happen */ + tor_fragile_assert(); + } + } SMARTLIST_FOREACH_END(cp); } if (conn->is_owning_control_connection) { @@ -6571,8 +6809,7 @@ monitor_owning_controller_process(const char *process_spec) "owning controller: %s. Exiting.", msg); owning_controller_process_spec = NULL; - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); } } @@ -6970,27 +7207,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address) * <b>rend_query</b> is used to fetch requested onion address and auth type. * <b>hs_dir</b> is the description of contacting hs directory. * <b>desc_id_base32</b> is the ID of requested hs descriptor. + * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string. */ void -control_event_hs_descriptor_requested(const rend_data_t *rend_query, +control_event_hs_descriptor_requested(const char *onion_address, + rend_auth_type_t auth_type, const char *id_digest, - const char *desc_id_base32) + const char *desc_id, + const char *hsdir_index) { - if (!id_digest || !rend_query || !desc_id_base32) { - log_warn(LD_BUG, "Called with rend_query==%p, " - "id_digest==%p, desc_id_base32==%p", - rend_query, id_digest, desc_id_base32); + char *hsdir_index_field = NULL; + + if (BUG(!id_digest || !desc_id)) { return; } + if (hsdir_index) { + tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC REQUESTED %s %s %s %s\r\n", - rend_hsaddress_str_or_unknown( - rend_data_get_address(rend_query)), - rend_auth_type_to_string( - TO_REND_DATA_V2(rend_query)->auth_type), + "650 HS_DESC REQUESTED %s %s %s %s%s\r\n", + rend_hsaddress_str_or_unknown(onion_address), + rend_auth_type_to_string(auth_type), node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** For an HS descriptor query <b>rend_data</b>, using the @@ -7039,89 +7282,87 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp) /** send HS_DESC CREATED event when a local service generates a descriptor. * - * <b>service_id</b> is the descriptor onion address. - * <b>desc_id_base32</b> is the descriptor ID. - * <b>replica</b> is the the descriptor replica number. + * <b>onion_address</b> is service address. + * <b>desc_id</b> is the descriptor ID. + * <b>replica</b> is the the descriptor replica number. If it is negative, it + * is ignored. */ void -control_event_hs_descriptor_created(const char *service_id, - const char *desc_id_base32, +control_event_hs_descriptor_created(const char *onion_address, + const char *desc_id, int replica) { - if (!service_id || !desc_id_base32) { - log_warn(LD_BUG, "Called with service_digest==%p, " - "desc_id_base32==%p", service_id, desc_id_base32); + char *replica_field = NULL; + + if (BUG(!onion_address || !desc_id)) { return; } + if (replica >= 0) { + tor_asprintf(&replica_field, " REPLICA=%d", replica); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s " - "REPLICA=%d\r\n", - service_id, - desc_id_base32, - replica); + "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n", + onion_address, desc_id, + replica_field ? replica_field : ""); + tor_free(replica_field); } /** send HS_DESC upload event. * - * <b>service_id</b> is the descriptor onion address. + * <b>onion_address</b> is service address. * <b>hs_dir</b> is the description of contacting hs directory. - * <b>desc_id_base32</b> is the ID of requested hs descriptor. + * <b>desc_id</b> is the ID of requested hs descriptor. */ void -control_event_hs_descriptor_upload(const char *service_id, +control_event_hs_descriptor_upload(const char *onion_address, const char *id_digest, - const char *desc_id_base32) + const char *desc_id, + const char *hsdir_index) { - if (!service_id || !id_digest || !desc_id_base32) { - log_warn(LD_BUG, "Called with service_digest==%p, " - "desc_id_base32==%p, id_digest==%p", service_id, - desc_id_base32, id_digest); + char *hsdir_index_field = NULL; + + if (BUG(!onion_address || !id_digest || !desc_id)) { return; } + if (hsdir_index) { + tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n", - service_id, + "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n", + onion_address, node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** send HS_DESC event after got response from hs directory. * * NOTE: this is an internal function used by following functions: - * control_event_hs_descriptor_received - * control_event_hs_descriptor_failed + * control_event_hsv2_descriptor_received + * control_event_hsv2_descriptor_failed + * control_event_hsv3_descriptor_failed * * So do not call this function directly. */ -void -control_event_hs_descriptor_receive_end(const char *action, - const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest, - const char *reason) +static void +event_hs_descriptor_receive_end(const char *action, + const char *onion_address, + const char *desc_id, + rend_auth_type_t auth_type, + const char *hsdir_id_digest, + const char *reason) { - char *desc_id_field = NULL; char *reason_field = NULL; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; - const char *desc_id = NULL; - if (!action || !rend_data || !onion_address) { - log_warn(LD_BUG, "Called with action==%p, rend_data==%p, " - "onion_address==%p", action, rend_data, onion_address); + if (BUG(!action || !onion_address)) { return; } - desc_id = get_desc_id_from_query(rend_data, id_digest); - if (desc_id != NULL) { - /* Set the descriptor ID digest to base32 so we can send it. */ - base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, - DIGEST_LEN); - /* Extra whitespace is needed before the value. */ - tor_asprintf(&desc_id_field, " %s", desc_id_base32); - } - if (reason) { tor_asprintf(&reason_field, " REASON=%s", reason); } @@ -7130,14 +7371,13 @@ control_event_hs_descriptor_receive_end(const char *action, "650 HS_DESC %s %s %s %s%s%s\r\n", action, rend_hsaddress_str_or_unknown(onion_address), - rend_auth_type_to_string( - TO_REND_DATA_V2(rend_data)->auth_type), - id_digest ? - node_describe_longname_by_id(id_digest) : "UNKNOWN", - desc_id_field ? desc_id_field : "", + rend_auth_type_to_string(auth_type), + hsdir_id_digest ? + node_describe_longname_by_id(hsdir_id_digest) : + "UNKNOWN", + desc_id ? desc_id : "", reason_field ? reason_field : ""); - tor_free(desc_id_field); tor_free(reason_field); } @@ -7157,9 +7397,7 @@ control_event_hs_descriptor_upload_end(const char *action, { char *reason_field = NULL; - if (!action || !id_digest) { - log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action, - id_digest); + if (BUG(!action || !id_digest)) { return; } @@ -7182,17 +7420,54 @@ control_event_hs_descriptor_upload_end(const char *action, * called when we successfully received a hidden service descriptor. */ void -control_event_hs_descriptor_received(const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest) +control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *hsdir_id_digest) +{ + char *desc_id_field = NULL; + const char *desc_id; + + if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) { + return; + } + + desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest); + if (desc_id != NULL) { + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + + event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field, + TO_REND_DATA_V2(rend_data)->auth_type, + hsdir_id_digest, NULL); + tor_free(desc_id_field); +} + +/* Send HS_DESC RECEIVED event + * + * Called when we successfully received a hidden service descriptor. */ +void +control_event_hsv3_descriptor_received(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest) { - if (!rend_data || !id_digest || !onion_address) { - log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, " - "onion_address==%p", rend_data, id_digest, onion_address); + char *desc_id_field = NULL; + + if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) { return; } - control_event_hs_descriptor_receive_end("RECEIVED", onion_address, - rend_data, id_digest, NULL); + + /* Because DescriptorID is an optional positional value, we need to add a + * whitespace before in order to not be next to the HsDir value. */ + tor_asprintf(&desc_id_field, " %s", desc_id); + + event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field, + REND_NO_AUTH, hsdir_id_digest, NULL); + tor_free(desc_id_field); } /** send HS_DESC UPLOADED event @@ -7203,9 +7478,7 @@ void control_event_hs_descriptor_uploaded(const char *id_digest, const char *onion_address) { - if (!id_digest) { - log_warn(LD_BUG, "Called with id_digest==%p", - id_digest); + if (BUG(!id_digest)) { return; } @@ -7219,17 +7492,58 @@ control_event_hs_descriptor_uploaded(const char *id_digest, * add it to REASON= field. */ void -control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, - const char *reason) +control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *hsdir_id_digest, + const char *reason) +{ + char *desc_id_field = NULL; + const char *desc_id; + + if (BUG(!rend_data)) { + return; + } + + desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest); + if (desc_id != NULL) { + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + + event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data), + desc_id_field, + TO_REND_DATA_V2(rend_data)->auth_type, + hsdir_id_digest, reason); + tor_free(desc_id_field); +} + +/** Send HS_DESC event to inform controller that the query to + * <b>onion_address</b> failed to retrieve hidden service descriptor + * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If + * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON= + * field. */ +void +control_event_hsv3_descriptor_failed(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *reason) { - if (!rend_data) { - log_warn(LD_BUG, "Called with rend_data==%p", rend_data); + char *desc_id_field = NULL; + + if (BUG(!onion_address || !desc_id || !reason)) { return; } - control_event_hs_descriptor_receive_end("FAILED", - rend_data_get_address(rend_data), - rend_data, id_digest, reason); + + /* Because DescriptorID is an optional positional value, we need to add a + * whitespace before in order to not be next to the HsDir value. */ + tor_asprintf(&desc_id_field, " %s", desc_id); + + event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field, + REND_NO_AUTH, hsdir_id_digest, reason); + tor_free(desc_id_field); } /** Send HS_DESC_CONTENT event after completion of a successful fetch from hs @@ -7279,9 +7593,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest, const char *onion_address, const char *reason) { - if (!id_digest) { - log_warn(LD_BUG, "Called with id_digest==%p", - id_digest); + if (BUG(!id_digest)) { return; } control_event_hs_descriptor_upload_end("FAILED", onion_address, diff --git a/src/or/control.h b/src/or/control.h index e957b593a6..28ffeaed86 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -27,6 +27,10 @@ void control_ports_write_to_file(void); #define LOG_FN_CONN(conn, args) \ CONN_LOG_PROTECT(conn, log_fn args) +#define CC_LOCAL_FD_IS_OWNER (1u<<0) +#define CC_LOCAL_FD_IS_AUTHENTICATED (1u<<1) +int control_connection_add_local_fd(tor_socket_t sock, unsigned flags); + int connection_control_finished_flushing(control_connection_t *conn); int connection_control_reached_eof(control_connection_t *conn); void connection_control_closed(control_connection_t *conn); @@ -111,32 +115,39 @@ void control_event_transport_launched(const char *mode, tor_addr_t *addr, uint16_t port); const char *rend_auth_type_to_string(rend_auth_type_t auth_type); MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); -void control_event_hs_descriptor_requested(const rend_data_t *rend_query, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_created(const char *service_id, - const char *desc_id_base32, +void control_event_hs_descriptor_requested(const char *onion_address, + rend_auth_type_t auth_type, + const char *id_digest, + const char *desc_id, + const char *hsdir_index); +void control_event_hs_descriptor_created(const char *onion_address, + const char *desc_id, int replica); -void control_event_hs_descriptor_upload(const char *service_id, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_receive_end(const char *action, - const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest, - const char *reason); +void control_event_hs_descriptor_upload(const char *onion_address, + const char *desc_id, + const char *hs_dir, + const char *hsdir_index); void control_event_hs_descriptor_upload_end(const char *action, const char *onion_address, const char *hs_dir, const char *reason); -void control_event_hs_descriptor_received(const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest); void control_event_hs_descriptor_uploaded(const char *hs_dir, const char *onion_address); -void control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, - const char *reason); +/* Hidden service v2 HS_DESC specific. */ +void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *id_digest, + const char *reason); +void control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest); +/* Hidden service v3 HS_DESC specific. */ +void control_event_hsv3_descriptor_failed(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *reason); +void control_event_hsv3_descriptor_received(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest); void control_event_hs_descriptor_upload_failed(const char *hs_dir, const char *onion_address, const char *reason); @@ -252,10 +263,22 @@ void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void); -STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, - const char **key_new_alg_out, - char **key_new_blob_out, - char **err_msg_out); +/* ADD_ONION secret key to create an ephemeral service. The command supports + * multiple versions so this union stores the key and passes it to the HS + * subsystem depending on the requested version. */ +typedef union add_onion_secret_key_t { + /* Hidden service v2 secret key. */ + crypto_pk_t *v2; + /* Hidden service v3 secret key. */ + ed25519_secret_key_t *v3; +} add_onion_secret_key_t; + +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, + int *hs_version, char **err_msg_out); + STATIC rend_authorized_client_t * add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out); diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index d9371b3446..9bec04d790 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -48,14 +48,25 @@ worker_state_new(void *arg) ws->onion_keys = server_onion_keys_new(); return ws; } + +#define worker_state_free(ws) \ + FREE_AND_NULL(worker_state_t, worker_state_free_, (ws)) + static void -worker_state_free(void *arg) +worker_state_free_(worker_state_t *ws) { - worker_state_t *ws = arg; + if (!ws) + return; server_onion_keys_free(ws->onion_keys); tor_free(ws); } +static void +worker_state_free_void(void *arg) +{ + worker_state_free_(arg); +} + static replyqueue_t *replyqueue = NULL; static threadpool_t *threadpool = NULL; static struct event *reply_event = NULL; @@ -102,7 +113,7 @@ cpu_init(void) threadpool = threadpool_new(n_threads, replyqueue, worker_state_new, - worker_state_free, + worker_state_free_void, NULL); } /* Total voodoo. Can we make this more sensible? */ @@ -198,7 +209,7 @@ cpuworkers_rotate_keyinfo(void) if (threadpool_queue_update(threadpool, worker_state_new, update_state_threadfn, - worker_state_free, + worker_state_free_void, NULL)) { log_warn(LD_OR, "Failed to queue key update for worker threads."); } diff --git a/src/or/dircollate.c b/src/or/dircollate.c index d34ebe8af5..ce4534ff6c 100644 --- a/src/or/dircollate.c +++ b/src/or/dircollate.c @@ -41,9 +41,12 @@ typedef struct ddmap_entry_s { vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER]; } ddmap_entry_t; +#define ddmap_entry_free(e) \ + FREE_AND_NULL(ddmap_entry_t, ddmap_entry_free_, (e)) + /** Release all storage held by e. */ static void -ddmap_entry_free(ddmap_entry_t *e) +ddmap_entry_free_(ddmap_entry_t *e) { tor_free(e); } @@ -158,7 +161,7 @@ dircollator_new(int n_votes, int n_authorities) /** Release all storage held by <b>dc</b>. */ void -dircollator_free(dircollator_t *dc) +dircollator_free_(dircollator_t *dc) { if (!dc) return; diff --git a/src/or/dircollate.h b/src/or/dircollate.h index 7932e18998..0584b2fe06 100644 --- a/src/or/dircollate.h +++ b/src/or/dircollate.h @@ -18,7 +18,9 @@ typedef struct dircollator_s dircollator_t; dircollator_t *dircollator_new(int n_votes, int n_authorities); -void dircollator_free(dircollator_t *obj); +void dircollator_free_(dircollator_t *obj); +#define dircollator_free(c) \ + FREE_AND_NULL(dircollator_t, dircollator_free_, (c)) void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v); void dircollator_collate(dircollator_t *dc, int consensus_method); diff --git a/src/or/directory.c b/src/or/directory.c index f14bdde90b..c55f81bc62 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -25,6 +25,7 @@ #include "geoip.h" #include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "hs_client.h" #include "main.h" #include "microdesc.h" @@ -1101,7 +1102,7 @@ directory_request_new(uint8_t dir_purpose) * Release all resources held by <b>req</b>. */ void -directory_request_free(directory_request_t *req) +directory_request_free_(directory_request_t *req) { if (req == NULL) return; @@ -2196,8 +2197,6 @@ load_downloaded_routers(const char *body, smartlist_t *which, return added; } -static int handle_response_fetch_consensus(dir_connection_t *, - const response_handler_args_t *); static int handle_response_fetch_certificate(dir_connection_t *, const response_handler_args_t *); static int handle_response_fetch_status_vote(dir_connection_t *, @@ -2542,7 +2541,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) * consensus document by checking the consensus, storing it, and marking * router requests as reachable. **/ -static int +STATIC int handle_response_fetch_consensus(dir_connection_t *conn, const response_handler_args_t *args) { @@ -3092,10 +3091,19 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* We got something: Try storing it in the cache. */ if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) { log_warn(LD_REND, "Failed to store hidden service descriptor"); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "BAD_DESC"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); } else { log_info(LD_REND, "Stored hidden service descriptor successfully."); TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC; hs_client_desc_has_arrived(conn->hs_ident); + /* Fire control port RECEIVED event. */ + hs_control_desc_event_received(conn->hs_ident, conn->identity_digest); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + body); } break; case 404: @@ -3103,13 +3111,22 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, * tries to clean this conn up. */ log_info(LD_REND, "Fetching hidden service v3 descriptor not found: " "Retrying at another directory."); - /* TODO: Inform the control port */ + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "NOT_FOUND"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; case 400: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " "http status 400 (%s). Dirserver didn't like our " "query? Retrying at another directory.", escaped(reason)); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "QUERY_REJECTED"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; default: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " @@ -3117,6 +3134,11 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, "'%s:%d'. Retrying at another directory.", status_code, escaped(reason), TO_CONN(conn)->address, TO_CONN(conn)->port); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UNEXPECTED"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; } @@ -3138,9 +3160,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, const size_t body_len = args->body_len; #define SEND_HS_DESC_FAILED_EVENT(reason) \ - (control_event_hs_descriptor_failed(conn->rend_data, \ - conn->identity_digest, \ - reason)) + (control_event_hsv2_descriptor_failed(conn->rend_data, \ + conn->identity_digest, \ + reason)) #define SEND_HS_DESC_FAILED_CONTENT() \ (control_event_hs_descriptor_content( \ rend_data_get_address(conn->rend_data), \ @@ -3175,9 +3197,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, /* success. notify pending connections about this. */ log_info(LD_REND, "Successfully fetched v2 rendezvous " "descriptor."); - control_event_hs_descriptor_received(service_id, - conn->rend_data, - conn->identity_digest); + control_event_hsv2_descriptor_received(service_id, + conn->rend_data, + conn->identity_digest); control_event_hs_descriptor_content(service_id, conn->requested_resource, conn->identity_digest, @@ -3294,7 +3316,7 @@ handle_response_upload_hsdesc(dir_connection_t *conn, case 200: log_info(LD_REND, "Uploading hidden service descriptor: " "finished with status 200 (%s)", escaped(reason)); - /* XXX: Trigger control event. */ + hs_control_desc_event_uploaded(conn->hs_ident, conn->identity_digest); break; case 400: log_fn(LOG_PROTOCOL_WARN, LD_REND, @@ -3302,7 +3324,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn, "status 400 (%s) response from dirserver " "'%s:%d'. Malformed hidden service descriptor?", escaped(reason), conn->base_.address, conn->base_.port); - /* XXX: Trigger control event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UPLOAD_REJECTED"); break; default: log_warn(LD_REND, "Uploading hidden service descriptor: http " @@ -3310,7 +3333,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn, "'%s:%d').", status_code, escaped(reason), conn->base_.address, conn->base_.port); - /* XXX: Trigger control event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UNEXPECTED"); break; } @@ -3476,63 +3500,47 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length, long cache_lifetime) { char date[RFC1123_TIME_LEN+1]; - char tmp[1024]; - char *cp; time_t now = time(NULL); + buf_t *buf = buf_new_with_capacity(1024); tor_assert(conn); format_rfc1123_time(date, now); - cp = tmp; - tor_snprintf(cp, sizeof(tmp), - "HTTP/1.0 200 OK\r\nDate: %s\r\n", - date); - cp += strlen(tmp); + + buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date); if (type) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type); - cp += strlen(cp); + buf_add_printf(buf, "Content-Type: %s\r\n", type); } if (!is_local_addr(&conn->base_.addr)) { /* Don't report the source address for a nearby/private connection. * Otherwise we tend to mis-report in cases where incoming ports are * being forwarded to a Tor server running behind the firewall. */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - X_ADDRESS_HEADER "%s\r\n", conn->base_.address); - cp += strlen(cp); + buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address); } if (encoding) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Encoding: %s\r\n", encoding); - cp += strlen(cp); + buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding); } if (length >= 0) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Length: %ld\r\n", (long)length); - cp += strlen(cp); + buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length); } if (cache_lifetime > 0) { char expbuf[RFC1123_TIME_LEN+1]; format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime)); /* We could say 'Cache-control: max-age=%d' here if we start doing * http/1.1 */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Expires: %s\r\n", expbuf); - cp += strlen(cp); + buf_add_printf(buf, "Expires: %s\r\n", expbuf); } else if (cache_lifetime == 0) { /* We could say 'Cache-control: no-cache' here if we start doing * http/1.1 */ - strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); + buf_add_string(buf, "Pragma: no-cache\r\n"); } if (extra_headers) { - strlcpy(cp, extra_headers, sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); + buf_add_string(buf, extra_headers); } - if (sizeof(tmp)-(cp-tmp) > 3) - memcpy(cp, "\r\n", 3); - else - tor_assert(0); - connection_buf_add(tmp, strlen(tmp), TO_CONN(conn)); + buf_add_string(buf, "\r\n"); + + connection_buf_add_buf(TO_CONN(conn), buf); + buf_free(buf); } /** As write_http_response_header_impl, but sets encoding and content-typed diff --git a/src/or/directory.h b/src/or/directory.h index 5e6a91d3e7..edf75ffe13 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -51,7 +51,9 @@ int directory_must_use_begindir(const or_options_t *options); */ typedef struct directory_request_t directory_request_t; directory_request_t *directory_request_new(uint8_t dir_purpose); -void directory_request_free(directory_request_t *req); +void directory_request_free_(directory_request_t *req); +#define directory_request_free(req) \ + FREE_AND_NULL(directory_request_t, directory_request_free_, (req)) void directory_request_set_or_addr_port(directory_request_t *req, const tor_addr_port_t *p); void directory_request_set_dir_addr_port(directory_request_t *req, @@ -238,6 +240,9 @@ STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn, STATIC int handle_response_fetch_microdesc(dir_connection_t *conn, const response_handler_args_t *args); +STATIC int handle_response_fetch_consensus(dir_connection_t *conn, + const response_handler_args_t *args); + #endif /* defined(DIRECTORY_PRIVATE) */ #ifdef TOR_UNIT_TESTS diff --git a/src/or/dirserv.c b/src/or/dirserv.c index ddee92da55..d3bae241f9 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1519,15 +1519,21 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) node->ri && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) continue; + + routerinfo_t *ri = node->ri; + if (ri) { + node->is_exit = (!router_exit_policy_rejects_all(ri) && + exit_policy_is_general_exit(ri->exit_policy)); + } + if (router_counts_toward_thresholds(node, now, omit_as_sybil, require_mbw)) { - routerinfo_t *ri = node->ri; const char *id = node->identity; uint32_t bw_kb; + /* resolve spurious clang shallow analysis null pointer errors */ tor_assert(ri); - node->is_exit = (!router_exit_policy_rejects_all(ri) && - exit_policy_is_general_exit(ri->exit_policy)); + uptimes[n_active] = (uint32_t)real_uptime(ri, now); mtbfs[n_active] = rep_hist_get_stability(id, now); tks [n_active] = rep_hist_get_weighted_time_known(id, now); @@ -1897,21 +1903,28 @@ version_from_platform(const char *platform) /** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. + * + * consensus_method is the current consensus method when format is + * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other + * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD. + * * Return 0 on success, -1 on failure. * * The format argument has one of the following values: * NS_V2 - Output an entry suitable for a V2 NS opinion document * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry + * for consensus_method. * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc - * consensus entry. + * consensus entry for consensus_method. * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present, * it contains additional information for the vote. - * NS_CONTROL_PORT - Output a NS document for the control port + * NS_CONTROL_PORT - Output a NS document for the control port. */ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, const char *protocols, routerstatus_format_type_t format, + int consensus_method, const vote_routerstatus_t *vrs) { char *summary; @@ -1942,8 +1955,10 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, * networkstatus_type_t values, with an additional control port value * added -MP */ - /* V3 microdesc consensuses don't have "a" lines. */ - if (format == NS_V3_CONSENSUS_MICRODESC) + /* V3 microdesc consensuses only have "a" lines in later consensus methods + */ + if (format == NS_V3_CONSENSUS_MICRODESC && + consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS) goto done; /* Possible "a" line. At most one for now. */ @@ -1952,7 +1967,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); } - if (format == NS_V3_CONSENSUS) + if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) goto done; smartlist_add_asprintf(chunks, @@ -2213,7 +2228,8 @@ routers_make_ed_keys_unique(smartlist_t *routers) } /** Extract status information from <b>ri</b> and from other authority - * functions and store it in <b>rs</b>>. + * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is + * set. * * We assume that ri-\>is_running has already been set, e.g. by * dirserv_set_router_is_running(ri, now); @@ -2279,6 +2295,9 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, OR port and it's reachable so copy it to the routerstatus. */ tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); rs->ipv6_orport = ri->ipv6_orport; + } else { + tor_addr_make_null(&rs->ipv6_addr, AF_INET6); + rs->ipv6_orport = 0; } if (options->TestingTorNetwork) { @@ -3284,7 +3303,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr, ri = node->ri; if (get_options()->AuthDirTestEd25519LinkKeys && - node_supports_ed25519_link_authentication(node) && + node_supports_ed25519_link_authentication(node, 1) && ri->cache_info.signing_key_cert) { /* We allow the node to have an ed25519 key if we haven't been told one in * the routerinfo, but if we *HAVE* been told one in the routerinfo, it @@ -3367,7 +3386,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) tor_assert(node); if (options->AuthDirTestEd25519LinkKeys && - node_supports_ed25519_link_authentication(node)) { + node_supports_ed25519_link_authentication(node, 1)) { ed_id_key = &router->cache_info.signing_key_cert->signing_key; } else { ed_id_key = NULL; @@ -3496,7 +3515,7 @@ spooled_resource_new_from_cache_entry(consensus_cache_entry_t *entry) /** Release all storage held by <b>spooled</b>. */ void -spooled_resource_free(spooled_resource_t *spooled) +spooled_resource_free_(spooled_resource_t *spooled) { if (spooled == NULL) return; diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 46967a6cb2..0fd7b79150 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -150,6 +150,7 @@ char *routerstatus_format_entry( const char *version, const char *protocols, routerstatus_format_type_t format, + int consensus_method, const vote_routerstatus_t *vrs); void dirserv_free_all(void); void cached_dir_decref(cached_dir_t *d); @@ -195,7 +196,9 @@ spooled_resource_t *spooled_resource_new(dir_spool_source_t source, size_t digestlen); spooled_resource_t *spooled_resource_new_from_cache_entry( struct consensus_cache_entry_t *entry); -void spooled_resource_free(spooled_resource_t *spooled); +void spooled_resource_free_(spooled_resource_t *spooled); +#define spooled_resource_free(sp) \ + FREE_AND_NULL(spooled_resource_t, spooled_resource_free_, (sp)) void dirserv_spool_remove_missing_and_guess_size(dir_connection_t *conn, time_t cutoff, int compression, diff --git a/src/or/dirvote.c b/src/or/dirvote.c index ce82a5ef4a..a75d9e55a5 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -278,7 +278,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, vote_microdesc_hash_t *h; rsf = routerstatus_format_entry(&vrs->status, vrs->version, vrs->protocols, - NS_V3_VOTE, vrs); + NS_V3_VOTE, + ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, + vrs); if (rsf) smartlist_add(chunks, rsf); @@ -519,7 +521,7 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, /* compare_vote_rs_() sorts the items by identity digest (all the same), * then by SD digest. That way, if we have a tie that the published_on - * date cannot tie, we use the descriptor with the smaller digest. + * date cannot break, we use the descriptor with the smaller digest. */ smartlist_sort(votes, compare_vote_rs_); SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { @@ -795,6 +797,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) output = smartlist_new(); SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) { + /* resolve spurious clang shallow analysis null pointer errors */ + tor_assert(param); + const char *next_param; int ok=0; eq = strchr(param, '='); @@ -807,8 +812,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) next_param = NULL; else next_param = smartlist_get(param_list, param_sl_idx+1); - /* resolve spurious clang shallow analysis null pointer errors */ - tor_assert(param); + if (!next_param || strncmp(next_param, param, cur_param_len)) { /* We've reached the end of a series. */ /* Make sure enough authorities voted on this param, unless the @@ -1315,8 +1319,9 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) /** Given a list of vote networkstatus_t in <b>votes</b>, our public * authority <b>identity_key</b>, our private authority <b>signing_key</b>, * and the number of <b>total_authorities</b> that we believe exist in our - * voting quorum, generate the text of a new v3 consensus vote, and return the - * value in a newly allocated string. + * voting quorum, generate the text of a new v3 consensus or microdescriptor + * consensus (depending on <b>flavor</b>), and return the value in a newly + * allocated string. * * Note: this function DOES NOT check whether the votes are from * recognized authorities. (dirvote_add_vote does that.) @@ -2099,7 +2104,8 @@ networkstatus_compute_consensus(smartlist_t *votes, char *buf; /* Okay!! Now we can write the descriptor... */ /* First line goes into "buf". */ - buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL); + buf = routerstatus_format_entry(&rs_out, NULL, NULL, + rs_format, consensus_method, NULL); if (buf) smartlist_add(chunks, buf); } @@ -2685,7 +2691,7 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending, /** Release all storage held in <b>s</b>. */ void -ns_detached_signatures_free(ns_detached_signatures_t *s) +ns_detached_signatures_free_(ns_detached_signatures_t *s) { if (!s) return; @@ -2843,10 +2849,13 @@ get_voting_schedule(const or_options_t *options, time_t now, int severity) return new_voting_schedule; } +#define voting_schedule_free(s) \ + FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) + /** Frees a voting_schedule_t. This should be used instead of the generic * tor_free. */ static void -voting_schedule_free(voting_schedule_t *voting_schedule_to_free) +voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) { if (!voting_schedule_to_free) return; @@ -3829,7 +3838,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } + /* We originally put a lines in the micrdescriptors, but then we worked out + * that we needed them in the microdesc consensus. See #20916. */ if (consensus_method >= MIN_METHOD_FOR_A_LINES && + consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC && !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) smartlist_add_asprintf(chunks, "a %s\n", fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); @@ -3938,7 +3950,9 @@ static const struct consensus_method_range_t { {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1}, {MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1}, {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1}, - {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, + {MIN_METHOD_FOR_ED25519_ID_IN_MD, + MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1}, + {MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; diff --git a/src/or/dirvote.h b/src/or/dirvote.h index 72a35fea6d..deeb27bfe1 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -51,11 +51,15 @@ #define MIN_VOTE_INTERVAL_TESTING_INITIAL \ ((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1) +/* A placeholder for routerstatus_format_entry() when the consensus method + * argument is not applicable. */ +#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0 + /** The lowest consensus method that we currently support. */ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 26 +#define MAX_SUPPORTED_CONSENSUS_METHOD 28 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -115,6 +119,14 @@ * instead of 0. See #14881 */ #define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26 +/** Lowest consensus method where the microdesc consensus contains relay IPv6 + * addresses. See #23826 and #20916. */ +#define MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS 27 + +/** Lowest consensus method where microdescriptors do not contain relay IPv6 + * addresses. See #23828 and #20916. */ +#define MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC 28 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ @@ -136,7 +148,9 @@ int networkstatus_add_detached_signatures(networkstatus_t *target, int severity, const char **msg_out); char *networkstatus_get_detached_signatures(smartlist_t *consensuses); -void ns_detached_signatures_free(ns_detached_signatures_t *s); +void ns_detached_signatures_free_(ns_detached_signatures_t *s); +#define ns_detached_signatures_free(s) \ + FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) /* cert manipulation */ authority_cert_t *authority_cert_dup(authority_cert_t *cert); diff --git a/src/or/dns.c b/src/or/dns.c index 7dc3575f53..1f00fe5b4d 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -457,7 +457,7 @@ purge_expired_resolves(time_t now) if (!pendconn->base_.marked_for_close) { connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT); circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } tor_free(pend); } @@ -670,7 +670,7 @@ dns_resolve(edge_connection_t *exitconn) /* If we made the connection pending, then we freed it already in * dns_cancel_pending_resolve(). If we marked it for close, it'll * get freed from the main loop. Otherwise, can free it now. */ - connection_free(TO_CONN(exitconn)); + connection_free_(TO_CONN(exitconn)); } break; default: @@ -1101,7 +1101,7 @@ dns_cancel_pending_resolve,(const char *address)) if (circ) circuit_detach_stream(circ, pendconn); if (!pendconn->base_.marked_for_close) - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); resolve->pending_connections = pend->next; tor_free(pend); } @@ -1230,7 +1230,7 @@ inform_pending_connections(cached_resolve_t *resolve) /* This detach must happen after we send the resolved cell. */ circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); } - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } else { circuit_t *circ; if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) { @@ -1259,7 +1259,7 @@ inform_pending_connections(cached_resolve_t *resolve) circ = circuit_get_by_edge_conn(pendconn); tor_assert(circ); circuit_detach_stream(circ, pendconn); - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } } resolve->pending_connections = pend->next; @@ -1666,7 +1666,7 @@ launch_resolve,(cached_resolve_t *resolve)) tor_addr_t a; int r; - if (get_options()->DisableNetwork) + if (net_is_disabled()) return -1; /* What? Nameservers not configured? Sounds like a bug. */ @@ -1901,7 +1901,7 @@ launch_test_addresses(evutil_socket_t fd, short event, void *args) (void)event; (void)args; - if (options->DisableNetwork) + if (net_is_disabled()) return; log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index d254717a54..9385561d99 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -172,7 +172,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) { log_warn(LD_APP, "Couldn't register dummy connection for DNS request"); evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); - connection_free(ENTRY_TO_CONN(entry_conn)); + connection_free_(ENTRY_TO_CONN(entry_conn)); return; } @@ -249,7 +249,7 @@ dnsserv_launch_request(const char *name, int reverse, if (connection_add(TO_CONN(conn))<0) { log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request"); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return -1; } diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 67b0259243..292a393e51 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1094,7 +1094,11 @@ select_and_add_guard_item_for_sample(guard_selection_t *gs, return added_guard; } -/** Return true iff we need a consensus to maintain our */ +/** + * Return true iff we need a consensus to update our guards, but we don't + * have one. (We can return 0 here either if the consensus is _not_ missing, + * or if we don't need a consensus because we're using bridges.) + */ static int live_consensus_is_missing(const guard_selection_t *gs) { @@ -2196,7 +2200,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b) /** Release all storage held in <b>restriction</b> */ STATIC void -entry_guard_restriction_free(entry_guard_restriction_t *rst) +entry_guard_restriction_free_(entry_guard_restriction_t *rst) { tor_free(rst); } @@ -2205,7 +2209,7 @@ entry_guard_restriction_free(entry_guard_restriction_t *rst) * Release all storage held in <b>state</b>. */ void -circuit_guard_state_free(circuit_guard_state_t *state) +circuit_guard_state_free_(circuit_guard_state_t *state) { if (!state) return; @@ -2216,9 +2220,9 @@ circuit_guard_state_free(circuit_guard_state_t *state) /** Allocate and return a new circuit_guard_state_t to track the result * of using <b>guard</b> for a given operation. */ -static circuit_guard_state_t * -circuit_guard_state_new(entry_guard_t *guard, unsigned state, - entry_guard_restriction_t *rst) +MOCK_IMPL(STATIC circuit_guard_state_t *, +circuit_guard_state_new,(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst)) { circuit_guard_state_t *result; @@ -3108,7 +3112,7 @@ get_guard_state_for_bridge_desc_fetch(const char *digest) /** Release all storage held by <b>e</b>. */ STATIC void -entry_guard_free(entry_guard_t *e) +entry_guard_free_(entry_guard_t *e) { if (!e) return; @@ -3615,7 +3619,7 @@ entry_guards_get_err_str_if_dir_info_missing(int using_mds, /** Free one guard selection context */ STATIC void -guard_selection_free(guard_selection_t *gs) +guard_selection_free_(guard_selection_t *gs) { if (!gs) return; diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index d909115b1f..aa9c8fe193 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -356,7 +356,10 @@ typedef enum { GUARD_USAGE_DIRGUARD = 1 } guard_usage_t; -void circuit_guard_state_free(circuit_guard_state_t *state); +#define circuit_guard_state_free(val) \ + FREE_AND_NULL(circuit_guard_state_t, circuit_guard_state_free_, (val)) + +void circuit_guard_state_free_(circuit_guard_state_t *state); int entry_guard_pick_for_circuit(guard_selection_t *gs, guard_usage_t usage, entry_guard_restriction_t *rst, @@ -475,6 +478,9 @@ STATIC double get_meaningful_restriction_threshold(void); STATIC double get_extreme_restriction_threshold(void); HANDLE_DECL(entry_guard, entry_guard_t, STATIC) +#define entry_guard_handle_free(h) \ + FREE_AND_NULL(entry_guard_handle_t, entry_guard_handle_free_, (h)) + STATIC guard_selection_type_t guard_selection_infer_type( guard_selection_type_t type_in, const char *name); @@ -482,7 +488,9 @@ STATIC guard_selection_t *guard_selection_new(const char *name, guard_selection_type_t type); STATIC guard_selection_t *get_guard_selection_by_name( const char *name, guard_selection_type_t type, int create_if_absent); -STATIC void guard_selection_free(guard_selection_t *gs); +STATIC void guard_selection_free_(guard_selection_t *gs); +#define guard_selection_free(gs) \ + FREE_AND_NULL(guard_selection_t, guard_selection_free_, (gs)) MOCK_DECL(STATIC int, entry_guard_is_listed, (guard_selection_t *gs, const entry_guard_t *guard)); STATIC const char *choose_guard_selection(const or_options_t *options, @@ -494,12 +502,18 @@ STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs, MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate)); +MOCK_DECL(STATIC circuit_guard_state_t *, + circuit_guard_state_new,(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst)); + STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs, const node_t *node); STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs); STATIC char *entry_guard_encode_for_state(entry_guard_t *guard); STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); -STATIC void entry_guard_free(entry_guard_t *e); +#define entry_guard_free(e) \ + FREE_AND_NULL(entry_guard_t, entry_guard_free_, (e)) +STATIC void entry_guard_free_(entry_guard_t *e); STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs); STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); /** @@ -562,7 +576,10 @@ STATIC entry_guard_restriction_t *guard_create_exit_restriction( STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void); -STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst); +STATIC void entry_guard_restriction_free_(entry_guard_restriction_t *rst); +#define entry_guard_restriction_free(rst) \ + FREE_AND_NULL(entry_guard_restriction_t, \ + entry_guard_restriction_free_, (rst)) #endif /* defined(ENTRYNODES_PRIVATE) */ diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index 28377a3f36..16a250fa58 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -40,7 +40,7 @@ ext_or_cmd_new(uint16_t len) /** Deallocate the Extended ORPort message in <b>cmd</b>. */ void -ext_or_cmd_free(ext_or_cmd_t *cmd) +ext_or_cmd_free_(ext_or_cmd_t *cmd) { tor_free(cmd); } diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index af2b97e77c..09acbc407e 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -10,7 +10,11 @@ int connection_ext_or_start_auth(or_connection_t *or_conn); ext_or_cmd_t *ext_or_cmd_new(uint16_t len); -void ext_or_cmd_free(ext_or_cmd_t *cmd); + +#define ext_or_cmd_free(cmd) \ + FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd)) + +void ext_or_cmd_free_(ext_or_cmd_t *cmd); void connection_or_set_ext_or_identifier(or_connection_t *conn); void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); void connection_or_clear_ext_or_id_map(void); diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c index f730106d06..c938e76678 100644 --- a/src/or/fp_pair.c +++ b/src/or/fp_pair.c @@ -196,7 +196,7 @@ fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key) */ void -fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)) +fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*)) { fp_pair_map_entry_t **ent, **next, *this; diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h index f7c060b459..4498a16101 100644 --- a/src/or/fp_pair.h +++ b/src/or/fp_pair.h @@ -26,7 +26,12 @@ void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key); void * fp_pair_map_get_by_digests(const fp_pair_map_t *map, const char *first, const char *second); void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key); -void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)); +void fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*)); +#define fp_pair_map_free(map, free_val) do { \ + fp_pair_map_free_((map), (free_val)); \ + (map) = NULL; \ + } while (0) + int fp_pair_map_isempty(const fp_pair_map_t *map); int fp_pair_map_size(const fp_pair_map_t *map); fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map); diff --git a/src/or/geoip.c b/src/or/geoip.c index 3944b2cf69..d7411e6aaa 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -30,6 +30,7 @@ #define GEOIP_PRIVATE #include "or.h" #include "ht.h" +#include "buffers.h" #include "config.h" #include "control.h" #include "dnsserv.h" @@ -526,9 +527,12 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash, HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash, clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_) +#define clientmap_entry_free(ent) \ + FREE_AND_NULL(clientmap_entry_t, clientmap_entry_free_, ent) + /** Free all storage held by <b>ent</b>. */ static void -clientmap_entry_free(clientmap_entry_t *ent) +clientmap_entry_free_(clientmap_entry_t *ent) { if (!ent) return; @@ -930,9 +934,9 @@ static char * geoip_get_dirreq_history(dirreq_type_t type) { char *result = NULL; + buf_t *buf = NULL; smartlist_t *dirreq_completed = NULL; uint32_t complete = 0, timeouts = 0, running = 0; - int bufsize = 1024, written; dirreq_map_entry_t **ptr, **next; struct timeval now; @@ -965,13 +969,9 @@ geoip_get_dirreq_history(dirreq_type_t type) DIR_REQ_GRANULARITY); running = round_uint32_to_next_multiple_of(running, DIR_REQ_GRANULARITY); - result = tor_malloc_zero(bufsize); - written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u," - "running=%u", complete, timeouts, running); - if (written < 0) { - tor_free(result); - goto done; - } + buf = buf_new_with_capacity(1024); + buf_add_printf(buf, "complete=%u,timeout=%u," + "running=%u", complete, timeouts, running); #define MIN_DIR_REQ_RESPONSES 16 if (complete >= MIN_DIR_REQ_RESPONSES) { @@ -992,7 +992,7 @@ geoip_get_dirreq_history(dirreq_type_t type) dltimes[ent_sl_idx] = bytes_per_second; } SMARTLIST_FOREACH_END(ent); median_uint32(dltimes, complete); /* sorts as a side effect. */ - written = tor_snprintf(result + written, bufsize - written, + buf_add_printf(buf, ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u," "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u", dltimes[0], @@ -1008,14 +1008,15 @@ geoip_get_dirreq_history(dirreq_type_t type) dltimes[8*complete/10-1], dltimes[9*complete/10-1], dltimes[complete-1]); - if (written<0) - tor_free(result); tor_free(dltimes); } - done: + + result = buf_extract(buf, NULL); + SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent, tor_free(ent)); smartlist_free(dirreq_completed); + buf_free(buf); return result; } diff --git a/src/or/git_revision.c b/src/or/git_revision.c new file mode 100644 index 0000000000..8f326b8751 --- /dev/null +++ b/src/or/git_revision.c @@ -0,0 +1,17 @@ +/* Copyright 2001-2004 Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "git_revision.h" + +/** String describing which Tor Git repository version the source was + * built from. This string is generated by a bit of shell kludging in + * src/or/include.am, and is usually right. + */ +const char tor_git_revision[] = +#ifndef _MSC_VER +#include "micro-revision.i" +#endif + ""; + diff --git a/src/or/git_revision.h b/src/or/git_revision.h new file mode 100644 index 0000000000..5613cb4335 --- /dev/null +++ b/src/or/git_revision.h @@ -0,0 +1,12 @@ +/* Copyright 2001-2004 Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_GIT_REVISION_H +#define TOR_GIT_REVISION_H + +extern const char tor_git_revision[]; + +#endif /* !defined(TOR_GIT_REVISION_H) */ + diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 74ab766468..4dc35f68d0 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -34,6 +34,7 @@ hibernating, phase 2: #include "config.h" #include "connection.h" #include "connection_edge.h" +#include "connection_or.h" #include "control.h" #include "hibernate.h" #include "main.h" @@ -818,8 +819,8 @@ hibernate_begin(hibernate_state_t new_state, time_t now) log_notice(LD_GENERAL,"SIGINT received %s; exiting now.", hibernate_state == HIBERNATE_STATE_EXITING ? "a second time" : "while hibernating"); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); + return; } if (new_state == HIBERNATE_STATE_LOWBANDWIDTH && @@ -906,20 +907,23 @@ hibernate_go_dormant(time_t now) while ((conn = connection_get_by_type(CONN_TYPE_OR)) || (conn = connection_get_by_type(CONN_TYPE_AP)) || (conn = connection_get_by_type(CONN_TYPE_EXIT))) { - if (CONN_IS_EDGE(conn)) + if (CONN_IS_EDGE(conn)) { connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING); + } log_info(LD_NET,"Closing conn type %d", conn->type); - if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */ + if (conn->type == CONN_TYPE_AP) { + /* send socks failure if needed */ connection_mark_unattached_ap(TO_ENTRY_CONN(conn), END_STREAM_REASON_HIBERNATING); - else if (conn->type == CONN_TYPE_OR) { + } else if (conn->type == CONN_TYPE_OR) { if (TO_OR_CONN(conn)->chan) { - channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan)); + connection_or_close_normally(TO_OR_CONN(conn), 0); } else { connection_mark_for_close(conn); } - } else + } else { connection_mark_for_close(conn); + } } if (now < interval_wakeup_time) @@ -980,8 +984,7 @@ consider_hibernation(time_t now) tor_assert(shutdown_time); if (shutdown_time <= now) { log_notice(LD_GENERAL, "Clean shutdown finished. Exiting."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); } return; /* if exiting soon, don't worry about bandwidth limits */ } diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c index 3ebe13fb4d..1a8fdbd03b 100644 --- a/src/or/hs_cache.c +++ b/src/or/hs_cache.c @@ -52,9 +52,12 @@ lookup_v3_desc_as_dir(const uint8_t *key) return digest256map_get(hs_cache_v3_dir, key); } +#define cache_dir_desc_free(val) \ + FREE_AND_NULL(hs_cache_dir_descriptor_t, cache_dir_desc_free_, (val)) + /* Free a directory descriptor object. */ static void -cache_dir_desc_free(hs_cache_dir_descriptor_t *desc) +cache_dir_desc_free_(hs_cache_dir_descriptor_t *desc) { if (desc == NULL) { return; @@ -67,10 +70,9 @@ cache_dir_desc_free(hs_cache_dir_descriptor_t *desc) /* Helper function: Use by the free all function using the digest256map * interface to cache entries. */ static void -cache_dir_desc_free_(void *ptr) +cache_dir_desc_free_void(void *ptr) { - hs_cache_dir_descriptor_t *desc = ptr; - cache_dir_desc_free(desc); + cache_dir_desc_free_(ptr); } /* Create a new directory cache descriptor object from a encoded descriptor. @@ -417,9 +419,12 @@ cache_client_desc_new(const char *desc_str, return client_desc; } +#define cache_client_desc_free(val) \ + FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val)) + /** Free memory allocated by <b>desc</b>. */ static void -cache_client_desc_free(hs_cache_client_descriptor_t *desc) +cache_client_desc_free_(hs_cache_client_descriptor_t *desc) { if (desc == NULL) { return; @@ -433,7 +438,7 @@ cache_client_desc_free(hs_cache_client_descriptor_t *desc) /** Helper function: Use by the free all function to clear the client cache */ static void -cache_client_desc_free_(void *ptr) +cache_client_desc_free_void(void *ptr) { hs_cache_client_descriptor_t *desc = ptr; cache_client_desc_free(desc); @@ -448,18 +453,21 @@ cache_intro_state_new(void) return state; } +#define cache_intro_state_free(val) \ + FREE_AND_NULL(hs_cache_intro_state_t, cache_intro_state_free_, (val)) + /* Free an hs_cache_intro_state_t object. */ static void -cache_intro_state_free(hs_cache_intro_state_t *state) +cache_intro_state_free_(hs_cache_intro_state_t *state) { tor_free(state); } /* Helper function: use by the free all function. */ static void -cache_intro_state_free_(void *state) +cache_intro_state_free_void(void *state) { - cache_intro_state_free(state); + cache_intro_state_free_(state); } /* Return a newly allocated and initialized hs_cache_client_intro_state_t @@ -472,22 +480,26 @@ cache_client_intro_state_new(void) return cache; } +#define cache_client_intro_state_free(val) \ + FREE_AND_NULL(hs_cache_client_intro_state_t, \ + cache_client_intro_state_free_, (val)) + /* Free a cache client intro state object. */ static void -cache_client_intro_state_free(hs_cache_client_intro_state_t *cache) +cache_client_intro_state_free_(hs_cache_client_intro_state_t *cache) { if (cache == NULL) { return; } - digest256map_free(cache->intro_points, cache_intro_state_free_); + digest256map_free(cache->intro_points, cache_intro_state_free_void); tor_free(cache); } /* Helper function: use by the free all function. */ static void -cache_client_intro_state_free_(void *entry) +cache_client_intro_state_free_void(void *entry) { - cache_client_intro_state_free(entry); + cache_client_intro_state_free_(entry); } /* For the given service identity key service_pk and an introduction @@ -706,6 +718,24 @@ cache_clean_v3_as_client(time_t now) } /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return + * its HS encoded descriptor if it's stored in our cache, or NULL if not. */ +const char * +hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key) +{ + hs_cache_client_descriptor_t *cached_desc = NULL; + + tor_assert(key); + + cached_desc = lookup_v3_desc_as_client(key->pubkey); + if (cached_desc) { + tor_assert(cached_desc->encoded_desc); + return cached_desc->encoded_desc; + } + + return NULL; +} + +/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return * its HS descriptor if it's stored in our cache, or NULL if not. */ const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key) @@ -933,14 +963,14 @@ hs_cache_init(void) void hs_cache_free_all(void) { - digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_); + digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_void); hs_cache_v3_dir = NULL; - digest256map_free(hs_cache_v3_client, cache_client_desc_free_); + digest256map_free(hs_cache_v3_client, cache_client_desc_free_void); hs_cache_v3_client = NULL; digest256map_free(hs_cache_client_intro_state, - cache_client_intro_state_free_); + cache_client_intro_state_free_void); hs_cache_client_intro_state = NULL; } diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h index 2dcc518a71..a141634cc4 100644 --- a/src/or/hs_cache.h +++ b/src/or/hs_cache.h @@ -81,6 +81,8 @@ int hs_cache_lookup_as_dir(uint32_t version, const char *query, const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key); +const char * +hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key); int hs_cache_store_as_client(const char *desc_str, const ed25519_public_key_t *identity_pk); void hs_cache_clean_as_client(time_t now); diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 11ce2a1887..c896eeb0cc 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -13,6 +13,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "nodelist.h" #include "policies.h" #include "relay.h" #include "rendservice.h" @@ -553,76 +554,99 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) return; } -/* Using an extend info object ei, set all possible link specifiers in lspecs. - * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present, - * logs a BUG() warning, and returns an empty smartlist. Clients never make - * direct connections to rendezvous points, so they should always have an - * IPv4 address in ei. */ +/* Add all possible link specifiers in node to lspecs. + * legacy ID is mandatory thus MUST be present in node. If the primary address + * is not IPv4, log a BUG() warning, and return an empty smartlist. + * Includes ed25519 id and IPv6 link specifiers if present in the node. */ static void -get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) +get_lspecs_from_node(const node_t *node, smartlist_t *lspecs) { link_specifier_t *ls; + tor_addr_port_t ap; - tor_assert(ei); + tor_assert(node); tor_assert(lspecs); - /* We require IPv4, we will add IPv6 support in a later tor version */ - if (BUG(!tor_addr_is_v4(&ei->addr))) { + /* Get the relay's IPv4 address. */ + node_get_prim_orport(node, &ap); + + /* We expect the node's primary address to be a valid IPv4 address. + * This conforms to the protocol, which requires either an IPv4 or IPv6 + * address (or both). */ + if (BUG(!tor_addr_is_v4(&ap.addr)) || + BUG(!tor_addr_port_is_valid_ap(&ap, 0))) { return; } ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_IPV4); - link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr)); - link_specifier_set_un_ipv4_port(ls, ei->port); + link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr)); + link_specifier_set_un_ipv4_port(ls, ap.port); /* Four bytes IPv4 and two bytes port. */ - link_specifier_set_ls_len(ls, sizeof(ei->addr.addr.in_addr) + - sizeof(ei->port)); + link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) + + sizeof(ap.port)); smartlist_add(lspecs, ls); - /* Legacy ID is mandatory. */ + /* Legacy ID is mandatory and will always be present in node. */ ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_LEGACY_ID); - memcpy(link_specifier_getarray_un_legacy_id(ls), ei->identity_digest, + memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity, link_specifier_getlen_un_legacy_id(ls)); link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); smartlist_add(lspecs, ls); - /* ed25519 ID is only included if the extend_info has it. */ - if (!ed25519_public_key_is_zero(&ei->ed_identity)) { + /* ed25519 ID is only included if the node has it. */ + if (!ed25519_public_key_is_zero(&node->ed25519_id)) { ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_ED25519_ID); - memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity, + memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id, link_specifier_getlen_un_ed25519_id(ls)); link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls)); smartlist_add(lspecs, ls); } + + /* Check for IPv6. If so, include it as well. */ + if (node_has_ipv6_orport(node)) { + ls = link_specifier_new(); + node_get_pref_ipv6_orport(node, &ap); + link_specifier_set_ls_type(ls, LS_IPV6); + size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls); + const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr); + uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls); + memcpy(ipv6_array, in6_addr, addr_len); + link_specifier_set_un_ipv6_port(ls, ap.port); + /* Sixteen bytes IPv6 and two bytes port. */ + link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port)); + smartlist_add(lspecs, ls); + } } -/* Using the given descriptor intro point ip, the extend information of the - * rendezvous point rp_ei and the service's subcredential, populate the +/* Using the given descriptor intro point ip, the node of the + * rendezvous point rp_node and the service's subcredential, populate the * already allocated intro1_data object with the needed key material and link * specifiers. * - * This can't fail but the ip MUST be a valid object containing the needed - * keys and authentication method. */ + * If rp_node has an invalid primary address, intro1_data->link_specifiers + * will be an empty list. Otherwise, this function can't fail. The ip + * MUST be a valid object containing the needed keys and authentication + * method. */ static void setup_introduce1_data(const hs_desc_intro_point_t *ip, - const extend_info_t *rp_ei, + const node_t *rp_node, const uint8_t *subcredential, hs_cell_introduce1_data_t *intro1_data) { smartlist_t *rp_lspecs; tor_assert(ip); - tor_assert(rp_ei); + tor_assert(rp_node); tor_assert(subcredential); tor_assert(intro1_data); /* Build the link specifiers from the extend information of the rendezvous * circuit that we've picked previously. */ rp_lspecs = smartlist_new(); - get_lspecs_from_extend_info(rp_ei, rp_lspecs); + get_lspecs_from_node(rp_node, rp_lspecs); /* Populate the introduce1 data object. */ memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); @@ -633,7 +657,7 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip, intro1_data->auth_pk = &ip->auth_key_cert->signed_key; intro1_data->enc_pk = &ip->enc_key; intro1_data->subcredential = subcredential; - intro1_data->onion_pk = &rp_ei->curve25519_onion_key; + intro1_data->onion_pk = node_get_curve25519_onion_key(rp_node); intro1_data->link_specifiers = rp_lspecs; } @@ -1077,11 +1101,21 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ, tor_assert(ip); tor_assert(subcredential); + /* It is undefined behavior in hs_cell_introduce1_data_clear() if intro1_data + * has been declared on the stack but not initialized. Here, we set it to 0. + */ + memset(&intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); + /* This takes various objects in order to populate the introduce1 data * object which is used to build the content of the cell. */ - setup_introduce1_data(ip, rend_circ->build_state->chosen_exit, - subcredential, &intro1_data); - /* If we didn't get any link specifiers, it's because our extend info was + const node_t *exit_node = build_state_get_exit_node(rend_circ->build_state); + if (exit_node == NULL) { + log_info(LD_REND, "Unable to get rendezvous point for circuit %u. " + "Failing.", TO_CIRCUIT(intro_circ)->n_circ_id); + goto done; + } + setup_introduce1_data(ip, exit_node, subcredential, &intro1_data); + /* If we didn't get any link specifiers, it's because our node was * bad. */ if (BUG(!intro1_data.link_specifiers) || !smartlist_len(intro1_data.link_specifiers)) { diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h index 63ff5e463c..2f5beaa168 100644 --- a/src/or/hs_circuit.h +++ b/src/or/hs_circuit.h @@ -70,7 +70,7 @@ create_rp_circuit_identifier(const hs_service_t *service, const curve25519_public_key_t *server_pk, const hs_ntor_rend_cell_keys_t *keys); -#endif +#endif /* defined(HS_CIRCUIT_PRIVATE) */ #endif /* !defined(TOR_HS_CIRCUIT_H) */ diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c index 97d6053e9b..112c8bdced 100644 --- a/src/or/hs_circuitmap.c +++ b/src/or/hs_circuitmap.c @@ -106,9 +106,12 @@ hs_token_new(hs_token_type_t type, size_t token_len, return hs_token; } +#define hs_token_free(val) \ + FREE_AND_NULL(hs_token_t, hs_token_free_, (val)) + /** Free memory allocated by this <b>hs_token</b>. */ static void -hs_token_free(hs_token_t *hs_token) +hs_token_free_(hs_token_t *hs_token) { if (!hs_token) { return; @@ -428,30 +431,54 @@ hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie) { origin_circuit_t *circ = NULL; - circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, - REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_READY); + circ = hs_circuitmap_get_established_rend_circ_client_side(cookie); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); + CIRCUIT_PURPOSE_C_ESTABLISH_REND); + return circ; +} + +/* Public function: Return client-side established rendezvous circuit with + * rendezvous <b>cookie</b>. It will look for circuits with the following + * purposes: + * + * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received + * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for + * INTRODUCE_ACK from intro point. + * + * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and + * introduce circuit acked. Waiting for RENDEZVOUS2 from service. + * + * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received + * RENDEZVOUS2 from service. + * + * Return NULL if no such circuit is found in the circuitmap. */ +origin_circuit_t * +hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie) +{ + origin_circuit_t *circ = NULL; + + circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, + REND_TOKEN_LEN, cookie, + CIRCUIT_PURPOSE_C_REND_READY); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_JOINED); + CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_ESTABLISH_REND); + CIRCUIT_PURPOSE_C_REND_JOINED); return circ; } diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h index 43b2947c17..9e653480b5 100644 --- a/src/or/hs_circuitmap.h +++ b/src/or/hs_circuitmap.h @@ -45,6 +45,8 @@ struct origin_circuit_t * hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie); struct origin_circuit_t * hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie); +struct origin_circuit_t * +hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie); void hs_circuitmap_register_intro_circ_v2_service_side( struct origin_circuit_t *circ, diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 9ac653c721..c863475aff 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -21,6 +21,7 @@ #include "config.h" #include "directory.h" #include "hs_client.h" +#include "hs_control.h" #include "router.h" #include "routerset.h" #include "circuitlist.h" @@ -349,6 +350,10 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk, safe_str_client(base64_blinded_pubkey), safe_str_client(routerstatus_describe(hsdir))); + /* Fire a REQUESTED event on the control port. */ + hs_control_desc_event_requested(onion_identity_pk, base64_blinded_pubkey, + hsdir); + /* Cleanup memory. */ memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey)); memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey)); @@ -940,7 +945,8 @@ handle_introduce_ack_success(origin_circuit_t *intro_circ) /* Get the rendezvous circuit for this rendezvous cookie. */ uint8_t *rendezvous_cookie = intro_circ->hs_ident->rendezvous_cookie; - rend_circ = hs_circuitmap_get_rend_circ_client_side(rendezvous_cookie); + rend_circ = + hs_circuitmap_get_established_rend_circ_client_side(rendezvous_cookie); if (rend_circ == NULL) { log_warn(LD_REND, "Can't find any rendezvous circuit. Stopping"); goto end; diff --git a/src/or/hs_common.c b/src/or/hs_common.c index a5cfaf03a8..4c56b2d11f 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -330,7 +330,7 @@ rend_data_alloc(uint32_t version) /** Free all storage associated with <b>data</b> */ void -rend_data_free(rend_data_t *data) +rend_data_free_(rend_data_t *data) { if (!data) { return; diff --git a/src/or/hs_common.h b/src/or/hs_common.h index b81c03d508..f59c2b6050 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -130,6 +130,17 @@ typedef enum { HS_AUTH_KEY_TYPE_ED25519 = 2, } hs_auth_key_type_t; +/* Return value when adding an ephemeral service through the ADD_ONION + * control port command. Both v2 and v3 share these. */ +typedef enum { + RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ + RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ + RSAE_ADDREXISTS = -3, /**< Onion address collision */ + RSAE_BADPRIVKEY = -2, /**< Invalid public key */ + RSAE_INTERNAL = -1, /**< Internal error */ + RSAE_OKAY = 0 /**< Service added as expected */ +} hs_service_add_ephemeral_status_t; + /* Represents the mapping from a virtual port of a rendezvous service to a * real port on some IP. */ typedef struct rend_service_port_config_t { @@ -183,7 +194,9 @@ void hs_build_blinded_keypair(const ed25519_keypair_t *kp, ed25519_keypair_t *kp_out); int hs_service_requires_uptime_circ(const smartlist_t *ports); -void rend_data_free(rend_data_t *data); +void rend_data_free_(rend_data_t *data); +#define rend_data_free(data) \ + FREE_AND_NULL(rend_data_t, rend_data_free_, (data)) rend_data_t *rend_data_dup(const rend_data_t *data); rend_data_t *rend_data_client_create(const char *onion_address, const char *desc_id, diff --git a/src/or/hs_control.c b/src/or/hs_control.c new file mode 100644 index 0000000000..87b4e3fca8 --- /dev/null +++ b/src/or/hs_control.c @@ -0,0 +1,258 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_control.c + * \brief Contains control port event related code. + **/ + +#include "or.h" +#include "control.h" +#include "hs_common.h" +#include "hs_control.h" +#include "hs_descriptor.h" +#include "hs_service.h" +#include "nodelist.h" + +/* Send on the control port the "HS_DESC REQUESTED [...]" event. + * + * The onion_pk is the onion service public key, base64_blinded_pk is the + * base64 encoded blinded key for the service and hsdir_rs is the routerstatus + * object of the HSDir that this request is for. */ +void +hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, + const char *base64_blinded_pk, + const routerstatus_t *hsdir_rs) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + const uint8_t *hsdir_index; + const node_t *hsdir_node; + + tor_assert(onion_pk); + tor_assert(base64_blinded_pk); + tor_assert(hsdir_rs); + + hs_build_address(onion_pk, HS_VERSION_THREE, onion_address); + + /* Get the node from the routerstatus object to get the HSDir index used for + * this request. We can't have a routerstatus entry without a node and we + * can't pick a node without an hsdir_index. */ + hsdir_node = node_get_by_id(hsdir_rs->identity_digest); + tor_assert(hsdir_node); + tor_assert(hsdir_node->hsdir_index); + /* This is a fetch event. */ + hsdir_index = hsdir_node->hsdir_index->fetch; + + /* Trigger the event. */ + control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH, + hsdir_rs->identity_digest, + base64_blinded_pk, + hex_str((const char *) hsdir_index, + DIGEST256_LEN)); + memwipe(onion_address, 0, sizeof(onion_address)); +} + +/* Send on the control port the "HS_DESC FAILED [...]" event. + * + * Using a directory connection identifier, the HSDir identity digest and a + * reason for the failure. None can be NULL. */ +void +hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *reason) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + tor_assert(reason); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk, + hsdir_id_digest, reason); +} + +/* Send on the control port the "HS_DESC RECEIVED [...]" event. + * + * Using a directory connection identifier and the HSDir identity digest. + * None can be NULL. */ +void +hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk, + hsdir_id_digest); +} + +/* Send on the control port the "HS_DESC CREATED [...]" event. + * + * Using the onion address of the descriptor's service and the blinded public + * key of the descriptor as a descriptor ID. None can be NULL. */ +void +hs_control_desc_event_created(const char *onion_address, + const ed25519_public_key_t *blinded_pk) +{ + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(onion_address); + tor_assert(blinded_pk); + + /* Build base64 encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) { + return; + } + + /* Version 3 doesn't use the replica number in its descriptor ID computation + * so we pass negative value so the control port subsystem can ignore it. */ + control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1); +} + +/* Send on the control port the "HS_DESC UPLOAD [...]" event. + * + * Using the onion address of the descriptor's service, the HSDir identity + * digest, the blinded public key of the descriptor as a descriptor ID and the + * HSDir index for this particular request. None can be NULL. */ +void +hs_control_desc_event_upload(const char *onion_address, + const char *hsdir_id_digest, + const ed25519_public_key_t *blinded_pk, + const uint8_t *hsdir_index) +{ + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(onion_address); + tor_assert(hsdir_id_digest); + tor_assert(blinded_pk); + tor_assert(hsdir_index); + + /* Build base64 encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) { + return; + } + + control_event_hs_descriptor_upload(onion_address, hsdir_id_digest, + base64_blinded_pk, + hex_str((const char *) hsdir_index, + DIGEST256_LEN)); +} + +/* Send on the control port the "HS_DESC UPLOADED [...]" event. + * + * Using the directory connection identifier and the HSDir identity digest. + * None can be NULL. */ +void +hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address); +} + +/* Send on the control port the "HS_DESC_CONTENT [...]" event. + * + * Using the directory connection identifier, the HSDir identity digest and + * the body of the descriptor (as it was received from the directory). None + * can be NULL. */ +void +hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *body) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hs_descriptor_content(onion_address, base64_blinded_pk, + hsdir_id_digest, body); +} + +/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for + * the given onion_address. The descriptor will be uploaded to each directory + * in hsdirs_rs. If NULL, the responsible directories for the current time + * period will be selected. + * + * Return -1 on if the descriptor plaintext section is not decodable. Else, 0 + * on success. */ +int +hs_control_hspost_command(const char *body, const char *onion_address, + const smartlist_t *hsdirs_rs) +{ + int ret = -1; + ed25519_public_key_t identity_pk; + hs_desc_plaintext_data_t plaintext; + smartlist_t *hsdirs = NULL; + + tor_assert(body); + tor_assert(onion_address); + + /* This can't fail because we require the caller to pass us a valid onion + * address that has passed hs_address_is_valid(). */ + if (BUG(hs_parse_address(onion_address, &identity_pk, NULL, NULL) < 0)) { + goto done; // LCOV_EXCL_LINE + } + + /* Only decode the plaintext part which is what the directory will do to + * validate before caching. */ + if (hs_desc_decode_plaintext(body, &plaintext) < 0) { + goto done; + } + + /* No HSDir(s) given, we'll compute what the current ones should be. */ + if (hsdirs_rs == NULL) { + hsdirs = smartlist_new(); + hs_get_responsible_hsdirs(&plaintext.blinded_pubkey, + hs_get_time_period_num(0), + 0, /* Always the current descriptor which uses + * the first hsdir index. */ + 0, /* It is for storing on a directory. */ + hsdirs); + hsdirs_rs = hsdirs; + } + + SMARTLIST_FOREACH_BEGIN(hsdirs_rs, const routerstatus_t *, rs) { + hs_service_upload_desc_to_dir(body, plaintext.version, &identity_pk, + &plaintext.blinded_pubkey, rs); + } SMARTLIST_FOREACH_END(rs); + ret = 0; + + done: + /* We don't have ownership of the objects in this list. */ + smartlist_free(hsdirs); + return ret; +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h new file mode 100644 index 0000000000..95c46e655e --- /dev/null +++ b/src/or/hs_control.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_control.h + * \brief Header file containing control port event related code. + **/ + +#ifndef TOR_HS_CONTROL_H +#define TOR_HS_CONTROL_H + +#include "hs_ident.h" + +/* Event "HS_DESC REQUESTED [...]" */ +void hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, + const char *base64_blinded_pk, + const routerstatus_t *hsdir_rs); + +/* Event "HS_DESC FAILED [...]" */ +void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *reason); + +/* Event "HS_DESC RECEIVED [...]" */ +void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest); + +/* Event "HS_DESC CREATED [...]" */ +void hs_control_desc_event_created(const char *onion_address, + const ed25519_public_key_t *blinded_pk); + +/* Event "HS_DESC UPLOAD [...]" */ +void hs_control_desc_event_upload(const char *onion_address, + const char *hsdir_id_digest, + const ed25519_public_key_t *blinded_pk, + const uint8_t *hsdir_index); + +/* Event "HS_DESC UPLOADED [...]" */ +void hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest); + +/* Event "HS_DESC_CONTENT [...]" */ +void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *body); + +/* Command "HSPOST [...]" */ +int hs_control_hspost_command(const char *body, const char *onion_address, + const smartlist_t *hsdirs_rs); + +#endif /* !defined(TOR_HS_CONTROL_H) */ + diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 1708866944..789bc1d046 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -2367,7 +2367,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc, /* Free the descriptor plaintext data object. */ void -hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc) +hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc) { desc_plaintext_data_free_contents(desc); tor_free(desc); @@ -2375,7 +2375,7 @@ hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc) /* Free the descriptor encrypted data object. */ void -hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc) +hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc) { desc_encrypted_data_free_contents(desc); tor_free(desc); @@ -2383,7 +2383,7 @@ hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc) /* Free the given descriptor object. */ void -hs_descriptor_free(hs_descriptor_t *desc) +hs_descriptor_free_(hs_descriptor_t *desc) { if (!desc) { return; @@ -2448,7 +2448,7 @@ hs_desc_intro_point_new(void) /* Free a descriptor intro point object. */ void -hs_desc_intro_point_free(hs_desc_intro_point_t *ip) +hs_desc_intro_point_free_(hs_desc_intro_point_t *ip) { if (ip == NULL) { return; @@ -2467,7 +2467,7 @@ hs_desc_intro_point_free(hs_desc_intro_point_t *ip) /* Free the given descriptor link specifier. */ void -hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls) +hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls) { if (ls == NULL) { return; diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 52bec8e244..09979410e1 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -208,11 +208,20 @@ hs_desc_is_supported_version(uint32_t version) /* Public API. */ -void hs_descriptor_free(hs_descriptor_t *desc); -void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc); -void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc); +void hs_descriptor_free_(hs_descriptor_t *desc); +#define hs_descriptor_free(desc) \ + FREE_AND_NULL(hs_descriptor_t, hs_descriptor_free_, (desc)) +void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc); +#define hs_desc_plaintext_data_free(desc) \ + FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc)) +void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc); +#define hs_desc_encrypted_data_free(desc) \ + FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc)) + +void hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls); +#define hs_desc_link_specifier_free(ls) \ + FREE_AND_NULL(hs_desc_link_specifier_t, hs_desc_link_specifier_free_, (ls)) -void hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls); hs_desc_link_specifier_t *hs_desc_link_specifier_new( const extend_info_t *info, uint8_t type); void hs_descriptor_clear_intro_points(hs_descriptor_t *desc); @@ -234,7 +243,9 @@ size_t hs_desc_obj_size(const hs_descriptor_t *data); size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data); hs_desc_intro_point_t *hs_desc_intro_point_new(void); -void hs_desc_intro_point_free(hs_desc_intro_point_t *ip); +void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip); +#define hs_desc_intro_point_free(ip) \ + FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip)) link_specifier_t *hs_desc_lspec_to_trunnel( const hs_desc_link_specifier_t *spec); diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c index b0e4e36a9b..0bce2f625b 100644 --- a/src/or/hs_ident.c +++ b/src/or/hs_ident.c @@ -25,7 +25,7 @@ hs_ident_circuit_new(const ed25519_public_key_t *identity_pk, /* Free the given circuit identifier. */ void -hs_ident_circuit_free(hs_ident_circuit_t *ident) +hs_ident_circuit_free_(hs_ident_circuit_t *ident) { if (ident == NULL) { return; @@ -56,7 +56,7 @@ hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src) /* Free the given directory connection identifier. */ void -hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident) +hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident) { if (ident == NULL) { return; @@ -93,7 +93,7 @@ hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk) /* Free the given edge connection identifier. */ void -hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident) +hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident) { if (ident == NULL) { return; diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h index 03150d25ea..91ec389aa4 100644 --- a/src/or/hs_ident.h +++ b/src/or/hs_ident.h @@ -119,12 +119,16 @@ typedef struct hs_ident_edge_conn_t { hs_ident_circuit_t *hs_ident_circuit_new( const ed25519_public_key_t *identity_pk, hs_ident_circuit_type_t circuit_type); -void hs_ident_circuit_free(hs_ident_circuit_t *ident); +void hs_ident_circuit_free_(hs_ident_circuit_t *ident); +#define hs_ident_circuit_free(id) \ + FREE_AND_NULL(hs_ident_circuit_t, hs_ident_circuit_free_, (id)) hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src); /* Directory connection identifier API. */ hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src); -void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident); +void hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident); +#define hs_ident_dir_conn_free(id) \ + FREE_AND_NULL(hs_ident_dir_conn_t, hs_ident_dir_conn_free_, (id)) void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, const ed25519_public_key_t *blinded_pk, hs_ident_dir_conn_t *ident); @@ -132,7 +136,9 @@ void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, /* Edge connection identifier API. */ hs_ident_edge_conn_t *hs_ident_edge_conn_new( const ed25519_public_key_t *identity_pk); -void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident); +void hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident); +#define hs_ident_edge_conn_free(id) \ + FREE_AND_NULL(hs_ident_edge_conn_t, hs_ident_edge_conn_free_, (id)) /* Validators */ int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident); diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 45810c5c5f..0db22991c2 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -30,6 +30,7 @@ #include "hs_circuit.h" #include "hs_common.h" #include "hs_config.h" +#include "hs_control.h" #include "hs_circuit.h" #include "hs_descriptor.h" #include "hs_ident.h" @@ -352,7 +353,7 @@ service_free_all(void) /* Free a given service intro point object. */ STATIC void -service_intro_point_free(hs_service_intro_point_t *ip) +service_intro_point_free_(hs_service_intro_point_t *ip) { if (!ip) { return; @@ -368,9 +369,9 @@ service_intro_point_free(hs_service_intro_point_t *ip) /* Helper: free an hs_service_intro_point_t object. This function is used by * digest256map_free() which requires a void * pointer. */ static void -service_intro_point_free_(void *obj) +service_intro_point_free_void(void *obj) { - service_intro_point_free(obj); + service_intro_point_free_(obj); } /* Return a newly allocated service intro point and fully initialized from the @@ -1027,7 +1028,7 @@ load_service_keys(hs_service_t *service) /* Free a given service descriptor object and all key material is wiped. */ STATIC void -service_descriptor_free(hs_service_descriptor_t *desc) +service_descriptor_free_(hs_service_descriptor_t *desc) { if (!desc) { return; @@ -1036,7 +1037,7 @@ service_descriptor_free(hs_service_descriptor_t *desc) memwipe(&desc->signing_kp, 0, sizeof(desc->signing_kp)); memwipe(&desc->blinded_kp, 0, sizeof(desc->blinded_kp)); /* Cleanup all intro points. */ - digest256map_free(desc->intro_points.map, service_intro_point_free_); + digest256map_free(desc->intro_points.map, service_intro_point_free_void); digestmap_free(desc->intro_points.failed_id, tor_free_); if (desc->previous_hsdirs) { SMARTLIST_FOREACH(desc->previous_hsdirs, char *, s, tor_free(s)); @@ -1431,6 +1432,9 @@ build_service_descriptor(hs_service_t *service, time_t now, /* Assign newly built descriptor to the next slot. */ *desc_out = desc; + /* Fire a CREATED control port event. */ + hs_control_desc_event_created(service->onion_address, + &desc->blinded_kp.pubkey); return; err: @@ -1572,7 +1576,7 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) /* Let's do a basic sanity check here so that we don't end up advertising the * ed25519 identity key of relays that don't actually support the link * protocol */ - if (!node_supports_ed25519_link_authentication(node)) { + if (!node_supports_ed25519_link_authentication(node, 0)) { tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity)); } else { /* Make sure we *do* have an ed key if we support the link authentication. @@ -2221,16 +2225,12 @@ static void upload_descriptor_to_hsdir(const hs_service_t *service, hs_service_descriptor_t *desc, const node_t *hsdir) { - char version_str[4] = {0}, *encoded_desc = NULL; - directory_request_t *dir_req; - hs_ident_dir_conn_t ident; + char *encoded_desc = NULL; tor_assert(service); tor_assert(desc); tor_assert(hsdir); - memset(&ident, 0, sizeof(ident)); - /* Let's avoid doing that if tor is configured to not publish. */ if (!get_options()->PublishHidServDescriptors) { log_info(LD_REND, "Service %s not publishing descriptor. " @@ -2246,29 +2246,10 @@ upload_descriptor_to_hsdir(const hs_service_t *service, goto end; } - /* Setup the connection identifier. */ - hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey, - &ident); - - /* This is our resource when uploading which is used to construct the URL - * with the version number: "/tor/hs/<version>/publish". */ - tor_snprintf(version_str, sizeof(version_str), "%u", - service->config.version); - - /* Build the directory request for this HSDir. */ - dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC); - directory_request_set_routerstatus(dir_req, hsdir->rs); - directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS); - directory_request_set_resource(dir_req, version_str); - directory_request_set_payload(dir_req, encoded_desc, - strlen(encoded_desc)); - /* The ident object is copied over the directory connection object once - * the directory request is initiated. */ - directory_request_upload_set_hs_ident(dir_req, &ident); - - /* Initiate the directory request to the hsdir.*/ - directory_initiate_request(dir_req); - directory_request_free(dir_req); + /* Time to upload the descriptor to the directory. */ + hs_service_upload_desc_to_dir(encoded_desc, service->config.version, + &service->keys.identity_pk, + &desc->blinded_kp.pubkey, hsdir->rs); /* Add this node to previous_hsdirs list */ service_desc_note_upload(desc, hsdir); @@ -2285,9 +2266,12 @@ upload_descriptor_to_hsdir(const hs_service_t *service, desc->desc->plaintext_data.revision_counter, safe_str_client(node_describe(hsdir)), safe_str_client(hex_str((const char *) idx, 32))); + + /* Fire a UPLOAD control port event. */ + hs_control_desc_event_upload(service->onion_address, hsdir->identity, + &desc->blinded_kp.pubkey, idx); } - /* XXX: Inform control port of the upload event (#20699). */ end: tor_free(encoded_desc); return; @@ -2922,6 +2906,205 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) /* Public API */ /* ========== */ +/* Upload an encoded descriptor in encoded_desc of the given version. This + * descriptor is for the service identity_pk and blinded_pk used to setup the + * directory connection identifier. It is uploaded to the directory hsdir_rs + * routerstatus_t object. + * + * NOTE: This function does NOT check for PublishHidServDescriptors because it + * is only used by the control port command HSPOST outside of this subsystem. + * Inside this code, upload_descriptor_to_hsdir() should be used. */ +void +hs_service_upload_desc_to_dir(const char *encoded_desc, + const uint8_t version, + const ed25519_public_key_t *identity_pk, + const ed25519_public_key_t *blinded_pk, + const routerstatus_t *hsdir_rs) +{ + char version_str[4] = {0}; + directory_request_t *dir_req; + hs_ident_dir_conn_t ident; + + tor_assert(encoded_desc); + tor_assert(identity_pk); + tor_assert(blinded_pk); + tor_assert(hsdir_rs); + + /* Setup the connection identifier. */ + memset(&ident, 0, sizeof(ident)); + hs_ident_dir_conn_init(identity_pk, blinded_pk, &ident); + + /* This is our resource when uploading which is used to construct the URL + * with the version number: "/tor/hs/<version>/publish". */ + tor_snprintf(version_str, sizeof(version_str), "%u", version); + + /* Build the directory request for this HSDir. */ + dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC); + directory_request_set_routerstatus(dir_req, hsdir_rs); + directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS); + directory_request_set_resource(dir_req, version_str); + directory_request_set_payload(dir_req, encoded_desc, + strlen(encoded_desc)); + /* The ident object is copied over the directory connection object once + * the directory request is initiated. */ + directory_request_upload_set_hs_ident(dir_req, &ident); + + /* Initiate the directory request to the hsdir.*/ + directory_initiate_request(dir_req); + directory_request_free(dir_req); +} + +/* Add the ephemeral service using the secret key sk and ports. Both max + * streams parameter will be set in the newly created service. + * + * Ownership of sk and ports is passed to this routine. Regardless of + * success/failure, callers should not touch these values after calling this + * routine, and may assume that correct cleanup has been done on failure. + * + * Return an appropriate hs_service_add_ephemeral_status_t. */ +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + hs_service_t *service = NULL; + + tor_assert(sk); + tor_assert(ports); + tor_assert(address_out); + + service = hs_service_new(get_options()); + + /* Setup the service configuration with specifics. A default service is + * HS_VERSION_TWO so explicitely set it. */ + service->config.version = HS_VERSION_THREE; + service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit; + service->config.max_streams_close_circuit = !!max_streams_close_circuit; + service->config.is_ephemeral = 1; + smartlist_free(service->config.ports); + service->config.ports = ports; + + /* Handle the keys. */ + memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk)); + if (ed25519_public_key_generate(&service->keys.identity_pk, + &service->keys.identity_sk) < 0) { + log_warn(LD_CONFIG, "Unable to generate ed25519 public key" + "for v3 service."); + ret = RSAE_BADPRIVKEY; + goto err; + } + + /* Make sure we have at least one port. */ + if (smartlist_len(service->config.ports) == 0) { + log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified " + "for v3 service."); + ret = RSAE_BADVIRTPORT; + goto err; + } + + /* The only way the registration can fail is if the service public key + * already exists. */ + if (BUG(register_service(hs_service_map, service) < 0)) { + log_warn(LD_CONFIG, "Onion Service private key collides with an " + "existing v3 service."); + ret = RSAE_ADDREXISTS; + goto err; + } + + /* Last step is to build the onion address. */ + hs_build_address(&service->keys.identity_pk, + (uint8_t) service->config.version, + service->onion_address); + *address_out = tor_strdup(service->onion_address); + + log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s", + safe_str_client(service->onion_address)); + ret = RSAE_OKAY; + goto end; + + err: + hs_service_free(service); + + end: + memwipe(sk, 0, sizeof(ed25519_secret_key_t)); + tor_free(sk); + return ret; +} + +/* For the given onion address, delete the ephemeral service. Return 0 on + * success else -1 on error. */ +int +hs_service_del_ephemeral(const char *address) +{ + uint8_t version; + ed25519_public_key_t pk; + hs_service_t *service = NULL; + + tor_assert(address); + + if (hs_parse_address(address, &pk, NULL, &version) < 0) { + log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal."); + goto err; + } + + if (version != HS_VERSION_THREE) { + log_warn(LD_CONFIG, "Requested version of onion address for removal " + "is not supported."); + goto err; + } + + service = find_service(hs_service_map, &pk); + if (service == NULL) { + log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for " + "removal."); + goto err; + } + + if (!service->config.is_ephemeral) { + log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for " + "removal."); + goto err; + } + + /* Close circuits, remove from map and finally free. */ + close_service_circuits(service); + remove_service(hs_service_map, service); + hs_service_free(service); + + log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s", + safe_str_client(address)); + return 0; + + err: + return -1; +} + +/* Using the ed25519 public key pk, find a service for that key and return the + * current encoded descriptor as a newly allocated string or NULL if not + * found. This is used by the control port subsystem. */ +char * +hs_service_lookup_current_desc(const ed25519_public_key_t *pk) +{ + const hs_service_t *service; + + tor_assert(pk); + + service = find_service(hs_service_map, pk); + if (service && service->desc_current) { + char *encoded_desc = NULL; + /* No matter what is the result (which should never be a failure), return + * the encoded variable, if success it will contain the right thing else + * it will be NULL. */ + hs_desc_encode_descriptor(service->desc_current->desc, + &service->desc_current->signing_kp, + &encoded_desc); + return encoded_desc; + } + + return NULL; +} + /* Return the number of service we have configured and usable. */ unsigned int hs_service_get_num_services(void) @@ -2950,7 +3133,9 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ) get_objects_from_ident(circ->hs_ident, &service, &ip, &desc); if (service == NULL) { - log_warn(LD_REND, "Unable to find any hidden service associated " + /* This is possible if the circuits are closed and the service is + * immediately deleted. */ + log_info(LD_REND, "Unable to find any hidden service associated " "identity key %s on intro circuit %u.", ed25519_fmt(&circ->hs_ident->identity_pk), TO_CIRCUIT(circ)->n_circ_id); @@ -3269,7 +3454,7 @@ hs_service_new(const or_options_t *options) * also takes care of wiping service keys from memory. It is safe to pass a * NULL pointer. */ void -hs_service_free(hs_service_t *service) +hs_service_free_(hs_service_t *service) { if (service == NULL) { return; diff --git a/src/or/hs_service.h b/src/or/hs_service.h index ed1053d850..d163eeef28 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -249,7 +249,8 @@ void hs_service_free_all(void); /* Service new/free functions. */ hs_service_t *hs_service_new(const or_options_t *options); -void hs_service_free(hs_service_t *service); +void hs_service_free_(hs_service_t *service); +#define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s)) unsigned int hs_service_get_num_services(void); void hs_service_stage_services(const smartlist_t *service_list); @@ -271,15 +272,33 @@ int hs_service_receive_introduce2(origin_circuit_t *circ, void hs_service_intro_circ_has_closed(origin_circuit_t *circ); +char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk); + +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out); +int hs_service_del_ephemeral(const char *address); + +/* Used outside of the HS subsystem by the control port command HSPOST. */ +void hs_service_upload_desc_to_dir(const char *encoded_desc, + const uint8_t version, + const ed25519_public_key_t *identity_pk, + const ed25519_public_key_t *blinded_pk, + const routerstatus_t *hsdir_rs); + #ifdef HS_SERVICE_PRIVATE #ifdef TOR_UNIT_TESTS - /* Useful getters for unit tests. */ STATIC unsigned int get_hs_service_map_size(void); STATIC int get_hs_service_staging_list_size(void); STATIC hs_service_ht *get_hs_service_map(void); STATIC hs_service_t *get_first_service(void); +STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( + const hs_service_t *service, + const hs_ident_circuit_t *ident); +#endif /* Service accessors. */ STATIC hs_service_t *find_service(hs_service_ht *map, @@ -290,7 +309,10 @@ STATIC int register_service(hs_service_ht *map, hs_service_t *service); STATIC hs_service_intro_point_t *service_intro_point_new( const extend_info_t *ei, unsigned int is_legacy); -STATIC void service_intro_point_free(hs_service_intro_point_t *ip); +STATIC void service_intro_point_free_(hs_service_intro_point_t *ip); +#define service_intro_point_free(ip) \ + FREE_AND_NULL(hs_service_intro_point_t, \ + service_intro_point_free_, (ip)) STATIC void service_intro_point_add(digest256map_t *map, hs_service_intro_point_t *ip); STATIC void service_intro_point_remove(const hs_service_t *service, @@ -298,9 +320,6 @@ STATIC void service_intro_point_remove(const hs_service_t *service, STATIC hs_service_intro_point_t *service_intro_point_find( const hs_service_t *service, const ed25519_public_key_t *auth_key); -STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( - const hs_service_t *service, - const hs_ident_circuit_t *ident); /* Service descriptor functions. */ STATIC hs_service_descriptor_t *service_descriptor_new(void); STATIC hs_service_descriptor_t *service_desc_find_by_intro( @@ -326,7 +345,10 @@ STATIC void run_upload_descriptor_event(time_t now); STATIC char * encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc); -STATIC void service_descriptor_free(hs_service_descriptor_t *desc); +STATIC void service_descriptor_free_(hs_service_descriptor_t *desc); +#define service_descriptor_free(d) \ + FREE_AND_NULL(hs_service_descriptor_t, \ + service_descriptor_free_, (d)) STATIC uint64_t check_state_line_for_service_rev_counter(const char *state_line, @@ -346,8 +368,6 @@ STATIC void service_desc_schedule_upload(hs_service_descriptor_t *desc, STATIC int service_desc_hsdirs_changed(const hs_service_t *service, const hs_service_descriptor_t *desc); -#endif /* defined(TOR_UNIT_TESTS) */ - #endif /* defined(HS_SERVICE_PRIVATE) */ #endif /* !defined(TOR_HS_SERVICE_H) */ diff --git a/src/or/include.am b/src/or/include.am index 7216aba9af..1c66cd2de3 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -51,6 +51,7 @@ LIBTOR_A_SOURCES = \ src/or/geoip.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ + src/or/git_revision.c \ src/or/hibernate.c \ src/or/hs_cache.c \ src/or/hs_cell.c \ @@ -59,6 +60,7 @@ LIBTOR_A_SOURCES = \ src/or/hs_client.c \ src/or/hs_common.c \ src/or/hs_config.c \ + src/or/hs_control.c \ src/or/hs_descriptor.c \ src/or/hs_ident.c \ src/or/hs_intropoint.c \ @@ -78,6 +80,7 @@ LIBTOR_A_SOURCES = \ src/or/parsecommon.c \ src/or/periodic.c \ src/or/protover.c \ + src/or/protover_rust.c \ src/or/proto_cell.c \ src/or/proto_control0.c \ src/or/proto_ext_or.c \ @@ -104,6 +107,7 @@ LIBTOR_A_SOURCES = \ src/or/statefile.c \ src/or/status.c \ src/or/torcert.c \ + src/or/tor_api.c \ src/or/onion_ntor.c \ $(tor_platform_source) @@ -189,14 +193,16 @@ ORHEADERS = \ src/or/fp_pair.h \ src/or/geoip.h \ src/or/entrynodes.h \ + src/or/git_revision.h \ src/or/hibernate.h \ src/or/hs_cache.h \ src/or/hs_cell.h \ - src/or/hs_config.h \ src/or/hs_circuit.h \ src/or/hs_circuitmap.h \ src/or/hs_client.h \ src/or/hs_common.h \ + src/or/hs_config.h \ + src/or/hs_control.h \ src/or/hs_descriptor.h \ src/or/hs_ident.h \ src/or/hs_intropoint.h \ @@ -243,9 +249,13 @@ ORHEADERS = \ src/or/scheduler.h \ src/or/statefile.h \ src/or/status.h \ - src/or/torcert.h + src/or/torcert.h \ + src/or/tor_api_internal.h + +# This may someday want to be an installed file? +noinst_HEADERS += src/or/tor_api.h -noinst_HEADERS+= $(ORHEADERS) micro-revision.i +noinst_HEADERS += $(ORHEADERS) micro-revision.i micro-revision.i: FORCE $(AM_V_at)rm -f micro-revision.tmp; \ diff --git a/src/or/main.c b/src/or/main.c index c340e4128b..fb372ad1ca 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -60,7 +60,6 @@ #include "circuitlist.h" #include "circuituse.h" #include "command.h" -#include "compat_rust.h" #include "compress.h" #include "config.h" #include "confparse.h" @@ -106,6 +105,8 @@ #include "shared_random.h" #include "statefile.h" #include "status.h" +#include "tor_api.h" +#include "tor_api_internal.h" #include "util_process.h" #include "ext_orport.h" #ifdef USE_DMALLOC @@ -128,6 +129,12 @@ void evdns_shutdown(int); +#ifdef HAVE_RUST +// helper function defined in Rust to output a log message indicating if tor is +// running with Rust enabled. See src/rust/tor_util +char *rust_welcome_string(void); +#endif + /********* PROTOTYPES **********/ static void dumpmemusage(int severity); @@ -140,6 +147,8 @@ static void connection_start_reading_from_linked_conn(connection_t *conn); static int connection_should_read_from_linked_conn(connection_t *conn); static int run_main_loop_until_done(void); static void process_signal(int sig); +static void shutdown_did_not_work_callback(evutil_socket_t fd, short event, + void *arg) ATTR_NORETURN; /********* START VARIABLES **********/ int global_read_bucket; /**< Max number of bytes I can read this second. */ @@ -170,6 +179,12 @@ static uint64_t stats_n_bytes_written = 0; time_t time_of_process_start = 0; /** How many seconds have we been running? */ long stats_n_seconds_working = 0; +/** How many times have we returned from the main loop successfully? */ +static uint64_t stats_n_main_loop_successes = 0; +/** How many times have we received an error from the main loop? */ +static uint64_t stats_n_main_loop_errors = 0; +/** How many times have we returned from the main loop with no events. */ +static uint64_t stats_n_main_loop_idle = 0; /** How often will we honor SIGNEWNYM requests? */ #define MAX_SIGNEWNYM_RATE 10 @@ -192,6 +207,14 @@ static smartlist_t *active_linked_connection_lst = NULL; * <b>loop_once</b>. If so, there's no need to trigger a loopexit in order * to handle linked connections. */ static int called_loop_once = 0; +/** Flag: if true, it's time to shut down, so the main loop should exit as + * soon as possible. + */ +static int main_loop_should_exit = 0; +/** The return value that the main loop should yield when it exits, if + * main_loop_should_exit is true. + */ +static int main_loop_exit_value = 0; /** We set this to 1 when we've opened a circuit, so we can print a log * entry to inform the user that Tor is working. We set it to 0 when @@ -476,6 +499,57 @@ connection_is_reading(connection_t *conn) (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); } +/** Reset our main loop counters. */ +void +reset_main_loop_counters(void) +{ + stats_n_main_loop_successes = 0; + stats_n_main_loop_errors = 0; + stats_n_main_loop_idle = 0; +} + +/** Increment the main loop success counter. */ +static void +increment_main_loop_success_count(void) +{ + ++stats_n_main_loop_successes; +} + +/** Get the main loop success counter. */ +uint64_t +get_main_loop_success_count(void) +{ + return stats_n_main_loop_successes; +} + +/** Increment the main loop error counter. */ +static void +increment_main_loop_error_count(void) +{ + ++stats_n_main_loop_errors; +} + +/** Get the main loop error counter. */ +uint64_t +get_main_loop_error_count(void) +{ + return stats_n_main_loop_errors; +} + +/** Increment the main loop idle counter. */ +static void +increment_main_loop_idle_count(void) +{ + ++stats_n_main_loop_idle; +} + +/** Get the main loop idle counter. */ +uint64_t +get_main_loop_idle_count(void) +{ + return stats_n_main_loop_idle; +} + /** Check whether <b>conn</b> is correct in having (or not having) a * read/write event (passed in <b>ev</b>). On success, return 0. On failure, * log a warning and return -1. */ @@ -637,9 +711,10 @@ connection_should_read_from_linked_conn(connection_t *conn) /** If we called event_base_loop() and told it to never stop until it * runs out of events, now we've changed our mind: tell it we want it to - * finish. */ + * exit once the current round of callbacks is done, so that we can + * run external code, and then return to the main loop. */ void -tell_event_loop_to_finish(void) +tell_event_loop_to_run_external_code(void) { if (!called_loop_once) { struct timeval tv = { 0, 0 }; @@ -648,6 +723,72 @@ tell_event_loop_to_finish(void) } } +/** Event to run 'shutdown did not work callback'. */ +static struct event *shutdown_did_not_work_event = NULL; + +/** Failsafe measure that should never actually be necessary: If + * tor_shutdown_event_loop_and_exit() somehow doesn't successfully exit the + * event loop, then this callback will kill Tor with an assertion failure + * seconds later + */ +static void +shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg) +{ + // LCOV_EXCL_START + (void) fd; + (void) event; + (void) arg; + tor_assert_unreached(); + // LCOV_EXCL_STOP +} + +#ifdef ENABLE_RESTART_DEBUGGING +static struct event *tor_shutdown_event_loop_for_restart_event = NULL; +static void +tor_shutdown_event_loop_for_restart_cb( + evutil_socket_t fd, short event, void *arg) +{ + (void)fd; + (void)event; + (void)arg; + tor_event_free(tor_shutdown_event_loop_for_restart_event); + tor_shutdown_event_loop_and_exit(0); +} +#endif + +/** + * After finishing the current callback (if any), shut down the main loop, + * clean up the process, and exit with <b>exitcode</b>. + */ +void +tor_shutdown_event_loop_and_exit(int exitcode) +{ + if (main_loop_should_exit) + return; /* Ignore multiple calls to this function. */ + + main_loop_should_exit = 1; + main_loop_exit_value = exitcode; + + /* Die with an assertion failure in ten seconds, if for some reason we don't + * exit normally. */ + /* XXXX We should consider this code if it's never used. */ + struct timeval ten_seconds = { 10, 0 }; + shutdown_did_not_work_event = tor_evtimer_new( + tor_libevent_get_base(), + shutdown_did_not_work_callback, NULL); + event_add(shutdown_did_not_work_event, &ten_seconds); + + /* Unlike loopexit, loopbreak prevents other callbacks from running. */ + tor_event_base_loopbreak(tor_libevent_get_base()); +} + +/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */ +int +tor_event_loop_shutdown_is_pending(void) +{ + return main_loop_should_exit; +} + /** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from * its linked connection, if it is not doing so already. Called by * connection_start_reading and connection_start_writing as appropriate. */ @@ -663,7 +804,7 @@ connection_start_reading_from_linked_conn(connection_t *conn) /* make sure that the event_base_loop() function exits at * the end of its run through the current connections, so we can * activate read events for linked connections. */ - tell_event_loop_to_finish(); + tell_event_loop_to_run_external_code(); } else { tor_assert(smartlist_contains(active_linked_connection_lst, conn)); } @@ -1055,7 +1196,7 @@ run_connection_housekeeping(int i, time_t now) if (!connection_speaks_cells(conn)) return; /* we're all done here, the rest is just for OR conns */ - /* If we haven't written to an OR connection for a while, then either nuke + /* If we haven't flushed to an OR connection for a while, then either nuke the connection or send a keepalive, depending. */ or_conn = TO_OR_CONN(conn); @@ -1275,6 +1416,9 @@ find_periodic_event(const char *name) return NULL; } +/** Event to run initialize_periodic_events_cb */ +static struct event *initialize_periodic_events_event = NULL; + /** Helper, run one second after setup: * Initializes all members of periodic_events and starts them running. * @@ -1286,6 +1430,7 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) (void) fd; (void) events; (void) data; + tor_event_free(initialize_periodic_events_event); int i; for (i = 0; periodic_events[i].name; ++i) { periodic_event_launch(&periodic_events[i]); @@ -1314,9 +1459,10 @@ initialize_periodic_events(void) NAMED_CALLBACK(check_dns_honesty); struct timeval one_second = { 1, 0 }; - event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT, - initialize_periodic_events_cb, NULL, - &one_second); + initialize_periodic_events_event = tor_evtimer_new( + tor_libevent_get_base(), + initialize_periodic_events_cb, NULL); + event_add(initialize_periodic_events_event, &one_second); } STATIC void @@ -1405,7 +1551,9 @@ run_scheduled_events(time_t now) /* Maybe enough time elapsed for us to reconsider a circuit. */ circuit_upgrade_circuits_from_guard_wait(); - if (options->UseBridges && !options->DisableNetwork) { + if (options->UseBridges && !net_is_disabled()) { + /* Note: this check uses net_is_disabled(), not should_delay_dir_fetches() + * -- the latter is only for fetching consensus-derived directory info. */ fetch_bridge_descriptors(options, now); } @@ -1511,7 +1659,7 @@ rotate_onion_key_callback(time_t now, const or_options_t *options) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode() && !options->DisableNetwork) + if (advertised_server_mode() && !net_is_disabled()) router_upload_dir_desc_to_dirservers(0); return ONION_KEY_CONSENSUS_CHECK_INTERVAL; } @@ -1554,8 +1702,7 @@ check_ed_keys_callback(time_t now, const or_options_t *options) if (new_signing_key < 0 || generate_ed_link_cert(options, now, new_signing_key > 0)) { log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); } } return 30; @@ -1877,9 +2024,11 @@ check_descriptor_callback(time_t now, const or_options_t *options) * address has changed. */ #define CHECK_DESCRIPTOR_INTERVAL (60) + (void)options; + /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (!options->DisableNetwork) { + if (!net_is_disabled()) { check_descriptor_bandwidth_changed(now); check_descriptor_ipaddress_changed(now); mark_my_descriptor_dirty_if_too_old(now); @@ -1911,7 +2060,7 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options) * 20 minutes of our uptime. */ if (server_mode(options) && (have_completed_a_circuit() || !any_predicted_circuits(now)) && - !we_are_hibernating()) { + !net_is_disabled()) { if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { consider_testing_reachability(1, dirport_reachability_count==0); if (++dirport_reachability_count > 5) @@ -2366,10 +2515,18 @@ do_hup(void) /* first, reload config variables, in case they've changed */ if (options->ReloadTorrcOnSIGHUP) { /* no need to provide argc/v, they've been cached in init_from_config */ - if (options_init_from_torrc(0, NULL) < 0) { + int init_rv = options_init_from_torrc(0, NULL); + if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above. " "For usage, try -h."); return -1; + } else if (BUG(init_rv > 0)) { + // LCOV_EXCL_START + /* This should be impossible: the only "return 1" cases in + * options_init_from_torrc are ones caused by command-line arguments; + * but they can't change while Tor is running. */ + return -1; + // LCOV_EXCL_STOP } options = get_options(); /* they have changed now */ /* Logs are only truncated the first time they are opened, but were @@ -2405,7 +2562,7 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - if (!options->DisableNetwork) + if (!net_is_disabled()) update_networkstatus_downloads(time(NULL)); /* We'll retry routerstatus downloads in about 10 seconds; no need to @@ -2595,6 +2752,34 @@ do_main_loop(void) } #endif /* defined(HAVE_SYSTEMD) */ + main_loop_should_exit = 0; + main_loop_exit_value = 0; + +#ifdef ENABLE_RESTART_DEBUGGING + { + static int first_time = 1; + + if (first_time && getenv("TOR_DEBUG_RESTART")) { + first_time = 0; + const char *sec_str = getenv("TOR_DEBUG_RESTART_AFTER_SECONDS"); + long sec; + int sec_ok=0; + if (sec_str && + (sec = tor_parse_long(sec_str, 10, 0, INT_MAX, &sec_ok, NULL)) && + sec_ok) { + /* Okay, we parsed the seconds. */ + } else { + sec = 5; + } + struct timeval restart_after = { (time_t) sec, 0 }; + tor_shutdown_event_loop_for_restart_event = + tor_evtimer_new(tor_libevent_get_base(), + tor_shutdown_event_loop_for_restart_cb, NULL); + event_add(tor_shutdown_event_loop_for_restart_event, &restart_after); + } + } +#endif + return run_main_loop_until_done(); } @@ -2610,6 +2795,9 @@ run_main_loop_once(void) if (nt_service_is_stopping()) return 0; + if (main_loop_should_exit) + return 0; + #ifndef _WIN32 /* Make it easier to tell whether libevent failure is our fault or not. */ errno = 0; @@ -2619,7 +2807,14 @@ run_main_loop_once(void) * so that libevent knows to run their callbacks. */ SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, event_active(conn->read_event, EV_READ, 1)); - called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + + if (get_options()->MainloopStats) { + /* We always enforce that EVLOOP_ONCE is passed to event_base_loop() if we + * are collecting main loop statistics. */ + called_loop_once = 1; + } else { + called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + } /* Make sure we know (about) what time it is. */ update_approx_time(time(NULL)); @@ -2631,6 +2826,21 @@ run_main_loop_once(void) loop_result = event_base_loop(tor_libevent_get_base(), called_loop_once ? EVLOOP_ONCE : 0); + if (get_options()->MainloopStats) { + /* Update our main loop counters. */ + if (loop_result == 0) { + // The call was succesful. + increment_main_loop_success_count(); + } else if (loop_result == -1) { + // The call was erroneous. + increment_main_loop_error_count(); + } else if (loop_result == 1) { + // The call didn't have any active or pending events + // to handle. + increment_main_loop_idle_count(); + } + } + /* Oh, the loop failed. That might be an error that we need to * catch, but more likely, it's just an interrupted poll() call or something, * and we should try again. */ @@ -2656,6 +2866,9 @@ run_main_loop_once(void) } } + if (main_loop_should_exit) + return 0; + /* And here is where we put callbacks that happen "every time the event loop * runs." They must be very fast, or else the whole Tor process will get * slowed down. @@ -2684,7 +2897,11 @@ run_main_loop_until_done(void) do { loop_result = run_main_loop_once(); } while (loop_result == 1); - return loop_result; + + if (main_loop_should_exit) + return main_loop_exit_value; + else + return loop_result; } /** Libevent callback: invoked when we get a signal. @@ -2708,14 +2925,13 @@ process_signal(int sig) { case SIGTERM: log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); break; case SIGINT: if (!server_mode(get_options())) { /* do it now */ log_notice(LD_GENERAL,"Interrupt: exiting cleanly."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); + return; } #ifdef HAVE_SYSTEMD sd_notify(0, "STOPPING=1"); @@ -2744,8 +2960,8 @@ process_signal(int sig) #endif if (do_hup() < 0) { log_warn(LD_CONFIG,"Restart failed (config error?). Exiting."); - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); + return; } #ifdef HAVE_SYSTEMD sd_notify(0, "READY=1"); @@ -3019,7 +3235,8 @@ activate_signal(int signal_num) } } -/** Main entry point for the Tor command-line client. +/** Main entry point for the Tor command-line client. Return 0 on "success", + * negative on "failure", and positive on "success and exit". */ int tor_init(int argc, char *argv[]) @@ -3111,14 +3328,13 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } - { - rust_str_t rust_str = rust_welcome_string(); - const char *s = rust_str_get(rust_str); - if (strlen(s) > 0) { - log_notice(LD_GENERAL, "%s", s); - } - rust_str_free(rust_str); +#ifdef HAVE_RUST + char *rust_str = rust_welcome_string(); + if (rust_str != NULL && strlen(rust_str) > 0) { + log_notice(LD_GENERAL, "%s", rust_str); } + tor_free(rust_str); +#endif /* defined(HAVE_RUST) */ if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); @@ -3126,9 +3342,14 @@ tor_init(int argc, char *argv[]) } atexit(exit_function); - if (options_init_from_torrc(argc,argv) < 0) { + int init_rv = options_init_from_torrc(argc,argv); + if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above."); return -1; + } else if (init_rv > 0) { + // We succeeded, and should exit anyway -- probably the user just said + // "--version" or something like that. + return 1; } /* The options are now initialised */ @@ -3180,7 +3401,7 @@ try_locking(const or_options_t *options, int err_if_locked) if (lockfile) return 0; else { - char *fname = options_get_datadir_fname2_suffix(options, "lock",NULL,NULL); + char *fname = options_get_datadir_fname(options, "lock"); int already_locked = 0; tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked); tor_free(fname); @@ -3198,7 +3419,7 @@ try_locking(const or_options_t *options, int err_if_locked) r = try_locking(options, 0); if (r<0) { log_err(LD_GENERAL, "No, it's still there. Exiting."); - exit(1); + return -1; } return r; } @@ -3290,10 +3511,13 @@ tor_free_all(int postfork) periodic_timer_free(second_timer); teardown_periodic_events(); periodic_timer_free(refill_timer); + tor_event_free(shutdown_did_not_work_event); + tor_event_free(initialize_periodic_events_event); if (!postfork) { release_lockfile(); } + tor_libevent_free_all(); /* Stuff in util.c and address.c*/ if (!postfork) { escaped(NULL); @@ -3303,6 +3527,19 @@ tor_free_all(int postfork) } } +/** + * Remove the specified file, and log a warning if the operation fails for + * any reason other than the file not existing. Ignores NULL filenames. + */ +void +tor_remove_file(const char *filename) +{ + if (filename && tor_unlink(filename) != 0 && errno != ENOENT) { + log_warn(LD_FS, "Couldn't unlink %s: %s", + filename, strerror(errno)); + } +} + /** Do whatever cleanup is necessary before shutting Tor down. */ void tor_cleanup(void) @@ -3312,18 +3549,20 @@ tor_cleanup(void) time_t now = time(NULL); /* Remove our pid file. We don't care if there was an error when we * unlink, nothing we could do about it anyways. */ - if (options->PidFile) { - if (unlink(options->PidFile) != 0) { - log_warn(LD_FS, "Couldn't unlink pid file %s: %s", - options->PidFile, strerror(errno)); - } + tor_remove_file(options->PidFile); + /* Remove control port file */ + tor_remove_file(options->ControlPortWriteToFile); + /* Remove cookie authentication file */ + { + char *cookie_fname = get_controller_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); } - if (options->ControlPortWriteToFile) { - if (unlink(options->ControlPortWriteToFile) != 0) { - log_warn(LD_FS, "Couldn't unlink control port file %s: %s", - options->ControlPortWriteToFile, - strerror(errno)); - } + /* Remove Extended ORPort cookie authentication file */ + { + char *cookie_fname = get_ext_or_auth_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); } if (accounting_is_enabled(options)) accounting_record_bandwidth_usage(now, get_or_state()); @@ -3456,7 +3695,7 @@ sandbox_init_filter(void) int i; sandbox_cfg_allow_openat_filename(&cfg, - get_datadir_fname("cached-status")); + get_cachedir_fname("cached-status")); #define OPEN(name) \ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name)) @@ -3477,21 +3716,38 @@ sandbox_init_filter(void) OPEN_DATADIR2(name, name2 suffix); \ } while (0) +#define OPEN_KEY_DIRECTORY() \ + sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory)) +#define OPEN_CACHEDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name)) +#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \ + OPEN_CACHEDIR(name); \ + OPEN_CACHEDIR(name suffix); \ + } while (0) +#define OPEN_KEYDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name)) +#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \ + OPEN_KEYDIR(name); \ + OPEN_KEYDIR(name suffix); \ + } while (0) + OPEN(options->DataDirectory); - OPEN_DATADIR("keys"); - OPEN_DATADIR_SUFFIX("cached-certs", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("unverified-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdesc-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdescs", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdescs.new", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-descriptors", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-descriptors.new", ".tmp"); - OPEN_DATADIR("cached-descriptors.tmp.tmp"); - OPEN_DATADIR_SUFFIX("cached-extrainfo", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp"); - OPEN_DATADIR("cached-extrainfo.tmp.tmp"); + OPEN_KEY_DIRECTORY(); + + OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + OPEN_CACHEDIR("cached-descriptors.tmp.tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + OPEN_CACHEDIR("cached-extrainfo.tmp.tmp"); + OPEN_DATADIR_SUFFIX("state", ".tmp"); OPEN_DATADIR_SUFFIX("sr-state", ".tmp"); OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); @@ -3521,6 +3777,10 @@ sandbox_init_filter(void) } } + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, { + OPEN(f); + }); + #define RENAME_SUFFIX(name, suffix) \ sandbox_cfg_allow_rename(&cfg, \ get_datadir_fname(name suffix), \ @@ -3531,20 +3791,31 @@ sandbox_init_filter(void) get_datadir_fname2(prefix, name suffix), \ get_datadir_fname2(prefix, name)) - RENAME_SUFFIX("cached-certs", ".tmp"); - RENAME_SUFFIX("cached-consensus", ".tmp"); - RENAME_SUFFIX("unverified-consensus", ".tmp"); - RENAME_SUFFIX("unverified-microdesc-consensus", ".tmp"); - RENAME_SUFFIX("cached-microdesc-consensus", ".tmp"); - RENAME_SUFFIX("cached-microdescs", ".tmp"); - RENAME_SUFFIX("cached-microdescs", ".new"); - RENAME_SUFFIX("cached-microdescs.new", ".tmp"); - RENAME_SUFFIX("cached-descriptors", ".tmp"); - RENAME_SUFFIX("cached-descriptors", ".new"); - RENAME_SUFFIX("cached-descriptors.new", ".tmp"); - RENAME_SUFFIX("cached-extrainfo", ".tmp"); - RENAME_SUFFIX("cached-extrainfo", ".new"); - RENAME_SUFFIX("cached-extrainfo.new", ".tmp"); +#define RENAME_CACHEDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_cachedir_fname(name suffix), \ + get_cachedir_fname(name)) + +#define RENAME_KEYDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_keydir_fname(name suffix), \ + get_keydir_fname(name)) + + RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + RENAME_SUFFIX("state", ".tmp"); RENAME_SUFFIX("sr-state", ".tmp"); RENAME_SUFFIX("unparseable-desc", ".tmp"); @@ -3556,14 +3827,21 @@ sandbox_init_filter(void) #define STAT_DATADIR(name) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name)) +#define STAT_CACHEDIR(name) \ + sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name)) + #define STAT_DATADIR2(name, name2) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2))) +#define STAT_KEY_DIRECTORY() \ + sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory)) + STAT_DATADIR(NULL); STAT_DATADIR("lock"); STAT_DATADIR("state"); STAT_DATADIR("router-stability"); - STAT_DATADIR("cached-extrainfo.new"); + + STAT_CACHEDIR("cached-extrainfo.new"); { smartlist_t *files = smartlist_new(); @@ -3628,22 +3906,20 @@ sandbox_init_filter(void) // orport if (server_mode(get_options())) { - OPEN_DATADIR2_SUFFIX("keys", "secret_id_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key_ntor", ".tmp"); - OPEN_DATADIR2("keys", "secret_id_key.old"); - OPEN_DATADIR2("keys", "secret_onion_key.old"); - OPEN_DATADIR2("keys", "secret_onion_key_ntor.old"); - - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted", - ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key_encrypted", - ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_public_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + OPEN_KEYDIR("secret_id_key.old"); + OPEN_KEYDIR("secret_onion_key.old"); + OPEN_KEYDIR("secret_onion_key_ntor.old"); + + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp"); @@ -3662,11 +3938,13 @@ sandbox_init_filter(void) OPEN("/etc/resolv.conf"); RENAME_SUFFIX("fingerprint", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key_ntor", ".tmp"); - RENAME_SUFFIX2("keys", "secret_id_key", ".tmp"); - RENAME_SUFFIX2("keys", "secret_id_key.old", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + + RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp"); + RENAME_SUFFIX2("stats", "bridge-stats", ".tmp"); RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp"); RENAME_SUFFIX2("stats", "entry-stats", ".tmp"); @@ -3677,20 +3955,20 @@ sandbox_init_filter(void) RENAME_SUFFIX("hashed-fingerprint", ".tmp"); RENAME_SUFFIX("router-stability", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); sandbox_cfg_allow_rename(&cfg, - get_datadir_fname2("keys", "secret_onion_key"), - get_datadir_fname2("keys", "secret_onion_key.old")); + get_keydir_fname("secret_onion_key"), + get_keydir_fname("secret_onion_key.old")); sandbox_cfg_allow_rename(&cfg, - get_datadir_fname2("keys", "secret_onion_key_ntor"), - get_datadir_fname2("keys", "secret_onion_key_ntor.old")); + get_keydir_fname("secret_onion_key_ntor"), + get_keydir_fname("secret_onion_key_ntor.old")); - STAT_DATADIR("keys"); + STAT_KEY_DIRECTORY(); OPEN_DATADIR("stats"); STAT_DATADIR("stats"); STAT_DATADIR2("stats", "dirreq-stats"); @@ -3703,14 +3981,16 @@ sandbox_init_filter(void) return cfg; } -/** Main entry point for the Tor process. Called from main(). */ -/* This function is distinct from main() only so we can link main.c into - * the unittest binary without conflicting with the unittests' main. */ +/* Main entry point for the Tor process. Called from tor_main(), and by + * anybody embedding Tor. */ int -tor_main(int argc, char *argv[]) +tor_run_main(const tor_main_configuration_t *tor_cfg) { int result = 0; + int argc = tor_cfg->argc; + char **argv = tor_cfg->argv; + #ifdef _WIN32 #ifndef HeapEnableTerminationOnCorruption #define HeapEnableTerminationOnCorruption 1 @@ -3754,8 +4034,13 @@ tor_main(int argc, char *argv[]) if (done) return result; } #endif /* defined(NT_SERVICE) */ - if (tor_init(argc, argv)<0) - return -1; + { + int init_rv = tor_init(argc, argv); + if (init_rv < 0) + return -1; + else if (init_rv > 0) + return 0; + } if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) { sandbox_cfg_t* cfg = sandbox_init_filter(); diff --git a/src/or/main.h b/src/or/main.h index 132ab12bbb..d8593b57d9 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -45,7 +45,9 @@ int connection_is_writing(connection_t *conn); MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); MOCK_DECL(void,connection_start_writing,(connection_t *conn)); -void tell_event_loop_to_finish(void); +void tell_event_loop_to_run_external_code(void); +void tor_shutdown_event_loop_and_exit(int exitcode); +int tor_event_loop_shutdown_is_pending(void); void connection_stop_reading_from_linked_conn(connection_t *conn); @@ -71,14 +73,19 @@ int try_locking(const or_options_t *options, int err_if_locked); int have_lockfile(void); void release_lockfile(void); +void tor_remove_file(const char *filename); + void tor_cleanup(void); void tor_free_all(int postfork); -int tor_main(int argc, char *argv[]); - int do_main_loop(void); int tor_init(int argc, char **argv); +void reset_main_loop_counters(void); +uint64_t get_main_loop_success_count(void); +uint64_t get_main_loop_error_count(void); +uint64_t get_main_loop_idle_count(void); + extern time_t time_of_process_start; extern long stats_n_seconds_working; extern int quiet_level; diff --git a/src/or/microdesc.c b/src/or/microdesc.c index fe327c6c82..d8a4660af1 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -238,8 +238,8 @@ get_microdesc_cache_noload(void) if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) { microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache)); HT_INIT(microdesc_map, &cache->map); - cache->cache_fname = get_datadir_fname("cached-microdescs"); - cache->journal_fname = get_datadir_fname("cached-microdescs.new"); + cache->cache_fname = get_cachedir_fname("cached-microdescs"); + cache->journal_fname = get_cachedir_fname("cached-microdescs.new"); the_microdesc_cache = cache; } return the_microdesc_cache; diff --git a/src/or/microdesc.h b/src/or/microdesc.h index 1be12156a4..83a90bd8ff 100644 --- a/src/or/microdesc.h +++ b/src/or/microdesc.h @@ -38,8 +38,10 @@ smartlist_t *microdesc_list_missing_digest256(networkstatus_t *ns, digest256map_t *skip); void microdesc_free_(microdesc_t *md, const char *fname, int line); -#define microdesc_free(md) \ - microdesc_free_((md), __FILE__, __LINE__) +#define microdesc_free(md) do { \ + microdesc_free_((md), __FILE__, __LINE__); \ + (md) = NULL; \ + } while (0) void microdesc_free_all(void); void update_microdesc_downloads(time_t now); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index e0a3e4cdc6..aa0a8d15c3 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -52,6 +52,7 @@ #include "dirserv.h" #include "dirvote.h" #include "entrynodes.h" +#include "hibernate.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -196,7 +197,7 @@ networkstatus_read_cached_consensus_impl(int flav, tor_snprintf(buf, sizeof(buf), "%s-%s-consensus", prefix, flavorname); } - char *filename = get_datadir_fname(buf); + char *filename = get_cachedir_fname(buf); char *result = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); return result; @@ -254,7 +255,7 @@ router_reload_consensus_networkstatus(void) /** Free all storage held by the vote_routerstatus object <b>rs</b>. */ void -vote_routerstatus_free(vote_routerstatus_t *rs) +vote_routerstatus_free_(vote_routerstatus_t *rs) { vote_microdesc_hash_t *h, *next; if (!rs) @@ -272,7 +273,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs) /** Free all storage held by the routerstatus object <b>rs</b>. */ void -routerstatus_free(routerstatus_t *rs) +routerstatus_free_(routerstatus_t *rs) { if (!rs) return; @@ -282,7 +283,7 @@ routerstatus_free(routerstatus_t *rs) /** Free all storage held in <b>sig</b> */ void -document_signature_free(document_signature_t *sig) +document_signature_free_(document_signature_t *sig) { tor_free(sig->signature); tor_free(sig); @@ -300,7 +301,7 @@ document_signature_dup(const document_signature_t *sig) /** Free all storage held in <b>ns</b>. */ void -networkstatus_vote_free(networkstatus_t *ns) +networkstatus_vote_free_(networkstatus_t *ns) { if (!ns) return; @@ -1208,6 +1209,14 @@ should_delay_dir_fetches(const or_options_t *options, const char **msg_out) return 1; } + if (we_are_hibernating()) { + if (msg_out) { + *msg_out = "We are hibernating or shutting down."; + } + log_info(LD_DIR, "Delaying dir fetches (Hibernating or shutting down)"); + return 1; + } + if (options->UseBridges) { /* If we know that none of our bridges can possibly work, avoid fetching * directory documents. But if some of them might work, try again. */ @@ -1500,6 +1509,32 @@ networkstatus_consensus_is_already_downloading(const char *resource) return answer; } +/* Does the current, reasonably live consensus have IPv6 addresses? + * Returns 1 if there is a reasonably live consensus and its consensus method + * includes IPv6 addresses in the consensus. + * Otherwise, if there is no consensus, or the method does not include IPv6 + * addresses, returns 0. */ +int +networkstatus_consensus_has_ipv6(const or_options_t* options) +{ + const networkstatus_t *cons = networkstatus_get_reasonably_live_consensus( + approx_time(), + usable_consensus_flavor()); + + /* If we have no consensus, we have no IPv6 in it */ + if (!cons) { + return 0; + } + + /* Different flavours of consensus gained IPv6 at different times */ + if (we_use_microdescriptors_for_circuits(options)) { + return + cons->consensus_method >= MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + } else { + return cons->consensus_method >= MIN_METHOD_FOR_A_LINES; + } +} + /** Given two router status entries for the same router identity, return 1 if * if the contents have changed between them. Otherwise, return 0. */ static int @@ -1673,7 +1708,7 @@ handle_missing_protocol_warning_impl(const networkstatus_t *c, } tor_free(protocol_warning); if (should_exit) - exit(1); + exit(1); // XXXX bad exit: should return from main. } /** Called when we have received a networkstatus <b>c</b>. If there are @@ -1722,7 +1757,7 @@ networkstatus_set_current_consensus(const char *consensus, { networkstatus_t *c=NULL; int r, result = -1; - time_t now = time(NULL); + time_t now = approx_time(); const or_options_t *options = get_options(); char *unverified_fname = NULL, *consensus_fname = NULL; int flav = networkstatus_parse_flavor_name(flavor); @@ -1787,15 +1822,15 @@ networkstatus_set_current_consensus(const char *consensus, } if (!strcmp(flavor, "ns")) { - consensus_fname = get_datadir_fname("cached-consensus"); - unverified_fname = get_datadir_fname("unverified-consensus"); + consensus_fname = get_cachedir_fname("cached-consensus"); + unverified_fname = get_cachedir_fname("unverified-consensus"); if (current_ns_consensus) { current_digests = ¤t_ns_consensus->digests; current_valid_after = current_ns_consensus->valid_after; } } else if (!strcmp(flavor, "microdesc")) { - consensus_fname = get_datadir_fname("cached-microdesc-consensus"); - unverified_fname = get_datadir_fname("unverified-microdesc-consensus"); + consensus_fname = get_cachedir_fname("cached-microdesc-consensus"); + unverified_fname = get_cachedir_fname("unverified-microdesc-consensus"); if (current_md_consensus) { current_digests = ¤t_md_consensus->digests; current_valid_after = current_md_consensus->valid_after; @@ -1804,9 +1839,9 @@ networkstatus_set_current_consensus(const char *consensus, cached_dir_t *cur; char buf[128]; tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor); - consensus_fname = get_datadir_fname(buf); + consensus_fname = get_cachedir_fname(buf); tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor); - unverified_fname = get_datadir_fname(buf); + unverified_fname = get_cachedir_fname(buf); cur = dirserv_get_consensus(flavor); if (cur) { current_digests = &cur->digests; @@ -2042,6 +2077,7 @@ networkstatus_note_certs_arrived(const char *source_dir) { int i; for (i=0; i<N_CONSENSUS_FLAVORS; ++i) { + const char *flavor_name = networkstatus_get_flavor_name(i); consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i]; if (!waiting->consensus) continue; @@ -2049,7 +2085,7 @@ networkstatus_note_certs_arrived(const char *source_dir) char *waiting_body = waiting->body; if (!networkstatus_set_current_consensus( waiting_body, - networkstatus_get_flavor_name(i), + flavor_name, NSSET_WAS_WAITING_FOR_CERTS, source_dir)) { tor_free(waiting_body); @@ -2202,7 +2238,9 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs) char * networkstatus_getinfo_helper_single(const routerstatus_t *rs) { - return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL); + return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, + ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, + NULL); } /** Alloc and return a string describing routerstatuses for the most @@ -2215,13 +2253,13 @@ networkstatus_getinfo_helper_single(const routerstatus_t *rs) char * networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) { - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + const time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; char *answer; routerlist_t *rl = router_get_routerlist(); smartlist_t *statuses; - uint8_t purpose = router_purpose_from_string(purpose_string); + const uint8_t purpose = router_purpose_from_string(purpose_string); routerstatus_t rs; - int bridge_auth = authdir_mode_bridge(get_options()); + const int bridge_auth = authdir_mode_bridge(get_options()); if (purpose == ROUTER_PURPOSE_UNKNOWN) { log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.", @@ -2238,6 +2276,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) continue; if (ri->purpose != purpose) continue; + /* TODO: modifying the running flag in a getinfo is a bad idea */ if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE) dirserv_set_router_is_running(ri, now); /* then generate and write out status lines for each of them */ @@ -2256,7 +2295,6 @@ void networkstatus_dump_bridge_status_to_file(time_t now) { char *status = networkstatus_getinfo_by_purpose("bridge", now); - const or_options_t *options = get_options(); char *fname = NULL; char *thresholds = NULL; char *published_thresholds_and_status = NULL; @@ -2278,8 +2316,7 @@ networkstatus_dump_bridge_status_to_file(time_t now) "published %s\nflag-thresholds %s\n%s%s", published, thresholds, fingerprint_line ? fingerprint_line : "", status); - tor_asprintf(&fname, "%s"PATH_SEPARATOR"networkstatus-bridges", - options->DataDirectory); + fname = get_datadir_fname("networkstatus-bridges"); write_str_to_file(fname,published_thresholds_and_status,0); tor_free(thresholds); tor_free(published_thresholds_and_status); diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 39a0f753d8..1851a55e82 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -18,8 +18,12 @@ void networkstatus_reset_warnings(void); void networkstatus_reset_download_failures(void); char *networkstatus_read_cached_consensus(const char *flavorname); int router_reload_consensus_networkstatus(void); -void routerstatus_free(routerstatus_t *rs); -void networkstatus_vote_free(networkstatus_t *ns); +void routerstatus_free_(routerstatus_t *rs); +#define routerstatus_free(rs) \ + FREE_AND_NULL(routerstatus_t, routerstatus_free_, (rs)) +void networkstatus_vote_free_(networkstatus_t *ns); +#define networkstatus_vote_free(ns) \ + FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns)) networkstatus_voter_info_t *networkstatus_get_voter_by_id( networkstatus_t *vote, const char *identity); @@ -89,6 +93,7 @@ int networkstatus_consensus_can_use_multiple_directories( MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,( const or_options_t *options)); int networkstatus_consensus_is_already_downloading(const char *resource); +int networkstatus_consensus_has_ipv6(const or_options_t* options); #define NSSET_FROM_CACHE 1 #define NSSET_WAS_WAITING_FOR_CERTS 2 @@ -124,12 +129,16 @@ int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight, int32_t default_val); const char *networkstatus_get_flavor_name(consensus_flavor_t flav); int networkstatus_parse_flavor_name(const char *flavname); -void document_signature_free(document_signature_t *sig); +void document_signature_free_(document_signature_t *sig); +#define document_signature_free(sig) \ + FREE_AND_NULL(document_signature_t, document_signature_free_, (sig)) document_signature_t *document_signature_dup(const document_signature_t *sig); void networkstatus_free_all(void); int networkstatus_get_weight_scale_param(networkstatus_t *ns); -void vote_routerstatus_free(vote_routerstatus_t *rs); +void vote_routerstatus_free_(vote_routerstatus_t *rs); +#define vote_routerstatus_free(rs) \ + FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs)) #ifdef NETWORKSTATUS_PRIVATE #ifdef TOR_UNIT_TESTS diff --git a/src/or/nodelist.c b/src/or/nodelist.c index f2e979be8b..2f039a9a52 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -65,7 +65,9 @@ #include <string.h> static void nodelist_drop_node(node_t *node, int remove_from_ht); -static void node_free(node_t *node); +#define node_free(val) \ + FREE_AND_NULL(node_t, node_free_, (val)) +static void node_free_(node_t *node); /** count_usable_descriptors counts descriptors with these flag(s) */ @@ -656,7 +658,7 @@ nodelist_find_nodes_with_microdesc(const microdesc_t *md) /** Release storage held by <b>node</b> */ static void -node_free(node_t *node) +node_free_(node_t *node) { if (!node) return; @@ -906,9 +908,12 @@ node_get_ed25519_id(const node_t *node) { const ed25519_public_key_t *ri_pk = NULL; const ed25519_public_key_t *md_pk = NULL; + if (node->ri) { if (node->ri->cache_info.signing_key_cert) { ri_pk = &node->ri->cache_info.signing_key_cert->signing_key; + /* Checking whether routerinfo ed25519 is all zero. + * Our descriptor parser should make sure this never happens. */ if (BUG(ed25519_public_key_is_zero(ri_pk))) ri_pk = NULL; } @@ -917,6 +922,10 @@ node_get_ed25519_id(const node_t *node) if (node->md) { if (node->md->ed25519_identity_pkey) { md_pk = node->md->ed25519_identity_pkey; + /* Checking whether microdesc ed25519 is all zero. + * Our descriptor parser should make sure this never happens. */ + if (BUG(ed25519_public_key_is_zero(md_pk))) + md_pk = NULL; } } @@ -951,23 +960,29 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) } /** Return true iff <b>node</b> supports authenticating itself - * by ed25519 ID during the link handshake in a way that we can understand - * when we probe it. */ + * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>, + * it needs to be using a link authentication method that we understand. + * If not, any plausible link authentication method will do. */ int -node_supports_ed25519_link_authentication(const node_t *node) +node_supports_ed25519_link_authentication(const node_t *node, + int compatible_with_us) { - /* XXXX Oh hm. What if some day in the future there are link handshake - * versions that aren't 3 but which are ed25519 */ if (! node_get_ed25519_id(node)) return 0; if (node->ri) { const char *protos = node->ri->protocol_list; if (protos == NULL) return 0; - return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3); + if (compatible_with_us) + return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3); + else + return protocol_list_supports_protocol_or_later(protos, PRT_LINKAUTH, 3); } if (node->rs) { - return node->rs->supports_ed25519_link_handshake; + if (compatible_with_us) + return node->rs->supports_ed25519_link_handshake_compat; + else + return node->rs->supports_ed25519_link_handshake_any; } tor_assert_nonfatal_unreached_once(); return 0; @@ -1622,15 +1637,21 @@ microdesc_has_curve25519_onion_key(const microdesc_t *md) int node_has_curve25519_onion_key(const node_t *node) { - if (!node) - return 0; + return node_get_curve25519_onion_key(node) != NULL; +} - if (node->ri) - return routerinfo_has_curve25519_onion_key(node->ri); - else if (node->md) - return microdesc_has_curve25519_onion_key(node->md); +/** Return the curve25519 key of <b>node</b>, or NULL if none. */ +const curve25519_public_key_t * +node_get_curve25519_onion_key(const node_t *node) +{ + if (!node) + return NULL; + if (routerinfo_has_curve25519_onion_key(node->ri)) + return node->ri->onion_curve25519_pkey; + else if (microdesc_has_curve25519_onion_key(node->md)) + return node->md->onion_curve25519_pkey; else - return 0; + return NULL; } /** Refresh the country code of <b>ri</b>. This function MUST be called on diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 754990ac8d..e879b4e8ff 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -65,7 +65,8 @@ const smartlist_t *node_get_declared_family(const node_t *node); const ed25519_public_key_t *node_get_ed25519_id(const node_t *node); int node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id); -int node_supports_ed25519_link_authentication(const node_t *node); +int node_supports_ed25519_link_authentication(const node_t *node, + int compatible_with_us); int node_supports_v3_hsdir(const node_t *node); int node_supports_ed25519_hs_intro(const node_t *node); int node_supports_v3_rendezvous_point(const node_t *node); @@ -85,6 +86,8 @@ int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out); int node_has_curve25519_onion_key(const node_t *node); +const curve25519_public_key_t *node_get_curve25519_onion_key( + const node_t *node); MOCK_DECL(smartlist_t *, nodelist_get_list, (void)); diff --git a/src/or/ntmain.c b/src/or/ntmain.c index 508e5844eb..ebbe0018bd 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -195,7 +195,7 @@ nt_service_loadlibrary(void) return; err: printf("Unable to load library support for NT services: exiting.\n"); - exit(1); + exit(1); // exit ok: ntmain can't read libraries } /** If we're compiled to run as an NT service, and the service wants to @@ -318,7 +318,7 @@ nt_service_main(void) printf("Service error %d : %s\n", (int) result, errmsg); tor_free(errmsg); if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { - if (tor_init(backup_argc, backup_argv) < 0) + if (tor_init(backup_argc, backup_argv)) return; switch (get_options()->command) { case CMD_RUN_TOR: diff --git a/src/or/onion.c b/src/or/onion.c index 7e1e89df1b..bd80c2f503 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -423,7 +423,7 @@ server_onion_keys_new(void) /** Release all storage held in <b>keys</b>. */ void -server_onion_keys_free(server_onion_keys_t *keys) +server_onion_keys_free_(server_onion_keys_t *keys) { if (! keys) return; diff --git a/src/or/onion.h b/src/or/onion.h index 95544dfac1..3b738debeb 100644 --- a/src/or/onion.h +++ b/src/or/onion.h @@ -31,7 +31,9 @@ typedef struct server_onion_keys_t { #define MAX_ONIONSKIN_REPLY_LEN 255 server_onion_keys_t *server_onion_keys_new(void); -void server_onion_keys_free(server_onion_keys_t *keys); +void server_onion_keys_free_(server_onion_keys_t *keys); +#define server_onion_keys_free(keys) \ + FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys)) void onion_handshake_state_release(onion_handshake_state_t *state); diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c index 146943a273..56c954829e 100644 --- a/src/or/onion_fast.c +++ b/src/or/onion_fast.c @@ -32,7 +32,7 @@ /** Release all state held in <b>victim</b>. */ void -fast_handshake_state_free(fast_handshake_state_t *victim) +fast_handshake_state_free_(fast_handshake_state_t *victim) { if (! victim) return; diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h index 3a5aefea3f..c56712e2c2 100644 --- a/src/or/onion_fast.h +++ b/src/or/onion_fast.h @@ -19,7 +19,9 @@ typedef struct fast_handshake_state_t { uint8_t state[DIGEST_LEN]; } fast_handshake_state_t; -void fast_handshake_state_free(fast_handshake_state_t *victim); +void fast_handshake_state_free_(fast_handshake_state_t *victim); +#define fast_handshake_state_free(st) \ + FREE_AND_NULL(fast_handshake_state_t, fast_handshake_state_free_, (st)) int fast_onionskin_create(fast_handshake_state_t **handshake_state_out, uint8_t *handshake_out); diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c index 902260b54b..b167cb61fb 100644 --- a/src/or/onion_ntor.c +++ b/src/or/onion_ntor.c @@ -28,7 +28,7 @@ /** Free storage held in an ntor handshake state. */ void -ntor_handshake_state_free(ntor_handshake_state_t *state) +ntor_handshake_state_free_(ntor_handshake_state_t *state) { if (!state) return; diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h index 02dea2dfc1..f7c962b7d0 100644 --- a/src/or/onion_ntor.h +++ b/src/or/onion_ntor.h @@ -17,7 +17,9 @@ typedef struct ntor_handshake_state_t ntor_handshake_state_t; /** Length of an ntor reply, as sent from server to client. */ #define NTOR_REPLY_LEN 64 -void ntor_handshake_state_free(ntor_handshake_state_t *state); +void ntor_handshake_state_free_(ntor_handshake_state_t *state); +#define ntor_handshake_state_free(state) \ + FREE_AND_NULL(ntor_handshake_state_t, ntor_handshake_state_free_, (state)) int onion_skin_ntor_create(const uint8_t *router_id, const curve25519_public_key_t *router_key, diff --git a/src/or/or.h b/src/or/or.h index 03e20b80de..2617d2d87d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1166,8 +1166,8 @@ typedef struct packed_cell_t { /** Next cell queued on this circuit. */ TOR_SIMPLEQ_ENTRY(packed_cell_t) next; char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */ - uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high - * bits truncated) when this cell was inserted. */ + uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell + * was inserted */ } packed_cell_t; /** A queue of cells on a circuit, waiting to be added to the @@ -1182,7 +1182,8 @@ typedef struct cell_queue_t { typedef struct destroy_cell_t { TOR_SIMPLEQ_ENTRY(destroy_cell_t) next; circid_t circid; - uint32_t inserted_time; /** Timestamp when this was queued. */ + uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell + * was inserted */ uint8_t reason; } destroy_cell_t; @@ -2249,18 +2250,18 @@ typedef struct { * uploaded it. */ #define ROUTER_PURPOSE_GENERAL 0 /** Tor should avoid using this router for circuit-building: we got it - * from a crontroller. If the controller wants to use it, it'll have to + * from a controller. If the controller wants to use it, it'll have to * ask for it by identity. */ #define ROUTER_PURPOSE_CONTROLLER 1 /** Tor should use this router only for bridge positions in circuits: we got * it via a directory request from the bridge itself, or a bridge - * authority. x*/ + * authority. */ #define ROUTER_PURPOSE_BRIDGE 2 /** Tor should not use this router; it was marked in cached-descriptors with * a purpose we didn't recognize. */ #define ROUTER_PURPOSE_UNKNOWN 255 - /* In what way did we find out about this router? One of ROUTER_PURPOSE_*. + /** In what way did we find out about this router? One of ROUTER_PURPOSE_*. * Routers of different purposes are kept segregated and used for different * things; see notes on ROUTER_PURPOSE_* macros above. */ @@ -2331,8 +2332,12 @@ typedef struct routerstatus_t { unsigned int supports_extend2_cells:1; /** True iff this router has a protocol list that allows it to negotiate - * ed25519 identity keys on a link handshake. */ - unsigned int supports_ed25519_link_handshake:1; + * ed25519 identity keys on a link handshake with us. */ + unsigned int supports_ed25519_link_handshake_compat:1; + + /** True iff this router has a protocol list that allows it to negotiate + * ed25519 identity keys on a link handshake, at all. */ + unsigned int supports_ed25519_link_handshake_any:1; /** True iff this router has a protocol list that allows it to be an * introduction point supporting ed25519 authentication key which is part of @@ -3650,10 +3655,24 @@ typedef struct { int TruncateLogFile; /**< Boolean: Should we truncate the log file before we start writing? */ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */ + char *AndroidIdentityTag; /**< Identity tag to add for Android logging. */ char *DebugLogFile; /**< Where to send verbose log messages. */ - char *DataDirectory; /**< OR only: where to store long-term data. */ + char *DataDirectory_option; /**< Where to store long-term data, as + * configured by the user. */ + char *DataDirectory; /**< Where to store long-term data, as modified. */ int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */ + + char *KeyDirectory_option; /**< Where to store keys, as + * configured by the user. */ + char *KeyDirectory; /**< Where to store keys data, as modified. */ + int KeyDirectoryGroupReadable; /**< Boolean: Is the KeyDirectory g+r? */ + + char *CacheDirectory_option; /**< Where to store cached data, as + * configured by the user. */ + char *CacheDirectory; /**< Where to store cached data, as modified. */ + int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */ + char *Nickname; /**< OR only: nickname of this onion router. */ char *Address; /**< OR only: configured address for this onion router. */ char *PidFile; /**< Where to store PID of Tor process. */ @@ -3689,6 +3708,7 @@ typedef struct { * interface addresses? * Includes OutboundBindAddresses and * configured ports. */ + int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */ config_line_t *SocksPolicy; /**< Lists of socks policy components */ config_line_t *DirPolicy; /**< Lists of dir policy components */ /** Local address to bind outbound sockets */ @@ -3968,6 +3988,8 @@ typedef struct { int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds * have passed. */ + int MainloopStats; /**< Log main loop statistics as part of the + * heartbeat messages. */ char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */ tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */ @@ -4085,6 +4107,8 @@ typedef struct { /** Process specifier for a controller that ‘owns’ this Tor * instance. Tor will terminate if its owning controller does. */ char *OwningControllerProcess; + /** FD specifier for a controller that owns this Tor instance. */ + int OwningControllerFD; int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how * long do we wait before exiting? */ @@ -4642,6 +4666,9 @@ typedef struct { smartlist_t *Schedulers; /* An ordered list of scheduler_types mapped from Schedulers. */ smartlist_t *SchedulerTypes_; + + /** List of files that were opened by %include in torrc and torrc-defaults */ + smartlist_t *FilesOpenedByIncludes; } or_options_t; #define LOG_PROTOCOL_WARN (get_protocol_warning_severity_level()) diff --git a/src/or/policies.c b/src/or/policies.c index 3bfea3a57c..e14b33c8e0 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -18,6 +18,7 @@ #define POLICIES_PRIVATE #include "or.h" +#include "bridges.h" #include "config.h" #include "dirserv.h" #include "microdesc.h" @@ -81,7 +82,8 @@ static int policies_parse_exit_policy_internal( const smartlist_t *configured_addresses, int reject_interface_addresses, int reject_configured_port_addresses, - int add_default_policy); + int add_default_policy, + int add_reduced_policy); /** Replace all "private" entries in *<b>policy</b> with their expanded * equivalents. */ @@ -894,9 +896,10 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, pref_ipv6, ap); } -/* The microdescriptor consensus has no IPv6 addresses in rs: they are in - * the microdescriptors. This means we can't rely on the node's IPv6 address - * until its microdescriptor is available (when using microdescs). +/* Some microdescriptor consensus methods have no IPv6 addresses in rs: they + * are in the microdescriptors. For these consensus methods, we can't rely on + * the node's IPv6 address until its microdescriptor is available (when using + * microdescs). * But for bridges, rewrite_node_address_for_bridge() updates node->ri with * the configured address, so we can trust bridge addresses. * (Bridges could gain an IPv6 address if their microdescriptor arrives, but @@ -914,11 +917,26 @@ node_awaiting_ipv6(const or_options_t* options, const node_t *node) return 0; } + /* If the node has an IPv6 address, we're not waiting */ + if (node_has_ipv6_addr(node)) { + return 0; + } + + /* If the current consensus method and flavour has IPv6 addresses, we're not + * waiting */ + if (networkstatus_consensus_has_ipv6(options)) { + return 0; + } + + /* Bridge clients never use the address from a bridge's md, so there's no + * need to wait for it. */ + if (node_is_a_configured_bridge(node)) { + return 0; + } + /* We are waiting if we_use_microdescriptors_for_circuits() and we have no - * md. Bridges have a ri based on their config. They would never use the - * address from their md, so there's no need to wait for it. */ - return (!node->md && we_use_microdescriptors_for_circuits(options) && - !node->ri); + * md. */ + return (!node->md && we_use_microdescriptors_for_circuits(options)); } /** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>. @@ -1146,7 +1164,7 @@ validate_addr_policies(const or_options_t *options, char **msg) "to 1 to disable this warning, and for forward compatibility.", options->ExitPolicy == NULL ? " with the default exit policy" : ""); - if (options->ExitPolicy == NULL) { + if (options->ExitPolicy == NULL && options->ReducedExitPolicy == 0) { log_warn(LD_CONFIG, "In a future version of Tor, ExitRelay 0 may become the " "default when no ExitPolicy is given."); @@ -1793,14 +1811,14 @@ policies_parse_exit_policy_reject_private( /* Reject public IPv4 addresses on any interface */ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0); addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0); - free_interface_address6_list(public_addresses); + interface_address6_list_free(public_addresses); /* Don't look for IPv6 addresses if we're configured as IPv4-only */ if (ipv6_exit) { /* Reject public IPv6 addresses on any interface */ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0); addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1); - free_interface_address6_list(public_addresses); + interface_address6_list_free(public_addresses); } } @@ -1879,6 +1897,24 @@ policies_log_first_redundant_entry(const smartlist_t *policy) "reject *:563,reject *:1214,reject *:4661-4666," \ "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*" +#define REDUCED_EXIT_POLICY \ + "accept *:20-23,accept *:43,accept *:53,accept *:79-81,accept *:88," \ + "accept *:110,accept *:143,accept *:194,accept *:220,accept *:389," \ + "accept *:443,accept *:464,accept *:465,accept *:531,accept *:543-544," \ + "accept *:554,accept *:563,accept *:587,accept *:636,accept *:706," \ + "accept *:749,accept *:873,accept *:902-904,accept *:981,accept *:989-995," \ + "accept *:1194,accept *:1220,accept *:1293,accept *:1500,accept *:1533," \ + "accept *:1677,accept *:1723,accept *:1755,accept *:1863," \ + "accept *:2082-2083,accept *:2086-2087,accept *:2095-2096," \ + "accept *:2102-2104,accept *:3128,accept *:3389,accept *:3690," \ + "accept *:4321,accept *:4643,accept *:5050,accept *:5190," \ + "accept *:5222-5223,accept *:5228,accept *:5900,accept *:6660-6669," \ + "accept *:6679,accept *:6697,accept *:8000,accept *:8008,accept *:8074," \ + "accept *:8080,accept *:8082,accept *:8087-8088,accept *:8232-8233," \ + "accept *:8332-8333,accept *:8443,accept *:8888,accept *:9418," \ + "accept *:9999,accept *:10000,accept *:11371,accept *:19294," \ + "accept *:19638,accept *:50002,accept *:64738,reject *:*" + /** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. * * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy. @@ -1914,7 +1950,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, const smartlist_t *configured_addresses, int reject_interface_addresses, int reject_configured_port_addresses, - int add_default_policy) + int add_default_policy, + int add_reduced_policy) { if (!ipv6_exit) { append_exit_policy_string(dest, "reject *6:*"); @@ -1940,7 +1977,9 @@ policies_parse_exit_policy_internal(config_line_t *cfg, * effect, and are most likely an error. */ policies_log_first_redundant_entry(*dest); - if (add_default_policy) { + if (add_reduced_policy) { + append_exit_policy_string(dest, REDUCED_EXIT_POLICY); + } else if (add_default_policy) { append_exit_policy_string(dest, DEFAULT_EXIT_POLICY); } else { append_exit_policy_string(dest, "reject *4:*"); @@ -1981,13 +2020,15 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0; int reject_local_interfaces = (options & EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0; + int add_reduced = (options & EXIT_POLICY_ADD_REDUCED) ? 1 : 0; return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled, reject_private, configured_addresses, reject_local_interfaces, reject_local_interfaces, - add_default); + add_default, + add_reduced); } /** Helper function that adds a copy of addr to a smartlist as long as it is @@ -2097,7 +2138,10 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options, } if (!or_options->BridgeRelay) { - parser_cfg |= EXIT_POLICY_ADD_DEFAULT; + if (or_options->ReducedExitPolicy) + parser_cfg |= EXIT_POLICY_ADD_REDUCED; + else + parser_cfg |= EXIT_POLICY_ADD_DEFAULT; } if (or_options->ExitPolicyRejectLocalInterfaces) { @@ -2769,7 +2813,7 @@ write_short_policy(const short_policy_t *policy) /** Release all storage held in <b>policy</b>. */ void -short_policy_free(short_policy_t *policy) +short_policy_free_(short_policy_t *policy) { tor_free(policy); } @@ -3019,7 +3063,7 @@ getinfo_helper_policies(control_connection_t *conn, /** Release all storage held by <b>p</b>. */ void -addr_policy_list_free(smartlist_t *lst) +addr_policy_list_free_(smartlist_t *lst) { if (!lst) return; @@ -3029,7 +3073,7 @@ addr_policy_list_free(smartlist_t *lst) /** Release all storage held by <b>p</b>. */ void -addr_policy_free(addr_policy_t *p) +addr_policy_free_(addr_policy_t *p) { if (!p) return; diff --git a/src/or/policies.h b/src/or/policies.h index 52ff4e2f99..35220a812f 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -22,7 +22,8 @@ #define EXIT_POLICY_REJECT_PRIVATE (1 << 1) #define EXIT_POLICY_ADD_DEFAULT (1 << 2) #define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3) -#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES +#define EXIT_POLICY_ADD_REDUCED (1 << 4) +#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_ADD_REDUCED /* All options set: used for unit testing */ #define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1) @@ -114,15 +115,21 @@ int getinfo_helper_policies(control_connection_t *conn, int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item, int format_for_desc); -void addr_policy_list_free(smartlist_t *p); -void addr_policy_free(addr_policy_t *p); +void addr_policy_list_free_(smartlist_t *p); +#define addr_policy_list_free(lst) \ + FREE_AND_NULL(smartlist_t, addr_policy_list_free_, (lst)) +void addr_policy_free_(addr_policy_t *p); +#define addr_policy_free(p) \ + FREE_AND_NULL(addr_policy_t, addr_policy_free_, (p)) void policies_free_all(void); char *policy_summarize(smartlist_t *policy, sa_family_t family); short_policy_t *parse_short_policy(const char *summary); char *write_short_policy(const short_policy_t *policy); -void short_policy_free(short_policy_t *policy); +void short_policy_free_(short_policy_t *policy); +#define short_policy_free(p) \ + FREE_AND_NULL(short_policy_t, short_policy_free_, (p)) int short_policy_is_reject_star(const short_policy_t *policy); addr_policy_result_t compare_tor_addr_to_short_policy( const tor_addr_t *addr, uint16_t port, diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c index 7649fcc4be..91633d02af 100644 --- a/src/or/proto_socks.c +++ b/src/or/proto_socks.c @@ -66,7 +66,7 @@ socks_request_new(void) /** Free all storage held in the socks_request_t <b>req</b>. */ void -socks_request_free(socks_request_t *req) +socks_request_free_(socks_request_t *req) { if (!req) return; diff --git a/src/or/proto_socks.h b/src/or/proto_socks.h index a714151414..02e0aca7e9 100644 --- a/src/or/proto_socks.h +++ b/src/or/proto_socks.h @@ -11,7 +11,9 @@ struct socks_request_t; struct buf_t; struct socks_request_t *socks_request_new(void); -void socks_request_free(struct socks_request_t *req); +void socks_request_free_(struct socks_request_t *req); +#define socks_request_free(req) \ + FREE_AND_NULL(socks_request_t, socks_request_free_, (req)) int fetch_from_buf_socks(struct buf_t *buf, socks_request_t *req, int log_sockstype, int safe_socks); int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason); diff --git a/src/or/protover.c b/src/or/protover.c index 0946092692..1a5f4ac91f 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -27,11 +27,14 @@ #include "protover.h" #include "routerparse.h" +#ifndef HAVE_RUST + static const smartlist_t *get_supported_protocol_list(void); static int protocol_list_contains(const smartlist_t *protos, protocol_type_t pr, uint32_t ver); /** Mapping between protocol type string and protocol type. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `PROTOCOL_NAMES` static const struct { protocol_type_t protover_type; const char *name; @@ -93,7 +96,7 @@ str_to_protocol_type(const char *s, protocol_type_t *pr_out) * Release all space held by a single proto_entry_t structure */ STATIC void -proto_entry_free(proto_entry_t *entry) +proto_entry_free_(proto_entry_t *entry) { if (!entry) return; @@ -280,8 +283,45 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp, return contains; } +/** + * Return true iff "list" encodes a protocol list that includes support for + * the indicated protocol and version, or some later version. + */ +int +protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version) +{ + /* NOTE: This is a pretty inefficient implementation. If it ever shows + * up in profiles, we should memoize it. + */ + smartlist_t *protocols = parse_protocol_list(list); + if (!protocols) { + return 0; + } + const char *pr_name = protocol_type_to_str(tp); + + int contains = 0; + SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) { + if (strcasecmp(proto->name, pr_name)) + continue; + SMARTLIST_FOREACH_BEGIN(proto->ranges, const proto_range_t *, range) { + if (range->high >= version) { + contains = 1; + goto found; + } + } SMARTLIST_FOREACH_END(range); + } SMARTLIST_FOREACH_END(proto); + + found: + SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(protocols); + return contains; +} + /** Return the canonical string containing the list of protocols * that we support. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `SUPPORTED_PROTOCOLS` const char * protover_get_supported_protocols(void) { @@ -365,6 +405,8 @@ encode_protocol_list(const smartlist_t *sl) /* We treat any protocol list with more than this many subprotocols in it * as a DoS attempt. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `MAX_PROTOCOLS_TO_EXPAND` static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16); /** Voting helper: Given a list of proto_entry_t, return a newly allocated @@ -691,6 +733,7 @@ protocol_list_contains(const smartlist_t *protos, * Note that this is only used to infer protocols for Tor versions that * can't declare their own. **/ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `compute_for_old_tor` const char * protover_compute_for_old_tor(const char *version) { @@ -740,3 +783,5 @@ protover_free_all(void) } } +#endif /* !defined(HAVE_RUST) */ + diff --git a/src/or/protover.h b/src/or/protover.h index 657977279e..477274e293 100644 --- a/src/or/protover.h +++ b/src/or/protover.h @@ -15,6 +15,8 @@ * descriptors. Authorities should use this to decide whether to * guess proto lines. */ /* This is a guess. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` #define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha" /** The protover version number that signifies HSDir support for HSv3 */ @@ -25,6 +27,8 @@ #define PROTOVER_HS_RENDEZVOUS_POINT_V3 2 /** List of recognized subprotocols. */ +/// C_RUST_COUPLED: src/rust/protover/ffi.rs `translate_to_rust` +/// C_RUST_COUPLED: src/rust/protover/protover.rs `Proto` typedef enum protocol_type_t { PRT_LINK, PRT_LINKAUTH, @@ -47,6 +51,9 @@ char *protover_compute_vote(const smartlist_t *list_of_proto_strings, const char *protover_compute_for_old_tor(const char *version); int protocol_list_supports_protocol(const char *list, protocol_type_t tp, uint32_t version); +int protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version); void protover_free_all(void); @@ -70,11 +77,17 @@ typedef struct proto_entry_t { smartlist_t *ranges; } proto_entry_t; +#if !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) STATIC smartlist_t *parse_protocol_list(const char *s); -STATIC void proto_entry_free(proto_entry_t *entry); STATIC char *encode_protocol_list(const smartlist_t *sl); STATIC const char *protocol_type_to_str(protocol_type_t pr); STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out); +STATIC void proto_entry_free_(proto_entry_t *entry); +#endif /* !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) */ + +#define proto_entry_free(entry) \ + FREE_AND_NULL(proto_entry_t, proto_entry_free_, (entry)) + #endif /* defined(PROTOVER_PRIVATE) */ #endif /* !defined(TOR_PROTOVER_H) */ diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c new file mode 100644 index 0000000000..26e21cc1c5 --- /dev/null +++ b/src/or/protover_rust.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * \file protover_rust.c + * \brief Provide a C wrapper for functions exposed in /src/rust/protover, + * and safe translation/handling between the Rust/C boundary. + */ + +#include "or.h" +#include "protover.h" + +#ifdef HAVE_RUST + +/* Define for compatibility, used in main.c */ +void protover_free_all(void) {} + +#endif /* defined(HAVE_RUST) */ + diff --git a/src/or/relay.c b/src/or/relay.c index 66e10567c1..b1b99526df 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1158,7 +1158,7 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell, /** Drop all storage held by <b>addr</b>. */ STATIC void -address_ttl_free(address_ttl_t *addr) +address_ttl_free_(address_ttl_t *addr) { if (!addr) return; @@ -2425,7 +2425,7 @@ packed_cell_new(void) /** Return a packed cell used outside by channel_t lower layer */ void -packed_cell_free(packed_cell_t *cell) +packed_cell_free_(packed_cell_t *cell) { if (!cell) return; @@ -2482,7 +2482,7 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, (void)exitward; (void)use_stats; - copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); + copy->inserted_timestamp = monotime_coarse_get_stamp(); cell_queue_append(queue, copy); } @@ -2565,7 +2565,7 @@ destroy_cell_queue_append(destroy_cell_queue_t *queue, cell->circid = circid; cell->reason = reason; /* Not yet used, but will be required for OOM handling. */ - cell->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); + cell->inserted_timestamp = monotime_coarse_get_stamp(); TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next); ++queue->n; @@ -2596,7 +2596,7 @@ packed_cell_mem_cost(void) } /* DOCDOC */ -STATIC size_t +size_t cell_queues_get_total_allocation(void) { return total_cells_allocated * packed_cell_mem_cost(); @@ -2820,8 +2820,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) tor_assert(dcell); /* frees dcell */ cell = destroy_cell_to_packed_cell(dcell, chan->wide_circ_ids); - /* frees cell */ - channel_write_packed_cell(chan, cell); + /* Send the DESTROY cell. It is very unlikely that this fails but just + * in case, get rid of the channel. */ + if (channel_write_packed_cell(chan, cell) < 0) { + /* The cell has been freed. */ + channel_mark_for_close(chan); + continue; + } /* Update the cmux destroy counter */ circuitmux_notify_xmit_destroy(cmux); cell = NULL; @@ -2864,9 +2869,10 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) /* Calculate the exact time that this cell has spent in the queue. */ if (get_options()->CellStatistics || get_options()->TestingEnableCellStatsEvent) { - uint32_t msec_waiting; - uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec(); - msec_waiting = msec_now - cell->inserted_time; + uint32_t timestamp_now = monotime_coarse_get_stamp(); + uint32_t msec_waiting = + (uint32_t) monotime_coarse_stamp_units_to_approx_msec( + timestamp_now - cell->inserted_timestamp); if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { or_circ = TO_OR_CIRCUIT(circ); @@ -2897,8 +2903,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) DIRREQ_TUNNELED, DIRREQ_CIRC_QUEUE_FLUSHED); - /* Now send the cell */ - channel_write_packed_cell(chan, cell); + /* Now send the cell. It is very unlikely that this fails but just in + * case, get rid of the channel. */ + if (channel_write_packed_cell(chan, cell) < 0) { + /* The cell has been freed at this point. */ + channel_mark_for_close(chan); + continue; + } cell = NULL; /* @@ -2933,22 +2944,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) return n_flushed; } -#if 0 -/** Indicate the current preferred cap for middle circuits; zero disables - * the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but - * the logic in append_cell_to_circuit_queue() is written to be correct - * if we want to base it on a consensus param or something that might change - * in the future. - */ -static int -get_max_middle_cells(void) -{ - return ORCIRC_MAX_MIDDLE_CELLS; -} -#endif /* 0 */ - /** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b> - * transmitting in <b>direction</b>. */ + * transmitting in <b>direction</b>. + * + * The given <b>cell</b> is copied over the circuit queue so the caller must + * cleanup the memory. + * + * This function is part of the fast path. */ void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, @@ -2957,10 +2959,6 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, or_circuit_t *orcirc = NULL; cell_queue_t *queue; int streams_blocked; -#if 0 - uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells; -#endif - int exitward; if (circ->marked_for_close) return; @@ -2975,93 +2973,14 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, streams_blocked = circ->streams_blocked_on_p_chan; } - /* - * Disabling this for now because of a possible guard discovery attack - */ -#if 0 - /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */ - if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) { - orcirc = TO_OR_CIRCUIT(circ); - if (orcirc->p_chan) { - /* We are a middle circuit if we have both n_chan and p_chan */ - /* We'll need to know the current preferred maximum */ - tgt_max_middle_cells = get_max_middle_cells(); - if (tgt_max_middle_cells > 0) { - /* Do we need to initialize middle_max_cells? */ - if (orcirc->max_middle_cells == 0) { - orcirc->max_middle_cells = tgt_max_middle_cells; - } else { - if (tgt_max_middle_cells > orcirc->max_middle_cells) { - /* If we want to increase the cap, we can do so right away */ - orcirc->max_middle_cells = tgt_max_middle_cells; - } else if (tgt_max_middle_cells < orcirc->max_middle_cells) { - /* - * If we're shrinking the cap, we can't shrink past either queue; - * compare tgt_max_middle_cells rather than tgt_max_middle_cells * - * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough - * to generate spurious warnings, either. - */ - n_len = circ->n_chan_cells.n; - p_len = orcirc->p_chan_cells.n; - tmp = tgt_max_middle_cells; - if (tmp < n_len) tmp = n_len; - if (tmp < p_len) tmp = p_len; - orcirc->max_middle_cells = tmp; - } - /* else no change */ - } - } else { - /* tgt_max_middle_cells == 0 indicates we should disable the cap */ - orcirc->max_middle_cells = 0; - } - - /* Now we know orcirc->max_middle_cells is set correctly */ - if (orcirc->max_middle_cells > 0) { - hard_max_middle_cells = - (uint32_t)(((double)orcirc->max_middle_cells) * - ORCIRC_MAX_MIDDLE_KILL_THRESH); - - if ((unsigned)queue->n + 1 >= hard_max_middle_cells) { - /* Queueing this cell would put queue over the kill theshold */ - log_warn(LD_CIRC, - "Got a cell exceeding the hard cap of %u in the " - "%s direction on middle circ ID %u on chan ID " - U64_FORMAT "; killing the circuit.", - hard_max_middle_cells, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( - (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); - return; - } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) { - /* Only use ==, not >= for this test so we don't spam the log */ - log_warn(LD_CIRC, - "While trying to queue a cell, reached the soft cap of %u " - "in the %s direction on middle circ ID %u " - "on chan ID " U64_FORMAT ".", - orcirc->max_middle_cells, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( - (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - } - } - } - } -#endif /* 0 */ - + /* Very important that we copy to the circuit queue because all calls to + * this function use the stack for the cell memory. */ cell_queue_append_packed_copy(circ, queue, exitward, cell, chan->wide_circ_ids, 1); + /* Check and run the OOM if needed. */ if (PREDICT_UNLIKELY(cell_queues_check_size())) { - /* We ran the OOM handler */ + /* We ran the OOM handler which might have closed this circuit. */ if (circ->marked_for_close) return; } diff --git a/src/or/relay.h b/src/or/relay.h index 4cc1a0fbdb..f0fa7e9870 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -17,6 +17,7 @@ extern uint64_t stats_n_relay_cells_delivered; int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, cell_direction_t cell_direction); +size_t cell_queues_get_total_allocation(void); void relay_header_pack(uint8_t *dest, const relay_header_t *src); void relay_header_unpack(relay_header_t *dest, const uint8_t *src); @@ -51,7 +52,9 @@ size_t packed_cell_mem_cost(void); int have_been_under_memory_pressure(void); /* For channeltls.c */ -void packed_cell_free(packed_cell_t *cell); +void packed_cell_free_(packed_cell_t *cell); +#define packed_cell_free(cell) \ + FREE_AND_NULL(packed_cell_t, packed_cell_free_, (cell)) void cell_queue_init(cell_queue_t *queue); void cell_queue_clear(cell_queue_t *queue); @@ -101,7 +104,9 @@ typedef struct address_ttl_s { char *hostname; int ttl; } address_ttl_t; -STATIC void address_ttl_free(address_ttl_t *addr); +STATIC void address_ttl_free_(address_ttl_t *addr); +#define address_ttl_free(addr) \ + FREE_AND_NULL(address_ttl_t, address_ttl_free_, (addr)) STATIC int resolved_cell_parse(const cell_t *cell, const relay_header_t *rh, smartlist_t *addresses_out, int *errcode_out); STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn, @@ -110,7 +115,6 @@ STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn, STATIC packed_cell_t *packed_cell_new(void); STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue); STATIC destroy_cell_t *destroy_cell_queue_pop(destroy_cell_queue_t *queue); -STATIC size_t cell_queues_get_total_allocation(void); STATIC int cell_queues_check_size(void); #endif /* defined(RELAY_PRIVATE) */ diff --git a/src/or/rendcache.c b/src/or/rendcache.c index b98b2bccfa..6f56df6718 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -120,7 +120,7 @@ rend_cache_increment_allocation(size_t n) /** Helper: free a rend cache failure intro object. */ STATIC void -rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) +rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t *entry) { if (entry == NULL) { return; @@ -129,9 +129,9 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) } static void -rend_cache_failure_intro_entry_free_(void *entry) +rend_cache_failure_intro_entry_free_void(void *entry) { - rend_cache_failure_intro_entry_free(entry); + rend_cache_failure_intro_entry_free_(entry); } /** Allocate a rend cache failure intro object and return it. <b>failure</b> @@ -147,7 +147,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) /** Helper: free a rend cache failure object. */ STATIC void -rend_cache_failure_entry_free(rend_cache_failure_t *entry) +rend_cache_failure_entry_free_(rend_cache_failure_t *entry) { if (entry == NULL) { return; @@ -155,7 +155,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /* Free and remove every intro failure object. */ digestmap_free(entry->intro_failures, - rend_cache_failure_intro_entry_free_); + rend_cache_failure_intro_entry_free_void); tor_free(entry); } @@ -163,9 +163,9 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(), * which requires a function pointer whose argument is void*). */ STATIC void -rend_cache_failure_entry_free_(void *entry) +rend_cache_failure_entry_free_void(void *entry) { - rend_cache_failure_entry_free(entry); + rend_cache_failure_entry_free_(entry); } /** Allocate a rend cache failure object and return it. This function can @@ -201,7 +201,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc) /** Helper: free storage held by a single service descriptor cache entry. */ STATIC void -rend_cache_entry_free(rend_cache_entry_t *e) +rend_cache_entry_free_(rend_cache_entry_t *e) { if (!e) return; @@ -217,19 +217,19 @@ rend_cache_entry_free(rend_cache_entry_t *e) /** Helper: deallocate a rend_cache_entry_t. (Used with strmap_free(), which * requires a function pointer whose argument is void*). */ static void -rend_cache_entry_free_(void *p) +rend_cache_entry_free_void(void *p) { - rend_cache_entry_free(p); + rend_cache_entry_free_(p); } /** Free all storage held by the service descriptor cache. */ void rend_cache_free_all(void) { - strmap_free(rend_cache, rend_cache_entry_free_); - digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); - strmap_free(rend_cache_local_service, rend_cache_entry_free_); - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache, rend_cache_entry_free_void); + digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_void); + strmap_free(rend_cache_local_service, rend_cache_entry_free_void); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); rend_cache = NULL; rend_cache_v2_dir = NULL; rend_cache_local_service = NULL; @@ -304,7 +304,7 @@ rend_cache_purge(void) { if (rend_cache) { log_info(LD_REND, "Purging HS v2 descriptor cache"); - strmap_free(rend_cache, rend_cache_entry_free_); + strmap_free(rend_cache, rend_cache_entry_free_void); } rend_cache = strmap_new(); } @@ -316,7 +316,7 @@ rend_cache_failure_purge(void) { if (rend_cache_failure) { log_info(LD_REND, "Purging HS v2 failure cache"); - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); } rend_cache_failure = strmap_new(); } diff --git a/src/or/rendcache.h b/src/or/rendcache.h index 5b13eadfa1..66e9785645 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -90,10 +90,18 @@ void rend_cache_increment_allocation(size_t n); #ifdef RENDCACHE_PRIVATE STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e); -STATIC void rend_cache_entry_free(rend_cache_entry_t *e); -STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t - *entry); -STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry); +STATIC void rend_cache_entry_free_(rend_cache_entry_t *e); +#define rend_cache_entry_free(e) \ + FREE_AND_NULL(rend_cache_entry_t, rend_cache_entry_free_, (e)) +STATIC void rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t + *entry); +#define rend_cache_failure_intro_entry_free(e) \ + FREE_AND_NULL(rend_cache_failure_intro_t, \ + rend_cache_failure_intro_entry_free_, (e)) +STATIC void rend_cache_failure_entry_free_(rend_cache_failure_t *entry); +#define rend_cache_failure_entry_free(e) \ + FREE_AND_NULL(rend_cache_failure_t, \ + rend_cache_failure_entry_free_, (e)) STATIC int cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, rend_cache_failure_intro_t @@ -108,7 +116,7 @@ STATIC void cache_failure_intro_add(const uint8_t *identity, STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc, const char *service_id); -STATIC void rend_cache_failure_entry_free_(void *entry); +STATIC void rend_cache_failure_entry_free_void(void *entry); #ifdef TOR_UNIT_TESTS extern strmap_t *rend_cache; diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3274819241..07fa33306d 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -459,7 +459,8 @@ directory_get_from_hs_dir(const char *desc_id, hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32); if (!hs_dir) { /* No suitable hs dir can be found, stop right now. */ - control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR"); + control_event_hsv2_descriptor_failed(rend_query, NULL, + "QUERY_NO_HSDIR"); control_event_hs_descriptor_content(rend_data_get_address(rend_query), desc_id_base32, NULL, NULL); return 0; @@ -482,7 +483,7 @@ directory_get_from_hs_dir(const char *desc_id, REND_DESC_COOKIE_LEN, 0)<0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); - control_event_hs_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC"); + control_event_hsv2_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC"); control_event_hs_descriptor_content(rend_data_get_address(rend_query), desc_id_base32, hsdir_fp, NULL); return 0; @@ -515,9 +516,10 @@ directory_get_from_hs_dir(const char *desc_id, (rend_data->auth_type == REND_NO_AUTH ? "[none]" : escaped_safe_str_client(descriptor_cookie_base64)), routerstatus_describe(hs_dir)); - control_event_hs_descriptor_requested(rend_query, + control_event_hs_descriptor_requested(rend_data->onion_address, + rend_data->auth_type, hs_dir->identity_digest, - desc_id_base32); + desc_id_base32, NULL); return 1; } @@ -1102,18 +1104,22 @@ rend_client_lookup_service_authorization(const char *onion_address) return strmap_get(auth_hid_servs, onion_address); } +#define rend_service_authorization_free(val) \ + FREE_AND_NULL(rend_service_authorization_t, \ + rend_service_authorization_free_, (val)) + /** Helper: Free storage held by rend_service_authorization_t. */ static void -rend_service_authorization_free(rend_service_authorization_t *auth) +rend_service_authorization_free_(rend_service_authorization_t *auth) { tor_free(auth); } /** Helper for strmap_free. */ static void -rend_service_authorization_strmap_item_free(void *service_auth) +rend_service_authorization_free_void(void *service_auth) { - rend_service_authorization_free(service_auth); + rend_service_authorization_free_(service_auth); } /** Release all the storage held in auth_hid_servs. @@ -1124,7 +1130,7 @@ rend_service_authorization_free_all(void) if (!auth_hid_servs) { return; } - strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free); + strmap_free(auth_hid_servs, rend_service_authorization_free_void); auth_hid_servs = NULL; } @@ -1199,7 +1205,7 @@ rend_parse_service_authorization(const or_options_t *options, rend_service_authorization_free_all(); auth_hid_servs = parsed; } else { - strmap_free(parsed, rend_service_authorization_strmap_item_free); + strmap_free(parsed, rend_service_authorization_free_void); } return res; } diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 458a90058f..230da4be5c 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -37,7 +37,7 @@ rend_cmp_service_ids(const char *one, const char *two) /** Free the storage held by the service descriptor <b>desc</b>. */ void -rend_service_descriptor_free(rend_service_descriptor_t *desc) +rend_service_descriptor_free_(rend_service_descriptor_t *desc) { if (!desc) return; @@ -419,7 +419,7 @@ rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc) /** Free the storage held by an encoded v2 service descriptor. */ void -rend_encoded_v2_service_descriptor_free( +rend_encoded_v2_service_descriptor_free_( rend_encoded_v2_service_descriptor_t *desc) { if (!desc) @@ -430,7 +430,7 @@ rend_encoded_v2_service_descriptor_free( /** Free the storage held by an introduction point info. */ void -rend_intro_point_free(rend_intro_point_t *intro) +rend_intro_point_free_(rend_intro_point_t *intro) { if (!intro) return; diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index c35d7272fd..1ed0f62609 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -24,11 +24,19 @@ void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, int command, size_t length, const uint8_t *payload); -void rend_service_descriptor_free(rend_service_descriptor_t *desc); +void rend_service_descriptor_free_(rend_service_descriptor_t *desc); +#define rend_service_descriptor_free(desc) \ + FREE_AND_NULL(rend_service_descriptor_t, rend_service_descriptor_free_, \ + (desc)) int rend_get_service_id(crypto_pk_t *pk, char *out); -void rend_encoded_v2_service_descriptor_free( +void rend_encoded_v2_service_descriptor_free_( rend_encoded_v2_service_descriptor_t *desc); -void rend_intro_point_free(rend_intro_point_t *intro); +#define rend_encoded_v2_service_descriptor_free(desc) \ + FREE_AND_NULL(rend_encoded_v2_service_descriptor_t, \ + rend_encoded_v2_service_descriptor_free_, (desc)) +void rend_intro_point_free_(rend_intro_point_t *intro); +#define rend_intro_point_free(intro) \ + FREE_AND_NULL(rend_intro_point_t, rend_intro_point_free_, (intro)) int rend_valid_v2_service_id(const char *query); int rend_valid_descriptor_id(const char *query); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 2cdafa6666..06166d7cf3 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -157,7 +157,7 @@ rend_num_services(void) /** Helper: free storage held by a single service authorized client entry. */ void -rend_authorized_client_free(rend_authorized_client_t *client) +rend_authorized_client_free_(rend_authorized_client_t *client) { if (!client) return; @@ -172,15 +172,15 @@ rend_authorized_client_free(rend_authorized_client_t *client) /** Helper for strmap_free. */ static void -rend_authorized_client_strmap_item_free(void *authorized_client) +rend_authorized_client_free_void(void *authorized_client) { - rend_authorized_client_free(authorized_client); + rend_authorized_client_free_(authorized_client); } /** Release the storage held by <b>service</b>. */ STATIC void -rend_service_free(rend_service_t *service) +rend_service_free_(rend_service_t *service) { if (!service) return; @@ -470,7 +470,7 @@ rend_service_parse_port_config(const char *string, const char *sep, /** Release all storage held in a rend_service_port_config_t. */ void -rend_service_port_config_free(rend_service_port_config_t *p) +rend_service_port_config_free_(rend_service_port_config_t *p) { tor_free(p); } @@ -847,9 +847,9 @@ rend_config_service(const config_line_t *line_, * after calling this routine, and may assume that correct cleanup has * been done on failure. * - * Return an appropriate rend_service_add_ephemeral_status_t. + * Return an appropriate hs_service_add_ephemeral_status_t. */ -rend_service_add_ephemeral_status_t +hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, smartlist_t *ports, int max_streams_per_circuit, @@ -1675,7 +1675,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) memwipe(client_keys_str, 0, strlen(client_keys_str)); tor_free(client_keys_str); } - strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); + strmap_free(parsed_clients, rend_authorized_client_free_void); if (cfname) { memwipe(cfname, 0, strlen(cfname)); @@ -2223,7 +2223,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro, * rend_service_parse_intro(). */ void -rend_service_free_intro(rend_intro_cell_t *request) +rend_service_free_intro_(rend_intro_cell_t *request) { if (!request) { return; @@ -3576,7 +3576,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, "directories to post descriptors to."); control_event_hs_descriptor_upload(service_id, "UNKNOWN", - "UNKNOWN"); + "UNKNOWN", NULL); goto done; } } @@ -3631,7 +3631,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, hs_dir->or_port); control_event_hs_descriptor_upload(service_id, hs_dir->identity_digest, - desc_id_base32); + desc_id_base32, NULL); tor_free(hs_dir_ip); /* Remember successful upload to this router for next time. */ if (!smartlist_contains_digest(successful_uploads, @@ -3728,7 +3728,7 @@ upload_service_descriptor(rend_service_t *service) } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_clear(descs); /* Update next upload time. */ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS @@ -3759,7 +3759,7 @@ upload_service_descriptor(rend_service_t *service) } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_clear(descs); } } diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 5946e31861..88da7b8665 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -117,7 +117,9 @@ typedef struct rend_service_t { int max_streams_close_circuit; } rend_service_t; -STATIC void rend_service_free(rend_service_t *service); +STATIC void rend_service_free_(rend_service_t *service); +#define rend_service_free(s) \ + FREE_AND_NULL(rend_service_t, rend_service_free_, (s)) STATIC char *rend_service_sos_poison_path(const rend_service_t *service); STATIC int rend_service_verify_single_onion_poison( const rend_service_t *s, @@ -160,7 +162,11 @@ int rend_service_receive_introduction(origin_circuit_t *circuit, int rend_service_decrypt_intro(rend_intro_cell_t *request, crypto_pk_t *key, char **err_msg_out); -void rend_service_free_intro(rend_intro_cell_t *request); +void rend_service_free_intro_(rend_intro_cell_t *request); +#define rend_service_free_intro(req) do { \ + rend_service_free_intro_(req); \ + (req) = NULL; \ + } while (0) rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request, size_t request_len, uint8_t type, @@ -183,20 +189,17 @@ void rend_service_init(void); rend_service_port_config_t *rend_service_parse_port_config(const char *string, const char *sep, char **err_msg_out); -void rend_service_port_config_free(rend_service_port_config_t *p); - -void rend_authorized_client_free(rend_authorized_client_t *client); - -/** Return value from rend_service_add_ephemeral. */ -typedef enum { - RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ - RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ - RSAE_ADDREXISTS = -3, /**< Onion address collision */ - RSAE_BADPRIVKEY = -2, /**< Invalid public key */ - RSAE_INTERNAL = -1, /**< Internal error */ - RSAE_OKAY = 0 /**< Service added as expected */ -} rend_service_add_ephemeral_status_t; -rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, +void rend_service_port_config_free_(rend_service_port_config_t *p); +#define rend_service_port_config_free(p) \ + FREE_AND_NULL(rend_service_port_config_t, rend_service_port_config_free_, \ + (p)) + +void rend_authorized_client_free_(rend_authorized_client_t *client); +#define rend_authorized_client_free(client) \ + FREE_AND_NULL(rend_authorized_client_t, rend_authorized_client_free_, \ + (client)) + +hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, smartlist_t *ports, int max_streams_per_circuit, int max_streams_close_circuit, diff --git a/src/or/rephist.c b/src/or/rephist.c index 345722d8ce..15fb674fff 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -1358,9 +1358,12 @@ bw_array_new(void) return b; } +#define bw_array_free(val) \ + FREE_AND_NULL(bw_array_t, bw_array_free_, (val)) + /** Free storage held by bandwidth array <b>b</b>. */ static void -bw_array_free(bw_array_t *b) +bw_array_free_(bw_array_t *b) { if (!b) { return; @@ -1885,7 +1888,7 @@ predicted_ports_init(void) * be used. */ static void -predicted_ports_free(void) +predicted_ports_free_all(void) { rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(predicted_port_t); @@ -2830,7 +2833,7 @@ HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, /* DOCDOC bidi_map_free */ static void -bidi_map_free(void) +bidi_map_free_all(void) { bidi_map_entry_t **ptr, **next, *ent; for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { @@ -2850,7 +2853,7 @@ rep_hist_reset_conn_stats(time_t now) mostly_read = 0; mostly_written = 0; both_read_and_written = 0; - bidi_map_free(); + bidi_map_free_all(); } /** Stop collecting connection stats in a way that we can re-start doing @@ -3039,9 +3042,12 @@ hs_stats_new(void) return new_hs_stats; } +#define hs_stats_free(val) \ + FREE_AND_NULL(hs_stats_t, hs_stats_free_, (val)) + /** Free an hs_stats_t structure. */ static void -hs_stats_free(hs_stats_t *victim_hs_stats) +hs_stats_free_(hs_stats_t *victim_hs_stats) { if (!victim_hs_stats) { return; @@ -3453,8 +3459,8 @@ rep_hist_free_all(void) tor_free(exit_bytes_read); tor_free(exit_bytes_written); tor_free(exit_streams); - predicted_ports_free(); - bidi_map_free(); + predicted_ports_free_all(); + bidi_map_free_all(); if (circuits_for_buffer_stats) { SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s, diff --git a/src/or/replaycache.c b/src/or/replaycache.c index 3d42deb90a..4a56bfd7d4 100644 --- a/src/or/replaycache.c +++ b/src/or/replaycache.c @@ -28,7 +28,7 @@ */ void -replaycache_free(replaycache_t *r) +replaycache_free_(replaycache_t *r) { if (!r) { log_info(LD_BUG, "replaycache_free() called on NULL"); diff --git a/src/or/replaycache.h b/src/or/replaycache.h index 1cae3497ae..81a8d907fd 100644 --- a/src/or/replaycache.h +++ b/src/or/replaycache.h @@ -33,7 +33,9 @@ struct replaycache_s { /* replaycache_t free/new */ -void replaycache_free(replaycache_t *r); +void replaycache_free_(replaycache_t *r); +#define replaycache_free(r) \ + FREE_AND_NULL(replaycache_t, replaycache_free_, (r)) replaycache_t * replaycache_new(time_t horizon, time_t interval); #ifdef REPLAYCACHE_PRIVATE diff --git a/src/or/router.c b/src/or/router.c index 010ee339a0..9c053cad46 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -174,7 +174,7 @@ expire_old_onion_keys(void) tor_mutex_release(key_lock); - fname = get_datadir_fname2("keys", "secret_onion_key.old"); + fname = get_keydir_fname("secret_onion_key.old"); if (file_status(fname) == FN_FILE) { if (tor_unlink(fname) != 0) { log_warn(LD_FS, "Couldn't unlink old onion key file %s: %s", @@ -183,7 +183,7 @@ expire_old_onion_keys(void) } tor_free(fname); - fname = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + fname = get_keydir_fname("secret_onion_key_ntor.old"); if (file_status(fname) == FN_FILE) { if (tor_unlink(fname) != 0) { log_warn(LD_FS, "Couldn't unlink old ntor onion key file %s: %s", @@ -233,7 +233,7 @@ ntor_key_map_free_helper(void *arg) } /** Release all storage from a keymap returned by construct_ntor_key_map. */ void -ntor_key_map_free(di_digest256_map_t *map) +ntor_key_map_free_(di_digest256_map_t *map) { if (!map) return; @@ -378,8 +378,8 @@ rotate_onion_key(void) or_state_t *state = get_or_state(); curve25519_keypair_t new_curve25519_keypair; time_t now; - fname = get_datadir_fname2("keys", "secret_onion_key"); - fname_prev = get_datadir_fname2("keys", "secret_onion_key.old"); + fname = get_keydir_fname("secret_onion_key"); + fname_prev = get_keydir_fname("secret_onion_key.old"); /* There isn't much point replacing an old key with an empty file */ if (file_status(fname) == FN_FILE) { if (replace_file(fname, fname_prev)) @@ -399,8 +399,8 @@ rotate_onion_key(void) } tor_free(fname); tor_free(fname_prev); - fname = get_datadir_fname2("keys", "secret_onion_key_ntor"); - fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + fname = get_keydir_fname("secret_onion_key_ntor"); + fname_prev = get_keydir_fname("secret_onion_key_ntor.old"); if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0) goto error; /* There isn't much point replacing an old key with an empty file */ @@ -624,7 +624,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, crypto_pk_t *signing_key = NULL; authority_cert_t *parsed = NULL; - fname = get_datadir_fname2("keys", + fname = get_keydir_fname( legacy ? "legacy_signing_key" : "authority_signing_key"); signing_key = init_key_from_file(fname, 0, LOG_ERR, 0); if (!signing_key) { @@ -632,7 +632,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, goto done; } tor_free(fname); - fname = get_datadir_fname2("keys", + fname = get_keydir_fname( legacy ? "legacy_certificate" : "authority_certificate"); cert = read_file_to_str(fname, 0, NULL); if (!cert) { @@ -932,22 +932,9 @@ init_keys(void) } if (init_keys_common() < 0) return -1; - /* Make sure DataDirectory exists, and is private. */ - cpd_check_t cpd_opts = CPD_CREATE; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { - log_err(LD_OR, "Can't create/check datadirectory %s", - options->DataDirectory); - return -1; - } - /* Check the key directory. */ - keydir = get_datadir_fname("keys"); - if (check_private_dir(keydir, CPD_CREATE, options->User)) { - tor_free(keydir); + + if (create_keys_directory(options) < 0) return -1; - } - tor_free(keydir); /* 1a. Read v3 directory authority key/cert information. */ memset(v3_digest, 0, sizeof(v3_digest)); @@ -971,7 +958,7 @@ init_keys(void) } /* 1b. Read identity key. Make it if none is found. */ - keydir = get_datadir_fname2("keys", "secret_id_key"); + keydir = get_keydir_fname("secret_id_key"); log_info(LD_GENERAL,"Reading/making identity key \"%s\"...",keydir); prkey = init_key_from_file(keydir, 1, LOG_ERR, 1); tor_free(keydir); @@ -999,7 +986,7 @@ init_keys(void) return -1; /* 2. Read onion key. Make it if none is found. */ - keydir = get_datadir_fname2("keys", "secret_onion_key"); + keydir = get_keydir_fname("secret_onion_key"); log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir); prkey = init_key_from_file(keydir, 1, LOG_ERR, 1); tor_free(keydir); @@ -1024,7 +1011,7 @@ init_keys(void) } } - keydir = get_datadir_fname2("keys", "secret_onion_key.old"); + keydir = get_keydir_fname("secret_onion_key.old"); if (!lastonionkey && file_status(keydir) == FN_FILE) { /* Load keys from non-empty files only. * Missing old keys won't be replaced with freshly generated keys. */ @@ -1037,14 +1024,14 @@ init_keys(void) { /* 2b. Load curve25519 onion keys. */ int r; - keydir = get_datadir_fname2("keys", "secret_onion_key_ntor"); + keydir = get_keydir_fname("secret_onion_key_ntor"); r = init_curve25519_keypair_from_file(&curve25519_onion_key, keydir, 1, LOG_ERR, "onion"); tor_free(keydir); if (r<0) return -1; - keydir = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + keydir = get_keydir_fname("secret_onion_key_ntor.old"); if (tor_mem_is_zero((const char *) last_curve25519_onion_key.pubkey.public_key, CURVE25519_PUBKEY_LEN) && @@ -3698,6 +3685,7 @@ router_free_all(void) crypto_pk_free(lastonionkey); crypto_pk_free(server_identitykey); crypto_pk_free(client_identitykey); + tor_mutex_free(key_lock); routerinfo_free(desc_routerinfo); extrainfo_free(desc_extrainfo); diff --git a/src/or/router.h b/src/or/router.h index 3351400911..696e983662 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -36,7 +36,9 @@ int get_onion_key_lifetime(void); int get_onion_key_grace_period(void); di_digest256_map_t *construct_ntor_key_map(void); -void ntor_key_map_free(di_digest256_map_t *map); +void ntor_key_map_free_(di_digest256_map_t *map); +#define ntor_key_map_free(map) \ + FREE_AND_NULL(di_digest256_map_t, ntor_key_map_free_, (map)) int router_initialize_tls_context(void); int init_keys(void); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 7295c19653..af230f07bf 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -718,7 +718,7 @@ load_ed_keys(const or_options_t *options, time_t now) /* First try to get the signing key to see how it is. */ { char *fname = - options_get_datadir_fname2(options, "keys", "ed25519_signing"); + options_get_keydir_fname(options, "ed25519_signing"); sign = ed_key_init_from_file( fname, INIT_ED_KEY_NEEDCERT| @@ -813,26 +813,15 @@ load_ed_keys(const or_options_t *options, time_t now) flags |= INIT_ED_KEY_TRY_ENCRYPTED; /* Check/Create the key directory */ - cpd_check_t cpd_opts = CPD_CREATE; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { - log_err(LD_OR, "Can't create/check datadirectory %s", - options->DataDirectory); - goto err; - } - char *fname = get_datadir_fname("keys"); - if (check_private_dir(fname, CPD_CREATE, options->User) < 0) { - log_err(LD_OR, "Problem creating/checking key directory %s", fname); - tor_free(fname); - goto err; - } - tor_free(fname); + if (create_keys_directory(options) < 0) + return -1; + + char *fname; if (options->master_key_fname) { fname = tor_strdup(options->master_key_fname); flags |= INIT_ED_KEY_EXPLICIT_FNAME; } else { - fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id"); + fname = options_get_keydir_fname(options, "ed25519_master_id"); } id = ed_key_init_from_file( fname, @@ -852,8 +841,8 @@ load_ed_keys(const or_options_t *options, time_t now) id = tor_malloc_zero(sizeof(*id)); memcpy(&id->pubkey, &check_signing_cert->signing_key, sizeof(ed25519_public_key_t)); - fname = options_get_datadir_fname2(options, "keys", - "ed25519_master_id_public_key"); + fname = options_get_keydir_fname(options, + "ed25519_master_id_public_key"); if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) { log_warn(LD_OR, "Error while attempting to write master public key " "to disk"); @@ -894,7 +883,7 @@ load_ed_keys(const or_options_t *options, time_t now) INIT_ED_KEY_NEEDCERT| INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); char *fname = - options_get_datadir_fname2(options, "keys", "ed25519_signing"); + options_get_keydir_fname(options, "ed25519_signing"); ed25519_keypair_free(sign); tor_cert_free(sign_cert); sign = ed_key_init_from_file(fname, @@ -1185,7 +1174,7 @@ log_master_signing_key_cert_expiration(const or_options_t *options) int failed = 0; time_t now = approx_time(); - fn = options_get_datadir_fname2(options, "keys", "ed25519_signing_cert"); + fn = options_get_keydir_fname(options, "ed25519_signing_cert"); /* Try to grab our cached copy of the key. */ signing_key = get_master_signing_key_cert(); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index af4f67dc12..2815c60963 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -143,6 +143,10 @@ DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t) #define DSMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \ valvar) +#define eimap_free(map, fn) MAP_FREE_AND_NULL(eimap, (map), (fn)) +#define rimap_free(map, fn) MAP_FREE_AND_NULL(rimap, (map), (fn)) +#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn)) +#define sdmap_free(map, fn) MAP_FREE_AND_NULL(sdmap, (map), (fn)) /* Forward declaration for cert_list_t */ typedef struct cert_list_t cert_list_t; @@ -159,7 +163,6 @@ static const routerstatus_t *router_pick_dirserver_generic( smartlist_t *sourcelist, dirinfo_type_t type, int flags); static void mark_all_dirservers_up(smartlist_t *server_list); -static void dir_server_free(dir_server_t *ds); static int signed_desc_digest_is_recognized(signed_descriptor_t *desc); static const char *signed_descriptor_get_body_impl( const signed_descriptor_t *desc, @@ -443,9 +446,12 @@ download_status_for_authority_id_and_sk,(const char *id_digest, return dl; } +#define cert_list_free(val) \ + FREE_AND_NULL(cert_list_t, cert_list_free_, (val)) + /** Release all space held by a cert_list_t */ static void -cert_list_free(cert_list_t *cl) +cert_list_free_(cert_list_t *cl) { if (!cl) return; @@ -459,9 +465,9 @@ cert_list_free(cert_list_t *cl) /** Wrapper for cert_list_free so we can pass it to digestmap_free */ static void -cert_list_free_(void *cl) +cert_list_free_void(void *cl) { - cert_list_free(cl); + cert_list_free_(cl); } /** Reload the cached v3 key certificates from the cached-certs file in @@ -473,7 +479,7 @@ trusted_dirs_reload_certs(void) char *contents; int r; - filename = get_datadir_fname("cached-certs"); + filename = get_cachedir_fname("cached-certs"); contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); if (!contents) @@ -662,7 +668,7 @@ trusted_dirs_flush_certs_to_disk(void) }); } DIGESTMAP_FOREACH_END; - filename = get_datadir_fname("cached-certs"); + filename = get_cachedir_fname("cached-certs"); if (write_chunks_to_file(filename, chunks, 0, 0)) { log_warn(LD_FS, "Error writing certificates to disk."); } @@ -1339,7 +1345,7 @@ static int signed_desc_append_to_journal(signed_descriptor_t *desc, desc_store_t *store) { - char *fname = get_datadir_fname_suffix(store->fname_base, ".new"); + char *fname = get_cachedir_fname_suffix(store->fname_base, ".new"); const char *body = signed_descriptor_get_body_impl(desc,1); size_t len = desc->signed_descriptor_len + desc->annotations_len; @@ -1410,8 +1416,8 @@ router_rebuild_store(int flags, desc_store_t *store) log_info(LD_DIR, "Rebuilding %s cache", store->description); - fname = get_datadir_fname(store->fname_base); - fname_tmp = get_datadir_fname_suffix(store->fname_base, ".tmp"); + fname = get_cachedir_fname(store->fname_base); + fname_tmp = get_cachedir_fname_suffix(store->fname_base, ".tmp"); chunk_list = smartlist_new(); @@ -1508,7 +1514,7 @@ router_rebuild_store(int flags, desc_store_t *store) } SMARTLIST_FOREACH_END(sd); tor_free(fname); - fname = get_datadir_fname_suffix(store->fname_base, ".new"); + fname = get_cachedir_fname_suffix(store->fname_base, ".new"); write_str_to_file(fname, "", 1); r = 0; @@ -1538,7 +1544,7 @@ router_reload_router_list_impl(desc_store_t *store) int extrainfo = (store->type == EXTRAINFO_STORE); store->journal_len = store->store_len = 0; - fname = get_datadir_fname(store->fname_base); + fname = get_cachedir_fname(store->fname_base); if (store->mmap) { /* get rid of it first */ @@ -1565,7 +1571,7 @@ router_reload_router_list_impl(desc_store_t *store) } tor_free(fname); - fname = get_datadir_fname_suffix(store->fname_base, ".new"); + fname = get_cachedir_fname_suffix(store->fname_base, ".new"); /* don't load empty files - we wouldn't get any data, even if we tried */ if (file_status(fname) == FN_FILE) contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); @@ -3099,7 +3105,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc, log_err(LD_DIR, "We couldn't read a descriptor that is supposedly " "mmaped in our cache. Is another process running in our data " "directory? Exiting."); - exit(1); + exit(1); // XXXX bad exit: should recover. } } if (!r) /* no mmap, or not in cache. */ @@ -3113,7 +3119,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc, log_err(LD_DIR, "descriptor at %p begins with unexpected string %s. " "Is another process running in our data directory? Exiting.", desc, escaped(cp)); - exit(1); + exit(1); // XXXX bad exit: should recover. } } @@ -3167,7 +3173,7 @@ router_get_routerlist(void) /** Free all storage held by <b>router</b>. */ void -routerinfo_free(routerinfo_t *router) +routerinfo_free_(routerinfo_t *router) { if (!router) return; @@ -3197,7 +3203,7 @@ routerinfo_free(routerinfo_t *router) /** Release all storage held by <b>extrainfo</b> */ void -extrainfo_free(extrainfo_t *extrainfo) +extrainfo_free_(extrainfo_t *extrainfo) { if (!extrainfo) return; @@ -3209,9 +3215,12 @@ extrainfo_free(extrainfo_t *extrainfo) tor_free(extrainfo); } +#define signed_descriptor_free(val) \ + FREE_AND_NULL(signed_descriptor_t, signed_descriptor_free_, (val)) + /** Release storage held by <b>sd</b>. */ static void -signed_descriptor_free(signed_descriptor_t *sd) +signed_descriptor_free_(signed_descriptor_t *sd) { if (!sd) return; @@ -3265,21 +3274,21 @@ signed_descriptor_from_routerinfo(routerinfo_t *ri) /** Helper: free the storage held by the extrainfo_t in <b>e</b>. */ static void -extrainfo_free_(void *e) +extrainfo_free_void(void *e) { - extrainfo_free(e); + extrainfo_free_(e); } /** Free all storage held by a routerlist <b>rl</b>. */ void -routerlist_free(routerlist_t *rl) +routerlist_free_(routerlist_t *rl) { if (!rl) return; rimap_free(rl->identity_map, NULL); sdmap_free(rl->desc_digest_map, NULL); sdmap_free(rl->desc_by_eid_map, NULL); - eimap_free(rl->extra_info_map, extrainfo_free_); + eimap_free(rl->extra_info_map, extrainfo_free_void); SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, routerinfo_free(r)); SMARTLIST_FOREACH(rl->old_routers, signed_descriptor_t *, sd, @@ -3771,7 +3780,7 @@ routerlist_free_all(void) smartlist_free(fallback_dir_servers); trusted_dir_servers = fallback_dir_servers = NULL; if (trusted_dir_certs) { - digestmap_free(trusted_dir_certs, cert_list_free_); + digestmap_free(trusted_dir_certs, cert_list_free_void); trusted_dir_certs = NULL; } } @@ -4548,7 +4557,7 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { - if (get_options()->DisableNetwork) + if (should_delay_dir_fetches(get_options(), NULL)) return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); @@ -4739,7 +4748,7 @@ dir_server_add(dir_server_t *ent) /** Free storage held in <b>cert</b>. */ void -authority_cert_free(authority_cert_t *cert) +authority_cert_free_(authority_cert_t *cert) { if (!cert) return; @@ -4751,9 +4760,12 @@ authority_cert_free(authority_cert_t *cert) tor_free(cert); } +#define dir_server_free(val) \ + FREE_AND_NULL(dir_server_t, dir_server_free_, (val)) + /** Free storage held in <b>ds</b>. */ static void -dir_server_free(dir_server_t *ds) +dir_server_free_(dir_server_t *ds) { if (!ds) return; @@ -5195,15 +5207,30 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, SMARTLIST_FOREACH_BEGIN(no_longer_old, signed_descriptor_t *, sd) { const char *msg; was_router_added_t r; + time_t tmp_cert_expiration_time; routerinfo_t *ri = routerlist_reparse_old(rl, sd); if (!ri) { log_warn(LD_BUG, "Failed to re-parse a router."); continue; } + /* need to remember for below, since add_to_routerlist may free. */ + tmp_cert_expiration_time = ri->cert_expiration_time; + r = router_add_to_routerlist(ri, &msg, 1, 0); if (WRA_WAS_OUTDATED(r)) { - log_warn(LD_DIR, "Couldn't add re-parsed router: %s", + log_warn(LD_DIR, "Couldn't add re-parsed router: %s. This isn't " + "usually a big deal, but you should make sure that your " + "clock and timezone are set correctly.", msg?msg:"???"); + if (r == ROUTER_CERTS_EXPIRED) { + char time_cons[ISO_TIME_LEN+1]; + char time_cert_expires[ISO_TIME_LEN+1]; + format_iso_time(time_cons, consensus->valid_after); + format_iso_time(time_cert_expires, tmp_cert_expiration_time); + log_warn(LD_DIR, " (I'm looking at a consensus from %s; This " + "router's certificates began expiring at %s.)", + time_cons, time_cert_expires); + } } } SMARTLIST_FOREACH_END(sd); routerlist_assert_ok(rl); @@ -5380,8 +5407,10 @@ update_extrainfo_downloads(time_t now) smartlist_free(wanted); } -/** Reset the descriptor download failure count on all routers, so that we - * can retry any long-failed routers immediately. +/** Reset the consensus and extra-info download failure count on all routers. + * When we get a new consensus, + * routers_update_status_from_consensus_networkstatus() will reset the + * download statuses on the descriptors in that consensus. */ void router_reset_descriptor_download_failures(void) @@ -5393,6 +5422,8 @@ router_reset_descriptor_download_failures(void) last_descriptor_download_attempted = 0; if (!routerlist) return; + /* We want to download *all* extra-info descriptors, not just those in + * the consensus we currently have (or are about to have) */ SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri, { download_status_reset(&ri->cache_info.ei_dl_status); diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 8384c7eb8c..83f4d1002f 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -96,9 +96,13 @@ MOCK_DECL(signed_descriptor_t *,extrainfo_get_by_descriptor_digest, const char *signed_descriptor_get_body(const signed_descriptor_t *desc); const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc); routerlist_t *router_get_routerlist(void); -void routerinfo_free(routerinfo_t *router); -void extrainfo_free(extrainfo_t *extrainfo); -void routerlist_free(routerlist_t *rl); +void routerinfo_free_(routerinfo_t *router); +#define routerinfo_free(router) \ + FREE_AND_NULL(routerinfo_t, routerinfo_free_, (router)) +void extrainfo_free_(extrainfo_t *extrainfo); +#define extrainfo_free(ei) FREE_AND_NULL(extrainfo_t, extrainfo_free_, (ei)) +void routerlist_free_(routerlist_t *rl); +#define routerlist_free(rl) FREE_AND_NULL(routerlist_t, routerlist_free_, (rl)) void dump_routerlist_mem_usage(int severity); void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old, time_t now); @@ -191,7 +195,9 @@ dir_server_t *fallback_dir_server_new(const tor_addr_t *addr, const char *id_digest, double weight); void dir_server_add(dir_server_t *ent); -void authority_cert_free(authority_cert_t *cert); +void authority_cert_free_(authority_cert_t *cert); +#define authority_cert_free(cert) \ + FREE_AND_NULL(authority_cert_t, authority_cert_free_, (cert)) void clear_dir_servers(void); void update_consensus_router_descriptor_downloads(time_t now, int is_vote, networkstatus_t *consensus); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 15cdb0bbde..f1895ce313 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -2701,8 +2701,10 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->protocols_known = 1; rs->supports_extend2_cells = protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2); - rs->supports_ed25519_link_handshake = + rs->supports_ed25519_link_handshake_compat = protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3); + rs->supports_ed25519_link_handshake_any = + protocol_list_supports_protocol_or_later(tok->args[0], PRT_LINKAUTH, 3); rs->supports_ed25519_hs_intro = protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4); rs->supports_v3_hsdir = @@ -4027,7 +4029,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /** Return the common_digests_t that holds the digests of the * <b>flavor_name</b>-flavored networkstatus according to the detached * signatures document <b>sigs</b>, allocating a new common_digests_t as - * neeeded. */ + * needed. */ static common_digests_t * detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) { @@ -4041,7 +4043,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) /** Return the list of signatures of the <b>flavor_name</b>-flavored * networkstatus according to the detached signatures document <b>sigs</b>, - * allocating a new common_digests_t as neeeded. */ + * allocating a new common_digests_t as needed. */ static smartlist_t * detached_get_signatures(ns_detached_signatures_t *sigs, const char *flavor_name) diff --git a/src/or/routerset.c b/src/or/routerset.c index 54e26ef943..a2599b316c 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -437,7 +437,7 @@ routerset_equal(const routerset_t *old, const routerset_t *new) /** Free all storage held in <b>routerset</b>. */ void -routerset_free(routerset_t *routerset) +routerset_free_(routerset_t *routerset) { if (!routerset) return; diff --git a/src/or/routerset.h b/src/or/routerset.h index d8819ef3fd..53e8c66c5e 100644 --- a/src/or/routerset.h +++ b/src/or/routerset.h @@ -40,7 +40,8 @@ void routerset_subtract_nodes(smartlist_t *out, char *routerset_to_string(const routerset_t *routerset); int routerset_equal(const routerset_t *old, const routerset_t *new); -void routerset_free(routerset_t *routerset); +void routerset_free_(routerset_t *routerset); +#define routerset_free(rs) FREE_AND_NULL(routerset_t, routerset_free_, (rs)) int routerset_len(const routerset_t *set); #ifdef ROUTERSET_PRIVATE diff --git a/src/or/scheduler.c b/src/or/scheduler.c index cbf51447bf..058efc7bee 100644 --- a/src/or/scheduler.c +++ b/src/or/scheduler.c @@ -305,7 +305,7 @@ select_scheduler(void) * wishes of using what it has been configured and don't do a sneaky * fallback. Because this can be changed at runtime, we have to stop tor * right now. */ - exit(1); + exit(1); // XXXX bad exit } /* Set the chosen scheduler. */ @@ -362,6 +362,36 @@ set_scheduler(void) * Functions that can only be accessed from scheduler*.c *****************************************************************************/ +/** Returns human readable string for the given channel scheduler state. */ +const char * +get_scheduler_state_string(int scheduler_state) +{ + switch (scheduler_state) { + case SCHED_CHAN_IDLE: + return "IDLE"; + case SCHED_CHAN_WAITING_FOR_CELLS: + return "WAITING_FOR_CELLS"; + case SCHED_CHAN_WAITING_TO_WRITE: + return "WAITING_TO_WRITE"; + case SCHED_CHAN_PENDING: + return "PENDING"; + default: + return "(invalid)"; + } +} + +/** Helper that logs channel scheduler_state changes. Use this instead of + * setting scheduler_state directly. */ +void +scheduler_set_channel_state(channel_t *chan, int new_state) +{ + log_debug(LD_SCHED, "chan %" PRIu64 " changed from scheduler state %s to %s", + chan->global_identifier, + get_scheduler_state_string(chan->scheduler_state), + get_scheduler_state_string(new_state)); + chan->scheduler_state = new_state; +} + /** Return the pending channel list. */ smartlist_t * get_channels_pending(void) @@ -499,11 +529,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan)) scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p went from pending " - "to waiting_to_write", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } else { /* * It's not in pending, so it can't become waiting_to_write; it's @@ -511,10 +537,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan)) * waiting_for_cells (remove it, can't write any more). */ if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) { - chan->scheduler_state = SCHED_CHAN_IDLE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p left waiting_for_cells", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } } } @@ -537,15 +560,11 @@ scheduler_channel_has_waiting_cells,(channel_t *chan)) * the other lists. It has waiting cells now, so it goes to * channels_pending. */ - chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); smartlist_pqueue_add(channels_pending, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p went from waiting_for_cells " - "to pending", - U64_PRINTF_ARG(chan->global_identifier), chan); /* If we made a channel pending, we potentially have scheduling work to * do. */ the_scheduler->schedule(); @@ -557,10 +576,7 @@ scheduler_channel_has_waiting_cells,(channel_t *chan)) */ if (!(chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE || chan->scheduler_state == SCHED_CHAN_PENDING)) { - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p entered waiting_to_write", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } } @@ -643,7 +659,7 @@ scheduler_release_channel,(channel_t *chan)) if (the_scheduler->on_channel_free) { the_scheduler->on_channel_free(chan); } - chan->scheduler_state = SCHED_CHAN_IDLE; + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } /** Mark a channel as ready to accept writes */ @@ -663,17 +679,11 @@ scheduler_channel_wants_writes(channel_t *chan) /* * It can write now, so it goes to channels_pending. */ - log_debug(LD_SCHED, "chan=%" PRIu64 " became pending", - chan->global_identifier); smartlist_pqueue_add(channels_pending, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); - chan->scheduler_state = SCHED_CHAN_PENDING; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p went from waiting_to_write " - "to pending", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); /* We just made a channel pending, we have scheduling work to do. */ the_scheduler->schedule(); } else { @@ -683,10 +693,7 @@ scheduler_channel_wants_writes(channel_t *chan) */ if (!(chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS || chan->scheduler_state == SCHED_CHAN_PENDING)) { - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p entered waiting_for_cells", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } } } @@ -703,11 +710,12 @@ scheduler_bug_occurred(const channel_t *chan) const size_t outbuf_len = buf_datalen(TO_CONN(BASE_CHAN_TO_TLS((channel_t *) chan)->conn)->outbuf); tor_snprintf(buf, sizeof(buf), - "Channel %" PRIu64 " in state %s and scheduler state %d." + "Channel %" PRIu64 " in state %s and scheduler state %s." " Num cells on cmux: %d. Connection outbuf len: %lu.", chan->global_identifier, channel_state_to_string(chan->state), - chan->scheduler_state, circuitmux_num_cells(chan->cmux), + get_scheduler_state_string(chan->scheduler_state), + circuitmux_num_cells(chan->cmux), (unsigned long)outbuf_len); } diff --git a/src/or/scheduler.h b/src/or/scheduler.h index 47c98f096a..ac405c21b7 100644 --- a/src/or/scheduler.h +++ b/src/or/scheduler.h @@ -144,6 +144,9 @@ MOCK_DECL(void, scheduler_channel_has_waiting_cells, (channel_t *chan)); * Defined in scheduler.c *********************************/ +void scheduler_set_channel_state(channel_t *chan, int new_state); +const char *get_scheduler_state_string(int scheduler_state); + /* Triggers a BUG() and extra information with chan if available. */ #define SCHED_BUG(cond, chan) \ (PREDICT_UNLIKELY(cond) ? \ diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c index f50f3aa9e9..246c73009a 100644 --- a/src/or/scheduler_kist.c +++ b/src/or/scheduler_kist.c @@ -474,7 +474,7 @@ kist_free_all(void) /* Function of the scheduler interface: on_channel_free() */ static void -kist_on_channel_free(const channel_t *chan) +kist_on_channel_free_fn(const channel_t *chan) { free_socket_info_by_chan(&socket_table, chan); } @@ -624,7 +624,7 @@ kist_scheduler_run(void) if (!CHANNEL_IS_OPEN(chan)) { /* Channel isn't open so we put it back in IDLE mode. It is either * renegotiating its TLS session or about to be released. */ - chan->scheduler_state = SCHED_CHAN_IDLE; + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); continue; } /* flush_result has the # cells flushed */ @@ -640,12 +640,12 @@ kist_scheduler_run(void) log_debug(LD_SCHED, "We didn't flush anything on a chan that we think " "can write and wants to write. The channel's state is '%s' " - "and in scheduler state %d. We're going to mark it as " + "and in scheduler state '%s'. We're going to mark it as " "waiting_for_cells (as that's most likely the issue) and " "stop scheduling it this round.", channel_state_to_string(chan->state), - chan->scheduler_state); - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; + get_scheduler_state_string(chan->scheduler_state)); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); continue; } } @@ -672,16 +672,12 @@ kist_scheduler_run(void) * SCHED_CHAN_WAITING_FOR_CELLS to SCHED_CHAN_IDLE and seeing if Tor * starts having serious throughput issues. Best done in shadow/chutney. */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells", - chan->global_identifier); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else if (!channel_more_to_flush(chan)) { /* Case 2: no more cells to send, but still open for writes */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells", - chan->global_identifier); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else if (!socket_can_write(&socket_table, chan)) { /* Case 3: cells to send, but cannot write */ @@ -693,18 +689,16 @@ kist_scheduler_run(void) * after the scheduling loop is over. They can hopefully be taken care of * in the next scheduling round. */ - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); if (!to_readd) { to_readd = smartlist_new(); } smartlist_add(to_readd, chan); - log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_to_write", - chan->global_identifier); } else { /* Case 4: cells to send, and still open for writes */ - chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); smartlist_pqueue_add(cp, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); } @@ -724,7 +718,7 @@ kist_scheduler_run(void) /* Re-add any channels we need to */ if (to_readd) { SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) { - readd_chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING); if (!smartlist_contains(cp, readd_chan)) { smartlist_pqueue_add(cp, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), readd_chan); @@ -744,7 +738,7 @@ kist_scheduler_run(void) static scheduler_t kist_scheduler = { .type = SCHEDULER_KIST, .free_all = kist_free_all, - .on_channel_free = kist_on_channel_free, + .on_channel_free = kist_on_channel_free_fn, .init = kist_scheduler_init, .on_new_consensus = kist_scheduler_on_new_consensus, .schedule = kist_scheduler_schedule, diff --git a/src/or/scheduler_vanilla.c b/src/or/scheduler_vanilla.c index 303b3dbba8..7a83b9da18 100644 --- a/src/or/scheduler_vanilla.c +++ b/src/or/scheduler_vanilla.c @@ -89,12 +89,7 @@ vanilla_scheduler_run(void) if (flushed < n_cells) { /* We ran out of cells to flush */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "entered waiting_for_cells from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else { /* The channel may still have some cells */ if (channel_more_to_flush(chan)) { @@ -110,12 +105,7 @@ vanilla_scheduler_run(void) chan); } else { /* It's waiting to be able to write more */ - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "entered waiting_to_write from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } else { /* No cells left; it can go to idle or waiting_for_cells */ @@ -124,23 +114,13 @@ vanilla_scheduler_run(void) * It can still accept writes, so it goes to * waiting_for_cells */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "entered waiting_for_cells from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else { /* * We exactly filled up the output queue with all available * cells; go to idle. */ - chan->scheduler_state = SCHED_CHAN_IDLE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "become idle from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } } } @@ -156,14 +136,14 @@ vanilla_scheduler_run(void) "no cells writeable", U64_PRINTF_ARG(chan->global_identifier), chan); /* Put it back to WAITING_TO_WRITE */ - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } /* Readd any channels we need to */ if (to_readd) { SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) { - readd_chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING); smartlist_pqueue_add(cp, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), diff --git a/src/or/shared_random.c b/src/or/shared_random.c index b3f62a8fd8..7723ad4611 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -384,7 +384,7 @@ commit_encode(const sr_commit_t *commit, char *dst, size_t len) static void sr_cleanup(void) { - sr_state_free(); + sr_state_free_all(); } /* Using <b>commit</b>, return a newly allocated string containing the commit @@ -897,7 +897,7 @@ sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) /* Free a commit object. */ void -sr_commit_free(sr_commit_t *commit) +sr_commit_free_(sr_commit_t *commit) { if (commit == NULL) { return; diff --git a/src/or/shared_random.h b/src/or/shared_random.h index c0992489cb..675a8d8b06 100644 --- a/src/or/shared_random.h +++ b/src/or/shared_random.h @@ -113,7 +113,8 @@ sr_srv_t *sr_parse_srv(const smartlist_t *args); char *sr_get_string_for_vote(void); char *sr_get_string_for_consensus(const smartlist_t *votes, int32_t num_srv_agreements); -void sr_commit_free(sr_commit_t *commit); +void sr_commit_free_(sr_commit_t *commit); +#define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr)) void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); /* Private methods (only used by shared_random_state.c): */ diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c index ae904cfda3..d6109b2de7 100644 --- a/src/or/shared_random_state.c +++ b/src/or/shared_random_state.c @@ -271,12 +271,15 @@ commit_add_to_state(sr_commit_t *commit, sr_state_t *state) static void commit_free_(void *p) { - sr_commit_free(p); + sr_commit_free_(p); } +#define state_free(val) \ + FREE_AND_NULL(sr_state_t, state_free_, (val)) + /* Free a state that was allocated with state_new(). */ static void -state_free(sr_state_t *state) +state_free_(sr_state_t *state) { if (state == NULL) { return; @@ -318,9 +321,12 @@ state_set(sr_state_t *state) sr_state = state; } +#define disk_state_free(val) \ + FREE_AND_NULL(sr_disk_state_t, disk_state_free_, (val)) + /* Free an allocated disk state. */ static void -disk_state_free(sr_disk_state_t *state) +disk_state_free_(sr_disk_state_t *state) { if (state == NULL) { return; @@ -1286,7 +1292,7 @@ sr_state_srv_is_fresh(void) /* Cleanup and free our disk and memory state. */ void -sr_state_free(void) +sr_state_free_all(void) { state_free(sr_state); disk_state_free(sr_disk_state); diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h index 866725c435..fdbbf4919a 100644 --- a/src/or/shared_random_state.h +++ b/src/or/shared_random_state.h @@ -119,7 +119,7 @@ void sr_state_unset_fresh_srv(void); int sr_state_init(int save_to_disk, int read_from_disk); int sr_state_is_initialized(void); void sr_state_save(void); -void sr_state_free(void); +void sr_state_free_all(void); time_t sr_state_get_start_time_of_current_protocol_run(time_t now); unsigned int sr_state_get_phase_duration(void); diff --git a/src/or/statefile.c b/src/or/statefile.c index 97bd9cac36..cc114f0a2b 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -681,7 +681,7 @@ save_transport_to_state(const char *transport, } STATIC void -or_state_free(or_state_t *state) +or_state_free_(or_state_t *state) { if (!state) return; diff --git a/src/or/statefile.h b/src/or/statefile.h index 574afb3622..b4cc4d1dc6 100644 --- a/src/or/statefile.h +++ b/src/or/statefile.h @@ -20,7 +20,8 @@ void or_state_free_all(void); #ifdef STATEFILE_PRIVATE STATIC config_line_t *get_transport_in_state_by_name(const char *transport); -STATIC void or_state_free(or_state_t *state); +STATIC void or_state_free_(or_state_t *state); +#define or_state_free(st) FREE_AND_NULL(or_state_t, or_state_free_, (st)) STATIC or_state_t *or_state_new(void); #endif diff --git a/src/or/status.c b/src/or/status.c index f7be41e412..1a8885e83e 100644 --- a/src/or/status.c +++ b/src/or/status.c @@ -157,6 +157,20 @@ log_heartbeat(time_t now) tor_free(msg); } + if (options->MainloopStats) { + const uint64_t main_loop_success_count = get_main_loop_success_count(); + const uint64_t main_loop_error_count = get_main_loop_error_count(); + const uint64_t main_loop_idle_count = get_main_loop_idle_count(); + + log_fn(LOG_NOTICE, LD_HEARTBEAT, "Main event loop statistics: " + U64_FORMAT " succesful returns, " + U64_FORMAT " erroneous returns, and " + U64_FORMAT " idle returns.", + U64_PRINTF_ARG(main_loop_success_count), + U64_PRINTF_ARG(main_loop_error_count), + U64_PRINTF_ARG(main_loop_idle_count)); + } + tor_free(uptime); tor_free(bw_sent); tor_free(bw_rcvd); diff --git a/src/or/tor_api.c b/src/or/tor_api.c new file mode 100644 index 0000000000..4260cc88f4 --- /dev/null +++ b/src/or/tor_api.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tor_api.c + **/ + +#include "tor_api.h" +#include "tor_api_internal.h" + +// Include this after the above headers, to insure that they don't +// depend on anything else. +#include "orconfig.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// We don't want to use tor_malloc and tor_free here, since this needs +// to run before anything is initialized at all, and ought to run when +// we're not linked to anything at all. + +#define raw_malloc malloc +#define raw_free free + +tor_main_configuration_t * +tor_main_configuration_new(void) +{ + static const char *fake_argv[] = { "tor" }; + tor_main_configuration_t *cfg = raw_malloc(sizeof(*cfg)); + if (cfg == NULL) + return NULL; + + memset(cfg, 0, sizeof(*cfg)); + + cfg->argc = 1; + cfg->argv = (char **) fake_argv; + + return cfg; +} + +int +tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]) +{ + if (cfg == NULL) + return -1; + cfg->argc = argc; + cfg->argv = argv; + return 0; +} + +void +tor_main_configuration_free(tor_main_configuration_t *cfg) +{ + if (cfg == NULL) + return; + raw_free(cfg); +} + +/* Main entry point for the Tor process. Called from main(). + * + * This function is distinct from main() only so we can link main.c into + * the unittest binary without conflicting with the unittests' main. + * + * Some embedders have historically called this function; but that usage is + * deprecated: they should use tor_run_main() instead. + */ +int +tor_main(int argc, char *argv[]) +{ + tor_main_configuration_t *cfg = tor_main_configuration_new(); + if (!cfg) { + puts("INTERNAL ERROR: Allocation failure. Cannot proceed"); + return 1; + } + if (tor_main_configuration_set_command_line(cfg, argc, argv) < 0) { + puts("INTERNAL ERROR: Can't set command line. Cannot proceed."); + return 1; + } + int rv = tor_run_main(cfg); + tor_main_configuration_free(cfg); + return rv; +} + diff --git a/src/or/tor_api.h b/src/or/tor_api.h new file mode 100644 index 0000000000..7e86c7fec5 --- /dev/null +++ b/src/or/tor_api.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tor_api.h + * \brief Public C API for the Tor network service. + * + * This interface is intended for use by programs that need to link Tor as + * a library, and launch it in a separate thread. If you have the ability + * to run Tor as a separate executable, you should probably do that instead + * of embedding it as a library. + * + * To use this API, first construct a tor_main_configuration_t object using + * tor_main_configuration_new(). Then, you use one or more other function + * calls (such as tor_main_configuration_set_command_line() to configure how + * Tor should be run. Finally, you pass the configuration object to + * tor_run_main(). + * + * At this point, tor_run_main() will block its thread to run a Tor daemon; + * when the Tor daemon exits, it will return. See notes on bugs and + * limitations below. + * + * There is no other public C API to Tor: calling any C Tor function not + * documented in this file is not guaranteed to be stable. + **/ + +#ifndef TOR_API_H +#define TOR_API_H + +typedef struct tor_main_configuration_t tor_main_configuration_t; + +/** + * Create and return a new tor_main_configuration(). + */ +tor_main_configuration_t *tor_main_configuration_new(void); + +/** + * Set the command-line arguments in <b>cfg</b>. + * + * The <b>argc</b> and <b>argv</b> values here are as for main(). The + * contents of the argv pointer must remain unchanged until tor_run_main() has + * finished and you call tor_main_configuration_free(). + * + * Return 0 on success, -1 on failure. + */ +int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]); + +/** + * Release all storage held in <b>cfg</b>. + * + * Once you have passed a tor_main_configuration_t to tor_run_main(), you + * must not free it until tor_run_main() has finished. + */ +void tor_main_configuration_free(tor_main_configuration_t *cfg); + +/** + * Run the tor process, as if from the command line. + * + * The command line arguments from tor_main_configuration_set_command_line() + * are taken as if they had been passed to main(). + * + * This function will not return until Tor is done running. It returns zero + * on success, and nonzero on failure. + * + * BUG 23848: In many cases, tor_main will call exit() or abort() instead of + * returning. This is not the intended long-term behavior; we are trying to + * fix it. + * + * BUG 23847: You can only call tor_main() once in a single process; if it + * returns and you call it again, you may crash, or you may encounter other + * unexpected behavior, including possible security issues. This is not + * intended long-term behavior; we are trying to fix it. + * + * LIMITATION: You cannot run more than one instance of Tor in the same + * process at the same time. Concurrent calls will cause undefined behavior. + * We do not currently have plans to change this. + * + * LIMITATION: While we will try to fix any problems found here, you + * should be aware that Tor was originally written to run as its own + * process, and that the functionality of this file was added later. If + * you find any bugs or strange behavior, please report them, and we'll + * try to straighten them out. + */ +int tor_run_main(const tor_main_configuration_t *); + +/** + * Run the tor process, as if from the command line. + * + * @deprecated Using this function from outside Tor is deprecated; you should + * use tor_run_main() instead. + * + * BUGS: This function has all the same bugs as tor_run_main(). + * + * LIMITATIONS: This function has all the limitations of tor_run_main(). + */ +int tor_main(int argc, char **argv); + +#endif /* !defined(TOR_API_H) */ + diff --git a/src/or/tor_api_internal.h b/src/or/tor_api_internal.h new file mode 100644 index 0000000000..10b6278b7b --- /dev/null +++ b/src/or/tor_api_internal.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_API_INTERNAL_H +#define TOR_API_INTERNAL_H + +/* The contents of this type are private; don't mess with them from outside + * Tor. */ +struct tor_main_configuration_t { + /** As in main() */ + int argc; + /** As in main(). This pointer is owned by the caller */ + char **argv; +}; + +#endif /* !defined(TOR_API_INTERNAL_H) */ + diff --git a/src/or/tor_main.c b/src/or/tor_main.c index a3a8838602..703669ac99 100644 --- a/src/or/tor_main.c +++ b/src/or/tor_main.c @@ -3,17 +3,10 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; - -/** String describing which Tor Git repository version the source was - * built from. This string is generated by a bit of shell kludging in - * src/or/include.am, and is usually right. - */ -const char tor_git_revision[] = -#ifndef _MSC_VER -#include "micro-revision.i" +#include "orconfig.h" +#ifdef ENABLE_RESTART_DEBUGGING +#include <stdlib.h> #endif - ""; /** * \file tor_main.c @@ -26,14 +19,23 @@ const char tor_git_revision[] = int tor_main(int argc, char *argv[]); /** We keep main() in a separate file so that our unit tests can use - * functions from main.c) + * functions from main.c. */ int main(int argc, char *argv[]) { - int r = tor_main(argc, argv); + int r; +#ifdef ENABLE_RESTART_DEBUGGING + int restart_count = getenv("TOR_DEBUG_RESTART") ? 1 : 0; + again: +#endif + r = tor_main(argc, argv); if (r < 0 || r > 255) return 1; +#ifdef ENABLE_RESTART_DEBUGGING + else if (r == 0 && restart_count--) + goto again; +#endif else return r; } diff --git a/src/or/torcert.c b/src/or/torcert.c index befb39d6e8..bd677d1f4a 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -137,7 +137,7 @@ tor_cert_create(const ed25519_keypair_t *signing_key, /** Release all storage held for <b>cert</b>. */ void -tor_cert_free(tor_cert_t *cert) +tor_cert_free_(tor_cert_t *cert) { if (! cert) return; @@ -453,7 +453,7 @@ or_handshake_certs_new(void) /** Release all storage held in <b>certs</b> */ void -or_handshake_certs_free(or_handshake_certs_t *certs) +or_handshake_certs_free_(or_handshake_certs_t *certs) { if (!certs) return; diff --git a/src/or/torcert.h b/src/or/torcert.h index c77ae2089d..0a8a252049 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -57,7 +57,8 @@ tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key, tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen); -void tor_cert_free(tor_cert_t *cert); +void tor_cert_free_(tor_cert_t *cert); +#define tor_cert_free(cert) FREE_AND_NULL(tor_cert_t, tor_cert_free_, (cert)) int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, const tor_cert_t *out, @@ -83,7 +84,9 @@ rsa_ed25519_crosscert_check, (const uint8_t *crosscert, const time_t reject_if_expired_before)); or_handshake_certs_t *or_handshake_certs_new(void); -void or_handshake_certs_free(or_handshake_certs_t *certs); +void or_handshake_certs_free_(or_handshake_certs_t *certs); +#define or_handshake_certs_free(certs) \ + FREE_AND_NULL(or_handshake_certs_t, or_handshake_certs_free_, (certs)) int or_handshake_certs_rsa_ok(int severity, or_handshake_certs_t *certs, tor_tls_t *tls, diff --git a/src/or/transports.c b/src/or/transports.c index 5fb24e11a0..1e6307b7d0 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -154,7 +154,7 @@ transport_new(const tor_addr_t *addr, uint16_t port, /** Free the pluggable transport struct <b>transport</b>. */ void -transport_free(transport_t *transport) +transport_free_(transport_t *transport) { if (!transport) return; diff --git a/src/or/transports.h b/src/or/transports.h index e368e447c3..1b2786472c 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -35,7 +35,8 @@ void sweep_transport_list(void); MOCK_DECL(int, transport_add_from_config, (const tor_addr_t *addr, uint16_t port, const char *name, int socks_ver)); -void transport_free(transport_t *transport); +void transport_free_(transport_t *transport); +#define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr)) transport_t *transport_get_by_name(const char *name); |