diff --git a/ipv8/messaging/anonymization/community.py b/ipv8/messaging/anonymization/community.py index 0f23be838..4209dc35d 100644 --- a/ipv8/messaging/anonymization/community.py +++ b/ipv8/messaging/anonymization/community.py @@ -576,6 +576,7 @@ def _ours_on_created_extended(self, circuit_id: int, payload: CreatedPayload | E circuit.unverified_hop = None circuit.add_hop(hop) self.circuits.get(circuit_id) # Needed for notifying the RustEndpoint + self.logger.info("Added hop %d (%s) to circuit %d", len(circuit.hops), hop.peer, circuit.circuit_id) if circuit.state == CIRCUIT_STATE_EXTENDING: candidates_enc = payload.candidates_enc @@ -592,10 +593,6 @@ def send_extend(self, circuit: Circuit, candidates: list[bytes], max_tries: int) """ Extend a circuit by choosing one of the given candidates. """ - ignore_candidates = [hop.public_key_bin for hop in circuit.hops] + [self.my_peer.public_key.key_to_bin()] - if circuit.required_exit: - ignore_candidates.append(circuit.required_exit.public_key.key_to_bin()) - become_exit = circuit.goal_hops - 1 == len(circuit.hops) if become_exit and circuit.required_exit: # Set the required exit according to the circuit setting (e.g. for linking e2e circuits) @@ -603,19 +600,26 @@ def send_extend(self, circuit: Circuit, candidates: list[bytes], max_tries: int) extend_hop_addr = circuit.required_exit.address else: - # The next candidate is chosen from the returned list of possible candidates - for ignore_candidate in ignore_candidates: - if ignore_candidate in candidates: - candidates.remove(ignore_candidate) - - for i in range(len(candidates) - 1, -1, -1): - public_key = self.crypto.key_from_public_bin(candidates[i]) - if not self.crypto.is_key_compatible(public_key): - candidates.pop(i) - + # Chose the next candidate. Ensure we didn't use this candidate already, and its key is compatible. + exclude = [hop.public_key_bin for hop in circuit.hops] + [self.my_peer.public_key.key_to_bin()] + if circuit.required_exit: + exclude.append(circuit.required_exit.public_key.key_to_bin()) + candidates = [c for c in candidates if c not in exclude and self.crypto.key_from_public_bin(c)] extend_hop_public_bin = next(iter(candidates), b'') extend_hop_addr = ('0.0.0.0', 0) + if not extend_hop_public_bin: + # By default, nodes will give a number of relays to which we can extend the circuit (i.e., peers + # that have already been punctured). However, it could be that there simply aren't enough relays + # available. When this happens, we try to extend to exit nodes (which we assume are connectable). + choices = [peer for peer in self.get_candidates(PEER_FLAG_EXIT_BT, PEER_FLAG_RELAY) + if peer.public_key.key_to_bin() not in exclude] + if choices: + peer = random.choice(choices) + extend_hop_public_bin = peer.public_key.key_to_bin() + extend_hop_addr = peer.address + self.logger.info('No candidates to extend to, trying exit node %s instead', peer) + if extend_hop_public_bin: if self.request_cache.has(RetryRequestCache, circuit.circuit_id): self.request_cache.pop(RetryRequestCache, circuit.circuit_id) @@ -751,10 +755,10 @@ def join_circuit(self, create_payload: CreatePayload, previous_node_address: Add peer = Peer(create_payload.node_public_key, previous_node_address) self.request_cache.add(CreatedRequestCache(self, circuit_id, peer, peers_keys, self.settings.unstable_timeout)) - self.exit_sockets[circuit_id] = TunnelExitSocket(circuit_id, Hop(peer, session_keys), self) candidates_bin = self.serializer.pack('varlenH-list', list(peers_keys.keys())) candidates_enc = self.crypto.encrypt_str(candidates_bin, session_keys, FORWARD) + self.exit_sockets[circuit_id] = TunnelExitSocket(circuit_id, Hop(peer, session_keys), self) self.send_cell(previous_node_address, CreatedPayload(circuit_id, create_payload.identifier, key, auth, candidates_enc)) diff --git a/ipv8/messaging/anonymization/hidden_services.py b/ipv8/messaging/anonymization/hidden_services.py index 92ebd1a9e..89f2a4823 100644 --- a/ipv8/messaging/anonymization/hidden_services.py +++ b/ipv8/messaging/anonymization/hidden_services.py @@ -471,12 +471,12 @@ def create_created_e2e(self, rp: RendezvousPoint, source_address: Address, key = self.swarms[payload.info_hash].seeder_sk shared_secret, y, auth = self.crypto.generate_diffie_shared_secret(payload.key, key) rp.circuit.hs_session_keys = self.crypto.generate_session_keys(shared_secret) - self.circuits.get(rp.circuit.circuit_id) # Needed for notifying the RustEndpoint rp_info = RendezvousInfo(rp.address, rp.circuit.hops[-1].public_key.key_to_bin(), rp.cookie) rp_info_bin = self.serializer.pack('payload', rp_info) rp_info_enc = self.crypto.encrypt_str(rp_info_bin, rp.circuit.hs_session_keys, FORWARD) + self.circuits.get(rp.circuit.circuit_id) # Needed for notifying the RustEndpoint circuit = self.circuits[cast(int, circuit_id)] self.tunnel_data(circuit, source_address, CreatedE2EPayload(payload.identifier, y, auth, rp_info_enc)) diff --git a/run_all_tests.py b/run_all_tests.py index a7c0adb36..a5f7d0bd8 100644 --- a/run_all_tests.py +++ b/run_all_tests.py @@ -259,21 +259,13 @@ def install_libsodium() -> None: """ # Ensure a libsodium.zip if not pathlib.Path("libsodium.zip").exists(): - import re - from http.client import HTTPSConnection - connection = HTTPSConnection("download.libsodium.org") - - connection.request("GET", "/libsodium/releases/", headers={}) - web_response = connection.getresponse().read().decode() - - # Extract the latest version - result = sorted(re.findall(r"libsodium-[0-9]*\.[0-9]*\.[0-9]*-stable-msvc.zip\"", - web_response))[-1][:-1] - - connection.request("GET", f"/libsodium/releases/{result}", headers={}) - pathlib.Path("libsodium.zip").write_bytes(connection.getresponse().read()) - - connection.close() + import json + import urllib.request as request + response = request.urlopen("https://api.github.com/repos/jedisct1/libsodium/releases") + release = json.loads(response.read())[0] + response.close() + asset = [asset for asset in release['assets'] if asset['name'].endswith("-msvc.zip")][0] + pathlib.Path("libsodium.zip").write_bytes(request.urlopen(asset['browser_download_url']).read()) # Unpack just the libsodium.dll if not pathlib.Path("libsodium.dll").exists():