Loading changes/bug4483-multiple-consensus-downloads 0 → 100644 +9 −0 Original line number Diff line number Diff line o Major features (consensus downloads): - Schedule multiple in-progress consensus downloads during client bootstrap. Use the first one that starts downloading, close the rest. This reduces failures when authorities are slow or down. With #15775, it reduces failures due to fallback churn. Implements #4483 (reduce failures when authorities are down). Patch by "teor". Implements IPv4 portions of proposal #210 by "mikeperry" and "teor". doc/tor.1.txt +59 −2 Original line number Diff line number Diff line Loading @@ -360,7 +360,11 @@ GENERAL OPTIONS [[FallbackDir]] **FallbackDir** __address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__]:: When we're unable to connect to any directory cache for directory info (usually because we don't know about any yet) we try a FallbackDir. (usually because we don't know about any yet) we try a directory authority. Clients also simultaneously try a FallbackDir, to avoid hangs on client startup if a directory authority is down. Clients retry FallbackDirs more often than directory authorities, to reduce the load on the directory authorities. By default, the directory authorities are also FallbackDirs. Specifying a FallbackDir replaces Tor's default hard-coded FallbackDirs (if any). Loading Loading @@ -2287,10 +2291,18 @@ The following options are used for running a testing Tor network. TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 TestingClientBootstrapConsensusAuthorityDownloadSchedule 0, 2, 4 (for 40 seconds), 8, 16, 32, 60 TestingClientBootstrapConsensusFallbackDownloadSchedule 0, 1, 4 (for 40 seconds), 8, 16, 32, 60 TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule 0, 1, 4 (for 40 seconds), 8, 16, 32, 60 TestingBridgeDownloadSchedule 60, 30, 30, 60 TestingClientMaxIntervalWithoutRequest 5 seconds TestingDirConnectionMaxStall 30 seconds TestingConsensusMaxDownloadTries 80 TestingClientBootstrapConsensusMaxDownloadTries 80 TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 80 TestingDescriptorMaxDownloadTries 80 TestingMicrodescMaxDownloadTries 80 TestingCertMaxDownloadTries 80 Loading Loading @@ -2351,6 +2363,36 @@ The following options are used for running a testing Tor network. requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600, 1800, 3600, 3600, 3600, 10800, 21600, 43200) [[TestingClientBootstrapConsensusAuthorityDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients fetching from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by connection failures. Changing this schedule requires that **TestingTorNetwork** is set. (Default: 10, 11, 3600, 10800, 25200, 54000, 111600, 262800) [[TestingClientBootstrapConsensusFallbackDownloadSchedule]] **TestingClientBootstrapConsensusFallbackDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download consensuses from fallback directory mirrors if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients fetching from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by connection failures. Changing this schedule requires that **TestingTorNetwork** is set. (Default: 0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800) [[TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients which don't have or won't fetch from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by connection failures. Changing this schedule requires that **TestingTorNetwork** is set. (Default: 0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800) [[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download bridge descriptors. Changing this requires that **TestingTorNetwork** is set. (Default: 3600, 900, 900, 3600) Loading @@ -2367,9 +2409,24 @@ The following options are used for running a testing Tor network. 5 minutes) [[TestingConsensusMaxDownloadTries]] **TestingConsensusMaxDownloadTries** __NUM__:: Try this often to download a consensus before giving up. Changing Try this many times to download a consensus before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 8) [[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__:: Try this many times to download a consensus while bootstrapping using fallback directory mirrors before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 7) [[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__:: Try this many times to download a consensus while bootstrapping using only authorities before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 4) [[TestingClientBootstrapConsensusMaxInProgressTries]] **TestingClientBootstrapConsensusMaxInProgressTries** __NUM__:: Try this many simultaneous connections to download a consensus before waiting for one to complete, timeout, or error out. Changing this requires that **TestingTorNetwork** is set. (Default: 4) [[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__:: Try this often to download a server descriptor before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 8) Loading src/common/torint.h +26 −0 Original line number Diff line number Diff line Loading @@ -336,6 +336,32 @@ typedef uint32_t uintptr_t; #endif /* time_t_is_signed */ #endif /* ifndef(TIME_MAX) */ #ifndef TIME_MIN #ifdef TIME_T_IS_SIGNED #if (SIZEOF_TIME_T == SIZEOF_INT) #define TIME_MIN ((time_t)INT_MIN) #elif (SIZEOF_TIME_T == SIZEOF_LONG) #define TIME_MIN ((time_t)LONG_MIN) #elif (SIZEOF_TIME_T == 8) #define TIME_MIN ((time_t)INT64_MIN) #else #error "Can't define (signed) TIME_MIN" #endif #else /* Unsigned case */ #if (SIZEOF_TIME_T == 4) #define TIME_MIN ((time_t)UINT32_MIN) #elif (SIZEOF_TIME_T == 8) #define TIME_MIN ((time_t)UINT64_MIN) #else #error "Can't define (unsigned) TIME_MIN" #endif #endif /* time_t_is_signed */ #endif /* ifndef(TIME_MIN) */ #ifndef SIZE_MAX #if (SIZEOF_SIZE_T == 4) #define SIZE_MAX UINT32_MAX Loading src/or/config.c +75 −1 Original line number Diff line number Diff line Loading @@ -476,10 +476,40 @@ static config_var_t option_vars_[] = { V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " "300, 600, 1800, 3600, 3600, 3600, " "10800, 21600, 43200"), /* With the TestingClientBootstrapConsensus*Download* below: * Clients with only authorities will try: * - 3 authorities over 10 seconds, then wait 60 minutes. * Clients with authorities and fallbacks will try: * - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes. * Clients will also retry when an application request arrives. * After a number of failed reqests, clients retry every 3 days + 1 hour. * * Clients used to try 2 authorities over 10 seconds, then wait for * 60 minutes or an application request. * * When clients have authorities and fallbacks available, they use these * schedules: (we stagger the times to avoid thundering herds) */ V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"), /* When clients only have authorities available, they use this schedule: */ V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"), /* We don't want to overwhelm slow networks (or mirrors whose replies are * blocked), but we also don't want to fail if only some mirrors are * blackholed. Clients will try 3 directories simultaneously. * (Relays never use simultaneous connections.) */ V(TestingClientBootstrapConsensusMaxInProgressTries, UINT, "3"), V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), V(TestingConsensusMaxDownloadTries, UINT, "8"), /* Since we try connections rapidly and simultaneously, we can afford * to give up earlier. (This protects against overloading directories.) */ V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "7"), /* We want to give up much earlier if we're only using authorities. */ V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"), V(TestingDescriptorMaxDownloadTries, UINT, "8"), V(TestingMicrodescMaxDownloadTries, UINT, "8"), V(TestingCertMaxDownloadTries, UINT, "8"), Loading Loading @@ -526,10 +556,18 @@ static const config_var_t testing_tor_network_defaults[] = { "15, 20, 30, 60"), V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " "15, 20, 30, 60"), V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"), V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"), V(TestingConsensusMaxDownloadTries, UINT, "80"), V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "80"), V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"), V(TestingDescriptorMaxDownloadTries, UINT, "80"), V(TestingMicrodescMaxDownloadTries, UINT, "80"), V(TestingCertMaxDownloadTries, UINT, "80"), Loading Loading @@ -3758,10 +3796,16 @@ options_validate(or_options_t *old_options, or_options_t *options, CHECK_DEFAULT(TestingClientDownloadSchedule); CHECK_DEFAULT(TestingServerConsensusDownloadSchedule); CHECK_DEFAULT(TestingClientConsensusDownloadSchedule); CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityDownloadSchedule); CHECK_DEFAULT(TestingClientBootstrapConsensusFallbackDownloadSchedule); CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule); CHECK_DEFAULT(TestingBridgeDownloadSchedule); CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); CHECK_DEFAULT(TestingDirConnectionMaxStall); CHECK_DEFAULT(TestingConsensusMaxDownloadTries); CHECK_DEFAULT(TestingClientBootstrapConsensusMaxDownloadTries); CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries); CHECK_DEFAULT(TestingClientBootstrapConsensusMaxInProgressTries); CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); CHECK_DEFAULT(TestingCertMaxDownloadTries); Loading Loading @@ -3836,11 +3880,41 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingConsensusMaxDownloadTries < 2) { REJECT("TestingConsensusMaxDownloadTries must be greater than 1."); REJECT("TestingConsensusMaxDownloadTries must be greater than 2."); } else if (options->TestingConsensusMaxDownloadTries > 800) { COMPLAIN("TestingConsensusMaxDownloadTries is insanely high."); } if (options->TestingClientBootstrapConsensusMaxDownloadTries < 2) { REJECT("TestingClientBootstrapConsensusMaxDownloadTries must be greater " "than 2." ); } else if (options->TestingClientBootstrapConsensusMaxDownloadTries > 800) { COMPLAIN("TestingClientBootstrapConsensusMaxDownloadTries is insanely " "high."); } if (options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries < 2) { REJECT("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must " "be greater than 2." ); } else if ( options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries > 800) { COMPLAIN("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is " "insanely high."); } if (options->TestingClientBootstrapConsensusMaxInProgressTries < 1) { REJECT("TestingClientBootstrapConsensusMaxInProgressTries must be greater " "than 0."); } else if (options->TestingClientBootstrapConsensusMaxInProgressTries > 100) { COMPLAIN("TestingClientBootstrapConsensusMaxInProgressTries is insanely " "high."); } if (options->TestingDescriptorMaxDownloadTries < 2) { REJECT("TestingDescriptorMaxDownloadTries must be greater than 1."); } else if (options->TestingDescriptorMaxDownloadTries > 800) { Loading src/or/connection.c +153 −77 Original line number Diff line number Diff line Loading @@ -1618,13 +1618,18 @@ connection_init_accepted_conn(connection_t *conn, return 0; } static int connection_connect_sockaddr(connection_t *conn, /** Take conn, make a nonblocking socket; try to connect to * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if * applicable put your best guess about errno into *<b>socket_error</b>. * If connected return 1, if EAGAIN return 0. */ MOCK_IMPL(STATIC int, connection_connect_sockaddr,(connection_t *conn, const struct sockaddr *sa, socklen_t sa_len, const struct sockaddr *bindaddr, socklen_t bindaddr_len, int *socket_error) int *socket_error)) { tor_socket_t s; int inprogress = 0; Loading Loading @@ -4222,6 +4227,19 @@ connection_write_to_buf_impl_,(const char *string, size_t len, } } /** Return a connection_t * from get_connection_array() that satisfies test on * var, and that is not marked for close. */ #define CONN_GET_TEMPLATE(var, test) \ STMT_BEGIN \ smartlist_t *conns = get_connection_array(); \ SMARTLIST_FOREACH(conns, connection_t *, var, \ { \ if (var && (test) && !var->marked_for_close) \ return var; \ }); \ return NULL; \ STMT_END /** Return a connection with given type, address, port, and purpose; * or NULL if no such connection exists (or if all such connections are marked * for close). */ Loading @@ -4230,17 +4248,11 @@ connection_get_by_type_addr_port_purpose(int type, const tor_addr_t *addr, uint16_t port, int purpose) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == type && CONN_GET_TEMPLATE(conn, (conn->type == type && tor_addr_eq(&conn->addr, addr) && conn->port == port && conn->purpose == purpose && !conn->marked_for_close) return conn; }); return NULL; conn->purpose == purpose)); } /** Return the stream with id <b>id</b> if it is not already marked for Loading @@ -4249,13 +4261,7 @@ connection_get_by_type_addr_port_purpose(int type, connection_t * connection_get_by_global_id(uint64_t id) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->global_identifier == id) return conn; }); return NULL; CONN_GET_TEMPLATE(conn, conn->global_identifier == id); } /** Return a connection of type <b>type</b> that is not marked for close. Loading @@ -4263,13 +4269,7 @@ connection_get_by_global_id(uint64_t id) connection_t * connection_get_by_type(int type) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == type && !conn->marked_for_close) return conn; }); return NULL; CONN_GET_TEMPLATE(conn, conn->type == type); } /** Return a connection of type <b>type</b> that is in state <b>state</b>, Loading @@ -4278,13 +4278,7 @@ connection_get_by_type(int type) connection_t * connection_get_by_type_state(int type, int state) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == type && conn->state == state && !conn->marked_for_close) return conn; }); return NULL; CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state); } /** Return a connection of type <b>type</b> that has rendquery equal Loading @@ -4295,55 +4289,142 @@ connection_t * connection_get_by_type_state_rendquery(int type, int state, const char *rendquery) { smartlist_t *conns = get_connection_array(); tor_assert(type == CONN_TYPE_DIR || type == CONN_TYPE_AP || type == CONN_TYPE_EXIT); tor_assert(rendquery); SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { if (conn->type == type && !conn->marked_for_close && (!state || state == conn->state)) { if (type == CONN_TYPE_DIR && CONN_GET_TEMPLATE(conn, (conn->type == type && (!state || state == conn->state)) && ( (type == CONN_TYPE_DIR && TO_DIR_CONN(conn)->rend_data && !rend_cmp_service_ids(rendquery, TO_DIR_CONN(conn)->rend_data->onion_address)) return conn; else if (CONN_IS_EDGE(conn) && || (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->rend_data && !rend_cmp_service_ids(rendquery, TO_EDGE_CONN(conn)->rend_data->onion_address)) return conn; } } SMARTLIST_FOREACH_END(conn); return NULL; )); } #define CONN_FIRST_AND_FREE_TEMPLATE(sl) \ STMT_BEGIN \ if (smartlist_len(sl) > 0) { \ void *first_item = smartlist_get(sl, 0); \ smartlist_free(sl); \ return first_item; \ } else { \ smartlist_free(sl); \ return NULL; \ } \ STMT_END /** Return a directory connection (if any one exists) that is fetching * the item described by <b>state</b>/<b>resource</b> */ * the item described by <b>purpose</b>/<b>resource</b>, otherwise return NULL. */ dir_connection_t * connection_dir_get_by_purpose_and_resource(int purpose, connection_dir_get_by_purpose_and_resource( int purpose, const char *resource) { smartlist_t *conns = get_connection_array(); smartlist_t *conns = connection_dir_list_by_purpose_and_resource( purpose, resource); CONN_FIRST_AND_FREE_TEMPLATE(conns); } /** Return a new smartlist of dir_connection_t * from get_connection_array() * that satisfy conn_test on connection_t *conn_var, and dirconn_test on * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not * marked for close to be included in the list. */ #define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test, \ dirconn_var, dirconn_test) \ STMT_BEGIN \ smartlist_t *conns = get_connection_array(); \ smartlist_t *dir_conns = smartlist_new(); \ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) { \ if (conn_var && (conn_test) \ && conn_var->type == CONN_TYPE_DIR \ && !conn_var->marked_for_close) { \ dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var); \ if (dirconn_var && (dirconn_test)) { \ smartlist_add(dir_conns, dirconn_var); \ } \ } \ } SMARTLIST_FOREACH_END(conn_var); \ return dir_conns; \ STMT_END /** Return a list of directory connections that are fetching the item * described by <b>purpose</b>/<b>resource</b>. If there are none, * return an empty list. This list must be freed using smartlist_free, * but the pointers in it must not be freed. * Note that this list should not be cached, as the pointers in it can be * freed if their connections close. */ smartlist_t * connection_dir_list_by_purpose_and_resource( int purpose, const char *resource) { DIR_CONN_LIST_TEMPLATE(conn, conn->purpose == purpose, dirconn, 0 == strcmp_opt(resource, dirconn->requested_resource)); } SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { dir_connection_t *dirconn; if (conn->type != CONN_TYPE_DIR || conn->marked_for_close || conn->purpose != purpose) continue; dirconn = TO_DIR_CONN(conn); if (dirconn->requested_resource == NULL) { if (resource == NULL) return dirconn; } else if (resource) { if (0 == strcmp(resource, dirconn->requested_resource)) return dirconn; /** Return a directory connection (if any one exists) that is fetching * the item described by <b>purpose</b>/<b>resource</b>/<b>state</b>, * otherwise return NULL. */ dir_connection_t * connection_dir_get_by_purpose_resource_and_state( int purpose, const char *resource, int state) { smartlist_t *conns = connection_dir_list_by_purpose_resource_and_state( purpose, resource, state); CONN_FIRST_AND_FREE_TEMPLATE(conns); } #undef CONN_FIRST_AND_FREE_TEMPLATE /** Return a list of directory connections that are fetching the item * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are * none, return an empty list. This list must be freed using smartlist_free, * but the pointers in it must not be freed. * Note that this list should not be cached, as the pointers in it can be * freed if their connections close. */ smartlist_t * connection_dir_list_by_purpose_resource_and_state( int purpose, const char *resource, int state) { DIR_CONN_LIST_TEMPLATE(conn, conn->purpose == purpose && conn->state == state, dirconn, 0 == strcmp_opt(resource, dirconn->requested_resource)); } } SMARTLIST_FOREACH_END(conn); return NULL; #undef DIR_CONN_LIST_TEMPLATE /** Return an arbitrary active OR connection that isn't <b>this_conn</b>. * * We use this to guess if we should tell the controller that we * didn't manage to connect to any of our bridges. */ static connection_t * connection_get_another_active_or_conn(const or_connection_t *this_conn) { CONN_GET_TEMPLATE(conn, conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR); } /** Return 1 if there are any active OR connections apart from Loading @@ -4354,23 +4435,18 @@ connection_dir_get_by_purpose_and_resource(int purpose, int any_other_active_or_conns(const or_connection_t *this_conn) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { if (conn == TO_CONN(this_conn)) { /* don't consider this conn */ continue; } if (conn->type == CONN_TYPE_OR && !conn->marked_for_close) { connection_t *conn = connection_get_another_active_or_conn(this_conn); if (conn != NULL) { log_debug(LD_DIR, "%s: Found an OR connection: %s", __func__, conn->address); return 1; } } SMARTLIST_FOREACH_END(conn); return 0; } #undef CONN_GET_TEMPLATE /** Return 1 if <b>conn</b> is a listener conn, else return 0. */ int connection_is_listener(connection_t *conn) Loading Loading
changes/bug4483-multiple-consensus-downloads 0 → 100644 +9 −0 Original line number Diff line number Diff line o Major features (consensus downloads): - Schedule multiple in-progress consensus downloads during client bootstrap. Use the first one that starts downloading, close the rest. This reduces failures when authorities are slow or down. With #15775, it reduces failures due to fallback churn. Implements #4483 (reduce failures when authorities are down). Patch by "teor". Implements IPv4 portions of proposal #210 by "mikeperry" and "teor".
doc/tor.1.txt +59 −2 Original line number Diff line number Diff line Loading @@ -360,7 +360,11 @@ GENERAL OPTIONS [[FallbackDir]] **FallbackDir** __address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__]:: When we're unable to connect to any directory cache for directory info (usually because we don't know about any yet) we try a FallbackDir. (usually because we don't know about any yet) we try a directory authority. Clients also simultaneously try a FallbackDir, to avoid hangs on client startup if a directory authority is down. Clients retry FallbackDirs more often than directory authorities, to reduce the load on the directory authorities. By default, the directory authorities are also FallbackDirs. Specifying a FallbackDir replaces Tor's default hard-coded FallbackDirs (if any). Loading Loading @@ -2287,10 +2291,18 @@ The following options are used for running a testing Tor network. TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 TestingClientBootstrapConsensusAuthorityDownloadSchedule 0, 2, 4 (for 40 seconds), 8, 16, 32, 60 TestingClientBootstrapConsensusFallbackDownloadSchedule 0, 1, 4 (for 40 seconds), 8, 16, 32, 60 TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule 0, 1, 4 (for 40 seconds), 8, 16, 32, 60 TestingBridgeDownloadSchedule 60, 30, 30, 60 TestingClientMaxIntervalWithoutRequest 5 seconds TestingDirConnectionMaxStall 30 seconds TestingConsensusMaxDownloadTries 80 TestingClientBootstrapConsensusMaxDownloadTries 80 TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 80 TestingDescriptorMaxDownloadTries 80 TestingMicrodescMaxDownloadTries 80 TestingCertMaxDownloadTries 80 Loading Loading @@ -2351,6 +2363,36 @@ The following options are used for running a testing Tor network. requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600, 1800, 3600, 3600, 3600, 10800, 21600, 43200) [[TestingClientBootstrapConsensusAuthorityDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients fetching from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by connection failures. Changing this schedule requires that **TestingTorNetwork** is set. (Default: 10, 11, 3600, 10800, 25200, 54000, 111600, 262800) [[TestingClientBootstrapConsensusFallbackDownloadSchedule]] **TestingClientBootstrapConsensusFallbackDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download consensuses from fallback directory mirrors if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients fetching from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by connection failures. Changing this schedule requires that **TestingTorNetwork** is set. (Default: 0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800) [[TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients which don't have or won't fetch from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by connection failures. Changing this schedule requires that **TestingTorNetwork** is set. (Default: 0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800) [[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__:: Schedule for when clients should download bridge descriptors. Changing this requires that **TestingTorNetwork** is set. (Default: 3600, 900, 900, 3600) Loading @@ -2367,9 +2409,24 @@ The following options are used for running a testing Tor network. 5 minutes) [[TestingConsensusMaxDownloadTries]] **TestingConsensusMaxDownloadTries** __NUM__:: Try this often to download a consensus before giving up. Changing Try this many times to download a consensus before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 8) [[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__:: Try this many times to download a consensus while bootstrapping using fallback directory mirrors before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 7) [[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__:: Try this many times to download a consensus while bootstrapping using only authorities before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 4) [[TestingClientBootstrapConsensusMaxInProgressTries]] **TestingClientBootstrapConsensusMaxInProgressTries** __NUM__:: Try this many simultaneous connections to download a consensus before waiting for one to complete, timeout, or error out. Changing this requires that **TestingTorNetwork** is set. (Default: 4) [[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__:: Try this often to download a server descriptor before giving up. Changing this requires that **TestingTorNetwork** is set. (Default: 8) Loading
src/common/torint.h +26 −0 Original line number Diff line number Diff line Loading @@ -336,6 +336,32 @@ typedef uint32_t uintptr_t; #endif /* time_t_is_signed */ #endif /* ifndef(TIME_MAX) */ #ifndef TIME_MIN #ifdef TIME_T_IS_SIGNED #if (SIZEOF_TIME_T == SIZEOF_INT) #define TIME_MIN ((time_t)INT_MIN) #elif (SIZEOF_TIME_T == SIZEOF_LONG) #define TIME_MIN ((time_t)LONG_MIN) #elif (SIZEOF_TIME_T == 8) #define TIME_MIN ((time_t)INT64_MIN) #else #error "Can't define (signed) TIME_MIN" #endif #else /* Unsigned case */ #if (SIZEOF_TIME_T == 4) #define TIME_MIN ((time_t)UINT32_MIN) #elif (SIZEOF_TIME_T == 8) #define TIME_MIN ((time_t)UINT64_MIN) #else #error "Can't define (unsigned) TIME_MIN" #endif #endif /* time_t_is_signed */ #endif /* ifndef(TIME_MIN) */ #ifndef SIZE_MAX #if (SIZEOF_SIZE_T == 4) #define SIZE_MAX UINT32_MAX Loading
src/or/config.c +75 −1 Original line number Diff line number Diff line Loading @@ -476,10 +476,40 @@ static config_var_t option_vars_[] = { V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " "300, 600, 1800, 3600, 3600, 3600, " "10800, 21600, 43200"), /* With the TestingClientBootstrapConsensus*Download* below: * Clients with only authorities will try: * - 3 authorities over 10 seconds, then wait 60 minutes. * Clients with authorities and fallbacks will try: * - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes. * Clients will also retry when an application request arrives. * After a number of failed reqests, clients retry every 3 days + 1 hour. * * Clients used to try 2 authorities over 10 seconds, then wait for * 60 minutes or an application request. * * When clients have authorities and fallbacks available, they use these * schedules: (we stagger the times to avoid thundering herds) */ V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"), /* When clients only have authorities available, they use this schedule: */ V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"), /* We don't want to overwhelm slow networks (or mirrors whose replies are * blocked), but we also don't want to fail if only some mirrors are * blackholed. Clients will try 3 directories simultaneously. * (Relays never use simultaneous connections.) */ V(TestingClientBootstrapConsensusMaxInProgressTries, UINT, "3"), V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), V(TestingConsensusMaxDownloadTries, UINT, "8"), /* Since we try connections rapidly and simultaneously, we can afford * to give up earlier. (This protects against overloading directories.) */ V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "7"), /* We want to give up much earlier if we're only using authorities. */ V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"), V(TestingDescriptorMaxDownloadTries, UINT, "8"), V(TestingMicrodescMaxDownloadTries, UINT, "8"), V(TestingCertMaxDownloadTries, UINT, "8"), Loading Loading @@ -526,10 +556,18 @@ static const config_var_t testing_tor_network_defaults[] = { "15, 20, 30, 60"), V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " "15, 20, 30, 60"), V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"), V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"), V(TestingConsensusMaxDownloadTries, UINT, "80"), V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "80"), V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"), V(TestingDescriptorMaxDownloadTries, UINT, "80"), V(TestingMicrodescMaxDownloadTries, UINT, "80"), V(TestingCertMaxDownloadTries, UINT, "80"), Loading Loading @@ -3758,10 +3796,16 @@ options_validate(or_options_t *old_options, or_options_t *options, CHECK_DEFAULT(TestingClientDownloadSchedule); CHECK_DEFAULT(TestingServerConsensusDownloadSchedule); CHECK_DEFAULT(TestingClientConsensusDownloadSchedule); CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityDownloadSchedule); CHECK_DEFAULT(TestingClientBootstrapConsensusFallbackDownloadSchedule); CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule); CHECK_DEFAULT(TestingBridgeDownloadSchedule); CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); CHECK_DEFAULT(TestingDirConnectionMaxStall); CHECK_DEFAULT(TestingConsensusMaxDownloadTries); CHECK_DEFAULT(TestingClientBootstrapConsensusMaxDownloadTries); CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries); CHECK_DEFAULT(TestingClientBootstrapConsensusMaxInProgressTries); CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); CHECK_DEFAULT(TestingCertMaxDownloadTries); Loading Loading @@ -3836,11 +3880,41 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingConsensusMaxDownloadTries < 2) { REJECT("TestingConsensusMaxDownloadTries must be greater than 1."); REJECT("TestingConsensusMaxDownloadTries must be greater than 2."); } else if (options->TestingConsensusMaxDownloadTries > 800) { COMPLAIN("TestingConsensusMaxDownloadTries is insanely high."); } if (options->TestingClientBootstrapConsensusMaxDownloadTries < 2) { REJECT("TestingClientBootstrapConsensusMaxDownloadTries must be greater " "than 2." ); } else if (options->TestingClientBootstrapConsensusMaxDownloadTries > 800) { COMPLAIN("TestingClientBootstrapConsensusMaxDownloadTries is insanely " "high."); } if (options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries < 2) { REJECT("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must " "be greater than 2." ); } else if ( options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries > 800) { COMPLAIN("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is " "insanely high."); } if (options->TestingClientBootstrapConsensusMaxInProgressTries < 1) { REJECT("TestingClientBootstrapConsensusMaxInProgressTries must be greater " "than 0."); } else if (options->TestingClientBootstrapConsensusMaxInProgressTries > 100) { COMPLAIN("TestingClientBootstrapConsensusMaxInProgressTries is insanely " "high."); } if (options->TestingDescriptorMaxDownloadTries < 2) { REJECT("TestingDescriptorMaxDownloadTries must be greater than 1."); } else if (options->TestingDescriptorMaxDownloadTries > 800) { Loading
src/or/connection.c +153 −77 Original line number Diff line number Diff line Loading @@ -1618,13 +1618,18 @@ connection_init_accepted_conn(connection_t *conn, return 0; } static int connection_connect_sockaddr(connection_t *conn, /** Take conn, make a nonblocking socket; try to connect to * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if * applicable put your best guess about errno into *<b>socket_error</b>. * If connected return 1, if EAGAIN return 0. */ MOCK_IMPL(STATIC int, connection_connect_sockaddr,(connection_t *conn, const struct sockaddr *sa, socklen_t sa_len, const struct sockaddr *bindaddr, socklen_t bindaddr_len, int *socket_error) int *socket_error)) { tor_socket_t s; int inprogress = 0; Loading Loading @@ -4222,6 +4227,19 @@ connection_write_to_buf_impl_,(const char *string, size_t len, } } /** Return a connection_t * from get_connection_array() that satisfies test on * var, and that is not marked for close. */ #define CONN_GET_TEMPLATE(var, test) \ STMT_BEGIN \ smartlist_t *conns = get_connection_array(); \ SMARTLIST_FOREACH(conns, connection_t *, var, \ { \ if (var && (test) && !var->marked_for_close) \ return var; \ }); \ return NULL; \ STMT_END /** Return a connection with given type, address, port, and purpose; * or NULL if no such connection exists (or if all such connections are marked * for close). */ Loading @@ -4230,17 +4248,11 @@ connection_get_by_type_addr_port_purpose(int type, const tor_addr_t *addr, uint16_t port, int purpose) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == type && CONN_GET_TEMPLATE(conn, (conn->type == type && tor_addr_eq(&conn->addr, addr) && conn->port == port && conn->purpose == purpose && !conn->marked_for_close) return conn; }); return NULL; conn->purpose == purpose)); } /** Return the stream with id <b>id</b> if it is not already marked for Loading @@ -4249,13 +4261,7 @@ connection_get_by_type_addr_port_purpose(int type, connection_t * connection_get_by_global_id(uint64_t id) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->global_identifier == id) return conn; }); return NULL; CONN_GET_TEMPLATE(conn, conn->global_identifier == id); } /** Return a connection of type <b>type</b> that is not marked for close. Loading @@ -4263,13 +4269,7 @@ connection_get_by_global_id(uint64_t id) connection_t * connection_get_by_type(int type) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == type && !conn->marked_for_close) return conn; }); return NULL; CONN_GET_TEMPLATE(conn, conn->type == type); } /** Return a connection of type <b>type</b> that is in state <b>state</b>, Loading @@ -4278,13 +4278,7 @@ connection_get_by_type(int type) connection_t * connection_get_by_type_state(int type, int state) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == type && conn->state == state && !conn->marked_for_close) return conn; }); return NULL; CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state); } /** Return a connection of type <b>type</b> that has rendquery equal Loading @@ -4295,55 +4289,142 @@ connection_t * connection_get_by_type_state_rendquery(int type, int state, const char *rendquery) { smartlist_t *conns = get_connection_array(); tor_assert(type == CONN_TYPE_DIR || type == CONN_TYPE_AP || type == CONN_TYPE_EXIT); tor_assert(rendquery); SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { if (conn->type == type && !conn->marked_for_close && (!state || state == conn->state)) { if (type == CONN_TYPE_DIR && CONN_GET_TEMPLATE(conn, (conn->type == type && (!state || state == conn->state)) && ( (type == CONN_TYPE_DIR && TO_DIR_CONN(conn)->rend_data && !rend_cmp_service_ids(rendquery, TO_DIR_CONN(conn)->rend_data->onion_address)) return conn; else if (CONN_IS_EDGE(conn) && || (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->rend_data && !rend_cmp_service_ids(rendquery, TO_EDGE_CONN(conn)->rend_data->onion_address)) return conn; } } SMARTLIST_FOREACH_END(conn); return NULL; )); } #define CONN_FIRST_AND_FREE_TEMPLATE(sl) \ STMT_BEGIN \ if (smartlist_len(sl) > 0) { \ void *first_item = smartlist_get(sl, 0); \ smartlist_free(sl); \ return first_item; \ } else { \ smartlist_free(sl); \ return NULL; \ } \ STMT_END /** Return a directory connection (if any one exists) that is fetching * the item described by <b>state</b>/<b>resource</b> */ * the item described by <b>purpose</b>/<b>resource</b>, otherwise return NULL. */ dir_connection_t * connection_dir_get_by_purpose_and_resource(int purpose, connection_dir_get_by_purpose_and_resource( int purpose, const char *resource) { smartlist_t *conns = get_connection_array(); smartlist_t *conns = connection_dir_list_by_purpose_and_resource( purpose, resource); CONN_FIRST_AND_FREE_TEMPLATE(conns); } /** Return a new smartlist of dir_connection_t * from get_connection_array() * that satisfy conn_test on connection_t *conn_var, and dirconn_test on * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not * marked for close to be included in the list. */ #define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test, \ dirconn_var, dirconn_test) \ STMT_BEGIN \ smartlist_t *conns = get_connection_array(); \ smartlist_t *dir_conns = smartlist_new(); \ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) { \ if (conn_var && (conn_test) \ && conn_var->type == CONN_TYPE_DIR \ && !conn_var->marked_for_close) { \ dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var); \ if (dirconn_var && (dirconn_test)) { \ smartlist_add(dir_conns, dirconn_var); \ } \ } \ } SMARTLIST_FOREACH_END(conn_var); \ return dir_conns; \ STMT_END /** Return a list of directory connections that are fetching the item * described by <b>purpose</b>/<b>resource</b>. If there are none, * return an empty list. This list must be freed using smartlist_free, * but the pointers in it must not be freed. * Note that this list should not be cached, as the pointers in it can be * freed if their connections close. */ smartlist_t * connection_dir_list_by_purpose_and_resource( int purpose, const char *resource) { DIR_CONN_LIST_TEMPLATE(conn, conn->purpose == purpose, dirconn, 0 == strcmp_opt(resource, dirconn->requested_resource)); } SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { dir_connection_t *dirconn; if (conn->type != CONN_TYPE_DIR || conn->marked_for_close || conn->purpose != purpose) continue; dirconn = TO_DIR_CONN(conn); if (dirconn->requested_resource == NULL) { if (resource == NULL) return dirconn; } else if (resource) { if (0 == strcmp(resource, dirconn->requested_resource)) return dirconn; /** Return a directory connection (if any one exists) that is fetching * the item described by <b>purpose</b>/<b>resource</b>/<b>state</b>, * otherwise return NULL. */ dir_connection_t * connection_dir_get_by_purpose_resource_and_state( int purpose, const char *resource, int state) { smartlist_t *conns = connection_dir_list_by_purpose_resource_and_state( purpose, resource, state); CONN_FIRST_AND_FREE_TEMPLATE(conns); } #undef CONN_FIRST_AND_FREE_TEMPLATE /** Return a list of directory connections that are fetching the item * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are * none, return an empty list. This list must be freed using smartlist_free, * but the pointers in it must not be freed. * Note that this list should not be cached, as the pointers in it can be * freed if their connections close. */ smartlist_t * connection_dir_list_by_purpose_resource_and_state( int purpose, const char *resource, int state) { DIR_CONN_LIST_TEMPLATE(conn, conn->purpose == purpose && conn->state == state, dirconn, 0 == strcmp_opt(resource, dirconn->requested_resource)); } } SMARTLIST_FOREACH_END(conn); return NULL; #undef DIR_CONN_LIST_TEMPLATE /** Return an arbitrary active OR connection that isn't <b>this_conn</b>. * * We use this to guess if we should tell the controller that we * didn't manage to connect to any of our bridges. */ static connection_t * connection_get_another_active_or_conn(const or_connection_t *this_conn) { CONN_GET_TEMPLATE(conn, conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR); } /** Return 1 if there are any active OR connections apart from Loading @@ -4354,23 +4435,18 @@ connection_dir_get_by_purpose_and_resource(int purpose, int any_other_active_or_conns(const or_connection_t *this_conn) { smartlist_t *conns = get_connection_array(); SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { if (conn == TO_CONN(this_conn)) { /* don't consider this conn */ continue; } if (conn->type == CONN_TYPE_OR && !conn->marked_for_close) { connection_t *conn = connection_get_another_active_or_conn(this_conn); if (conn != NULL) { log_debug(LD_DIR, "%s: Found an OR connection: %s", __func__, conn->address); return 1; } } SMARTLIST_FOREACH_END(conn); return 0; } #undef CONN_GET_TEMPLATE /** Return 1 if <b>conn</b> is a listener conn, else return 0. */ int connection_is_listener(connection_t *conn) Loading