|
@@ -1,6 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -29,17 +30,28 @@ import dateutil.parser
|
|
|
from stem.descriptor.remote import DescriptorDownloader
|
|
|
|
|
|
import logging
|
|
|
-logging.basicConfig(level=logging.DEBUG)
|
|
|
+
|
|
|
+
|
|
|
+logging.basicConfig(level=logging.WARNING)
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-PERFORM_IPV6_DIRPORT_CHECKS = False
|
|
|
-
|
|
|
|
|
|
OUTPUT_CANDIDATES = False
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+PERFORM_IPV4_DIRPORT_CHECKS = False if OUTPUT_CANDIDATES else True
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+PERFORM_IPV6_DIRPORT_CHECKS = False if OUTPUT_CANDIDATES else False
|
|
|
+
|
|
|
|
|
|
|
|
|
ONIONOO = 'https://onionoo.torproject.org/'
|
|
@@ -81,7 +93,7 @@ MAX_LIST_FILE_SIZE = 1024 * 1024
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
ADDRESS_AND_PORT_STABLE_DAYS = 7
|
|
|
|
|
@@ -157,36 +169,52 @@ def parse_ts(t):
|
|
|
|
|
|
def remove_bad_chars(raw_string, bad_char_list):
|
|
|
|
|
|
- escaped_string = raw_string
|
|
|
+ cleansed_string = raw_string
|
|
|
for c in bad_char_list:
|
|
|
- escaped_string = escaped_string.replace(c, '')
|
|
|
- return escaped_string
|
|
|
+ cleansed_string = cleansed_string.replace(c, '')
|
|
|
+ return cleansed_string
|
|
|
+
|
|
|
+def cleanse_unprintable(raw_string):
|
|
|
+
|
|
|
+ cleansed_string = ''
|
|
|
+ for c in raw_string:
|
|
|
+ if (c in string.ascii_letters or c in string.digits
|
|
|
+ or c in string.punctuation or c in string.whitespace):
|
|
|
+ cleansed_string += c
|
|
|
+ return cleansed_string
|
|
|
|
|
|
def cleanse_whitespace(raw_string):
|
|
|
|
|
|
- escaped_string = raw_string
|
|
|
+ cleansed_string = raw_string
|
|
|
for c in string.whitespace:
|
|
|
- escaped_string = escaped_string.replace(c, ' ')
|
|
|
- return escaped_string
|
|
|
+ cleansed_string = cleansed_string.replace(c, ' ')
|
|
|
+ return cleansed_string
|
|
|
|
|
|
def cleanse_c_multiline_comment(raw_string):
|
|
|
+ cleansed_string = raw_string
|
|
|
+
|
|
|
+ cleansed_string = cleanse_whitespace(cleansed_string)
|
|
|
+
|
|
|
+ cleansed_string = cleanse_unprintable(cleansed_string)
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- bad_char_list = '*'
|
|
|
+
|
|
|
+ bad_char_list = '*/'
|
|
|
|
|
|
bad_char_list += '\0'
|
|
|
|
|
|
- escaped_string = remove_bad_chars(raw_string, bad_char_list)
|
|
|
-
|
|
|
- escaped_string = cleanse_whitespace(escaped_string)
|
|
|
+ cleansed_string = remove_bad_chars(cleansed_string, bad_char_list)
|
|
|
|
|
|
|
|
|
|
|
|
- return escaped_string
|
|
|
+ return cleansed_string
|
|
|
|
|
|
def cleanse_c_string(raw_string):
|
|
|
+ cleansed_string = raw_string
|
|
|
+
|
|
|
+ cleansed_string = cleanse_whitespace(cleansed_string)
|
|
|
+
|
|
|
+ cleansed_string = cleanse_unprintable(cleansed_string)
|
|
|
|
|
|
|
|
|
bad_char_list = '"'
|
|
@@ -195,13 +223,11 @@ def cleanse_c_string(raw_string):
|
|
|
|
|
|
bad_char_list += '\0'
|
|
|
|
|
|
- escaped_string = remove_bad_chars(raw_string, bad_char_list)
|
|
|
-
|
|
|
- escaped_string = cleanse_whitespace(escaped_string)
|
|
|
+ cleansed_string = remove_bad_chars(cleansed_string, bad_char_list)
|
|
|
|
|
|
|
|
|
|
|
|
- return escaped_string
|
|
|
+ return cleansed_string
|
|
|
|
|
|
|
|
|
|
|
@@ -244,11 +270,11 @@ def write_to_file(str, file_name, max_len):
|
|
|
with open(file_name, 'w') as f:
|
|
|
f.write(str[0:max_len])
|
|
|
except EnvironmentError, error:
|
|
|
- logging.debug('Writing file %s failed: %d: %s'%
|
|
|
- (file_name,
|
|
|
- error.errno,
|
|
|
- error.strerror)
|
|
|
- )
|
|
|
+ logging.warning('Writing file %s failed: %d: %s'%
|
|
|
+ (file_name,
|
|
|
+ error.errno,
|
|
|
+ error.strerror)
|
|
|
+ )
|
|
|
|
|
|
def read_from_file(file_name, max_len):
|
|
|
try:
|
|
@@ -256,11 +282,11 @@ def read_from_file(file_name, max_len):
|
|
|
with open(file_name, 'r') as f:
|
|
|
return f.read(max_len)
|
|
|
except EnvironmentError, error:
|
|
|
- logging.debug('Loading file %s failed: %d: %s'%
|
|
|
- (file_name,
|
|
|
- error.errno,
|
|
|
- error.strerror)
|
|
|
- )
|
|
|
+ logging.info('Loading file %s failed: %d: %s'%
|
|
|
+ (file_name,
|
|
|
+ error.errno,
|
|
|
+ error.strerror)
|
|
|
+ )
|
|
|
return None
|
|
|
|
|
|
def load_possibly_compressed_response_json(response):
|
|
@@ -699,30 +725,37 @@ class Candidate(object):
|
|
|
self._badexit = self._avg_generic_history(badexit) / ONIONOO_SCALE_ONE
|
|
|
|
|
|
def is_candidate(self):
|
|
|
+ must_be_running_now = (PERFORM_IPV4_DIRPORT_CHECKS
|
|
|
+ or PERFORM_IPV6_DIRPORT_CHECKS)
|
|
|
+ if (must_be_running_now and not self.is_running()):
|
|
|
+ logging.info('%s not a candidate: not running now, unable to check ' +
|
|
|
+ 'DirPort consensus download', self._fpr)
|
|
|
+ return False
|
|
|
if (self._data['last_changed_address_or_port'] >
|
|
|
self.CUTOFF_ADDRESS_AND_PORT_STABLE):
|
|
|
- logging.debug('%s not a candidate: changed address/port recently (%s)',
|
|
|
- self._fpr, self._data['last_changed_address_or_port'])
|
|
|
+ logging.info('%s not a candidate: changed address/port recently (%s)',
|
|
|
+ self._fpr, self._data['last_changed_address_or_port'])
|
|
|
return False
|
|
|
if self._running < CUTOFF_RUNNING:
|
|
|
- logging.debug('%s not a candidate: running avg too low (%lf)',
|
|
|
- self._fpr, self._running)
|
|
|
+ logging.info('%s not a candidate: running avg too low (%lf)',
|
|
|
+ self._fpr, self._running)
|
|
|
return False
|
|
|
if self._v2dir < CUTOFF_V2DIR:
|
|
|
- logging.debug('%s not a candidate: v2dir avg too low (%lf)',
|
|
|
- self._fpr, self._v2dir)
|
|
|
+ logging.info('%s not a candidate: v2dir avg too low (%lf)',
|
|
|
+ self._fpr, self._v2dir)
|
|
|
return False
|
|
|
if self._badexit is not None and self._badexit > PERMITTED_BADEXIT:
|
|
|
- logging.debug('%s not a candidate: badexit avg too high (%lf)',
|
|
|
- self._fpr, self._badexit)
|
|
|
+ logging.info('%s not a candidate: badexit avg too high (%lf)',
|
|
|
+ self._fpr, self._badexit)
|
|
|
return False
|
|
|
|
|
|
if (not self._data.has_key('recommended_version')
|
|
|
or not self._data['recommended_version']):
|
|
|
+ logging.info('%s not a candidate: version not recommended', self._fpr)
|
|
|
return False
|
|
|
if self._guard < CUTOFF_GUARD:
|
|
|
- logging.debug('%s not a candidate: guard avg too low (%lf)',
|
|
|
- self._fpr, self._guard)
|
|
|
+ logging.info('%s not a candidate: guard avg too low (%lf)',
|
|
|
+ self._fpr, self._guard)
|
|
|
return False
|
|
|
return True
|
|
|
|
|
@@ -736,24 +769,48 @@ class Candidate(object):
|
|
|
If the fallback has an ipv6 key, the whitelist line must also have
|
|
|
it, and vice versa, otherwise they don't match. """
|
|
|
for entry in relaylist:
|
|
|
+ if entry['id'] != self._fpr:
|
|
|
+
|
|
|
+ continue
|
|
|
if entry['ipv4'] != self.dirip:
|
|
|
+ logging.info('%s is not in the whitelist: fingerprint matches, but ' +
|
|
|
+ 'IPv4 (%s) does not match entry IPv4 (%s)',
|
|
|
+ self._fpr, self.dirip, entry['ipv4'])
|
|
|
continue
|
|
|
if int(entry['dirport']) != self.dirport:
|
|
|
+ logging.info('%s is not in the whitelist: fingerprint matches, but ' +
|
|
|
+ 'DirPort (%d) does not match entry DirPort (%d)',
|
|
|
+ self._fpr, self.dirport, int(entry['dirport']))
|
|
|
continue
|
|
|
if int(entry['orport']) != self.orport:
|
|
|
+ logging.info('%s is not in the whitelist: fingerprint matches, but ' +
|
|
|
+ 'ORPort (%d) does not match entry ORPort (%d)',
|
|
|
+ self._fpr, self.orport, int(entry['orport']))
|
|
|
continue
|
|
|
- if entry['id'] != self._fpr:
|
|
|
- continue
|
|
|
- if (entry.has_key('ipv6')
|
|
|
- and self.ipv6addr is not None and self.ipv6orport is not None):
|
|
|
+ has_ipv6 = self.ipv6addr is not None and self.ipv6orport is not None
|
|
|
+ if (entry.has_key('ipv6') and has_ipv6):
|
|
|
+ ipv6 = self.ipv6addr + ':' + self.ipv6orport
|
|
|
|
|
|
- if entry['ipv6'] != self.ipv6addr + ':' + self.ipv6orport:
|
|
|
+ if entry['ipv6'] != ipv6:
|
|
|
+ logging.info('%s is not in the whitelist: fingerprint matches, ' +
|
|
|
+ 'but IPv6 (%s) does not match entry IPv6 (%s)',
|
|
|
+ self._fpr, ipv6, entry['ipv6'])
|
|
|
continue
|
|
|
|
|
|
|
|
|
- elif entry.has_key('ipv6') and self.ipv6addr is None:
|
|
|
+ elif entry.has_key('ipv6') and not has_ipv6:
|
|
|
+ logging.info('%s is not in the whitelist: fingerprint matches, but ' +
|
|
|
+ 'it has no IPv6, and entry has IPv6 (%s)', self._fpr,
|
|
|
+ entry['ipv6'])
|
|
|
+ logging.warning('%s excluded: has it lost its former IPv6 address %s?',
|
|
|
+ self._fpr, entry['ipv6'])
|
|
|
continue
|
|
|
- elif not entry.has_key('ipv6') and self.ipv6addr is not None:
|
|
|
+ elif not entry.has_key('ipv6') and has_ipv6:
|
|
|
+ logging.info('%s is not in the whitelist: fingerprint matches, but ' +
|
|
|
+ 'it has IPv6 (%s), and entry has no IPv6', self._fpr,
|
|
|
+ ipv6)
|
|
|
+ logging.warning('%s excluded: has it gained an IPv6 address %s?',
|
|
|
+ self._fpr, ipv6)
|
|
|
continue
|
|
|
return True
|
|
|
return False
|
|
@@ -773,34 +830,60 @@ class Candidate(object):
|
|
|
for entry in relaylist:
|
|
|
for key in entry:
|
|
|
value = entry[key]
|
|
|
+ if key == 'id' and value == self._fpr:
|
|
|
+ logging.info('%s is in the blacklist: fingerprint matches',
|
|
|
+ self._fpr)
|
|
|
+ return True
|
|
|
if key == 'ipv4' and value == self.dirip:
|
|
|
|
|
|
if entry.has_key('dirport'):
|
|
|
if int(entry['dirport']) == self.dirport:
|
|
|
+ logging.info('%s is in the blacklist: IPv4 (%s) and ' +
|
|
|
+ 'DirPort (%d) match', self._fpr, self.dirip,
|
|
|
+ self.dirport)
|
|
|
return True
|
|
|
|
|
|
elif entry.has_key('orport'):
|
|
|
if int(entry['orport']) == self.orport:
|
|
|
+ logging.info('%s is in the blacklist: IPv4 (%s) and ' +
|
|
|
+ 'ORPort (%d) match', self._fpr, self.dirip,
|
|
|
+ self.orport)
|
|
|
return True
|
|
|
else:
|
|
|
+ logging.info('%s is in the blacklist: IPv4 (%s) matches, and ' +
|
|
|
+ 'entry has no DirPort or ORPort', self._fpr,
|
|
|
+ self.dirip)
|
|
|
return True
|
|
|
- if key == 'id' and value == self._fpr:
|
|
|
- return True
|
|
|
- if (key == 'ipv6'
|
|
|
- and self.ipv6addr is not None and self.ipv6orport is not None):
|
|
|
+ has_ipv6 = self.ipv6addr is not None and self.ipv6orport is not None
|
|
|
+ ipv6 = (self.ipv6addr + ':' + self.ipv6orport) if has_ipv6 else None
|
|
|
+ if (key == 'ipv6' and has_ipv6):
|
|
|
|
|
|
|
|
|
- if value == self.ipv6addr + ':' + self.ipv6orport:
|
|
|
+ if value == ipv6:
|
|
|
|
|
|
if entry.has_key('dirport'):
|
|
|
if int(entry['dirport']) == self.dirport:
|
|
|
+ logging.info('%s is in the blacklist: IPv6 (%s) and ' +
|
|
|
+ 'DirPort (%d) match', self._fpr, ipv6,
|
|
|
+ self.dirport)
|
|
|
return True
|
|
|
-
|
|
|
- elif entry.has_key('orport'):
|
|
|
- if int(entry['orport']) == self.orport:
|
|
|
- return True
|
|
|
+
|
|
|
else:
|
|
|
+ logging.info('%s is in the blacklist: IPv6 (%s) matches, and' +
|
|
|
+ 'entry has no DirPort', self._fpr, ipv6)
|
|
|
return True
|
|
|
+ elif (key == 'ipv6' or has_ipv6):
|
|
|
+
|
|
|
+ if entry.has_key('id') and entry['id'] == self._fpr:
|
|
|
+ logging.info('%s skipping IPv6 blacklist comparison: relay ' +
|
|
|
+ 'has%s IPv6%s, but entry has%s IPv6%s', self._fpr,
|
|
|
+ '' if has_ipv6 else ' no',
|
|
|
+ (' (' + ipv6 + ')') if has_ipv6 else '',
|
|
|
+ '' if key == 'ipv6' else ' no',
|
|
|
+ (' (' + value + ')') if key == 'ipv6' else '')
|
|
|
+ logging.warning('Has %s %s IPv6 address %s?', self._fpr,
|
|
|
+ 'gained an' if has_ipv6 else 'lost its former',
|
|
|
+ ipv6 if has_ipv6 else value)
|
|
|
return False
|
|
|
|
|
|
def is_exit(self):
|
|
@@ -809,6 +892,9 @@ class Candidate(object):
|
|
|
def is_guard(self):
|
|
|
return 'Guard' in self._data['flags']
|
|
|
|
|
|
+ def is_running(self):
|
|
|
+ return 'Running' in self._data['flags']
|
|
|
+
|
|
|
def fallback_weight_fraction(self, total_weight):
|
|
|
return float(self._data['consensus_weight']) / total_weight
|
|
|
|
|
@@ -825,53 +911,70 @@ class Candidate(object):
|
|
|
|
|
|
@staticmethod
|
|
|
def fallback_consensus_dl_speed(dirip, dirport, nickname, max_time):
|
|
|
+ download_failed = False
|
|
|
downloader = DescriptorDownloader()
|
|
|
start = datetime.datetime.utcnow()
|
|
|
+
|
|
|
+
|
|
|
+ logging.info('Initiating consensus download from %s (%s:%d).', nickname,
|
|
|
+ dirip, dirport)
|
|
|
|
|
|
|
|
|
- downloader.get_consensus(endpoints = [(dirip, dirport)]).run()
|
|
|
+ TIMEOUT_SLOP = 1.0
|
|
|
+ try:
|
|
|
+ downloader.get_consensus(endpoints = [(dirip, dirport)],
|
|
|
+ timeout = (max_time + TIMEOUT_SLOP),
|
|
|
+ validate = True,
|
|
|
+ retries = 0,
|
|
|
+ fall_back_to_authority = False).run()
|
|
|
+ except Exception, stem_error:
|
|
|
+ logging.debug('Unable to retrieve a consensus from %s: %s', nickname,
|
|
|
+ stem_error)
|
|
|
+ status = 'error: "%s"' % (stem_error)
|
|
|
+ level = logging.WARNING
|
|
|
+ download_failed = True
|
|
|
elapsed = (datetime.datetime.utcnow() - start).total_seconds()
|
|
|
if elapsed > max_time:
|
|
|
status = 'too slow'
|
|
|
+ level = logging.WARNING
|
|
|
+ download_failed = True
|
|
|
else:
|
|
|
status = 'ok'
|
|
|
- logging.debug(('Consensus download: %0.2fs %s from %s (%s:%d), '
|
|
|
- + 'max download time %0.2fs.') % (elapsed, status,
|
|
|
- nickname, dirip, dirport,
|
|
|
- max_time))
|
|
|
- return elapsed
|
|
|
+ level = logging.DEBUG
|
|
|
+ logging.log(level, 'Consensus download: %0.1fs %s from %s (%s:%d), ' +
|
|
|
+ 'max download time %0.1fs.', elapsed, status, nickname,
|
|
|
+ dirip, dirport, max_time)
|
|
|
+ return download_failed
|
|
|
|
|
|
def fallback_consensus_dl_check(self):
|
|
|
- ipv4_speed = Candidate.fallback_consensus_dl_speed(self.dirip,
|
|
|
+
|
|
|
+ ipv4_failed = False
|
|
|
+ ipv6_failed = False
|
|
|
+ if PERFORM_IPV4_DIRPORT_CHECKS:
|
|
|
+ ipv4_failed = Candidate.fallback_consensus_dl_speed(self.dirip,
|
|
|
self.dirport,
|
|
|
self._data['nickname'],
|
|
|
CONSENSUS_DOWNLOAD_SPEED_MAX)
|
|
|
if self.ipv6addr is not None and PERFORM_IPV6_DIRPORT_CHECKS:
|
|
|
|
|
|
- ipv6_speed = Candidate.fallback_consensus_dl_speed(self.ipv6addr,
|
|
|
+ ipv6_failed = Candidate.fallback_consensus_dl_speed(self.ipv6addr,
|
|
|
self.dirport,
|
|
|
self._data['nickname'],
|
|
|
CONSENSUS_DOWNLOAD_SPEED_MAX)
|
|
|
- else:
|
|
|
- ipv6_speed = None
|
|
|
|
|
|
- if (ipv4_speed > CONSENSUS_DOWNLOAD_SPEED_MAX
|
|
|
+ if (PERFORM_IPV4_DIRPORT_CHECKS and ipv4_failed
|
|
|
and CONSENSUS_DOWNLOAD_RETRY):
|
|
|
- ipv4_speed = Candidate.fallback_consensus_dl_speed(self.dirip,
|
|
|
+ ipv4_failed = Candidate.fallback_consensus_dl_speed(self.dirip,
|
|
|
self.dirport,
|
|
|
self._data['nickname'],
|
|
|
CONSENSUS_DOWNLOAD_SPEED_MAX)
|
|
|
if (self.ipv6addr is not None and PERFORM_IPV6_DIRPORT_CHECKS
|
|
|
- and ipv6_speed > CONSENSUS_DOWNLOAD_SPEED_MAX
|
|
|
- and CONSENSUS_DOWNLOAD_RETRY):
|
|
|
- ipv6_speed = Candidate.fallback_consensus_dl_speed(self.ipv6addr,
|
|
|
+ and ipv6_failed and CONSENSUS_DOWNLOAD_RETRY):
|
|
|
+ ipv6_failed = Candidate.fallback_consensus_dl_speed(self.ipv6addr,
|
|
|
self.dirport,
|
|
|
self._data['nickname'],
|
|
|
CONSENSUS_DOWNLOAD_SPEED_MAX)
|
|
|
-
|
|
|
- return (ipv4_speed <= CONSENSUS_DOWNLOAD_SPEED_MAX
|
|
|
- and (not PERFORM_IPV6_DIRPORT_CHECKS
|
|
|
- or ipv6_speed <= CONSENSUS_DOWNLOAD_SPEED_MAX))
|
|
|
+ return ((not ipv4_failed) and (not ipv6_failed))
|
|
|
|
|
|
def fallbackdir_line(self, total_weight, original_total_weight, dl_speed_ok):
|
|
|
|
|
@@ -1071,8 +1174,8 @@ class CandidateList(dict):
|
|
|
if BLACKLIST_EXCLUDES_WHITELIST_ENTRIES:
|
|
|
|
|
|
excluded_count += 1
|
|
|
- logging.debug('Excluding %s: in both blacklist and whitelist.' %
|
|
|
- f._fpr)
|
|
|
+ logging.warning('Excluding %s: in both blacklist and whitelist.',
|
|
|
+ f._fpr)
|
|
|
else:
|
|
|
|
|
|
filtered_fallbacks.append(f)
|
|
@@ -1082,8 +1185,7 @@ class CandidateList(dict):
|
|
|
elif in_blacklist:
|
|
|
|
|
|
excluded_count += 1
|
|
|
- logging.debug('Excluding %s: in blacklist.' %
|
|
|
- f._fpr)
|
|
|
+ logging.debug('Excluding %s: in blacklist.', f._fpr)
|
|
|
else:
|
|
|
if INCLUDE_UNLISTED_ENTRIES:
|
|
|
|
|
@@ -1091,8 +1193,8 @@ class CandidateList(dict):
|
|
|
else:
|
|
|
|
|
|
excluded_count += 1
|
|
|
- logging.debug('Excluding %s: in neither blacklist nor whitelist.' %
|
|
|
- f._fpr)
|
|
|
+ logging.info('Excluding %s: in neither blacklist nor whitelist.',
|
|
|
+ f._fpr)
|
|
|
self.fallbacks = filtered_fallbacks
|
|
|
return excluded_count
|
|
|
|
|
@@ -1173,15 +1275,14 @@ class CandidateList(dict):
|
|
|
|
|
|
fallback_count = len(self.fallbacks)
|
|
|
if FALLBACK_PROPORTION_OF_GUARDS is None:
|
|
|
- fallback_proportion = ''
|
|
|
+ fallback_proportion = ' (none)'
|
|
|
else:
|
|
|
- fallback_proportion = ' (%d * %f)'%(guard_count,
|
|
|
+ fallback_proportion = '%d (%d * %f)'%(target_count, guard_count,
|
|
|
FALLBACK_PROPORTION_OF_GUARDS)
|
|
|
s += 'Final Count: %d (Eligible %d, Usable %d, Target %d%s'%(
|
|
|
min(max_count, fallback_count),
|
|
|
eligible_count,
|
|
|
fallback_count,
|
|
|
- target_count,
|
|
|
fallback_proportion)
|
|
|
if MAX_FALLBACK_COUNT is not None:
|
|
|
s += ', Clamped to %d'%(MAX_FALLBACK_COUNT)
|
|
@@ -1242,6 +1343,16 @@ class CandidateList(dict):
|
|
|
s += '#error ' + error_str
|
|
|
else:
|
|
|
s += '/* ' + error_str + ' */'
|
|
|
+ s += '\n'
|
|
|
+ if PERFORM_IPV4_DIRPORT_CHECKS or PERFORM_IPV6_DIRPORT_CHECKS:
|
|
|
+ s += '/* Checked %s%s%s DirPorts served a consensus within %.1fs. */'%(
|
|
|
+ 'IPv4' if PERFORM_IPV4_DIRPORT_CHECKS else '',
|
|
|
+ ' and ' if (PERFORM_IPV4_DIRPORT_CHECKS
|
|
|
+ and PERFORM_IPV6_DIRPORT_CHECKS) else '',
|
|
|
+ 'IPv6' if PERFORM_IPV6_DIRPORT_CHECKS else '',
|
|
|
+ CONSENSUS_DOWNLOAD_SPEED_MAX)
|
|
|
+ else:
|
|
|
+ s += '/* Did not check IPv4 or IPv6 DirPort consensus downloads. */'
|
|
|
return s
|
|
|
|
|
|
|
|
@@ -1250,9 +1361,11 @@ def list_fallbacks():
|
|
|
""" Fetches required onionoo documents and evaluates the
|
|
|
fallback directory criteria for each of the relays """
|
|
|
|
|
|
+
|
|
|
candidates = CandidateList()
|
|
|
candidates.add_relays()
|
|
|
|
|
|
+
|
|
|
guard_count = candidates.count_guards()
|
|
|
if FALLBACK_PROPORTION_OF_GUARDS is None:
|
|
|
target_count = guard_count
|
|
@@ -1268,10 +1381,10 @@ def list_fallbacks():
|
|
|
|
|
|
candidates.compute_fallbacks()
|
|
|
|
|
|
+
|
|
|
initial_count = len(candidates.fallbacks)
|
|
|
excluded_count = candidates.apply_filter_lists()
|
|
|
print candidates.summarise_filters(initial_count, excluded_count)
|
|
|
-
|
|
|
eligible_count = len(candidates.fallbacks)
|
|
|
eligible_weight = candidates.fallback_weight_total()
|
|
|
|