def _pick_ideal_second_hop(relay, dest, rl, cont, is_exit): ''' Sbws builds two hop circuits. Given the **relay** to measure with destination **dest**, pick a second relay that is or is not an exit according to **is_exit**. ''' candidates = [] candidates.extend(rl.exits if is_exit else rl.non_exits) if not len(candidates): return None log.debug('Picking a 2nd hop to measure %s from %d choices. is_exit=%s', relay.nickname, len(candidates), is_exit) for min_bw_factor in [2, 1.75, 1.5, 1.25, 1]: min_bw = relay.bandwidth * min_bw_factor new_candidates = stem_utils.only_relays_with_bandwidth(cont, candidates, min_bw=min_bw) if len(new_candidates) > 0: chosen = rng.choice(new_candidates) log.debug( 'Found %d candidate 2nd hops with at least %sx the bandwidth ' 'of %s. Returning %s (bw=%s).', len(new_candidates), min_bw_factor, relay.nickname, chosen.nickname, chosen.bandwidth) return chosen candidates = sorted(candidates, key=lambda r: r.bandwidth, reverse=True) chosen = candidates[0] log.debug( 'Didn\'t find any 2nd hops at least as fast as %s (bw=%s). It\'s ' 'probably really fast. Returning %s (bw=%s), the fastest ' 'candidate we have.', relay.nickname, relay.bandwidth, chosen.nickname, chosen.bandwidth) return chosen
def _pick_ideal_second_hop(relay, dest, rl, cont, is_exit): ''' Sbws builds two hop circuits. Given the **relay** to measure with destination **dest**, pick a second relay that is or is not an exit according to **is_exit**. ''' # 40041: Instead of using exits that can exit to all IPs, to ensure that # they can make requests to the Web servers, try with the exits that # allow some IPs, since there're more. # In the case that a concrete exit can't exit to the Web server, it is not # a problem since the relay will be measured in the next loop with other # random exit. candidates = rl.exits_not_bad_allowing_port(dest.port) \ if is_exit else rl.non_exits if not len(candidates): return None # In the case the helper is an exit, the entry could be an exit too # (#40041), so ensure the helper is not the same as the entry, likely to # happen in a test network. if is_exit: candidates = [ c for c in candidates if c.fingerprint != relay.fingerprint ] min_relay_bw = rl.exit_min_bw() if is_exit else rl.non_exit_min_bw() log.debug('Picking a 2nd hop to measure %s from %d choices. is_exit=%s', relay.nickname, len(candidates), is_exit) for min_bw_factor in [2, 1.75, 1.5, 1.25, 1]: min_bw = relay.consensus_bandwidth * min_bw_factor # We might have a really slow/new relay. Try to measure it properly by # using only relays with or above our calculated min_relay_bw (see: # _calculate_min_bw_second_hop() in relaylist.py). if min_bw < min_relay_bw: min_bw = min_relay_bw new_candidates = stem_utils.only_relays_with_bandwidth(cont, candidates, min_bw=min_bw) if len(new_candidates) > 0: chosen = rng.choice(new_candidates) log.debug( 'Found %d candidate 2nd hops with at least %sx the bandwidth ' 'of %s. Returning %s (bw=%s).', len(new_candidates), min_bw_factor, relay.nickname, chosen.nickname, chosen.consensus_bandwidth) return chosen candidates = sorted(candidates, key=lambda r: r.consensus_bandwidth, reverse=True) chosen = candidates[0] log.debug( 'Didn\'t find any 2nd hops at least as fast as %s (bw=%s). It\'s ' 'probably really fast. Returning %s (bw=%s), the fastest ' 'candidate we have.', relay.nickname, relay.consensus_bandwidth, chosen.nickname, chosen.consensus_bandwidth) return chosen
def _pick_ideal_second_hop(relay, dest, rl, cont, is_exit): ''' Sbws builds two hop circuits. Given the **relay** to measure with destination **dest**, pick a second relay that is or is not an exit according to **is_exit**. ''' candidates = rl.exits_not_bad_allowing_port(dest.port) if is_exit \ else rl.non_exits if not len(candidates): return None min_relay_bw = rl.exit_min_bw() if is_exit else rl.non_exit_min_bw() log.debug('Picking a 2nd hop to measure %s from %d choices. is_exit=%s', relay.nickname, len(candidates), is_exit) for min_bw_factor in [2, 1.75, 1.5, 1.25, 1]: min_bw = relay.consensus_bandwidth * min_bw_factor # We might have a really slow/new relay. Try to measure it properly by # using only relays with or above our calculated min_relay_bw (see: # _calculate_min_bw_second_hop() in relaylist.py). if min_bw < min_relay_bw: min_bw = min_relay_bw new_candidates = stem_utils.only_relays_with_bandwidth(cont, candidates, min_bw=min_bw) if len(new_candidates) > 0: chosen = rng.choice(new_candidates) log.debug( 'Found %d candidate 2nd hops with at least %sx the bandwidth ' 'of %s. Returning %s (bw=%s).', len(new_candidates), min_bw_factor, relay.nickname, chosen.nickname, chosen.consensus_bandwidth) return chosen candidates = sorted(candidates, key=lambda r: r.consensus_bandwidth, reverse=True) chosen = candidates[0] log.debug( 'Didn\'t find any 2nd hops at least as fast as %s (bw=%s). It\'s ' 'probably really fast. Returning %s (bw=%s), the fastest ' 'candidate we have.', relay.nickname, relay.consensus_bandwidth, chosen.nickname, chosen.consensus_bandwidth) return chosen
def measure_relay(args, conf, destinations, cb, rl, relay): s = requests_utils.make_session(cb.controller, conf.getfloat('general', 'http_timeout')) # Pick a destionation dest = destinations.next() if not dest: log.warning('Unable to get destination to measure %s %s', relay.nickname, relay.fingerprint[0:8]) return None # Pick an exit exits = rl.exits_can_exit_to(dest.hostname, dest.port) exits = [e for e in exits if e.fingerprint != relay.fingerprint] exits = stem_utils.only_relays_with_bandwidth( cb.controller, exits, min_bw=round(relay.bandwidth * 1.25), max_bw=max(round(relay.bandwidth * 2.00), 100)) if len(exits) < 1: log.warning('No available exits to help measure %s %s', relay.nickname, relay.fingerprint[0:8]) # TODO: Return ResultError of some sort return None exit = rng.choice(exits) # Build the circuit log.debug('We selected exit %s %s (cw=%d) to help measure %s %s (cw=%d)', exit.nickname, exit.fingerprint[0:8], exit.bandwidth, relay.nickname, relay.fingerprint[0:8], relay.bandwidth) our_nick = conf['scanner']['nickname'] circ_fps = [relay.fingerprint, exit.fingerprint] circ_id = cb.build_circuit(circ_fps) if not circ_id: log.warning('Could not build circuit involving %s', relay.nickname) msg = 'Unable to complete circuit' return [ ResultErrorCircuit(relay, circ_fps, dest.url, our_nick, msg=msg), ] log.debug('Built circ %s %s for relay %s %s', circ_id, stem_utils.circuit_str(cb.controller, circ_id), relay.nickname, relay.fingerprint[0:8]) # Make a connection to the destionation webserver and make sure it can # still help us measure is_usable, usable_data = dest.is_usable(circ_id, s, cb.controller) if not is_usable: log.warning( 'When measuring %s %s the destination seemed to have ' 'stopped being usable: %s', relay.nickname, relay.fingerprint[0:8], usable_data) cb.close_circuit(circ_id) # TODO: Return a different/new type of ResultError? msg = 'The destination seemed to have stopped being usable' return [ ResultErrorStream(relay, circ_fps, dest.url, our_nick, msg=msg), ] assert is_usable assert 'content_length' in usable_data # FIRST: measure RTT rtts = measure_rtt_to_server(s, conf, dest, usable_data['content_length']) if rtts is None: log.warning('Unable to measure RTT to %s via relay %s %s', dest.url, relay.nickname, relay.fingerprint[0:8]) cb.close_circuit(circ_id) # TODO: Return a different/new type of ResultError? msg = 'Something bad happened while measuring RTTs' return [ ResultErrorStream(relay, circ_fps, dest.url, our_nick, msg=msg), ] # SECOND: measure bandwidth bw_results = measure_bandwidth_to_server(s, conf, dest, usable_data['content_length']) if bw_results is None: log.warning('Unable to measure bandwidth to %s via relay %s %s', dest.url, relay.nickname, relay.fingerprint[0:8]) cb.close_circuit(circ_id) # TODO: Return a different/new type of ResultError? msg = 'Something bad happened while measuring bandwidth' return [ ResultErrorStream(relay, circ_fps, dest.url, our_nick, msg=msg), ] cb.close_circuit(circ_id) # Finally: store result return [ ResultSuccess(rtts, bw_results, relay, circ_fps, dest.url, our_nick), ]