mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
entry nodes are now entry guards.
this is our last easy chance for a wholesale change. heave ho. svn:r5782
This commit is contained in:
parent
e27c47b6de
commit
7d1f675c85
@ -18,7 +18,7 @@ const char circuitbuild_c_id[] =
|
|||||||
/** A global list of all circuits at this hop. */
|
/** A global list of all circuits at this hop. */
|
||||||
extern circuit_t *global_circuitlist;
|
extern circuit_t *global_circuitlist;
|
||||||
|
|
||||||
/** An entry_node_t represents our information about a chosen long-term
|
/** An entry_guard_t represents our information about a chosen long-term
|
||||||
* first hop, known as a "helper" node in the literature. We can't just
|
* first hop, known as a "helper" node in the literature. We can't just
|
||||||
* use a routerinfo_t, since we want to remember these even when we
|
* use a routerinfo_t, since we want to remember these even when we
|
||||||
* don't have a directory. */
|
* don't have a directory. */
|
||||||
@ -31,13 +31,13 @@ typedef struct {
|
|||||||
* which it was observed to go down. */
|
* which it was observed to go down. */
|
||||||
time_t unlisted_since; /**< 0 if this router is currently listed, or the
|
time_t unlisted_since; /**< 0 if this router is currently listed, or the
|
||||||
* time at which it became unlisted */
|
* time at which it became unlisted */
|
||||||
} entry_node_t;
|
} entry_guard_t;
|
||||||
|
|
||||||
/** A list of our chosen entry nodes. */
|
/** A list of our chosen entry guards. */
|
||||||
static smartlist_t *entry_nodes = NULL;
|
static smartlist_t *entry_guards = NULL;
|
||||||
/** A value of 1 means that the entry_nodes list has changed
|
/** A value of 1 means that the entry_guards list has changed
|
||||||
* and those changes need to be flushed to disk. */
|
* and those changes need to be flushed to disk. */
|
||||||
static int entry_nodes_dirty = 0;
|
static int entry_guards_dirty = 0;
|
||||||
|
|
||||||
/********* END VARIABLES ************/
|
/********* END VARIABLES ************/
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ static int count_acceptable_routers(smartlist_t *routers);
|
|||||||
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 routerinfo_t *choose_random_entry(cpath_build_state_t *state);
|
static routerinfo_t *choose_random_entry(cpath_build_state_t *state);
|
||||||
static void entry_nodes_changed(void);
|
static void entry_guards_changed(void);
|
||||||
|
|
||||||
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
|
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
|
||||||
* and with the high bit specified by circ_id_type (see
|
* and with the high bit specified by circ_id_type (see
|
||||||
@ -1486,7 +1486,7 @@ choose_good_middle_server(uint8_t purpose,
|
|||||||
/** Pick a good entry server for the circuit to be built according to
|
/** Pick a good entry server for the circuit to be built according to
|
||||||
* <b>state</b>. Don't reuse a chosen exit (if any), don't use this
|
* <b>state</b>. Don't reuse a chosen exit (if any), don't use this
|
||||||
* router (if we're an OR), and respect firewall settings; if we're
|
* router (if we're an OR), and respect firewall settings; if we're
|
||||||
* using entry_nodes, return one.
|
* using entry_guards, return one.
|
||||||
*
|
*
|
||||||
* If <b>state</b> is NULL, we're choosing routers to serve as entry
|
* If <b>state</b> is NULL, we're choosing routers to serve as entry
|
||||||
* nodes, not for any particular circuit.
|
* nodes, not for any particular circuit.
|
||||||
@ -1498,7 +1498,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
|
|||||||
smartlist_t *excluded = smartlist_create();
|
smartlist_t *excluded = smartlist_create();
|
||||||
or_options_t *options = get_options();
|
or_options_t *options = get_options();
|
||||||
|
|
||||||
if (state && options->UseEntryNodes) {
|
if (state && options->UseEntryGuards) {
|
||||||
return choose_random_entry(state);
|
return choose_random_entry(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1698,7 +1698,7 @@ build_state_get_exit_nickname(cpath_build_state_t *state)
|
|||||||
* - Allowed by our current ReachableAddresses config option.
|
* - Allowed by our current ReachableAddresses config option.
|
||||||
*/
|
*/
|
||||||
static INLINE routerinfo_t *
|
static INLINE routerinfo_t *
|
||||||
entry_is_live(entry_node_t *e, int need_uptime, int need_capacity)
|
entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity)
|
||||||
{
|
{
|
||||||
routerinfo_t *r;
|
routerinfo_t *r;
|
||||||
if (e->down_since && e->made_contact)
|
if (e->down_since && e->made_contact)
|
||||||
@ -1714,14 +1714,14 @@ entry_is_live(entry_node_t *e, int need_uptime, int need_capacity)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the number of entry nodes that we think are usable. */
|
/** Return the number of entry guards that we think are usable. */
|
||||||
static int
|
static int
|
||||||
num_live_entry_nodes(void)
|
num_live_entry_guards(void)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (! entry_nodes)
|
if (! entry_guards)
|
||||||
return 0;
|
return 0;
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
|
||||||
{
|
{
|
||||||
if (entry_is_live(entry, 0, 1))
|
if (entry_is_live(entry, 0, 1))
|
||||||
++n;
|
++n;
|
||||||
@ -1730,11 +1730,11 @@ num_live_entry_nodes(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Return 1 if <b>digest</b> matches the identity of any node
|
/** Return 1 if <b>digest</b> matches the identity of any node
|
||||||
* in the entry_nodes list. Else return 0. */
|
* in the entry_guards list. Else return 0. */
|
||||||
static INLINE int
|
static INLINE int
|
||||||
is_an_entry_node(char *digest)
|
is_an_entry_guard(char *digest)
|
||||||
{
|
{
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
|
||||||
if (!memcmp(digest, entry->identity, DIGEST_LEN))
|
if (!memcmp(digest, entry->identity, DIGEST_LEN))
|
||||||
return 1;
|
return 1;
|
||||||
);
|
);
|
||||||
@ -1742,13 +1742,13 @@ is_an_entry_node(char *digest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
log_entry_nodes(int severity)
|
log_entry_guards(int severity)
|
||||||
{
|
{
|
||||||
smartlist_t *elements = smartlist_create();
|
smartlist_t *elements = smartlist_create();
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
char *s;
|
char *s;
|
||||||
|
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
|
||||||
{
|
{
|
||||||
tor_snprintf(buf, sizeof(buf), "%s (%s%s%s)",
|
tor_snprintf(buf, sizeof(buf), "%s (%s%s%s)",
|
||||||
e->nickname,
|
e->nickname,
|
||||||
@ -1768,17 +1768,17 @@ log_entry_nodes(int severity)
|
|||||||
#define NUM_ENTRY_PICK_TRIES 100
|
#define NUM_ENTRY_PICK_TRIES 100
|
||||||
|
|
||||||
/** Add a new (preferably stable and fast) entry to our
|
/** Add a new (preferably stable and fast) entry to our
|
||||||
* entry_nodes list. Return a pointer to the router if we succeed,
|
* entry_guards list. Return a pointer to the router if we succeed,
|
||||||
* or NULL if we can't find any more suitable entries.
|
* or NULL if we can't find any more suitable entries.
|
||||||
*
|
*
|
||||||
* If <b>chosen</b> is defined, use that one, and if it's not
|
* If <b>chosen</b> is defined, use that one, and if it's not
|
||||||
* already in our entry_nodes list, put it at the *beginning*.
|
* already in our entry_guards list, put it at the *beginning*.
|
||||||
* Else, put the one we pick at the end of the list. */
|
* Else, put the one we pick at the end of the list. */
|
||||||
static routerinfo_t *
|
static routerinfo_t *
|
||||||
add_an_entry_node(routerinfo_t *chosen)
|
add_an_entry_guard(routerinfo_t *chosen)
|
||||||
{
|
{
|
||||||
routerinfo_t *router;
|
routerinfo_t *router;
|
||||||
entry_node_t *entry;
|
entry_guard_t *entry;
|
||||||
int tries_left = NUM_ENTRY_PICK_TRIES;
|
int tries_left = NUM_ENTRY_PICK_TRIES;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
@ -1793,64 +1793,64 @@ again:
|
|||||||
if (!router)
|
if (!router)
|
||||||
return NULL;
|
return NULL;
|
||||||
/* make sure it's not already an entry */
|
/* make sure it's not already an entry */
|
||||||
if (is_an_entry_node(router->cache_info.identity_digest)) {
|
if (is_an_entry_guard(router->cache_info.identity_digest)) {
|
||||||
if (chosen)
|
if (chosen)
|
||||||
return NULL;
|
return NULL;
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
entry = tor_malloc_zero(sizeof(entry_node_t));
|
entry = tor_malloc_zero(sizeof(entry_guard_t));
|
||||||
/* XXXX Downgrade this to info before release. NM */
|
/* XXXX Downgrade this to info before release. NM */
|
||||||
notice(LD_CIRC, "Chose '%s' as new entry node.", router->nickname);
|
notice(LD_CIRC, "Chose '%s' as new entry guard.", router->nickname);
|
||||||
strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname));
|
strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname));
|
||||||
memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN);
|
memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN);
|
||||||
if (chosen)
|
if (chosen)
|
||||||
smartlist_insert(entry_nodes, 0, entry);
|
smartlist_insert(entry_guards, 0, entry);
|
||||||
else
|
else
|
||||||
smartlist_add(entry_nodes, entry);
|
smartlist_add(entry_guards, entry);
|
||||||
log_entry_nodes(LOG_INFO);
|
log_entry_guards(LOG_INFO);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If the use of entry nodes is configured, choose more entry nodes
|
/** If the use of entry guards is configured, choose more entry guards
|
||||||
* until we have enough in the list. */
|
* until we have enough in the list. */
|
||||||
static void
|
static void
|
||||||
pick_entry_nodes(void)
|
pick_entry_guards(void)
|
||||||
{
|
{
|
||||||
or_options_t *options = get_options();
|
or_options_t *options = get_options();
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
|
|
||||||
tor_assert(entry_nodes);
|
tor_assert(entry_guards);
|
||||||
|
|
||||||
while (num_live_entry_nodes() < options->NumEntryNodes) {
|
while (num_live_entry_guards() < options->NumEntryGuards) {
|
||||||
if (!add_an_entry_node(NULL))
|
if (!add_an_entry_guard(NULL))
|
||||||
break;
|
break;
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
entry_nodes_changed();
|
entry_guards_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Release all storage held by the list of entry nodes. */
|
/** Release all storage held by the list of entry guards. */
|
||||||
void
|
void
|
||||||
entry_nodes_free_all(void)
|
entry_guards_free_all(void)
|
||||||
{
|
{
|
||||||
if (entry_nodes) {
|
if (entry_guards) {
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e, tor_free(e));
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, tor_free(e));
|
||||||
smartlist_free(entry_nodes);
|
smartlist_free(entry_guards);
|
||||||
entry_nodes = NULL;
|
entry_guards = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX These are 12 hours for now, but I'd like to make them 30 days */
|
/* XXX These are 12 hours for now, but I'd like to make them 30 days */
|
||||||
|
|
||||||
/** How long (in seconds) do we allow an entry node to be nonfunctional
|
/** How long (in seconds) do we allow an entry guard to be nonfunctional
|
||||||
* before we give up on it? */
|
* before we give up on it? */
|
||||||
#define ENTRY_ALLOW_DOWNTIME (1*12*60*60)
|
#define ENTRY_ALLOW_DOWNTIME (1*12*60*60)
|
||||||
/** How long (in seconds) do we allow an entry node to be unlisted in the
|
/** How long (in seconds) do we allow an entry guard to be unlisted in the
|
||||||
* directory before we give up on it? */
|
* directory before we give up on it? */
|
||||||
#define ENTRY_ALLOW_UNLISTED (1*12*60*60)
|
#define ENTRY_ALLOW_UNLISTED (1*12*60*60)
|
||||||
|
|
||||||
/** Remove all entry nodes that have been down or unlisted for so
|
/** Remove all entry guards that have been down or unlisted for so
|
||||||
* long that we don't think they'll come up again. Return 1 if we
|
* long that we don't think they'll come up again. Return 1 if we
|
||||||
* removed any, or 0 if we did nothing. */
|
* removed any, or 0 if we did nothing. */
|
||||||
static int
|
static int
|
||||||
@ -1862,8 +1862,8 @@ remove_dead_entries(void)
|
|||||||
int i;
|
int i;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
|
|
||||||
for (i = 0; i < smartlist_len(entry_nodes); ) {
|
for (i = 0; i < smartlist_len(entry_guards); ) {
|
||||||
entry_node_t *entry = smartlist_get(entry_nodes, i);
|
entry_guard_t *entry = smartlist_get(entry_guards, i);
|
||||||
const char *why = NULL;
|
const char *why = NULL;
|
||||||
time_t since = 0;
|
time_t since = 0;
|
||||||
if (entry->unlisted_since &&
|
if (entry->unlisted_since &&
|
||||||
@ -1878,11 +1878,11 @@ remove_dead_entries(void)
|
|||||||
if (why) {
|
if (why) {
|
||||||
base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN);
|
base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN);
|
||||||
format_local_iso_time(tbuf, since);
|
format_local_iso_time(tbuf, since);
|
||||||
warn(LD_CIRC, "Entry node '%s' (%s) has been %s since %s; removing.",
|
warn(LD_CIRC, "Entry guard '%s' (%s) has been %s since %s; removing.",
|
||||||
entry->nickname, dbuf, why, tbuf);
|
entry->nickname, dbuf, why, tbuf);
|
||||||
tor_free(entry);
|
tor_free(entry);
|
||||||
smartlist_del_keeporder(entry_nodes, i);
|
smartlist_del_keeporder(entry_guards, i);
|
||||||
log_entry_nodes(LOG_INFO);
|
log_entry_guards(LOG_INFO);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
} else
|
} else
|
||||||
++i;
|
++i;
|
||||||
@ -1891,13 +1891,13 @@ remove_dead_entries(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A new directory or router-status has arrived; update the down/listed
|
/** A new directory or router-status has arrived; update the down/listed
|
||||||
* status of the entry nodes.
|
* status of the entry guards.
|
||||||
*
|
*
|
||||||
* An entry is 'down' if the directory lists it as nonrunning.
|
* An entry is 'down' if the directory lists it as nonrunning.
|
||||||
* An entry is 'unlisted' if the directory doesn't include it.
|
* An entry is 'unlisted' if the directory doesn't include it.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
entry_nodes_set_status_from_directory(void)
|
entry_guards_set_status_from_directory(void)
|
||||||
{
|
{
|
||||||
/* Don't call this on startup; only on a fresh download. Otherwise we'll
|
/* Don't call this on startup; only on a fresh download. Otherwise we'll
|
||||||
* think that things are unlisted. */
|
* think that things are unlisted. */
|
||||||
@ -1905,7 +1905,7 @@ entry_nodes_set_status_from_directory(void)
|
|||||||
time_t now;
|
time_t now;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
int severity = LOG_NOTICE;
|
int severity = LOG_NOTICE;
|
||||||
if (! entry_nodes)
|
if (! entry_guards)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
routers = router_get_routerlist();
|
routers = router_get_routerlist();
|
||||||
@ -1914,20 +1914,20 @@ entry_nodes_set_status_from_directory(void)
|
|||||||
|
|
||||||
/*XXXX Most of these warns should be non-warns. */
|
/*XXXX Most of these warns should be non-warns. */
|
||||||
|
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
|
||||||
{
|
{
|
||||||
routerinfo_t *r = router_get_by_digest(entry->identity);
|
routerinfo_t *r = router_get_by_digest(entry->identity);
|
||||||
if (! r) {
|
if (! r) {
|
||||||
if (! entry->unlisted_since) {
|
if (! entry->unlisted_since) {
|
||||||
entry->unlisted_since = time(NULL);
|
entry->unlisted_since = time(NULL);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
warn(LD_CIRC,"Entry node '%s' is not listed by directories.",
|
warn(LD_CIRC,"Entry guard '%s' is not listed by directories.",
|
||||||
entry->nickname);
|
entry->nickname);
|
||||||
severity = LOG_WARN;
|
severity = LOG_WARN;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (entry->unlisted_since) {
|
if (entry->unlisted_since) {
|
||||||
warn(LD_CIRC,"Entry node '%s' is listed again by directories.",
|
warn(LD_CIRC,"Entry guard '%s' is listed again by directories.",
|
||||||
entry->nickname);
|
entry->nickname);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
severity = LOG_WARN;
|
severity = LOG_WARN;
|
||||||
@ -1936,13 +1936,13 @@ entry_nodes_set_status_from_directory(void)
|
|||||||
if (! r->is_running) {
|
if (! r->is_running) {
|
||||||
if (! entry->down_since) {
|
if (! entry->down_since) {
|
||||||
entry->down_since = now;
|
entry->down_since = now;
|
||||||
warn(LD_CIRC, "Entry node '%s' is now down.", entry->nickname);
|
warn(LD_CIRC, "Entry guard '%s' is now down.", entry->nickname);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
severity = LOG_WARN;
|
severity = LOG_WARN;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (entry->down_since) {
|
if (entry->down_since) {
|
||||||
notice(LD_CIRC,"Entry node '%s' is up in latest directories.",
|
notice(LD_CIRC,"Entry guard '%s' is up in latest directories.",
|
||||||
entry->nickname);
|
entry->nickname);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
@ -1960,10 +1960,10 @@ entry_nodes_set_status_from_directory(void)
|
|||||||
changed = 1;
|
changed = 1;
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
log_fn(severity, LD_CIRC, " (%d/%d entry nodes are usable/new)",
|
log_fn(severity, LD_CIRC, " (%d/%d entry guards are usable/new)",
|
||||||
num_live_entry_nodes(), smartlist_len(entry_nodes));
|
num_live_entry_guards(), smartlist_len(entry_guards));
|
||||||
log_entry_nodes(LOG_INFO);
|
log_entry_guards(LOG_INFO);
|
||||||
entry_nodes_changed();
|
entry_guards_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1973,25 +1973,25 @@ entry_nodes_set_status_from_directory(void)
|
|||||||
* Return 0 normally, or -1 if we want to tear down the new connection.
|
* Return 0 normally, or -1 if we want to tear down the new connection.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
entry_node_set_status(const char *digest, int succeeded)
|
entry_guard_set_status(const char *digest, int succeeded)
|
||||||
{
|
{
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
int refuse_conn = 0;
|
int refuse_conn = 0;
|
||||||
|
|
||||||
if (! entry_nodes)
|
if (! entry_guards)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
|
||||||
{
|
{
|
||||||
if (!memcmp(entry->identity, digest, DIGEST_LEN)) {
|
if (!memcmp(entry->identity, digest, DIGEST_LEN)) {
|
||||||
if (succeeded) {
|
if (succeeded) {
|
||||||
if (!entry->made_contact) {
|
if (!entry->made_contact) {
|
||||||
/* We've just added a new long-term entry node. Perhaps
|
/* We've just added a new long-term entry guard. Perhaps
|
||||||
* the network just came back? We should give our earlier
|
* the network just came back? We should give our earlier
|
||||||
* entries another try too, and close this connection so
|
* entries another try too, and close this connection so
|
||||||
* we don't use it before we've given the others a shot. */
|
* we don't use it before we've given the others a shot. */
|
||||||
entry->made_contact = 1;
|
entry->made_contact = 1;
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
|
||||||
{
|
{
|
||||||
routerinfo_t *r;
|
routerinfo_t *r;
|
||||||
if (e->made_contact) {
|
if (e->made_contact) {
|
||||||
@ -2006,40 +2006,41 @@ entry_node_set_status(const char *digest, int succeeded)
|
|||||||
break;
|
break;
|
||||||
});
|
});
|
||||||
notice(LD_CIRC,
|
notice(LD_CIRC,
|
||||||
"Connected to new entry node '%s'. Marking earlier "
|
"Connected to new entry guard '%s'. Marking earlier "
|
||||||
"entries up. %d/%d entries usable/new.", entry->nickname,
|
"entry guards up. %d/%d entry guards usable/new.",
|
||||||
num_live_entry_nodes(), smartlist_len(entry_nodes));
|
entry->nickname,
|
||||||
log_entry_nodes(LOG_INFO);
|
num_live_entry_guards(), smartlist_len(entry_guards));
|
||||||
|
log_entry_guards(LOG_INFO);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
if (entry->down_since) {
|
if (entry->down_since) {
|
||||||
/*XXXX shouldn't be so loud. NM */
|
/*XXXX shouldn't be so loud. NM */
|
||||||
notice(LD_CIRC,
|
notice(LD_CIRC,
|
||||||
"Connection to formerly down entry node '%s' succeeded. "
|
"Connection to formerly down entry guard '%s' succeeded. "
|
||||||
"%d/%d entry nodes usable/new.", entry->nickname,
|
"%d/%d entry guards usable/new.", entry->nickname,
|
||||||
num_live_entry_nodes(), smartlist_len(entry_nodes));
|
num_live_entry_guards(), smartlist_len(entry_guards));
|
||||||
entry->down_since = 0;
|
entry->down_since = 0;
|
||||||
log_entry_nodes(LOG_INFO);
|
log_entry_guards(LOG_INFO);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!entry->made_contact) { /* dump him */
|
if (!entry->made_contact) { /* dump him */
|
||||||
notice(LD_CIRC,
|
notice(LD_CIRC,
|
||||||
"Connection to never-contacted entry node '%s' failed. "
|
"Connection to never-contacted entry guard '%s' failed. "
|
||||||
"Removing from the list. %d/%d entry nodes usable/new.",
|
"Removing from the list. %d/%d entry guards usable/new.",
|
||||||
entry->nickname,
|
entry->nickname,
|
||||||
num_live_entry_nodes()-1, smartlist_len(entry_nodes)-1);
|
num_live_entry_guards()-1, smartlist_len(entry_guards)-1);
|
||||||
tor_free(entry);
|
tor_free(entry);
|
||||||
smartlist_del_keeporder(entry_nodes, entry_sl_idx);
|
smartlist_del_keeporder(entry_guards, entry_sl_idx);
|
||||||
log_entry_nodes(LOG_INFO);
|
log_entry_guards(LOG_INFO);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
} else if (!entry->down_since) {
|
} else if (!entry->down_since) {
|
||||||
entry->down_since = time(NULL);
|
entry->down_since = time(NULL);
|
||||||
warn(LD_CIRC, "Connection to entry node '%s' failed."
|
warn(LD_CIRC, "Connection to entry guard '%s' failed."
|
||||||
" %d/%d entry nodes usable/new.",
|
" %d/%d entry guards usable/new.",
|
||||||
entry->nickname,
|
entry->nickname,
|
||||||
num_live_entry_nodes(), smartlist_len(entry_nodes));
|
num_live_entry_guards(), smartlist_len(entry_guards));
|
||||||
log_entry_nodes(LOG_INFO);
|
log_entry_guards(LOG_INFO);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2047,11 +2048,11 @@ entry_node_set_status(const char *digest, int succeeded)
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
entry_nodes_changed();
|
entry_guards_changed();
|
||||||
return refuse_conn ? -1 : 0;
|
return refuse_conn ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** When we try to choose an entry node, should we parse and add
|
/** When we try to choose an entry guard, should we parse and add
|
||||||
* config's EntryNodes first? */
|
* config's EntryNodes first? */
|
||||||
static int should_add_entry_nodes = 0;
|
static int should_add_entry_nodes = 0;
|
||||||
|
|
||||||
@ -2063,7 +2064,7 @@ entry_nodes_should_be_added(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
entry_nodes_prepend_from_config(void)
|
entry_guards_prepend_from_config(void)
|
||||||
{
|
{
|
||||||
int missed_some = 0;
|
int missed_some = 0;
|
||||||
int idx;
|
int idx;
|
||||||
@ -2071,14 +2072,14 @@ entry_nodes_prepend_from_config(void)
|
|||||||
smartlist_t *routers = smartlist_create();
|
smartlist_t *routers = smartlist_create();
|
||||||
smartlist_t *tmp = smartlist_create();
|
smartlist_t *tmp = smartlist_create();
|
||||||
|
|
||||||
tor_assert(entry_nodes);
|
tor_assert(entry_guards);
|
||||||
tor_assert(options->EntryNodes);
|
tor_assert(options->EntryNodes);
|
||||||
|
|
||||||
if (options->StrictEntryNodes) {
|
if (options->StrictEntryNodes) {
|
||||||
info(LD_CIRC,"Clearing old entry nodes");
|
info(LD_CIRC,"Clearing old entry guards");
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e, tor_free(e));
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, tor_free(e));
|
||||||
smartlist_clear(entry_nodes);
|
smartlist_clear(entry_guards);
|
||||||
entry_nodes_changed();
|
entry_guards_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
add_nickname_list_to_smartlist(routers, options->EntryNodes,
|
add_nickname_list_to_smartlist(routers, options->EntryNodes,
|
||||||
@ -2095,10 +2096,10 @@ entry_nodes_prepend_from_config(void)
|
|||||||
|
|
||||||
for (idx = smartlist_len(routers)-1 ; idx >= 0; idx--) {
|
for (idx = smartlist_len(routers)-1 ; idx >= 0; idx--) {
|
||||||
/* pick off the last one, turn it into a router, prepend it
|
/* pick off the last one, turn it into a router, prepend it
|
||||||
* to our entry_nodes list. If we can't find it, set missed_some
|
* to our entry_guards list. If we can't find it, set missed_some
|
||||||
* to 1. */
|
* to 1. */
|
||||||
routerinfo_t *r = smartlist_get(routers, idx);
|
routerinfo_t *r = smartlist_get(routers, idx);
|
||||||
add_an_entry_node(r);
|
add_an_entry_guard(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!missed_some)
|
if (!missed_some)
|
||||||
@ -2107,37 +2108,37 @@ entry_nodes_prepend_from_config(void)
|
|||||||
smartlist_free(routers);
|
smartlist_free(routers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pick a live (up and listed) entry node from entry_nodes, and
|
/** Pick a live (up and listed) entry guard from entry_guards, and
|
||||||
* make sure not to pick this circuit's exit. */
|
* make sure not to pick this circuit's exit. */
|
||||||
static routerinfo_t *
|
static routerinfo_t *
|
||||||
choose_random_entry(cpath_build_state_t *state)
|
choose_random_entry(cpath_build_state_t *state)
|
||||||
{
|
{
|
||||||
or_options_t *options = get_options();
|
or_options_t *options = get_options();
|
||||||
smartlist_t *live_entry_nodes = smartlist_create();
|
smartlist_t *live_entry_guards = smartlist_create();
|
||||||
routerinfo_t *chosen_exit = build_state_get_exit_router(state);
|
routerinfo_t *chosen_exit = build_state_get_exit_router(state);
|
||||||
routerinfo_t *r = NULL;
|
routerinfo_t *r = NULL;
|
||||||
int need_uptime = state->need_uptime;
|
int need_uptime = state->need_uptime;
|
||||||
int need_capacity = state->need_capacity;
|
int need_capacity = state->need_capacity;
|
||||||
|
|
||||||
if (!entry_nodes)
|
if (!entry_guards)
|
||||||
entry_nodes = smartlist_create();
|
entry_guards = smartlist_create();
|
||||||
|
|
||||||
if (should_add_entry_nodes)
|
if (should_add_entry_nodes)
|
||||||
entry_nodes_prepend_from_config();
|
entry_guards_prepend_from_config();
|
||||||
|
|
||||||
if (!options->StrictEntryNodes &&
|
if (!options->StrictEntryNodes &&
|
||||||
(! entry_nodes ||
|
(! entry_guards ||
|
||||||
smartlist_len(entry_nodes) < options->NumEntryNodes))
|
smartlist_len(entry_guards) < options->NumEntryGuards))
|
||||||
pick_entry_nodes();
|
pick_entry_guards();
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
smartlist_clear(live_entry_nodes);
|
smartlist_clear(live_entry_guards);
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
|
||||||
{
|
{
|
||||||
r = entry_is_live(entry, need_uptime, need_capacity);
|
r = entry_is_live(entry, need_uptime, need_capacity);
|
||||||
if (r && r != chosen_exit) {
|
if (r && r != chosen_exit) {
|
||||||
smartlist_add(live_entry_nodes, r);
|
smartlist_add(live_entry_guards, r);
|
||||||
if (smartlist_len(live_entry_nodes) >= options->NumEntryNodes)
|
if (smartlist_len(live_entry_guards) >= options->NumEntryGuards)
|
||||||
break; /* we have enough */
|
break; /* we have enough */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -2145,18 +2146,18 @@ choose_random_entry(cpath_build_state_t *state)
|
|||||||
/* Try to have at least 2 choices available. This way we don't
|
/* Try to have at least 2 choices available. This way we don't
|
||||||
* get stuck with a single live-but-crummy entry and just keep
|
* get stuck with a single live-but-crummy entry and just keep
|
||||||
* using him.
|
* using him.
|
||||||
* (We might get 2 live-but-crummy entry nodes, but so be it.) */
|
* (We might get 2 live-but-crummy entry guards, but so be it.) */
|
||||||
if (smartlist_len(live_entry_nodes) < 2) {
|
if (smartlist_len(live_entry_guards) < 2) {
|
||||||
if (need_uptime) {
|
if (need_uptime) {
|
||||||
need_uptime = 0; /* try without that requirement */
|
need_uptime = 0; /* try without that requirement */
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
if (!options->StrictEntryNodes) {
|
if (!options->StrictEntryNodes) {
|
||||||
/* still no? try adding a new entry then */
|
/* still no? try adding a new entry then */
|
||||||
r = add_an_entry_node(NULL);
|
r = add_an_entry_guard(NULL);
|
||||||
if (r) {
|
if (r) {
|
||||||
smartlist_add(live_entry_nodes, r);
|
smartlist_add(live_entry_guards, r);
|
||||||
entry_nodes_changed();
|
entry_guards_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!r && need_capacity) {
|
if (!r && need_capacity) {
|
||||||
@ -2164,45 +2165,45 @@ choose_random_entry(cpath_build_state_t *state)
|
|||||||
need_capacity = 0;
|
need_capacity = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
/* live_entry_nodes will be empty below. Oh well, we tried. */
|
/* live_entry_guards will be empty below. Oh well, we tried. */
|
||||||
}
|
}
|
||||||
|
|
||||||
r = smartlist_choose(live_entry_nodes);
|
r = smartlist_choose(live_entry_guards);
|
||||||
smartlist_free(live_entry_nodes);
|
smartlist_free(live_entry_guards);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parse <b>state</b> and learn about the entry nodes it describes.
|
/** Parse <b>state</b> and learn about the entry guards it describes.
|
||||||
* If <b>set</b> is true, and there are no errors, replace the global
|
* If <b>set</b> is true, and there are no errors, replace the global
|
||||||
* entry_list with what we find.
|
* entry_list with what we find.
|
||||||
* On success, return 0. On failure, set *<b>err</b> to a string
|
* On success, return 0. On failure, set *<b>err</b> to a string
|
||||||
* describing the error, and return -1.
|
* describing the error, and return -1.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
entry_nodes_parse_state(or_state_t *state, int set, const char **err)
|
entry_guards_parse_state(or_state_t *state, int set, const char **err)
|
||||||
{
|
{
|
||||||
entry_node_t *node = NULL;
|
entry_guard_t *node = NULL;
|
||||||
smartlist_t *new_entry_nodes = smartlist_create();
|
smartlist_t *new_entry_guards = smartlist_create();
|
||||||
config_line_t *line;
|
config_line_t *line;
|
||||||
|
|
||||||
*err = NULL;
|
*err = NULL;
|
||||||
for (line = state->EntryNodes; line; line = line->next) {
|
for (line = state->EntryGuards; line; line = line->next) {
|
||||||
if (!strcasecmp(line->key, "EntryNode")) {
|
if (!strcasecmp(line->key, "EntryGuard")) {
|
||||||
smartlist_t *args = smartlist_create();
|
smartlist_t *args = smartlist_create();
|
||||||
node = tor_malloc_zero(sizeof(entry_node_t));
|
node = tor_malloc_zero(sizeof(entry_guard_t));
|
||||||
node->made_contact = 1; /* all entry nodes on disk have been contacted */
|
node->made_contact = 1; /* all entry guards on disk have been contacted */
|
||||||
smartlist_add(new_entry_nodes, node);
|
smartlist_add(new_entry_guards, node);
|
||||||
smartlist_split_string(args, line->value, " ",
|
smartlist_split_string(args, line->value, " ",
|
||||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||||
if (smartlist_len(args)<2) {
|
if (smartlist_len(args)<2) {
|
||||||
*err = "Too few arguments to EntryNode";
|
*err = "Too few arguments to EntryGuard";
|
||||||
} else if (!is_legal_nickname(smartlist_get(args,0))) {
|
} else if (!is_legal_nickname(smartlist_get(args,0))) {
|
||||||
*err = "Bad nickname for EntryNode";
|
*err = "Bad nickname for EntryGuard";
|
||||||
} else {
|
} else {
|
||||||
strlcpy(node->nickname, smartlist_get(args,0), MAX_NICKNAME_LEN+1);
|
strlcpy(node->nickname, smartlist_get(args,0), MAX_NICKNAME_LEN+1);
|
||||||
if (base16_decode(node->identity, DIGEST_LEN, smartlist_get(args,1),
|
if (base16_decode(node->identity, DIGEST_LEN, smartlist_get(args,1),
|
||||||
strlen(smartlist_get(args,1)))<0) {
|
strlen(smartlist_get(args,1)))<0) {
|
||||||
*err = "Bad hex digest for EntryNode";
|
*err = "Bad hex digest for EntryGuard";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SMARTLIST_FOREACH(args, char*, cp, tor_free(cp));
|
SMARTLIST_FOREACH(args, char*, cp, tor_free(cp));
|
||||||
@ -2212,14 +2213,14 @@ entry_nodes_parse_state(or_state_t *state, int set, const char **err)
|
|||||||
} else {
|
} else {
|
||||||
time_t when;
|
time_t when;
|
||||||
if (!node) {
|
if (!node) {
|
||||||
*err = "EntryNodeDownSince/UnlistedSince without EntryNode";
|
*err = "EntryGuardDownSince/UnlistedSince without EntryGuard";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (parse_iso_time(line->value, &when)<0) {
|
if (parse_iso_time(line->value, &when)<0) {
|
||||||
*err = "Bad time in EntryNodeDownSince/UnlistedSince";
|
*err = "Bad time in EntryGuardDownSince/UnlistedSince";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!strcasecmp(line->key, "EntryNodeDownSince"))
|
if (!strcasecmp(line->key, "EntryGuardDownSince"))
|
||||||
node->down_since = when;
|
node->down_since = when;
|
||||||
else
|
else
|
||||||
node->unlisted_since = when;
|
node->unlisted_since = when;
|
||||||
@ -2227,53 +2228,53 @@ entry_nodes_parse_state(or_state_t *state, int set, const char **err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (*err || !set) {
|
if (*err || !set) {
|
||||||
SMARTLIST_FOREACH(new_entry_nodes, entry_node_t *, e, tor_free(e));
|
SMARTLIST_FOREACH(new_entry_guards, entry_guard_t *, e, tor_free(e));
|
||||||
smartlist_free(new_entry_nodes);
|
smartlist_free(new_entry_guards);
|
||||||
} else { /* !*err && set */
|
} else { /* !*err && set */
|
||||||
if (entry_nodes) {
|
if (entry_guards) {
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e, tor_free(e));
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, tor_free(e));
|
||||||
smartlist_free(entry_nodes);
|
smartlist_free(entry_guards);
|
||||||
}
|
}
|
||||||
entry_nodes = new_entry_nodes;
|
entry_guards = new_entry_guards;
|
||||||
entry_nodes_dirty = 0;
|
entry_guards_dirty = 0;
|
||||||
}
|
}
|
||||||
return *err ? -1 : 0;
|
return *err ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Our list of entry nodes has changed, or some element of one
|
/** Our list of entry guards has changed, or some element of one
|
||||||
* of our entry nodes has changed. Write the changes to disk. */
|
* of our entry guards has changed. Write the changes to disk. */
|
||||||
static void
|
static void
|
||||||
entry_nodes_changed(void)
|
entry_guards_changed(void)
|
||||||
{
|
{
|
||||||
entry_nodes_dirty = 1;
|
entry_guards_dirty = 1;
|
||||||
|
|
||||||
or_state_save();
|
or_state_save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If the entry node info has not changed, do nothing and return.
|
/** If the entry guard info has not changed, do nothing and return.
|
||||||
* Otherwise, free the EntryNodes piece of <b>state</b> and create
|
* Otherwise, free the EntryGuards piece of <b>state</b> and create
|
||||||
* a new one out of the global entry_nodes list, and then mark
|
* a new one out of the global entry_guards list, and then mark
|
||||||
* <b>state</b> dirty so it will get saved to disk.
|
* <b>state</b> dirty so it will get saved to disk.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
entry_nodes_update_state(or_state_t *state)
|
entry_guards_update_state(or_state_t *state)
|
||||||
{
|
{
|
||||||
config_line_t **next, *line;
|
config_line_t **next, *line;
|
||||||
if (! entry_nodes_dirty)
|
if (! entry_guards_dirty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
config_free_lines(state->EntryNodes);
|
config_free_lines(state->EntryGuards);
|
||||||
next = &state->EntryNodes;
|
next = &state->EntryGuards;
|
||||||
*next = NULL;
|
*next = NULL;
|
||||||
if (!entry_nodes)
|
if (!entry_guards)
|
||||||
entry_nodes = smartlist_create();
|
entry_guards = smartlist_create();
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
|
||||||
{
|
{
|
||||||
char dbuf[HEX_DIGEST_LEN+1];
|
char dbuf[HEX_DIGEST_LEN+1];
|
||||||
if (!e->made_contact)
|
if (!e->made_contact)
|
||||||
continue; /* don't write this one to disk */
|
continue; /* don't write this one to disk */
|
||||||
*next = line = tor_malloc_zero(sizeof(config_line_t));
|
*next = line = tor_malloc_zero(sizeof(config_line_t));
|
||||||
line->key = tor_strdup("EntryNode");
|
line->key = tor_strdup("EntryGuard");
|
||||||
line->value = tor_malloc(HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2);
|
line->value = tor_malloc(HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2);
|
||||||
base16_encode(dbuf, sizeof(dbuf), e->identity, DIGEST_LEN);
|
base16_encode(dbuf, sizeof(dbuf), e->identity, DIGEST_LEN);
|
||||||
tor_snprintf(line->value,HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2,
|
tor_snprintf(line->value,HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2,
|
||||||
@ -2281,40 +2282,40 @@ entry_nodes_update_state(or_state_t *state)
|
|||||||
next = &(line->next);
|
next = &(line->next);
|
||||||
if (e->down_since) {
|
if (e->down_since) {
|
||||||
*next = line = tor_malloc_zero(sizeof(config_line_t));
|
*next = line = tor_malloc_zero(sizeof(config_line_t));
|
||||||
line->key = tor_strdup("EntryNodeDownSince");
|
line->key = tor_strdup("EntryGuardDownSince");
|
||||||
line->value = tor_malloc(ISO_TIME_LEN+1);
|
line->value = tor_malloc(ISO_TIME_LEN+1);
|
||||||
format_iso_time(line->value, e->down_since);
|
format_iso_time(line->value, e->down_since);
|
||||||
next = &(line->next);
|
next = &(line->next);
|
||||||
}
|
}
|
||||||
if (e->unlisted_since) {
|
if (e->unlisted_since) {
|
||||||
*next = line = tor_malloc_zero(sizeof(config_line_t));
|
*next = line = tor_malloc_zero(sizeof(config_line_t));
|
||||||
line->key = tor_strdup("EntryNodeUnlistedSince");
|
line->key = tor_strdup("EntryGuardUnlistedSince");
|
||||||
line->value = tor_malloc(ISO_TIME_LEN+1);
|
line->value = tor_malloc(ISO_TIME_LEN+1);
|
||||||
format_iso_time(line->value, e->unlisted_since);
|
format_iso_time(line->value, e->unlisted_since);
|
||||||
next = &(line->next);
|
next = &(line->next);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
state->dirty = 1;
|
state->dirty = 1;
|
||||||
entry_nodes_dirty = 0;
|
entry_guards_dirty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If <b>question</b> is the string "entry-nodes", then dump
|
/** If <b>question</b> is the string "entry-guards", then dump
|
||||||
* to *<b>answer</b> a newly allocated string describing all of
|
* to *<b>answer</b> a newly allocated string describing all of
|
||||||
* the nodes in the global entry_nodes list. See control-spec.txt
|
* the nodes in the global entry_guards list. See control-spec.txt
|
||||||
* for details.
|
* for details.
|
||||||
* For backward compatibility, we also handle the string "helper-nodes".
|
* For backward compatibility, we also handle the string "helper-nodes".
|
||||||
* */
|
* */
|
||||||
int
|
int
|
||||||
entry_nodes_getinfo(const char *question, char **answer)
|
entry_guards_getinfo(const char *question, char **answer)
|
||||||
{
|
{
|
||||||
if (!strcmp(question,"entry-nodes") ||
|
if (!strcmp(question,"entry-guards") ||
|
||||||
!strcmp(question,"helper-nodes")) {
|
!strcmp(question,"helper-nodes")) {
|
||||||
smartlist_t *sl = smartlist_create();
|
smartlist_t *sl = smartlist_create();
|
||||||
char tbuf[ISO_TIME_LEN+1];
|
char tbuf[ISO_TIME_LEN+1];
|
||||||
char dbuf[HEX_DIGEST_LEN+1];
|
char dbuf[HEX_DIGEST_LEN+1];
|
||||||
if (!entry_nodes)
|
if (!entry_guards)
|
||||||
entry_nodes = smartlist_create();
|
entry_guards = smartlist_create();
|
||||||
SMARTLIST_FOREACH(entry_nodes, entry_node_t *, e,
|
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
|
||||||
{
|
{
|
||||||
size_t len = HEX_DIGEST_LEN+ISO_TIME_LEN+32;
|
size_t len = HEX_DIGEST_LEN+ISO_TIME_LEN+32;
|
||||||
char *c = tor_malloc(len);
|
char *c = tor_malloc(len);
|
||||||
|
@ -680,7 +680,7 @@ circuit_build_failed(circuit_t *circ)
|
|||||||
"(%s:%d). I'm going to try to rotate to a better connection.",
|
"(%s:%d). I'm going to try to rotate to a better connection.",
|
||||||
n_conn->address, n_conn->port);
|
n_conn->address, n_conn->port);
|
||||||
n_conn->is_obsolete = 1;
|
n_conn->is_obsolete = 1;
|
||||||
entry_node_set_status(n_conn->identity_digest, 0);
|
entry_guard_set_status(n_conn->identity_digest, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,16 +71,21 @@ static config_abbrev_t _option_abbrevs[] = {
|
|||||||
{ "ORBindAddress", "ORListenAddress", 0, 0},
|
{ "ORBindAddress", "ORListenAddress", 0, 0},
|
||||||
{ "DirBindAddress", "DirListenAddress", 0, 0},
|
{ "DirBindAddress", "DirListenAddress", 0, 0},
|
||||||
{ "SocksBindAddress", "SocksListenAddress", 0, 0},
|
{ "SocksBindAddress", "SocksListenAddress", 0, 0},
|
||||||
{ "UseHelperNodes", "UseEntryNodes", 0, 0},
|
{ "UseHelperNodes", "UseEntryGuards", 0, 0},
|
||||||
{ "NumHelperNodes", "NumEntryNodes", 0, 0},
|
{ "NumHelperNodes", "NumEntryGuards", 0, 0},
|
||||||
|
{ "UseEntryNodes", "UseEntryGuards", 0, 0},
|
||||||
|
{ "NumEntryNodes", "NumEntryGuards", 0, 0},
|
||||||
{ NULL, NULL, 0, 0},
|
{ NULL, NULL, 0, 0},
|
||||||
};
|
};
|
||||||
/* A list of state-file abbreviations, for compatibility. */
|
/* A list of state-file abbreviations, for compatibility. */
|
||||||
static config_abbrev_t _state_abbrevs[] = {
|
static config_abbrev_t _state_abbrevs[] = {
|
||||||
{ "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 },
|
{ "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 },
|
||||||
{ "HelperNode", "EntryNode", 0, 0 },
|
{ "HelperNode", "EntryGuard", 0, 0 },
|
||||||
{ "HelperNodeDownSince", "EntryNodeDownSince", 0, 0 },
|
{ "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 },
|
||||||
{ "HelperNodeUnlistedSince", "EntryNodeUnlistedSince", 0, 0 },
|
{ "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
|
||||||
|
{ "EntryNode", "EntryGuard", 0, 0 },
|
||||||
|
{ "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 },
|
||||||
|
{ "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
|
||||||
{ NULL, NULL, 0, 0},
|
{ NULL, NULL, 0, 0},
|
||||||
};
|
};
|
||||||
#undef PLURAL
|
#undef PLURAL
|
||||||
@ -179,7 +184,7 @@ static config_var_t _option_vars[] = {
|
|||||||
VAR("NoPublish", BOOL, NoPublish, "0"),
|
VAR("NoPublish", BOOL, NoPublish, "0"),
|
||||||
VAR("NodeFamily", LINELIST, NodeFamilies, NULL),
|
VAR("NodeFamily", LINELIST, NodeFamilies, NULL),
|
||||||
VAR("NumCpus", UINT, NumCpus, "1"),
|
VAR("NumCpus", UINT, NumCpus, "1"),
|
||||||
VAR("NumEntryNodes", UINT, NumEntryNodes, "3"),
|
VAR("NumEntryGuards", UINT, NumEntryGuards, "3"),
|
||||||
VAR("ORListenAddress", LINELIST, ORListenAddress, NULL),
|
VAR("ORListenAddress", LINELIST, ORListenAddress, NULL),
|
||||||
VAR("ORPort", UINT, ORPort, "0"),
|
VAR("ORPort", UINT, ORPort, "0"),
|
||||||
VAR("OutboundBindAddress", STRING, OutboundBindAddress, NULL),
|
VAR("OutboundBindAddress", STRING, OutboundBindAddress, NULL),
|
||||||
@ -212,7 +217,7 @@ static config_var_t _option_vars[] = {
|
|||||||
VAR("TrackHostExits", CSV, TrackHostExits, NULL),
|
VAR("TrackHostExits", CSV, TrackHostExits, NULL),
|
||||||
VAR("TrackHostExitsExpire",INTERVAL, TrackHostExitsExpire, "30 minutes"),
|
VAR("TrackHostExitsExpire",INTERVAL, TrackHostExitsExpire, "30 minutes"),
|
||||||
OBSOLETE("TrafficShaping"),
|
OBSOLETE("TrafficShaping"),
|
||||||
VAR("UseEntryNodes", BOOL, UseEntryNodes, "1"),
|
VAR("UseEntryGuards", BOOL, UseEntryGuards, "1"),
|
||||||
VAR("User", STRING, User, NULL),
|
VAR("User", STRING, User, NULL),
|
||||||
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
|
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
|
||||||
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
|
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
|
||||||
@ -232,10 +237,10 @@ static config_var_t _state_vars[] = {
|
|||||||
VAR("AccountingExpectedUsage", MEMUNIT, AccountingExpectedUsage, NULL),
|
VAR("AccountingExpectedUsage", MEMUNIT, AccountingExpectedUsage, NULL),
|
||||||
VAR("AccountingIntervalStart", ISOTIME, AccountingIntervalStart, NULL),
|
VAR("AccountingIntervalStart", ISOTIME, AccountingIntervalStart, NULL),
|
||||||
VAR("AccountingSecondsActive", INTERVAL, AccountingSecondsActive, NULL),
|
VAR("AccountingSecondsActive", INTERVAL, AccountingSecondsActive, NULL),
|
||||||
VAR("EntryNode", LINELIST_S, EntryNodes, NULL),
|
VAR("EntryGuard", LINELIST_S, EntryGuards, NULL),
|
||||||
VAR("EntryNodeDownSince", LINELIST_S, EntryNodes, NULL),
|
VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL),
|
||||||
VAR("EntryNodeUnlistedSince", LINELIST_S, EntryNodes, NULL),
|
VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL),
|
||||||
VAR("EntryNodes", LINELIST_V, EntryNodes, NULL),
|
VAR("EntryGuards", LINELIST_V, EntryGuards, NULL),
|
||||||
|
|
||||||
VAR("BWHistoryReadEnds", ISOTIME, BWHistoryReadEnds, NULL),
|
VAR("BWHistoryReadEnds", ISOTIME, BWHistoryReadEnds, NULL),
|
||||||
VAR("BWHistoryReadInterval", UINT, BWHistoryReadInterval, NULL),
|
VAR("BWHistoryReadInterval", UINT, BWHistoryReadInterval, NULL),
|
||||||
@ -283,11 +288,11 @@ static config_var_description_t state_description[] = {
|
|||||||
{ "BWHistoryWriteInterval", "How long is each write-interval (in seconds)?"},
|
{ "BWHistoryWriteInterval", "How long is each write-interval (in seconds)?"},
|
||||||
{ "BWHistoryWriteValues", "Number of bytes written in each interval." },
|
{ "BWHistoryWriteValues", "Number of bytes written in each interval." },
|
||||||
|
|
||||||
{ "EntryNode", "One of the nodes we have chosen as a fixed entry" },
|
{ "EntryGuard", "One of the nodes we have chosen as a fixed entry" },
|
||||||
{ "EntryNodeDownSince",
|
{ "EntryGuardDownSince",
|
||||||
"The last entry node has been down since this time." },
|
"The last entry guard has been down since this time." },
|
||||||
{ "EntryNodeUnlistedSince",
|
{ "EntryGuardUnlistedSince",
|
||||||
"The last entry node has been unlisted since this time." },
|
"The last entry guard has been unlisted since this time." },
|
||||||
{ "LastWritten", "When was this state file last regenerated?" },
|
{ "LastWritten", "When was this state file last regenerated?" },
|
||||||
|
|
||||||
{ "TorVersion", "Which version of Tor generated this state file?" },
|
{ "TorVersion", "Which version of Tor generated this state file?" },
|
||||||
@ -710,8 +715,8 @@ options_act(or_options_t *old_options)
|
|||||||
|
|
||||||
/* Check for transitions that need action. */
|
/* Check for transitions that need action. */
|
||||||
if (old_options) {
|
if (old_options) {
|
||||||
if (options->UseEntryNodes && !old_options->UseEntryNodes) {
|
if (options->UseEntryGuards && !old_options->UseEntryGuards) {
|
||||||
info(LD_CIRC,"Switching to entry nodes; abandoning previous circuits");
|
info(LD_CIRC,"Switching to entry guards; abandoning previous circuits");
|
||||||
circuit_mark_all_unused_circs();
|
circuit_mark_all_unused_circs();
|
||||||
circuit_expire_all_dirty_circs();
|
circuit_expire_all_dirty_circs();
|
||||||
}
|
}
|
||||||
@ -1937,10 +1942,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
|||||||
if (!options->RecommendedServerVersions)
|
if (!options->RecommendedServerVersions)
|
||||||
options->RecommendedServerVersions =
|
options->RecommendedServerVersions =
|
||||||
config_lines_dup(options->RecommendedVersions);
|
config_lines_dup(options->RecommendedVersions);
|
||||||
if (options->UseEntryNodes) {
|
if (options->UseEntryGuards) {
|
||||||
notice(LD_CONFIG, "Authoritative directory servers can't set "
|
notice(LD_CONFIG, "Authoritative directory servers can't set "
|
||||||
"UseEntryNodes. Disabling.");
|
"UseEntryGuards. Disabling.");
|
||||||
options->UseEntryNodes = 0;
|
options->UseEntryGuards = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2170,8 +2175,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
|||||||
if (options->HashedControlPassword && options->CookieAuthentication)
|
if (options->HashedControlPassword && options->CookieAuthentication)
|
||||||
REJECT("Cannot set both HashedControlPassword and CookieAuthentication");
|
REJECT("Cannot set both HashedControlPassword and CookieAuthentication");
|
||||||
|
|
||||||
if (options->UseEntryNodes && ! options->NumEntryNodes)
|
if (options->UseEntryGuards && ! options->NumEntryGuards)
|
||||||
REJECT("Cannot enable UseEntryNodes with NumEntryNodes set to 0");
|
REJECT("Cannot enable UseEntryGuards with NumEntryGuards set to 0");
|
||||||
|
|
||||||
if (check_nickname_list(options->ExitNodes, "ExitNodes"))
|
if (check_nickname_list(options->ExitNodes, "ExitNodes"))
|
||||||
result = -1;
|
result = -1;
|
||||||
@ -3569,7 +3574,7 @@ or_state_validate(or_state_t *old_state, or_state_t *state, int from_setconf)
|
|||||||
{
|
{
|
||||||
const char *err;
|
const char *err;
|
||||||
tor_version_t v;
|
tor_version_t v;
|
||||||
if (entry_nodes_parse_state(state, 0, &err)<0) {
|
if (entry_guards_parse_state(state, 0, &err)<0) {
|
||||||
warn(LD_GENERAL, "Unable to parse entry nodes: %s", err);
|
warn(LD_GENERAL, "Unable to parse entry nodes: %s", err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -3589,7 +3594,7 @@ or_state_set(or_state_t *new_state)
|
|||||||
if (global_state)
|
if (global_state)
|
||||||
config_free(&state_format, global_state);
|
config_free(&state_format, global_state);
|
||||||
global_state = new_state;
|
global_state = new_state;
|
||||||
if (entry_nodes_parse_state(global_state, 1, &err)<0)
|
if (entry_guards_parse_state(global_state, 1, &err)<0)
|
||||||
warn(LD_GENERAL,"Unparseable helper nodes state: %s",err);
|
warn(LD_GENERAL,"Unparseable helper nodes state: %s",err);
|
||||||
if (rep_hist_load_state(global_state, &err)<0)
|
if (rep_hist_load_state(global_state, &err)<0)
|
||||||
warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
|
warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
|
||||||
@ -3666,7 +3671,7 @@ or_state_save(void)
|
|||||||
|
|
||||||
tor_assert(global_state);
|
tor_assert(global_state);
|
||||||
|
|
||||||
entry_nodes_update_state(global_state);
|
entry_guards_update_state(global_state);
|
||||||
rep_hist_update_state(global_state);
|
rep_hist_update_state(global_state);
|
||||||
|
|
||||||
if (!global_state->dirty)
|
if (!global_state->dirty)
|
||||||
|
@ -346,7 +346,7 @@ connection_about_to_close_connection(connection_t *conn)
|
|||||||
if (conn->state != OR_CONN_STATE_OPEN) {
|
if (conn->state != OR_CONN_STATE_OPEN) {
|
||||||
if (connection_or_nonopen_was_started_here(conn)) {
|
if (connection_or_nonopen_was_started_here(conn)) {
|
||||||
rep_hist_note_connect_failed(conn->identity_digest, time(NULL));
|
rep_hist_note_connect_failed(conn->identity_digest, time(NULL));
|
||||||
entry_node_set_status(conn->identity_digest, 0);
|
entry_guard_set_status(conn->identity_digest, 0);
|
||||||
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
|
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
|
||||||
}
|
}
|
||||||
} else if (conn->hold_open_until_flushed) {
|
} else if (conn->hold_open_until_flushed) {
|
||||||
|
@ -452,7 +452,7 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
|
|||||||
* Tor server. */
|
* Tor server. */
|
||||||
if (!options->HttpsProxy) {
|
if (!options->HttpsProxy) {
|
||||||
router_mark_as_down(conn->identity_digest);
|
router_mark_as_down(conn->identity_digest);
|
||||||
entry_node_set_status(conn->identity_digest, 0);
|
entry_guard_set_status(conn->identity_digest, 0);
|
||||||
}
|
}
|
||||||
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
|
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
|
||||||
connection_free(conn);
|
connection_free(conn);
|
||||||
@ -625,7 +625,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
|
|||||||
"Identity key not as expected for router at %s:%d: wanted %s "
|
"Identity key not as expected for router at %s:%d: wanted %s "
|
||||||
"but got %s",
|
"but got %s",
|
||||||
conn->address, conn->port, expected, seen);
|
conn->address, conn->port, expected, seen);
|
||||||
entry_node_set_status(conn->identity_digest, 0);
|
entry_guard_set_status(conn->identity_digest, 0);
|
||||||
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
|
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
|
||||||
as_advertised = 0;
|
as_advertised = 0;
|
||||||
}
|
}
|
||||||
@ -687,7 +687,7 @@ connection_tls_finish_handshake(connection_t *conn)
|
|||||||
control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED);
|
control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED);
|
||||||
if (started_here) {
|
if (started_here) {
|
||||||
rep_hist_note_connect_succeeded(conn->identity_digest, time(NULL));
|
rep_hist_note_connect_succeeded(conn->identity_digest, time(NULL));
|
||||||
if (entry_node_set_status(conn->identity_digest, 1) < 0) {
|
if (entry_guard_set_status(conn->identity_digest, 1) < 0) {
|
||||||
/* pending circs get closed in circuit_about_to_close_connection() */
|
/* pending circs get closed in circuit_about_to_close_connection() */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1248,7 +1248,7 @@ list_getinfo_options(void)
|
|||||||
"desc/id/* Server descriptor by hex ID\n"
|
"desc/id/* Server descriptor by hex ID\n"
|
||||||
"desc/name/* Server descriptor by nickname.\n"
|
"desc/name/* Server descriptor by nickname.\n"
|
||||||
"desc/all-recent Latest server descriptor for every router\n"
|
"desc/all-recent Latest server descriptor for every router\n"
|
||||||
"entry-nodes Which nodes will we use as entry nodes?\n"
|
"entry-guards Which nodes will we use as entry guards?\n"
|
||||||
"info/names List of GETINFO options, types, and documentation.\n"
|
"info/names List of GETINFO options, types, and documentation.\n"
|
||||||
"network-status List of hex IDs, nicknames, server statuses.\n"
|
"network-status List of hex IDs, nicknames, server statuses.\n"
|
||||||
"orconn-status Status of each current OR connection.\n"
|
"orconn-status Status of each current OR connection.\n"
|
||||||
@ -1270,9 +1270,9 @@ handle_getinfo_helper(const char *question, char **answer)
|
|||||||
} else if (!strcmpstart(question, "accounting/")) {
|
} else if (!strcmpstart(question, "accounting/")) {
|
||||||
return accounting_getinfo_helper(question, answer);
|
return accounting_getinfo_helper(question, answer);
|
||||||
} else if (!strcmpstart(question, "helper-nodes")) { /* deprecated */
|
} else if (!strcmpstart(question, "helper-nodes")) { /* deprecated */
|
||||||
return entry_nodes_getinfo(question, answer);
|
return entry_guards_getinfo(question, answer);
|
||||||
} else if (!strcmpstart(question, "entry-nodes")) {
|
} else if (!strcmpstart(question, "entry-guards")) {
|
||||||
return entry_nodes_getinfo(question, answer);
|
return entry_guards_getinfo(question, answer);
|
||||||
} else if (!strcmpstart(question, "config/")) {
|
} else if (!strcmpstart(question, "config/")) {
|
||||||
return config_getinfo_helper(question, answer);
|
return config_getinfo_helper(question, answer);
|
||||||
} else if (!strcmp(question, "info/names")) {
|
} else if (!strcmp(question, "info/names")) {
|
||||||
|
@ -1483,7 +1483,7 @@ tor_free_all(int postfork)
|
|||||||
dns_free_all();
|
dns_free_all();
|
||||||
clear_pending_onions();
|
clear_pending_onions();
|
||||||
circuit_free_all();
|
circuit_free_all();
|
||||||
entry_nodes_free_all();
|
entry_guards_free_all();
|
||||||
connection_free_all();
|
connection_free_all();
|
||||||
if (!postfork) {
|
if (!postfork) {
|
||||||
config_free_all();
|
config_free_all();
|
||||||
|
20
src/or/or.h
20
src/or/or.h
@ -1361,9 +1361,9 @@ typedef struct {
|
|||||||
* log whether it was DNS-leaking or not? */
|
* log whether it was DNS-leaking or not? */
|
||||||
int HardwareAccel; /**< Boolean: Should we enable OpenSSL hardware
|
int HardwareAccel; /**< Boolean: Should we enable OpenSSL hardware
|
||||||
* acceleration where available? */
|
* acceleration where available? */
|
||||||
int UseEntryNodes; /**< Boolean: Do we try to enter from a smallish number
|
int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number
|
||||||
* of fixed nodes? */
|
* of fixed nodes? */
|
||||||
int NumEntryNodes; /**< How many helper nodes do we try to establish? */
|
int NumEntryGuards; /**< How many entry guards do we try to establish? */
|
||||||
int RephistTrackTime; /**< How many seconds do we keep rephist info? */
|
int RephistTrackTime; /**< How many seconds do we keep rephist info? */
|
||||||
int FastFirstHopPK; /**< If Tor believes it is safe, should we save a third
|
int FastFirstHopPK; /**< If Tor believes it is safe, should we save a third
|
||||||
* of our PK time by sending CREATE_FAST cells? */
|
* of our PK time by sending CREATE_FAST cells? */
|
||||||
@ -1384,7 +1384,7 @@ typedef struct {
|
|||||||
int AccountingSecondsActive;
|
int AccountingSecondsActive;
|
||||||
uint64_t AccountingExpectedUsage;
|
uint64_t AccountingExpectedUsage;
|
||||||
|
|
||||||
config_line_t *EntryNodes;
|
config_line_t *EntryGuards;
|
||||||
|
|
||||||
time_t BWHistoryReadEnds;
|
time_t BWHistoryReadEnds;
|
||||||
int BWHistoryReadInterval;
|
int BWHistoryReadInterval;
|
||||||
@ -1484,14 +1484,14 @@ void extend_info_free(extend_info_t *info);
|
|||||||
routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
|
routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
|
||||||
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
|
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
|
||||||
|
|
||||||
void entry_nodes_set_status_from_directory(void);
|
void entry_guards_set_status_from_directory(void);
|
||||||
int entry_node_set_status(const char *digest, int succeeded);
|
int entry_guard_set_status(const char *digest, int succeeded);
|
||||||
void entry_nodes_should_be_added(void);
|
void entry_nodes_should_be_added(void);
|
||||||
void entry_nodes_prepend_from_config(void);
|
void entry_guards_prepend_from_config(void);
|
||||||
void entry_nodes_update_state(or_state_t *state);
|
void entry_guards_update_state(or_state_t *state);
|
||||||
int entry_nodes_parse_state(or_state_t *state, int set, const char **err);
|
int entry_guards_parse_state(or_state_t *state, int set, const char **err);
|
||||||
int entry_nodes_getinfo(const char *question, char **answer);
|
int entry_guards_getinfo(const char *question, char **answer);
|
||||||
void entry_nodes_free_all(void);
|
void entry_guards_free_all(void);
|
||||||
|
|
||||||
/********************************* circuitlist.c ***********************/
|
/********************************* circuitlist.c ***********************/
|
||||||
|
|
||||||
|
@ -2757,7 +2757,7 @@ routers_update_all_from_networkstatus(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry_nodes_set_status_from_directory();
|
entry_guards_set_status_from_directory();
|
||||||
|
|
||||||
if (!have_warned_about_old_version) {
|
if (!have_warned_about_old_version) {
|
||||||
int n_recent = 0;
|
int n_recent = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user