From d33a2e95eefe80ee077bb8b327414eaeb252bab4 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 5 Mar 2015 17:37:03 +0100 Subject: [PATCH 01/11] Exiting data over tunnels is only allowed after switching the opt-in setting --- Tribler/Core/SessionConfig.py | 8 +++ Tribler/Core/defaults.py | 5 +- Tribler/Main/tribler_main.py | 2 +- Tribler/Main/vwxGUI/settingsDialog.py | 38 ++++++++++++ Tribler/Test/test_tunnel_community.py | 7 ++- Tribler/community/tunnel/conversion.py | 20 +++---- Tribler/community/tunnel/main.py | 8 +++ Tribler/community/tunnel/payload.py | 16 ++++- Tribler/community/tunnel/tunnel_community.py | 63 +++++++++++++------- 9 files changed, 129 insertions(+), 38 deletions(-) diff --git a/Tribler/Core/SessionConfig.py b/Tribler/Core/SessionConfig.py index 0d4f27cd11c..9423f4a9cc9 100644 --- a/Tribler/Core/SessionConfig.py +++ b/Tribler/Core/SessionConfig.py @@ -190,6 +190,14 @@ def get_tunnel_community_socks5_listen_ports(self): path = u'tunnel_community~socks5_listen_ports~' return [self._get_random_port(path + unicode(index)) if port < 0 else port for index, port in enumerate(ports)] + def set_tunnel_community_exitnode_enabled(self, value): + self.sessconfig.set(u'tunnel_community', u'exitnode_enabled', value) + + def get_tunnel_community_exitnode_enabled(self): + """ Returns whether being an exitnode is allowed + @return Boolean. """ + return self.sessconfig.get(u'tunnel_community', u'exitnode_enabled') + def get_listen_port(self): """ Returns the current UDP/TCP listen port. @return Port number. """ diff --git a/Tribler/Core/defaults.py b/Tribler/Core/defaults.py index b32033768fc..7cb4e54be39 100644 --- a/Tribler/Core/defaults.py +++ b/Tribler/Core/defaults.py @@ -32,9 +32,9 @@ # Version 2: as released in Tribler 4.5.0 # Version 3: cleanup unused params # Version 4: remove swift -# +# Version 7: exitnode optin switch added -SESSDEFAULTS_VERSION = 6 +SESSDEFAULTS_VERSION = 7 sessdefaults = OrderedDict() # General Tribler settings @@ -65,6 +65,7 @@ sessdefaults['tunnel_community']['optin_dialog_shown'] = False sessdefaults['tunnel_community']['enabled'] = False sessdefaults['tunnel_community']['socks5_listen_ports'] = [-1] * 5 +sessdefaults['tunnel_community']['exitnode_enabled'] = False # Mainline DHT settings sessdefaults['mainline_dht'] = OrderedDict() diff --git a/Tribler/Main/tribler_main.py b/Tribler/Main/tribler_main.py index 4728ba8347f..30e0de4ee6c 100644 --- a/Tribler/Main/tribler_main.py +++ b/Tribler/Main/tribler_main.py @@ -515,7 +515,7 @@ def define_communities(*args): if self.sconfig.get_tunnel_community_enabled(): keypair = dispersy.crypto.generate_key(u"curve25519") dispersy_member = dispersy.get_member(private_key=dispersy.crypto.key_to_bin(keypair),) - settings = TunnelSettings(session.get_install_dir()) + settings = TunnelSettings(session.get_install_dir(), tribler_session=session) tunnel_kwargs = {'tribler_session': session, 'settings': settings} self.tunnel_community = dispersy.define_auto_load(HiddenTunnelCommunity, dispersy_member, load=True, diff --git a/Tribler/Main/vwxGUI/settingsDialog.py b/Tribler/Main/vwxGUI/settingsDialog.py index d31a90a05d1..b525f304286 100644 --- a/Tribler/Main/vwxGUI/settingsDialog.py +++ b/Tribler/Main/vwxGUI/settingsDialog.py @@ -90,12 +90,14 @@ def __init__(self): self._bandwidth_panel, self._bandwidth_id = self.__create_s3(tree_root, hsizer) self._seeding_panel, self._seeding_id = self.__create_s4(tree_root, hsizer) self._experimental_panel, self._experimental_id = self.__create_s5(tree_root, hsizer) + self._tunnel_panel, self._tunnel_id = self.__create_s6(tree_root, hsizer) self._general_panel.Show(True) self._conn_panel.Show(False) self._bandwidth_panel.Show(False) self._seeding_panel.Show(False) self._experimental_panel.Show(False) + self._tunnel_panel.Show(False) self._save_btn = wx.Button(self, wx.ID_OK, label="Save") self._cancel_btn = wx.Button(self, wx.ID_CANCEL, label="Cancel") @@ -207,6 +209,13 @@ def saveAll(self, event): self.utility.write_config('use_webui', useWebUI) restart = True + + becomeExitNode = self._become_exitnode.IsChecked() + if becomeExitNode != scfg.get_tunnel_community_exitnode_enabled(): + scfg.set_tunnel_community_exitnode_enabled(becomeExitNode) + self.saveDefaultSessionConfig(scfg) + restart = True + valwebuiport = self._webui_port.GetValue() if valwebuiport != str(self.utility.read_config('webui_port')): self.utility.write_config('webui_port', valwebuiport) @@ -353,6 +362,11 @@ def saveDefaultDownloadConfig(self, scfg): scfg.save(cfgfilename) + + def saveDefaultSessionConfig(self, scfg): + cfgfilename = Session.get_default_config_filename(scfg.get_state_dir()) + scfg.save(cfgfilename) + def moveCollectedTorrents(self, old_dir, new_dir): def rename_or_merge(old, new, ignore=True): if os.path.exists(old): @@ -729,3 +743,27 @@ def __create_s5(self, tree_root, sizer): self._webui_port.SetValue(str(self.utility.read_config('webui_port'))) return exp_panel, item_id + + + def __create_s6(self, tree_root, sizer): + exp_panel, exp_vsizer = create_section(self, sizer, "Anonimity") + + item_id = self._tree_ctrl.AppendItem(tree_root, "Anonimity", data=wx.TreeItemData(exp_panel)) + + # Web UI + exp_s1_sizer = create_subsection(exp_panel, exp_vsizer, "Relaying", 2, 3) + self._become_exitnode = wx.CheckBox(exp_panel, label="Become an exit node") + exp_s1_sizer.Add(self._become_exitnode, 0, wx.EXPAND) + + exp_s1_faq_text = wx.StaticText( + exp_panel, label="By allowing Tribler to be an exit node, it's possible to become a proxy for someone elses traffic. \nThis may cause problems in some countries.") + exp_vsizer.Add(exp_s1_faq_text, 0, wx.EXPAND | wx.TOP, 10) + + # load values + state_dir = self.utility.session.get_state_dir() + cfgfilename = self.utility.session.get_default_config_filename(state_dir) + scfg = SessionStartupConfig.load(cfgfilename) + self._become_exitnode.SetValue(scfg.get_tunnel_community_exitnode_enabled()) + + + return exp_panel, item_id diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 4ea171dfe7f..783b818681c 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -223,7 +223,7 @@ def startTest(self, callback, min_timeout=5): def setup_proxies(): tunnel_communities = [] for i in range(3, 7): - tunnel_communities.append(create_proxy(i)) + tunnel_communities.append(create_proxy(i, i > 5)) # Connect the proxies to the Tribler instance for community in self.lm.dispersy.get_communities(): @@ -245,7 +245,7 @@ def setup_proxies(): callback(tunnel_communities) - def create_proxy(index): + def create_proxy(index, become_exit_node): from Tribler.Core.Session import Session self.setUpPreSession() @@ -269,8 +269,9 @@ def create_proxy(index): def load_community(session): keypair = dispersy.crypto.generate_key(u"curve25519") dispersy_member = dispersy.get_member(private_key=dispersy.crypto.key_to_bin(keypair)) - settings = TunnelSettings() + settings = TunnelSettings(tribler_session=session) settings.do_test = False + settings.become_exitnode = True #become_exit_node return dispersy.define_auto_load(HiddenTunnelCommunity, dispersy_member, (session, settings), load=True)[0] return blockingCallFromThread(reactor, load_community, session) diff --git a/Tribler/community/tunnel/conversion.py b/Tribler/community/tunnel/conversion.py index fcd5938ad2e..206f3a9b5ea 100644 --- a/Tribler/community/tunnel/conversion.py +++ b/Tribler/community/tunnel/conversion.py @@ -58,13 +58,13 @@ def _decode_cell(self, placeholder, offset, data): def _encode_create(self, message): payload = message.payload - packet = pack("!IHH20s", payload.circuit_id, len(payload.node_public_key), - len(payload.key), payload.node_id) + payload.node_public_key + payload.key + packet = pack("!IHH20s?", payload.circuit_id, len(payload.node_public_key), + len(payload.key), payload.node_id, payload.become_exit) + payload.node_public_key + payload.key return packet, def _decode_create(self, placeholder, offset, data): - circuit_id, len_pubic_key, len_key, nodeid = unpack_from('!IHH20s', data, offset) - offset += 28 + circuit_id, len_pubic_key, len_key, nodeid, become_exit = unpack_from('!IHH20s?', data, offset) + offset += 29 node_public_key = data[offset: offset + len_pubic_key] offset += len_pubic_key @@ -72,7 +72,7 @@ def _decode_create(self, placeholder, offset, data): key = data[offset:offset + len_key] offset += len_key - return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, key) + return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, key, become_exit) def _encode_created(self, message): payload = message.payload @@ -94,8 +94,8 @@ def _decode_created(self, placeholder, offset, data): def _encode_extend(self, message): payload = message.payload - packet = pack("!IHH20s", payload.circuit_id, len(payload.node_public_key), len(payload.key), payload.node_id) + \ - payload.node_public_key + payload.key + packet = pack("!IHH20s?", payload.circuit_id, len(payload.node_public_key), len(payload.key), + payload.node_id, payload.become_exit) + payload.node_public_key + payload.key if message.payload.node_addr: host, port = message.payload.node_addr @@ -103,8 +103,8 @@ def _encode_extend(self, message): return packet, def _decode_extend(self, placeholder, offset, data): - circuit_id, len_public_key, len_key, nodeid = unpack_from('!IHH20s', data, offset) - offset += 28 + circuit_id, len_public_key, len_key, nodeid, become_exit = unpack_from('!IHH20s?', data, offset) + offset += 29 node_public_key = data[offset:offset + len_public_key] offset += len_public_key @@ -118,7 +118,7 @@ def _decode_extend(self, placeholder, offset, data): offset += 6 node_addr = (inet_ntoa(host), port) - return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, node_addr, key) + return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, node_addr, key, become_exit) def _encode_extended(self, message): payload = message.payload diff --git a/Tribler/community/tunnel/main.py b/Tribler/community/tunnel/main.py index 2b709b3c22f..27ba180a619 100644 --- a/Tribler/community/tunnel/main.py +++ b/Tribler/community/tunnel/main.py @@ -319,6 +319,7 @@ def main(argv): try: parser.add_argument('-p', '--socks5', help='Socks5 port') + parser.add_argument('-x', '--exit', help='Allow being an exit-node') parser.add_argument('-d', '--dispersy', help='Dispersy port') parser.add_argument('-c', '--crawl', help='Enable crawler and use the keypair specified in the given filename') parser.add_argument('-j', '--json', help='Enable JSON api, which will run on the provided port number ' + @@ -350,6 +351,13 @@ def main(argv): settings.socks_listen_ports = range(socks5_port, socks5_port + 5) else: settings.socks_listen_ports = [random.randint(1000, 65535) for _ in range(5)] + + settings.become_exitnode = True if args.exit in ['true'] else False + if settings.become_exitnode: + print "Exit-node enabled" + else: + print "Exit-node disabled" + settings.do_test = False tunnel = Tunnel(settings, crawl_keypair_filename, dispersy_port) StandardIO(LineHandler(tunnel, profile)) diff --git a/Tribler/community/tunnel/payload.py b/Tribler/community/tunnel/payload.py index 7d8017eaf89..e502fffd3ee 100644 --- a/Tribler/community/tunnel/payload.py +++ b/Tribler/community/tunnel/payload.py @@ -32,17 +32,19 @@ class CreatePayload(Payload): class Implementation(Payload.Implementation): - def __init__(self, meta, circuit_id, node_id, node_public_key, key): + def __init__(self, meta, circuit_id, node_id, node_public_key, key, become_exit): assert isinstance(circuit_id, (int, long)), type(circuit_id) assert isinstance(node_id, basestring), type(node_id) assert isinstance(node_public_key, basestring), type(node_public_key) assert isinstance(key, basestring), type(key) + assert isinstance(become_exit, bool), type(become_exit) super(CreatePayload.Implementation, self).__init__(meta) self._circuit_id = circuit_id self._node_id = node_id self._node_public_key = node_public_key self._key = key + self._become_exit = become_exit @property def circuit_id(self): @@ -60,6 +62,10 @@ def node_public_key(self): def key(self): return self._key + @property + def become_exit(self): + return self._become_exit + class CreatedPayload(Payload): @@ -98,12 +104,13 @@ class ExtendPayload(Payload): class Implementation(Payload.Implementation): - def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key): + def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key, become_exit): assert isinstance(circuit_id, (int, long)), type(circuit_id) assert isinstance(node_id, basestring), type(node_id) assert isinstance(node_public_key, basestring), type(node_public_key) assert node_addr == None or isinstance(node_addr, tuple), type(node_addr) assert isinstance(key, basestring), type(key) + assert isinstance(become_exit, bool), type(become_exit) super(ExtendPayload.Implementation, self).__init__(meta) self._circuit_id = circuit_id @@ -111,6 +118,7 @@ def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key): self._node_public_key = node_public_key self._node_addr = node_addr self._key = key + self._become_exit = become_exit @property def circuit_id(self): @@ -132,6 +140,10 @@ def node_addr(self): def key(self): return self._key + @property + def become_exit(self): + return self._become_exit + class ExtendedPayload(Payload): diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index eec9460683c..b75f09c1cf0 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -38,6 +38,8 @@ from Tribler.dispersy.util import call_on_reactor_thread from Tribler.dispersy.requestcache import NumberCache, RandomNumberCache from Tribler.community.bartercast4.statistics import BartercastStatisticTypes, _barter_statistics +from Tribler.Main.Utility.utility import Utility +from Tribler.Core.SessionConfig import SessionStartupConfig class CircuitRequestCache(NumberCache): @@ -169,7 +171,7 @@ def check_num_packets(self, ip, incoming): class TunnelSettings(object): - def __init__(self, install_dir=None): + def __init__(self, install_dir=None, tribler_session=None): self.circuit_length = 3 self.crypto = TunnelCrypto() self.socks_listen_ports = range(1080, 1085) @@ -184,7 +186,16 @@ def __init__(self, install_dir=None): self.max_traffic = 55 * 1024 * 1024 self.max_packets_without_reply = 50 - + + if tribler_session: + state_dir = tribler_session.get_state_dir() + cfgfilename = tribler_session.get_default_config_filename(state_dir) + scfg = SessionStartupConfig.load(cfgfilename) + self.become_exitnode = scfg.get_tunnel_community_exitnode_enabled() + else: + self.become_exitnode = False + + class RoundRobin(object): @@ -243,7 +254,7 @@ def initialize(self, tribler_session=None, settings=None): super(TunnelCommunity, self).initialize() self.trsession = tribler_session - self.settings = settings if settings else TunnelSettings() + self.settings = settings if settings else TunnelSettings(tribler_session=tribler_session) assert isinstance(self.settings.crypto, TunnelCrypto), self.settings.crypto @@ -277,18 +288,18 @@ def start_download_test(self): @classmethod def get_master_members(cls, dispersy): - # generated: Fri Jan 02 19:44:50 2015 - # curve: NID_sect571r1 - # 144 bytes signature - # pub: 170 3081a7301006072a8648ce3d020106052b81040027038192000403ab6c5ddea44f806e3fc581ea90cc380d735d2c24ca87b3a68cdf1f95de50010aeff312344eedcae1bb96038db160be4b31a20289023251a2ad88b1f18bde0f14049843f3a35df5017630a445e63bb5005621c04c9b7ba8ca8ccebe567b355b6b21d846199bd8dcc9e7048dc59bf3f1ab70372f19e85d3c2133f56579fa5108840f52ff4ea3e41906623c1d6e8e2eaf - # pub-sha1 28bad8f1722de818700228c471ab0d7786736c05 - # -----BEGIN PUBLIC KEY----- - # MIGnMBAGByqGSM49AgEGBSuBBAAnA4GSAAQDq2xd3qRPgG4/xYHqkMw4DXNdLCTK - # h7OmjN8fld5QAQrv8xI0Tu3K4buWA42xYL5LMaICiQIyUaKtiLHxi94PFASYQ/Oj - # XfUBdjCkReY7tQBWIcBMm3uoyozOvlZ7NVtrIdhGGZvY3MnnBI3Fm/Pxq3A3Lxno - # XTwhM/VlefpRCIQPUv9Oo+QZBmI8HW6OLq8= - # -----END PUBLIC KEY----- - master_key = "3081a7301006072a8648ce3d020106052b81040027038192000403ab6c5ddea44f806e3fc581ea90cc380d735d2c24ca87b3a68cdf1f95de50010aeff312344eedcae1bb96038db160be4b31a20289023251a2ad88b1f18bde0f14049843f3a35df5017630a445e63bb5005621c04c9b7ba8ca8ccebe567b355b6b21d846199bd8dcc9e7048dc59bf3f1ab70372f19e85d3c2133f56579fa5108840f52ff4ea3e41906623c1d6e8e2eaf".decode( + #generated: Mon Mar 9 16:21:28 2015 + #curve: None + #len: 571 bits ~ 144 bytes signature + #pub: 170 3081a7301006072a8648ce3d020106052b81040027038192000404dc19d38890e0de983aed312d0b0c8a27732d7499a3e0c5d7bebfb8451270215d788ca671040935b4b4fc7faa48fd021226f5580995d63d0e9c82b0586850f93768debf550f4459054e6fb91318d8a0346c4059a4e84c95e4b7769cadc296d567ad353752a630d20f077a9f068998136338f2f0663d327d8934110565fb41040ac2d94c4fb78308118206a3930b68a8 + #pub-sha1 3df2df7afa551c6d876a94b44c4b37d417b7c4e8 + #-----BEGIN PUBLIC KEY----- + #MIGnMBAGByqGSM49AgEGBSuBBAAnA4GSAAQE3BnTiJDg3pg67TEtCwyKJ3MtdJmj + #4MXXvr+4RRJwIV14jKZxBAk1tLT8f6pI/QISJvVYCZXWPQ6cgrBYaFD5N2jev1UP + #RFkFTm+5ExjYoDRsQFmk6EyV5Ld2nK3CltVnrTU3UqYw0g8Hep8GiZgTYzjy8GY9 + #Mn2JNBEFZftBBArC2UxPt4MIEYIGo5MLaKg= + #-----END PUBLIC KEY----- + master_key = "3081a7301006072a8648ce3d020106052b81040027038192000404dc19d38890e0de983aed312d0b0c8a27732d7499a3e0c5d7bebfb8451270215d788ca671040935b4b4fc7faa48fd021226f5580995d63d0e9c82b0586850f93768debf550f4459054e6fb91318d8a0346c4059a4e84c95e4b7769cadc296d567ad353752a630d20f077a9f068998136338f2f0663d327d8934110565fb41040ac2d94c4fb78308118206a3930b68a8".decode( "HEX") master = dispersy.get_member(public_key=master_key) return [master] @@ -440,10 +451,13 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ self.circuits[circuit_id] = circuit self.waiting_for.add(circuit_id) + # FIXME + becomes_exit = False self.increase_bytes_sent(circuit, self.send_cell([first_hop], u"create", (circuit_id, circuit.unverified_hop.node_id, circuit.unverified_hop.node_public_key, - circuit.unverified_hop.dh_first_part))) + circuit.unverified_hop.dh_first_part, + becomes_exit))) _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_SENT, circuit.mid) return True @@ -643,6 +657,10 @@ def check_create(self, messages): if self.crypto.key.pub().key_to_bin() != message.payload.node_public_key: yield DropMessage(message, "TunnelCommunity: public keys do not match") continue + + if not self.settings.become_exitnode and message.payload.become_exit: + yield DropMessage(message, "Exit-node functionality disabled") + continue yield message @@ -699,7 +717,8 @@ def _ours_on_created_extended(self, circuit, message): if circuit.state == CIRCUIT_STATE_EXTENDING: - if circuit.goal_hops - 1 == len(circuit.hops) and circuit.required_exit: + next_is_exit = circuit.goal_hops - 1 == len(circuit.hops) + if next_is_exit and circuit.required_exit: host, port, pub_key = circuit.required_exit extend_hop_public_bin = pub_key extend_hop_addr = (host, port) @@ -738,7 +757,8 @@ def _ours_on_created_extended(self, circuit, message): circuit.unverified_hop.node_id, circuit.unverified_hop.node_public_key, extend_hop_addr, - circuit.unverified_hop.dh_first_part))) + circuit.unverified_hop.dh_first_part, + next_is_exit))) else: self.remove_circuit(circuit.circuit_id, "no candidates to extend, bailing out.") @@ -896,7 +916,8 @@ def on_extend(self, messages): self.increase_bytes_sent(new_circuit_id, self.send_cell([extend_candidate], u"create", (new_circuit_id, message.payload.node_id, message.payload.node_public_key, - message.payload.key))) + message.payload.key, + message.payload.become_exit))) def on_extended(self, messages): for message in messages: @@ -1031,7 +1052,9 @@ def tunnel_data_to_origin(self, circuit_id, sock_addr, source_address, data): self.send_data([Candidate(sock_addr, False)], u'data', packet) def exit_data(self, circuit_id, sock_addr, destination, data): - if circuit_id in self.exit_sockets: + if not self.settings.become_exitnode: + self._logger.error("Dropping data packets, I don't want to be an exit node") + elif circuit_id in self.exit_sockets: if not self.exit_sockets[circuit_id].enabled: # We got the correct circuit_id, but from a wrong IP. assert sock_addr == self.exit_sockets[circuit_id].sock_addr, "%s != %s" % ( From 31dba6eb910c7f76eb03a31076c8dcb2a7dc78c5 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Mon, 16 Mar 2015 19:30:47 +0100 Subject: [PATCH 02/11] Payload message for hops refusing to be exit-node --- .gitmodules | 2 +- Tribler/Core/SessionConfig.py | 8 ++ Tribler/Core/defaults.py | 2 + Tribler/Main/vwxGUI/MainFrame.py | 9 +- Tribler/Main/vwxGUI/settingsDialog.py | 14 ++- Tribler/Test/test_tunnel_community.py | 12 +-- Tribler/community/tunnel/conversion.py | 41 +++++++-- Tribler/community/tunnel/main.py | 3 + Tribler/community/tunnel/payload.py | 69 +++++++++++--- Tribler/community/tunnel/tunnel_community.py | 94 +++++++++++++++----- Tribler/dispersy | 2 +- 11 files changed, 203 insertions(+), 53 deletions(-) diff --git a/.gitmodules b/.gitmodules index e647739f4fa..523e7e701d7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Tribler/dispersy"] path = Tribler/dispersy - url = https://github.com/Tribler/dispersy.git + url = https://github.com/rjruigrok/dispersy.git [submodule "Tribler/Core/DecentralizedTracking/pymdht"] path = Tribler/Core/DecentralizedTracking/pymdht url = https://github.com/LipuFei/pymdht.git diff --git a/Tribler/Core/SessionConfig.py b/Tribler/Core/SessionConfig.py index 9423f4a9cc9..77ebe193bba 100644 --- a/Tribler/Core/SessionConfig.py +++ b/Tribler/Core/SessionConfig.py @@ -190,6 +190,14 @@ def get_tunnel_community_socks5_listen_ports(self): path = u'tunnel_community~socks5_listen_ports~' return [self._get_random_port(path + unicode(index)) if port < 0 else port for index, port in enumerate(ports)] + def set_tunnel_community_hs_timeout_switch(self, value): + self.sessconfig.set(u'tunnel_community', u'hs_timeout_switch', value) + + def get_tunnel_community_hs_timeout_switch(self): + """ Returns whether hidden services switch to anonymous downloading on timeout + @return Boolean. """ + return self.sessconfig.get(u'tunnel_community', u'hs_timeout_switch') + def set_tunnel_community_exitnode_enabled(self, value): self.sessconfig.set(u'tunnel_community', u'exitnode_enabled', value) diff --git a/Tribler/Core/defaults.py b/Tribler/Core/defaults.py index 7cb4e54be39..17a7727a341 100644 --- a/Tribler/Core/defaults.py +++ b/Tribler/Core/defaults.py @@ -66,6 +66,8 @@ sessdefaults['tunnel_community']['enabled'] = False sessdefaults['tunnel_community']['socks5_listen_ports'] = [-1] * 5 sessdefaults['tunnel_community']['exitnode_enabled'] = False +sessdefaults['tunnel_community']['hs_timeout_switch'] = True + # Mainline DHT settings sessdefaults['mainline_dht'] = OrderedDict() diff --git a/Tribler/Main/vwxGUI/MainFrame.py b/Tribler/Main/vwxGUI/MainFrame.py index dae9a89281c..4798e948aab 100644 --- a/Tribler/Main/vwxGUI/MainFrame.py +++ b/Tribler/Main/vwxGUI/MainFrame.py @@ -56,6 +56,7 @@ from Tribler.Main.vwxGUI.SRstatusbar import SRstatusbar from Tribler.Core.Video.utils import videoextdefaults +from Tribler.Core.SessionConfig import SessionStartupConfig # @@ -614,8 +615,12 @@ def do_gui(): def monitorHiddenSerivcesProgress(self, ds, tdef, dscfg, selectedFiles): if ds.get_status() in [DLSTATUS_ALLOCATING_DISKSPACE, DLSTATUS_HASHCHECKING, DLSTATUS_WAITING4HASHCHECK]: return (5.0, True) - - if ds.get_current_speed(DOWNLOAD) == 0: + + state_dir = self.utility.session.get_state_dir() + cfgfilename = self.utility.session.get_default_config_filename(state_dir) + scfg = SessionStartupConfig.load(cfgfilename) + + if ds.get_current_speed(DOWNLOAD) == 0 and scfg.get_tunnel_community_hs_timeout_switch(): download = ds.get_download() self.utility.session.remove_download(download) diff --git a/Tribler/Main/vwxGUI/settingsDialog.py b/Tribler/Main/vwxGUI/settingsDialog.py index b525f304286..f684a2899cb 100644 --- a/Tribler/Main/vwxGUI/settingsDialog.py +++ b/Tribler/Main/vwxGUI/settingsDialog.py @@ -216,6 +216,12 @@ def saveAll(self, event): self.saveDefaultSessionConfig(scfg) restart = True + switchHsOnTimeout = self._switch_hs_timeout.IsChecked() + if switchHsOnTimeout != scfg.get_tunnel_community_hs_timeout_switch(): + scfg.set_tunnel_community_hs_timeout_switch(switchHsOnTimeout) + self.saveDefaultSessionConfig(scfg) + restart = True + valwebuiport = self._webui_port.GetValue() if valwebuiport != str(self.utility.read_config('webui_port')): self.utility.write_config('webui_port', valwebuiport) @@ -750,10 +756,11 @@ def __create_s6(self, tree_root, sizer): item_id = self._tree_ctrl.AppendItem(tree_root, "Anonimity", data=wx.TreeItemData(exp_panel)) - # Web UI - exp_s1_sizer = create_subsection(exp_panel, exp_vsizer, "Relaying", 2, 3) - self._become_exitnode = wx.CheckBox(exp_panel, label="Become an exit node") + exp_s1_sizer = create_subsection(exp_panel, exp_vsizer, "Relaying", 1, 3) + self._become_exitnode = wx.CheckBox(exp_panel, label="Allow being an exit node") exp_s1_sizer.Add(self._become_exitnode, 0, wx.EXPAND) + self._switch_hs_timeout = wx.CheckBox(exp_panel, label="Switch from hidden services to exit nodes") + exp_s1_sizer.Add(self._switch_hs_timeout, 0, wx.EXPAND) exp_s1_faq_text = wx.StaticText( exp_panel, label="By allowing Tribler to be an exit node, it's possible to become a proxy for someone elses traffic. \nThis may cause problems in some countries.") @@ -764,6 +771,7 @@ def __create_s6(self, tree_root, sizer): cfgfilename = self.utility.session.get_default_config_filename(state_dir) scfg = SessionStartupConfig.load(cfgfilename) self._become_exitnode.SetValue(scfg.get_tunnel_community_exitnode_enabled()) + self._switch_hs_timeout.SetValue(scfg.get_tunnel_community_hs_timeout_switch()) return exp_panel, item_id diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 783b818681c..5922b250fec 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -106,14 +106,14 @@ def replace_socks(tunnel_communities): self.startTest(replace_socks) - @timed(120) + @timed(180) def test_hidden_services(self): def take_second_screenshot(): self.screenshot() self.quit() def take_screenshot(download_time): - self.screenshot("After an anonymous libtorrent download (took %.2f s)" % download_time) + self.screenshot("After libtorrent download over hidden services (took %.2f s)" % download_time) self.guiUtility.ShowPage('networkgraph') self.Call(1, take_second_screenshot) @@ -133,7 +133,7 @@ def do_progress(download, start_time): self.CallConditional(120, lambda: download.get_progress() == 1.0, lambda: take_screenshot(time.time() - start_time), - 'Anonymous download should be finished in 120 seconds (%.1f%% downloaded)' % ( + 'Hidden services download should be finished in 120 seconds (%.1f%% downloaded)' % ( download.get_progress() * 100), on_fail ) @@ -222,8 +222,8 @@ def startTest(self, callback, min_timeout=5): def setup_proxies(): tunnel_communities = [] - for i in range(3, 7): - tunnel_communities.append(create_proxy(i, i > 5)) + for i in range(3, 10): + tunnel_communities.append(create_proxy(i, i > 7)) # Connect the proxies to the Tribler instance for community in self.lm.dispersy.get_communities(): @@ -271,7 +271,7 @@ def load_community(session): dispersy_member = dispersy.get_member(private_key=dispersy.crypto.key_to_bin(keypair)) settings = TunnelSettings(tribler_session=session) settings.do_test = False - settings.become_exitnode = True #become_exit_node + settings.become_exitnode = become_exit_node return dispersy.define_auto_load(HiddenTunnelCommunity, dispersy_member, (session, settings), load=True)[0] return blockingCallFromThread(reactor, load_community, session) diff --git a/Tribler/community/tunnel/conversion.py b/Tribler/community/tunnel/conversion.py index 206f3a9b5ea..a7c0f72969f 100644 --- a/Tribler/community/tunnel/conversion.py +++ b/Tribler/community/tunnel/conversion.py @@ -36,6 +36,35 @@ def __init__(self, community): self.define_meta_message(chr(19), community.get_meta_message(u"link-e2e"), self._encode_link_e2e, self._decode_link_e2e) self.define_meta_message(chr(20), community.get_meta_message(u"linked-e2e"), self._encode_linked_e2e, self._decode_linked_e2e) + + def _encode_introduction_response(self, message): + payload = message.payload + data = [pack("!??", payload.exitnode, payload.connectable)] + data += list(super(TunnelConversion, self)._encode_introduction_response(message)) + return tuple(data) + + def _decode_introduction_response(self, placeholder, offset, data): + exitnode, connectable, = unpack_from('!??', data, offset) + offset += 2 + offset, payload = super(TunnelConversion, self)._decode_introduction_response(placeholder, offset, data) + payload._exitnode = exitnode + payload._connectable = connectable + return (offset, payload) + + def _encode_introduction_request(self, message): + payload = message.payload + data = [pack("!??", payload.exitnode, payload.connectable)] + data += super(TunnelConversion, self)._encode_introduction_request(message) + return data + + def _decode_introduction_request(self, placeholder, offset, data): + exitnode, connectable, = unpack_from('!??', data, offset) + offset += 2 + offset, payload = super(TunnelConversion, self)._decode_introduction_request(placeholder, offset, data) + payload._exitnode = exitnode + payload._connectable = connectable + return (offset, payload) + def _encode_cell(self, message): payload = message.payload packet = pack("!IB", payload.circuit_id, self._encode_message_map[ @@ -59,11 +88,11 @@ def _decode_cell(self, placeholder, offset, data): def _encode_create(self, message): payload = message.payload packet = pack("!IHH20s?", payload.circuit_id, len(payload.node_public_key), - len(payload.key), payload.node_id, payload.become_exit) + payload.node_public_key + payload.key + len(payload.key), payload.node_id, payload.exit_candidates) + payload.node_public_key + payload.key return packet, def _decode_create(self, placeholder, offset, data): - circuit_id, len_pubic_key, len_key, nodeid, become_exit = unpack_from('!IHH20s?', data, offset) + circuit_id, len_pubic_key, len_key, nodeid, exit_candidates = unpack_from('!IHH20s?', data, offset) offset += 29 node_public_key = data[offset: offset + len_pubic_key] @@ -72,7 +101,7 @@ def _decode_create(self, placeholder, offset, data): key = data[offset:offset + len_key] offset += len_key - return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, key, become_exit) + return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, key, exit_candidates) def _encode_created(self, message): payload = message.payload @@ -95,7 +124,7 @@ def _decode_created(self, placeholder, offset, data): def _encode_extend(self, message): payload = message.payload packet = pack("!IHH20s?", payload.circuit_id, len(payload.node_public_key), len(payload.key), - payload.node_id, payload.become_exit) + payload.node_public_key + payload.key + payload.node_id, payload.exit_candidates) + payload.node_public_key + payload.key if message.payload.node_addr: host, port = message.payload.node_addr @@ -103,7 +132,7 @@ def _encode_extend(self, message): return packet, def _decode_extend(self, placeholder, offset, data): - circuit_id, len_public_key, len_key, nodeid, become_exit = unpack_from('!IHH20s?', data, offset) + circuit_id, len_public_key, len_key, nodeid, exit_candidates = unpack_from('!IHH20s?', data, offset) offset += 29 node_public_key = data[offset:offset + len_public_key] @@ -118,7 +147,7 @@ def _decode_extend(self, placeholder, offset, data): offset += 6 node_addr = (inet_ntoa(host), port) - return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, node_addr, key, become_exit) + return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, node_addr, key, exit_candidates) def _encode_extended(self, message): payload = message.payload diff --git a/Tribler/community/tunnel/main.py b/Tribler/community/tunnel/main.py index 27ba180a619..00fcbbcac61 100644 --- a/Tribler/community/tunnel/main.py +++ b/Tribler/community/tunnel/main.py @@ -24,6 +24,9 @@ from Tribler.Main.globals import DefaultDownloadStartupConfig from Tribler.community.tunnel.hidden_community import HiddenTunnelCommunity +import logging.config +#logging.config.fileConfig("logger.conf") + try: import yappi except ImportError: diff --git a/Tribler/community/tunnel/payload.py b/Tribler/community/tunnel/payload.py index e502fffd3ee..cfe68332dd5 100644 --- a/Tribler/community/tunnel/payload.py +++ b/Tribler/community/tunnel/payload.py @@ -1,5 +1,47 @@ -from Tribler.dispersy.payload import Payload - +from Tribler.dispersy.payload import Payload, IntroductionRequestPayload,\ + IntroductionResponsePayload + + +class TunnelIntroductionRequestPayload(IntroductionRequestPayload): + + class Implementation(IntroductionRequestPayload.Implementation): + + def __init__(self, meta, destination_address, source_lan_address, source_wan_address, advice, connection_type, sync, identifier, exitnode = False, connectable = False): + super(TunnelIntroductionRequestPayload.Implementation, self).__init__(meta, destination_address, source_lan_address, source_wan_address, advice, connection_type, sync, identifier) + assert isinstance(exitnode, bool), type(exitnode) + assert isinstance(connectable, bool), type(connectable) + self._exitnode = exitnode + self._connectable = connectable + + @property + def exitnode(self): + return self._exitnode + + @property + def connectable(self): + return self._connectable + + +class TunnelIntroductionResponsePayload(IntroductionResponsePayload): + + class Implementation(IntroductionResponsePayload.Implementation): + + def __init__(self, meta, destination_address, source_lan_address, source_wan_address, lan_introduction_address, wan_introduction_address, connection_type, tunnel, identifier, exitnode = False, connectable = False): + super(TunnelIntroductionResponsePayload.Implementation, self).__init__(meta, destination_address, source_lan_address, source_wan_address, lan_introduction_address, wan_introduction_address, connection_type, tunnel, identifier) + assert isinstance(exitnode, bool), type(exitnode) + assert isinstance(connectable, bool), type(connectable) + self._exitnode = exitnode + self._connectable = connectable + + + @property + def exitnode(self): + return self._exitnode + + @property + def connectable(self): + return self._connectable + class CellPayload(Payload): @@ -32,19 +74,19 @@ class CreatePayload(Payload): class Implementation(Payload.Implementation): - def __init__(self, meta, circuit_id, node_id, node_public_key, key, become_exit): + def __init__(self, meta, circuit_id, node_id, node_public_key, key, exit_candidates): assert isinstance(circuit_id, (int, long)), type(circuit_id) assert isinstance(node_id, basestring), type(node_id) assert isinstance(node_public_key, basestring), type(node_public_key) assert isinstance(key, basestring), type(key) - assert isinstance(become_exit, bool), type(become_exit) + assert isinstance(exit_candidates, bool), type(exit_candidates) super(CreatePayload.Implementation, self).__init__(meta) self._circuit_id = circuit_id self._node_id = node_id self._node_public_key = node_public_key self._key = key - self._become_exit = become_exit + self._exit_candidates = exit_candidates @property def circuit_id(self): @@ -61,10 +103,10 @@ def node_public_key(self): @property def key(self): return self._key - + @property - def become_exit(self): - return self._become_exit + def exit_candidates(self): + return self._exit_candidates class CreatedPayload(Payload): @@ -104,13 +146,13 @@ class ExtendPayload(Payload): class Implementation(Payload.Implementation): - def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key, become_exit): + def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key, exit_candidates): assert isinstance(circuit_id, (int, long)), type(circuit_id) assert isinstance(node_id, basestring), type(node_id) assert isinstance(node_public_key, basestring), type(node_public_key) assert node_addr == None or isinstance(node_addr, tuple), type(node_addr) assert isinstance(key, basestring), type(key) - assert isinstance(become_exit, bool), type(become_exit) + assert isinstance(exit_candidates, bool), type(exit_candidates) super(ExtendPayload.Implementation, self).__init__(meta) self._circuit_id = circuit_id @@ -118,7 +160,7 @@ def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key, b self._node_public_key = node_public_key self._node_addr = node_addr self._key = key - self._become_exit = become_exit + self._exit_candidates = exit_candidates @property def circuit_id(self): @@ -141,8 +183,9 @@ def key(self): return self._key @property - def become_exit(self): - return self._become_exit + def exit_candidates(self): + return self._exit_candidates + class ExtendedPayload(Payload): diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index b75f09c1cf0..bb787129595 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -20,7 +20,8 @@ from Tribler.community.tunnel.conversion import TunnelConversion from Tribler.community.tunnel.payload import (CellPayload, CreatePayload, CreatedPayload, ExtendPayload, ExtendedPayload, DestroyPayload, PongPayload, PingPayload, - StatsRequestPayload, StatsResponsePayload) + StatsRequestPayload, StatsResponsePayload, + TunnelIntroductionResponsePayload, TunnelIntroductionRequestPayload) from Tribler.community.tunnel.routing import Circuit, Hop, RelayRoute from Tribler.community.tunnel.tests.test_libtorrent import LibtorrentTest from Tribler.community.tunnel.Socks5.server import Socks5Server @@ -58,7 +59,6 @@ def on_timeout(self): if self.retry: self.retry() - class CreatedRequestCache(NumberCache): def __init__(self, community, circuit_id, candidate, candidates): @@ -238,6 +238,8 @@ def __init__(self, *args, **kwargs): self.waiting_for = set() self.exit_sockets = {} self.circuits_needed = {} + self.exit_candidates = {} + self.connectable_candidates = {} self.notifier = None self.made_anon_session = False self.selection_strategy = RoundRobin(self) @@ -251,10 +253,11 @@ def __init__(self, *args, **kwargs): self.trsession = self.settings = self.socks_server = self.libtorrent_test = None def initialize(self, tribler_session=None, settings=None): - super(TunnelCommunity, self).initialize() - + self.trsession = tribler_session self.settings = settings if settings else TunnelSettings(tribler_session=tribler_session) + + super(TunnelCommunity, self).initialize() assert isinstance(self.settings.crypto, TunnelCrypto), self.settings.crypto @@ -282,7 +285,7 @@ def start_download_test(self): if not self.libtorrent_test.has_completed_before(): self._logger.debug("Scheduling Anonymous LibTorrent download") self.register_task("start_test", reactor.callLater( - 60, lambda: reactor.callInThread(self.libtorrent_test.start))) + 5, lambda: reactor.callInThread(self.libtorrent_test.start))) return True return False @@ -305,7 +308,30 @@ def get_master_members(cls, dispersy): return [master] def initiate_meta_messages(self): - return super(TunnelCommunity, self).initiate_meta_messages() + \ + messages = super(TunnelCommunity, self).initiate_meta_messages() + copy_messages = messages[:] + for message in copy_messages: + if message.name == "dispersy-introduction-request" or message.name == "dispersy-introduction-response": + messages.remove(message) + + messages += [Message(self, u"dispersy-introduction-request", + MemberAuthentication(), + PublicResolution(), + DirectDistribution(), + CandidateDestination(), + TunnelIntroductionRequestPayload(), + self.check_introduction_request, + self.on_introduction_request), + Message(self, u"dispersy-introduction-response", + MemberAuthentication(), + PublicResolution(), + DirectDistribution(), + CandidateDestination(), + TunnelIntroductionResponsePayload(), + self.check_introduction_response, + self.on_introduction_response)] + + return messages + \ [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), CellPayload(), self._generic_timeline_check, self.on_cell), Message(self, u"create", NoAuthentication(), PublicResolution(), DirectDistribution(), @@ -451,13 +477,13 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ self.circuits[circuit_id] = circuit self.waiting_for.add(circuit_id) - # FIXME - becomes_exit = False + exit_candidates = circuit.goal_hops - 1 == len(circuit.hops) + 1 + self.increase_bytes_sent(circuit, self.send_cell([first_hop], u"create", (circuit_id, circuit.unverified_hop.node_id, circuit.unverified_hop.node_public_key, circuit.unverified_hop.dh_first_part, - becomes_exit))) + exit_candidates))) _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_SENT, circuit.mid) return True @@ -658,10 +684,6 @@ def check_create(self, messages): yield DropMessage(message, "TunnelCommunity: public keys do not match") continue - if not self.settings.become_exitnode and message.payload.become_exit: - yield DropMessage(message, "Exit-node functionality disabled") - continue - yield message def check_extend(self, messages): @@ -716,9 +738,9 @@ def _ours_on_created_extended(self, circuit, message): circuit.unverified_hop = None if circuit.state == CIRCUIT_STATE_EXTENDING: - - next_is_exit = circuit.goal_hops - 1 == len(circuit.hops) - if next_is_exit and circuit.required_exit: + become_exit = circuit.goal_hops - 1 == len(circuit.hops) + exit_candidates = circuit.goal_hops - 1 == len(circuit.hops) + 1 + if become_exit and circuit.required_exit: host, port, pub_key = circuit.required_exit extend_hop_public_bin = pub_key extend_hop_addr = (host, port) @@ -758,7 +780,7 @@ def _ours_on_created_extended(self, circuit, message): circuit.unverified_hop.node_public_key, extend_hop_addr, circuit.unverified_hop.dh_first_part, - next_is_exit))) + exit_candidates))) else: self.remove_circuit(circuit.circuit_id, "no candidates to extend, bailing out.") @@ -778,6 +800,30 @@ def _ours_on_created_extended(self, circuit, message): from Tribler.Core.simpledefs import NTFY_TUNNEL, NTFY_CREATED, NTFY_EXTENDED self.notifier.notify(NTFY_TUNNEL, NTFY_CREATED if len(circuit.hops) == 1 else NTFY_EXTENDED, circuit) + + def on_introduction_request(self, messages): + connectable = False # Todo. Unused so far + exitnode = self.settings.become_exitnode + extra_payload = [exitnode, connectable] + super(TunnelCommunity, self).on_introduction_request(messages, extra_payload) + for message in messages: + pubkey = message.candidate.get_member().public_key + self.exit_candidates[pubkey] = message.payload.exitnode + self.connectable_candidates[pubkey] = message.payload.connectable + + def create_introduction_request(self, destination, allow_sync, forward=True, is_fast_walker=False): + connectable = False # Todo. Unused so far + exitnode = self.settings.become_exitnode + extra_payload = [exitnode, connectable] + super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, is_fast_walker, extra_payload) + + def on_introduction_response(self, messages): + super(TunnelCommunity, self).on_introduction_response(messages) + for message in messages: + pubkey = message.candidate.get_member().public_key + self.exit_candidates[pubkey] = message.payload.exitnode + self.connectable_candidates[pubkey] = message.payload.connectable + def on_cell(self, messages): for message in messages: circuit_id = message.payload.circuit_id @@ -808,7 +854,7 @@ def on_create(self, messages): for message in messages: candidate = message.candidate circuit_id = message.payload.circuit_id - + if self.settings.max_relays_or_exits <= len(self.relay_from_to) + len(self.exit_sockets): self._logger.error('TunnelCommunity: ignoring create for circuit %d from %s (too many relays %d)', circuit_id, candidate.sock_addr, len(self.relay_from_to) + len(self.exit_sockets)) @@ -827,7 +873,12 @@ def on_create(self, messages): candidates = {} for c in self.dispersy_yield_verified_candidates(): - candidates[c.get_member().public_key] = c + pubkey = c.get_member().public_key + if message.payload.exit_candidates and not self.exit_candidates[pubkey]: + # Next candidates need to be exit nodes, and this candidate isn't + continue + + candidates[pubkey] = c if len(candidates) >= 4: break @@ -917,7 +968,7 @@ def on_extend(self, messages): message.payload.node_id, message.payload.node_public_key, message.payload.key, - message.payload.become_exit))) + message.payload.exit_candidates))) def on_extended(self, messages): for message in messages: @@ -925,6 +976,7 @@ def on_extended(self, messages): circuit = self.circuits[circuit_id] self._ours_on_created_extended(circuit, message) + @call_on_reactor_thread def on_data(self, sock_addr, packet): # If its our circuit, the messenger is the candidate assigned to that circuit and the DATA's destination @@ -1053,7 +1105,7 @@ def tunnel_data_to_origin(self, circuit_id, sock_addr, source_address, data): def exit_data(self, circuit_id, sock_addr, destination, data): if not self.settings.become_exitnode: - self._logger.error("Dropping data packets, I don't want to be an exit node") + self._logger.error("Dropping data packets, refusing to be an exit node") elif circuit_id in self.exit_sockets: if not self.exit_sockets[circuit_id].enabled: # We got the correct circuit_id, but from a wrong IP. diff --git a/Tribler/dispersy b/Tribler/dispersy index f9a6e5461dd..a0407add7fe 160000 --- a/Tribler/dispersy +++ b/Tribler/dispersy @@ -1 +1 @@ -Subproject commit f9a6e5461dd63a6d009267122028197842cc0bf3 +Subproject commit a0407add7feab7353a0ce9c3887a5af08eec5594 From 584212bbda6e995790059ff47a3ed44109f15261 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Wed, 25 Mar 2015 00:10:03 +0100 Subject: [PATCH 03/11] Connectability check on being exit-node --- Tribler/Main/tribler_main.py | 2 + Tribler/Test/test_tunnel_community.py | 1 + Tribler/community/tunnel/conversion.py | 14 ++-- Tribler/community/tunnel/main.py | 1 + Tribler/community/tunnel/payload.py | 16 +---- Tribler/community/tunnel/tunnel_community.py | 67 ++++++++++++-------- 6 files changed, 52 insertions(+), 49 deletions(-) diff --git a/Tribler/Main/tribler_main.py b/Tribler/Main/tribler_main.py index 59c09876923..8ff44803780 100644 --- a/Tribler/Main/tribler_main.py +++ b/Tribler/Main/tribler_main.py @@ -127,6 +127,7 @@ SKIP_TUNNEL_DIALOG = os.environ.get("TRIBLER_SKIP_OPTIN_DLG", "False") == "True" # used by the anon tunnel tests as there's no way to mess with the Session before running the test ATM. FORCE_ENABLE_TUNNEL_COMMUNITY = False +TUNNEL_COMMUNITY_DO_TEST = True # # @@ -517,6 +518,7 @@ def define_communities(*args): keypair = dispersy.crypto.generate_key(u"curve25519") dispersy_member = dispersy.get_member(private_key=dispersy.crypto.key_to_bin(keypair),) settings = TunnelSettings(session.get_install_dir(), tribler_session=session) + settings.do_test = TUNNEL_COMMUNITY_DO_TEST tunnel_kwargs = {'tribler_session': session, 'settings': settings} self.tunnel_community = dispersy.define_auto_load(HiddenTunnelCommunity, dispersy_member, load=True, diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 5922b250fec..7d14dcab984 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -217,6 +217,7 @@ def cb_dht(info_hash, peers, source): def startTest(self, callback, min_timeout=5): from Tribler.Main import tribler_main tribler_main.FORCE_ENABLE_TUNNEL_COMMUNITY = True + tribler_main.TUNNEL_COMMUNITY_DO_TEST = False self.getStateDir() # getStateDir copies the bootstrap file into the statedir diff --git a/Tribler/community/tunnel/conversion.py b/Tribler/community/tunnel/conversion.py index 0dac92f572d..fafb2e4f58d 100644 --- a/Tribler/community/tunnel/conversion.py +++ b/Tribler/community/tunnel/conversion.py @@ -39,30 +39,28 @@ def __init__(self, community): def _encode_introduction_response(self, message): payload = message.payload - data = [pack("!??", payload.exitnode, payload.connectable)] + data = [pack("!?", payload.exitnode,)] data += list(super(TunnelConversion, self)._encode_introduction_response(message)) return tuple(data) def _decode_introduction_response(self, placeholder, offset, data): - exitnode, connectable, = unpack_from('!??', data, offset) - offset += 2 + exitnode, = unpack_from('!?', data, offset) + offset += 1 offset, payload = super(TunnelConversion, self)._decode_introduction_response(placeholder, offset, data) payload._exitnode = exitnode - payload._connectable = connectable return (offset, payload) def _encode_introduction_request(self, message): payload = message.payload - data = [pack("!??", payload.exitnode, payload.connectable)] + data = [pack("!?", payload.exitnode,)] data += super(TunnelConversion, self)._encode_introduction_request(message) return data def _decode_introduction_request(self, placeholder, offset, data): - exitnode, connectable, = unpack_from('!??', data, offset) - offset += 2 + exitnode, = unpack_from('!?', data, offset) + offset += 1 offset, payload = super(TunnelConversion, self)._decode_introduction_request(placeholder, offset, data) payload._exitnode = exitnode - payload._connectable = connectable return (offset, payload) def _encode_cell(self, message): diff --git a/Tribler/community/tunnel/main.py b/Tribler/community/tunnel/main.py index 00fcbbcac61..fbf4228861a 100644 --- a/Tribler/community/tunnel/main.py +++ b/Tribler/community/tunnel/main.py @@ -116,6 +116,7 @@ def start_tribler(self): config.set_torrent_collecting(False) config.set_libtorrent(True) config.set_dht_torrent_collecting(False) + config.set_enable_torrent_search(False) config.set_videoplayer(False) config.set_dispersy_port(self.dispersy_port) self.session = Session(config) diff --git a/Tribler/community/tunnel/payload.py b/Tribler/community/tunnel/payload.py index cfe68332dd5..360dc84e88a 100644 --- a/Tribler/community/tunnel/payload.py +++ b/Tribler/community/tunnel/payload.py @@ -6,42 +6,30 @@ class TunnelIntroductionRequestPayload(IntroductionRequestPayload): class Implementation(IntroductionRequestPayload.Implementation): - def __init__(self, meta, destination_address, source_lan_address, source_wan_address, advice, connection_type, sync, identifier, exitnode = False, connectable = False): + def __init__(self, meta, destination_address, source_lan_address, source_wan_address, advice, connection_type, sync, identifier, exitnode = False): super(TunnelIntroductionRequestPayload.Implementation, self).__init__(meta, destination_address, source_lan_address, source_wan_address, advice, connection_type, sync, identifier) assert isinstance(exitnode, bool), type(exitnode) - assert isinstance(connectable, bool), type(connectable) self._exitnode = exitnode - self._connectable = connectable @property def exitnode(self): return self._exitnode - - @property - def connectable(self): - return self._connectable class TunnelIntroductionResponsePayload(IntroductionResponsePayload): class Implementation(IntroductionResponsePayload.Implementation): - def __init__(self, meta, destination_address, source_lan_address, source_wan_address, lan_introduction_address, wan_introduction_address, connection_type, tunnel, identifier, exitnode = False, connectable = False): + def __init__(self, meta, destination_address, source_lan_address, source_wan_address, lan_introduction_address, wan_introduction_address, connection_type, tunnel, identifier, exitnode = False): super(TunnelIntroductionResponsePayload.Implementation, self).__init__(meta, destination_address, source_lan_address, source_wan_address, lan_introduction_address, wan_introduction_address, connection_type, tunnel, identifier) assert isinstance(exitnode, bool), type(exitnode) - assert isinstance(connectable, bool), type(connectable) self._exitnode = exitnode - self._connectable = connectable @property def exitnode(self): return self._exitnode - @property - def connectable(self): - return self._connectable - class CellPayload(Payload): diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index bb787129595..576ea97ab1a 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -188,13 +188,17 @@ def __init__(self, install_dir=None, tribler_session=None): self.max_packets_without_reply = 50 if tribler_session: - state_dir = tribler_session.get_state_dir() - cfgfilename = tribler_session.get_default_config_filename(state_dir) - scfg = SessionStartupConfig.load(cfgfilename) - self.become_exitnode = scfg.get_tunnel_community_exitnode_enabled() + self.become_exitnode = tribler_session.get_tunnel_community_exitnode_enabled() else: self.become_exitnode = False + +class ExitCandidate(object): + + def __init__(self, connectable, become_exit): + self.connectable = connectable + self.become_exit = become_exit + self.creation_time = time.time() class RoundRobin(object): @@ -239,7 +243,6 @@ def __init__(self, *args, **kwargs): self.exit_sockets = {} self.circuits_needed = {} self.exit_candidates = {} - self.connectable_candidates = {} self.notifier = None self.made_anon_session = False self.selection_strategy = RoundRobin(self) @@ -285,7 +288,7 @@ def start_download_test(self): if not self.libtorrent_test.has_completed_before(): self._logger.debug("Scheduling Anonymous LibTorrent download") self.register_task("start_test", reactor.callLater( - 5, lambda: reactor.callInThread(self.libtorrent_test.start))) + 60, lambda: reactor.callInThread(self.libtorrent_test.start))) return True return False @@ -308,31 +311,29 @@ def get_master_members(cls, dispersy): return [master] def initiate_meta_messages(self): - messages = super(TunnelCommunity, self).initiate_meta_messages() - copy_messages = messages[:] - for message in copy_messages: - if message.name == "dispersy-introduction-request" or message.name == "dispersy-introduction-response": - messages.remove(message) - - messages += [Message(self, u"dispersy-introduction-request", + meta_messages = super(TunnelCommunity, self).initiate_meta_messages() + for i, mm in enumerate(meta_messages): + if mm.name == "dispersy-introduction-request": + meta_messages[i] = Message(self, u"dispersy-introduction-request", MemberAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), TunnelIntroductionRequestPayload(), self.check_introduction_request, - self.on_introduction_request), - Message(self, u"dispersy-introduction-response", + self.on_introduction_request) + elif mm.name == "dispersy-introduction-response": + meta_messages[i] = Message(self, u"dispersy-introduction-response", MemberAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), TunnelIntroductionResponsePayload(), self.check_introduction_response, - self.on_introduction_response)] + self.on_introduction_response) - return messages + \ - [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), + + return meta_messages + [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), CellPayload(), self._generic_timeline_check, self.on_cell), Message(self, u"create", NoAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), CreatePayload(), self.check_create, self.on_create), @@ -437,6 +438,19 @@ def do_remove(self): self.remove_exit_socket(circuit_id, 'too old') elif exit_socket.bytes_up + exit_socket.bytes_down > self.settings.max_traffic: self.remove_exit_socket(circuit_id, 'traffic limit exceeded') + + # Remove exit_candidates that are not returned as dispersy verified candidates + current_candidates = {} + for c in self.dispersy_yield_verified_candidates(): + pubkey = c.get_member().public_key + current_candidates[pubkey] = pubkey + ckeys = self.exit_candidates.keys() + for pubkey in ckeys: + if pubkey not in current_candidates: + self.exit_candidates.pop(pubkey) + logging.debug("Removed candidate from exit_candidates dictionary") + + def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_retries=0, required_exit=None): assert required_exit is None or isinstance(required_exit, tuple), type(required_exit) @@ -802,27 +816,25 @@ def _ours_on_created_extended(self, circuit, message): def on_introduction_request(self, messages): - connectable = False # Todo. Unused so far exitnode = self.settings.become_exitnode - extra_payload = [exitnode, connectable] + extra_payload = [exitnode] super(TunnelCommunity, self).on_introduction_request(messages, extra_payload) for message in messages: pubkey = message.candidate.get_member().public_key - self.exit_candidates[pubkey] = message.payload.exitnode - self.connectable_candidates[pubkey] = message.payload.connectable + connectable = message.candidate.connection_type == u"public" + self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode, connectable) def create_introduction_request(self, destination, allow_sync, forward=True, is_fast_walker=False): - connectable = False # Todo. Unused so far exitnode = self.settings.become_exitnode - extra_payload = [exitnode, connectable] + extra_payload = [exitnode] super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, is_fast_walker, extra_payload) def on_introduction_response(self, messages): super(TunnelCommunity, self).on_introduction_response(messages) for message in messages: pubkey = message.candidate.get_member().public_key - self.exit_candidates[pubkey] = message.payload.exitnode - self.connectable_candidates[pubkey] = message.payload.connectable + connectable = message.candidate.connection_type == u"public" + self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode, connectable) def on_cell(self, messages): for message in messages: @@ -874,7 +886,8 @@ def on_create(self, messages): candidates = {} for c in self.dispersy_yield_verified_candidates(): pubkey = c.get_member().public_key - if message.payload.exit_candidates and not self.exit_candidates[pubkey]: + exit_candidate = self.exit_candidates[pubkey] + if message.payload.exit_candidates and not exit_candidate.connectable: # Next candidates need to be exit nodes, and this candidate isn't continue From 92282bac36e8285c122da3ffa705588bf87fbd34 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 26 Mar 2015 12:51:46 +0100 Subject: [PATCH 04/11] update dispersy pointer --- .gitmodules | 2 +- Tribler/dispersy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 523e7e701d7..308e73dcf8c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Tribler/dispersy"] path = Tribler/dispersy - url = https://github.com/rjruigrok/dispersy.git + url = https://github.com/tribler/dispersy.git [submodule "Tribler/Core/DecentralizedTracking/pymdht"] path = Tribler/Core/DecentralizedTracking/pymdht url = https://github.com/LipuFei/pymdht.git diff --git a/Tribler/dispersy b/Tribler/dispersy index a0407add7fe..2c6efd6d266 160000 --- a/Tribler/dispersy +++ b/Tribler/dispersy @@ -1 +1 @@ -Subproject commit a0407add7feab7353a0ce9c3887a5af08eec5594 +Subproject commit 2c6efd6d2661f96dc0180d0acdc96afac5590382 From 942347f69b594d701316dafb5acd2559dc14245b Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 26 Mar 2015 13:40:56 +0100 Subject: [PATCH 05/11] small improvements in coding style --- Tribler/Main/vwxGUI/MainFrame.py | 6 +---- Tribler/Main/vwxGUI/settingsDialog.py | 14 ++---------- Tribler/community/tunnel/tunnel_community.py | 23 +++++--------------- 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/Tribler/Main/vwxGUI/MainFrame.py b/Tribler/Main/vwxGUI/MainFrame.py index 4798e948aab..4c777e110db 100644 --- a/Tribler/Main/vwxGUI/MainFrame.py +++ b/Tribler/Main/vwxGUI/MainFrame.py @@ -616,11 +616,7 @@ def monitorHiddenSerivcesProgress(self, ds, tdef, dscfg, selectedFiles): if ds.get_status() in [DLSTATUS_ALLOCATING_DISKSPACE, DLSTATUS_HASHCHECKING, DLSTATUS_WAITING4HASHCHECK]: return (5.0, True) - state_dir = self.utility.session.get_state_dir() - cfgfilename = self.utility.session.get_default_config_filename(state_dir) - scfg = SessionStartupConfig.load(cfgfilename) - - if ds.get_current_speed(DOWNLOAD) == 0 and scfg.get_tunnel_community_hs_timeout_switch(): + if ds.get_current_speed(DOWNLOAD) == 0 and self.utility.session.get_tunnel_community_hs_timeout_switch(): download = ds.get_download() self.utility.session.remove_download(download) diff --git a/Tribler/Main/vwxGUI/settingsDialog.py b/Tribler/Main/vwxGUI/settingsDialog.py index f684a2899cb..ef5898d9282 100644 --- a/Tribler/Main/vwxGUI/settingsDialog.py +++ b/Tribler/Main/vwxGUI/settingsDialog.py @@ -209,17 +209,14 @@ def saveAll(self, event): self.utility.write_config('use_webui', useWebUI) restart = True - becomeExitNode = self._become_exitnode.IsChecked() if becomeExitNode != scfg.get_tunnel_community_exitnode_enabled(): scfg.set_tunnel_community_exitnode_enabled(becomeExitNode) - self.saveDefaultSessionConfig(scfg) restart = True switchHsOnTimeout = self._switch_hs_timeout.IsChecked() if switchHsOnTimeout != scfg.get_tunnel_community_hs_timeout_switch(): scfg.set_tunnel_community_hs_timeout_switch(switchHsOnTimeout) - self.saveDefaultSessionConfig(scfg) restart = True valwebuiport = self._webui_port.GetValue() @@ -369,10 +366,6 @@ def saveDefaultDownloadConfig(self, scfg): scfg.save(cfgfilename) - def saveDefaultSessionConfig(self, scfg): - cfgfilename = Session.get_default_config_filename(scfg.get_state_dir()) - scfg.save(cfgfilename) - def moveCollectedTorrents(self, old_dir, new_dir): def rename_or_merge(old, new, ignore=True): if os.path.exists(old): @@ -767,11 +760,8 @@ def __create_s6(self, tree_root, sizer): exp_vsizer.Add(exp_s1_faq_text, 0, wx.EXPAND | wx.TOP, 10) # load values - state_dir = self.utility.session.get_state_dir() - cfgfilename = self.utility.session.get_default_config_filename(state_dir) - scfg = SessionStartupConfig.load(cfgfilename) - self._become_exitnode.SetValue(scfg.get_tunnel_community_exitnode_enabled()) - self._switch_hs_timeout.SetValue(scfg.get_tunnel_community_hs_timeout_switch()) + self._become_exitnode.SetValue(self.utility.session.get_tunnel_community_exitnode_enabled()) + self._switch_hs_timeout.SetValue(self.utility.session.get_tunnel_community_hs_timeout_switch()) return exp_panel, item_id diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index 576ea97ab1a..8588f4ffc6c 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -314,24 +314,13 @@ def initiate_meta_messages(self): meta_messages = super(TunnelCommunity, self).initiate_meta_messages() for i, mm in enumerate(meta_messages): if mm.name == "dispersy-introduction-request": - meta_messages[i] = Message(self, u"dispersy-introduction-request", - MemberAuthentication(), - PublicResolution(), - DirectDistribution(), - CandidateDestination(), - TunnelIntroductionRequestPayload(), - self.check_introduction_request, - self.on_introduction_request) + meta_messages[i] = Message(self, mm.name, mm.authentication, mm.resolution, mm.distribution, + mm.destination, TunnelIntroductionRequestPayload(), + mm.check_callback, mm.handle_callback) elif mm.name == "dispersy-introduction-response": - meta_messages[i] = Message(self, u"dispersy-introduction-response", - MemberAuthentication(), - PublicResolution(), - DirectDistribution(), - CandidateDestination(), - TunnelIntroductionResponsePayload(), - self.check_introduction_response, - self.on_introduction_response) - + meta_messages[i] = Message(self, mm.name, mm.authentication, mm.resolution, mm.distribution, + mm.destination, TunnelIntroductionResponsePayload(), + mm.check_callback, mm.handle_callback) return meta_messages + [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), CellPayload(), self._generic_timeline_check, self.on_cell), From ab86a4d9eb80b17606c511aacc35558d8d04114c Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 26 Mar 2015 14:35:34 +0100 Subject: [PATCH 06/11] Fix check on return list of exitnodes --- .gitmodules | 2 +- Tribler/Test/test_tunnel_community.py | 60 ++++++++++++++++- Tribler/community/tunnel/tunnel_community.py | 71 ++++++++++---------- 3 files changed, 93 insertions(+), 40 deletions(-) diff --git a/.gitmodules b/.gitmodules index 308e73dcf8c..e647739f4fa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Tribler/dispersy"] path = Tribler/dispersy - url = https://github.com/tribler/dispersy.git + url = https://github.com/Tribler/dispersy.git [submodule "Tribler/Core/DecentralizedTracking/pymdht"] path = Tribler/Core/DecentralizedTracking/pymdht url = https://github.com/LipuFei/pymdht.git diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 7d14dcab984..ccc6d90c9b2 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -68,6 +68,55 @@ def do_create_local_torrent(_): self.startTest(do_create_local_torrent) + + @timed(120) + def test_anon_download_without_exitnodes(self): + def take_second_screenshot(): + self.screenshot() + self.quit() + + def take_screenshot(download_time): + self.screenshot("After an anonymous libtorrent download without exitnodes") + self.guiUtility.ShowPage('networkgraph') + self.Call(1, take_second_screenshot) + + def on_fail(expected, reason, do_assert): + dispersy = self.session.lm.dispersy + tunnel_community = next(c for c in dispersy.get_communities() if isinstance(c, HiddenTunnelCommunity)) + + self.guiUtility.ShowPage('networkgraph') + + def do_asserts(): + self.assert_(len(tunnel_community.circuits) == 0, + "No circuits should have been created (got %d)" % len(tunnel_community.circuits), + False) + self.assert_(expected, reason, do_assert) + + self.Call(1, do_asserts) + + def do_progress(download, start_time): + self.CallConditional(120, + lambda: download.get_progress() == 0.0, + lambda: take_screenshot(time.time() - start_time), + 'Anonymous download should not have any progress (%.1f%% downloaded)' % ( + download.get_progress() * 100), + on_fail + ) + + def do_create_local_torrent(_): + tf = self.setupSeeder() + start_time = time.time() + download = self.guiUtility.frame.startDownload(torrentfilename=tf, destdir=self.getDestDir(), + hops=3, try_hidden_services=False) + + self.guiUtility.ShowPage('my_files') + self.Call(5, lambda: download.add_peer(("127.0.0.1", self.session2.get_listen_port()))) + + do_progress(download, start_time) + + self.startTest(do_create_local_torrent, nr_exitnodes=0) + + @timed(120) def test_anon_tunnel(self): got_data = Event() @@ -214,7 +263,7 @@ def cb_dht(info_hash, peers, source): self.startTest(setup_seeder) - def startTest(self, callback, min_timeout=5): + def startTest(self, callback, min_timeout=5, nr_relays=5, nr_exitnodes=3): from Tribler.Main import tribler_main tribler_main.FORCE_ENABLE_TUNNEL_COMMUNITY = True tribler_main.TUNNEL_COMMUNITY_DO_TEST = False @@ -223,8 +272,13 @@ def startTest(self, callback, min_timeout=5): def setup_proxies(): tunnel_communities = [] - for i in range(3, 10): - tunnel_communities.append(create_proxy(i, i > 7)) + baseindex = 3 + for i in range(baseindex, baseindex + nr_relays): # Normal relays + tunnel_communities.append(create_proxy(i, False)) + + baseindex += nr_relays + 1 + for i in range(baseindex, baseindex + nr_exitnodes): # Exit nodes + tunnel_communities.append(create_proxy(i, True)) # Connect the proxies to the Tribler instance for community in self.lm.dispersy.get_communities(): diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index 8588f4ffc6c..6403ad3013d 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -195,8 +195,7 @@ def __init__(self, install_dir=None, tribler_session=None): class ExitCandidate(object): - def __init__(self, connectable, become_exit): - self.connectable = connectable + def __init__(self, become_exit): self.become_exit = become_exit self.creation_time = time.time() @@ -256,10 +255,11 @@ def __init__(self, *args, **kwargs): self.trsession = self.settings = self.socks_server = self.libtorrent_test = None def initialize(self, tribler_session=None, settings=None): - self.trsession = tribler_session self.settings = settings if settings else TunnelSettings(tribler_session=tribler_session) - + + self._logger.debug("TunnelCommunity: become_exitnide = %s" % settings.become_exitnode) + super(TunnelCommunity, self).initialize() assert isinstance(self.settings.crypto, TunnelCrypto), self.settings.crypto @@ -315,13 +315,13 @@ def initiate_meta_messages(self): for i, mm in enumerate(meta_messages): if mm.name == "dispersy-introduction-request": meta_messages[i] = Message(self, mm.name, mm.authentication, mm.resolution, mm.distribution, - mm.destination, TunnelIntroductionRequestPayload(), + mm.destination, TunnelIntroductionRequestPayload(), mm.check_callback, mm.handle_callback) elif mm.name == "dispersy-introduction-response": meta_messages[i] = Message(self, mm.name, mm.authentication, mm.resolution, mm.distribution, mm.destination, TunnelIntroductionResponsePayload(), mm.check_callback, mm.handle_callback) - + return meta_messages + [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), CellPayload(), self._generic_timeline_check, self.on_cell), Message(self, u"create", NoAuthentication(), PublicResolution(), DirectDistribution(), @@ -438,8 +438,7 @@ def do_remove(self): if pubkey not in current_candidates: self.exit_candidates.pop(pubkey) logging.debug("Removed candidate from exit_candidates dictionary") - - + def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_retries=0, required_exit=None): assert required_exit is None or isinstance(required_exit, tuple), type(required_exit) @@ -481,12 +480,13 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ self.waiting_for.add(circuit_id) exit_candidates = circuit.goal_hops - 1 == len(circuit.hops) + 1 - - self.increase_bytes_sent(circuit, self.send_cell([first_hop], u"create", (circuit_id, - circuit.unverified_hop.node_id, - circuit.unverified_hop.node_public_key, - circuit.unverified_hop.dh_first_part, - exit_candidates))) + + self.increase_bytes_sent(circuit, self.send_cell([first_hop], + u"create", (circuit_id, + circuit.unverified_hop.node_id, + circuit.unverified_hop.node_public_key, + circuit.unverified_hop.dh_first_part, + exit_candidates))) _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_SENT, circuit.mid) return True @@ -603,7 +603,7 @@ def active_data_circuits(self, hops=None): (hops is None or hops == len(c.hops))} def is_relay(self, circuit_id): - return circuit_id > 0 and circuit_id in self.relay_from_to and not circuit_id in self.waiting_for + return circuit_id > 0 and circuit_id in self.relay_from_to and circuit_id not in self.waiting_for def send_cell(self, candidates, message_type, payload): meta = self.get_meta_message(message_type) @@ -686,7 +686,7 @@ def check_create(self, messages): if self.crypto.key.pub().key_to_bin() != message.payload.node_public_key: yield DropMessage(message, "TunnelCommunity: public keys do not match") continue - + yield message def check_extend(self, messages): @@ -772,18 +772,18 @@ def _ours_on_created_extended(self, circuit, message): if extend_hop_public_bin: extend_hop_public_key = self.dispersy.crypto.key_from_public_bin(extend_hop_public_bin) circuit.unverified_hop = Hop(extend_hop_public_key) - circuit.unverified_hop.dh_secret, circuit.unverified_hop.dh_first_part = self.crypto.generate_diffie_secret( - ) + circuit.unverified_hop.dh_secret, circuit.unverified_hop.dh_first_part = self.crypto.generate_diffie_secret() self._logger.info( "extending circuit %d with %s", circuit.circuit_id, extend_hop_public_bin[:20].encode('hex')) self.increase_bytes_sent( - circuit, self.send_cell([Candidate(circuit.first_hop, False)], u"extend", (circuit.circuit_id, - circuit.unverified_hop.node_id, - circuit.unverified_hop.node_public_key, - extend_hop_addr, - circuit.unverified_hop.dh_first_part, - exit_candidates))) + circuit, self.send_cell([Candidate(circuit.first_hop, False)], + u"extend", (circuit.circuit_id, + circuit.unverified_hop.node_id, + circuit.unverified_hop.node_public_key, + extend_hop_addr, + circuit.unverified_hop.dh_first_part, + exit_candidates))) else: self.remove_circuit(circuit.circuit_id, "no candidates to extend, bailing out.") @@ -810,21 +810,20 @@ def on_introduction_request(self, messages): super(TunnelCommunity, self).on_introduction_request(messages, extra_payload) for message in messages: pubkey = message.candidate.get_member().public_key - connectable = message.candidate.connection_type == u"public" - self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode, connectable) - + self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode) + def create_introduction_request(self, destination, allow_sync, forward=True, is_fast_walker=False): exitnode = self.settings.become_exitnode extra_payload = [exitnode] - super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, is_fast_walker, extra_payload) - + super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, + is_fast_walker, extra_payload) + def on_introduction_response(self, messages): super(TunnelCommunity, self).on_introduction_response(messages) for message in messages: pubkey = message.candidate.get_member().public_key - connectable = message.candidate.connection_type == u"public" - self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode, connectable) - + self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode) + def on_cell(self, messages): for message in messages: circuit_id = message.payload.circuit_id @@ -855,7 +854,7 @@ def on_create(self, messages): for message in messages: candidate = message.candidate circuit_id = message.payload.circuit_id - + if self.settings.max_relays_or_exits <= len(self.relay_from_to) + len(self.exit_sockets): self._logger.error('TunnelCommunity: ignoring create for circuit %d from %s (too many relays %d)', circuit_id, candidate.sock_addr, len(self.relay_from_to) + len(self.exit_sockets)) @@ -876,10 +875,10 @@ def on_create(self, messages): for c in self.dispersy_yield_verified_candidates(): pubkey = c.get_member().public_key exit_candidate = self.exit_candidates[pubkey] - if message.payload.exit_candidates and not exit_candidate.connectable: - # Next candidates need to be exit nodes, and this candidate isn't + if message.payload.exit_candidates and not (exit_candidate.become_exit and message.candidate.connection_type == u"public"): + # Next candidates need to be connectable exit nodes, and this candidate isn't continue - + candidates[pubkey] = c if len(candidates) >= 4: break From be925fe54595287947712dfe88a4c381f234404f Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 26 Mar 2015 14:35:34 +0100 Subject: [PATCH 07/11] Fix check on return list of exitnodes --- .gitmodules | 2 +- Tribler/Test/test_tunnel_community.py | 111 ++++++++++++++++++- Tribler/community/tunnel/tunnel_community.py | 99 +++++++++-------- 3 files changed, 159 insertions(+), 53 deletions(-) diff --git a/.gitmodules b/.gitmodules index 308e73dcf8c..e647739f4fa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Tribler/dispersy"] path = Tribler/dispersy - url = https://github.com/tribler/dispersy.git + url = https://github.com/Tribler/dispersy.git [submodule "Tribler/Core/DecentralizedTracking/pymdht"] path = Tribler/Core/DecentralizedTracking/pymdht url = https://github.com/LipuFei/pymdht.git diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 7d14dcab984..a7f3fc03d34 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -68,6 +68,106 @@ def do_create_local_torrent(_): self.startTest(do_create_local_torrent) + @timed(180) + def test_anon_download_without_relays(self): + def take_second_screenshot(): + self.screenshot() + self.quit() + + def take_screenshot(download_time): + self.screenshot("After an anonymous libtorrent download without relays") + self.guiUtility.ShowPage('networkgraph') + self.Call(1, take_second_screenshot) + + def on_success(download, start_time): + take_screenshot(time.time() - start_time) + + def on_fail(expected, reason, do_assert): + dispersy = self.session.lm.dispersy + tunnel_community = next(c for c in dispersy.get_communities() if isinstance(c, HiddenTunnelCommunity)) + + self.guiUtility.ShowPage('networkgraph') + + def do_asserts(): + self.assert_(len(tunnel_community.circuits) == 0, + "No circuits should have been created (got %d)" % len(tunnel_community.circuits), + False) + self.assert_(expected, reason, do_assert) + + self.Call(1, do_asserts) + + def check_progress(download, start_time): + self.CallConditional(1, + lambda: download.get_progress() == 0.0, + lambda: on_success(download, start_time), + 'Anonymous download without relays should not have any progress (%.1f%% downloaded)' % ( + download.get_progress() * 100), + on_fail + ) + + def do_create_local_torrent(_): + tf = self.setupSeeder() + start_time = time.time() + download = self.guiUtility.frame.startDownload(torrentfilename=tf, destdir=self.getDestDir(), + hops=3, try_hidden_services=False) + + self.guiUtility.ShowPage('my_files') + self.Call(5, lambda: download.add_peer(("127.0.0.1", self.session2.get_listen_port()))) + self.Call(120, lambda: check_progress(download, start_time)) + + self.startTest(do_create_local_torrent, nr_exitnodes=5, nr_relays=0) + + @timed(180) + def test_anon_download_without_exitnodes(self): + def take_second_screenshot(): + self.screenshot() + self.quit() + + def take_screenshot(download_time): + self.screenshot("After an anonymous libtorrent download without exitnodes") + self.guiUtility.ShowPage('networkgraph') + self.Call(1, take_second_screenshot) + + def on_success(download, start_time): + take_screenshot(time.time() - start_time) + + def on_fail(expected, reason, do_assert): + dispersy = self.session.lm.dispersy + tunnel_community = next(c for c in dispersy.get_communities() if isinstance(c, HiddenTunnelCommunity)) + + self.guiUtility.ShowPage('networkgraph') + + def do_asserts(): + self.assert_(len(tunnel_community.circuits) == 0, + "No circuits should have been created (got %d)" % len(tunnel_community.circuits), + False) + self.assert_(expected, reason, do_assert) + + self.Call(1, do_asserts) + + def check_progress(download, start_time): + self.download = download + self.CallConditional(1, + lambda: download.get_progress() == 0.0, + lambda: on_success(download, start_time), + 'Anonymous download without exit nodes should not have any progress (%.1f%% downloaded)' % ( + download.get_progress() * 100), + on_fail + ) + + def do_create_local_torrent(_): + tf = self.setupSeeder() + start_time = time.time() + download = self.guiUtility.frame.startDownload(torrentfilename=tf, destdir=self.getDestDir(), + hops=3, try_hidden_services=False) + + self.guiUtility.ShowPage('my_files') + self.Call(5, lambda: download.add_peer(("127.0.0.1", self.session2.get_listen_port()))) + self.Call(120, lambda: check_progress(download, start_time)) + + self.startTest(do_create_local_torrent, nr_exitnodes=0, nr_relays=5) + + @timed(120) def test_anon_tunnel(self): got_data = Event() @@ -214,7 +314,7 @@ def cb_dht(info_hash, peers, source): self.startTest(setup_seeder) - def startTest(self, callback, min_timeout=5): + def startTest(self, callback, min_timeout=5, nr_relays=5, nr_exitnodes=3): from Tribler.Main import tribler_main tribler_main.FORCE_ENABLE_TUNNEL_COMMUNITY = True tribler_main.TUNNEL_COMMUNITY_DO_TEST = False @@ -223,8 +323,13 @@ def startTest(self, callback, min_timeout=5): def setup_proxies(): tunnel_communities = [] - for i in range(3, 10): - tunnel_communities.append(create_proxy(i, i > 7)) + baseindex = 3 + for i in range(baseindex, baseindex + nr_relays): # Normal relays + tunnel_communities.append(create_proxy(i, False)) + + baseindex += nr_relays + 1 + for i in range(baseindex, baseindex + nr_exitnodes): # Exit nodes + tunnel_communities.append(create_proxy(i, True)) # Connect the proxies to the Tribler instance for community in self.lm.dispersy.get_communities(): diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index 8588f4ffc6c..a90cfd06e96 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -20,7 +20,7 @@ from Tribler.community.tunnel.conversion import TunnelConversion from Tribler.community.tunnel.payload import (CellPayload, CreatePayload, CreatedPayload, ExtendPayload, ExtendedPayload, DestroyPayload, PongPayload, PingPayload, - StatsRequestPayload, StatsResponsePayload, + StatsRequestPayload, StatsResponsePayload, TunnelIntroductionResponsePayload, TunnelIntroductionRequestPayload) from Tribler.community.tunnel.routing import Circuit, Hop, RelayRoute from Tribler.community.tunnel.tests.test_libtorrent import LibtorrentTest @@ -39,8 +39,6 @@ from Tribler.dispersy.util import call_on_reactor_thread from Tribler.dispersy.requestcache import NumberCache, RandomNumberCache from Tribler.community.bartercast4.statistics import BartercastStatisticTypes, _barter_statistics -from Tribler.Main.Utility.utility import Utility -from Tribler.Core.SessionConfig import SessionStartupConfig class CircuitRequestCache(NumberCache): @@ -186,20 +184,19 @@ def __init__(self, install_dir=None, tribler_session=None): self.max_traffic = 55 * 1024 * 1024 self.max_packets_without_reply = 50 - + if tribler_session: self.become_exitnode = tribler_session.get_tunnel_community_exitnode_enabled() else: self.become_exitnode = False - - + + class ExitCandidate(object): - - def __init__(self, connectable, become_exit): - self.connectable = connectable + + def __init__(self, become_exit): self.become_exit = become_exit self.creation_time = time.time() - + class RoundRobin(object): @@ -256,10 +253,11 @@ def __init__(self, *args, **kwargs): self.trsession = self.settings = self.socks_server = self.libtorrent_test = None def initialize(self, tribler_session=None, settings=None): - self.trsession = tribler_session self.settings = settings if settings else TunnelSettings(tribler_session=tribler_session) - + + self._logger.debug("TunnelCommunity: become_exitnode = %s" % settings.become_exitnode) + super(TunnelCommunity, self).initialize() assert isinstance(self.settings.crypto, TunnelCrypto), self.settings.crypto @@ -315,13 +313,13 @@ def initiate_meta_messages(self): for i, mm in enumerate(meta_messages): if mm.name == "dispersy-introduction-request": meta_messages[i] = Message(self, mm.name, mm.authentication, mm.resolution, mm.distribution, - mm.destination, TunnelIntroductionRequestPayload(), + mm.destination, TunnelIntroductionRequestPayload(), mm.check_callback, mm.handle_callback) elif mm.name == "dispersy-introduction-response": meta_messages[i] = Message(self, mm.name, mm.authentication, mm.resolution, mm.distribution, mm.destination, TunnelIntroductionResponsePayload(), mm.check_callback, mm.handle_callback) - + return meta_messages + [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), CandidateDestination(), CellPayload(), self._generic_timeline_check, self.on_cell), Message(self, u"create", NoAuthentication(), PublicResolution(), DirectDistribution(), @@ -438,8 +436,7 @@ def do_remove(self): if pubkey not in current_candidates: self.exit_candidates.pop(pubkey) logging.debug("Removed candidate from exit_candidates dictionary") - - + def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_retries=0, required_exit=None): assert required_exit is None or isinstance(required_exit, tuple), type(required_exit) @@ -481,12 +478,13 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ self.waiting_for.add(circuit_id) exit_candidates = circuit.goal_hops - 1 == len(circuit.hops) + 1 - - self.increase_bytes_sent(circuit, self.send_cell([first_hop], u"create", (circuit_id, - circuit.unverified_hop.node_id, - circuit.unverified_hop.node_public_key, - circuit.unverified_hop.dh_first_part, - exit_candidates))) + + self.increase_bytes_sent(circuit, self.send_cell([first_hop], + u"create", (circuit_id, + circuit.unverified_hop.node_id, + circuit.unverified_hop.node_public_key, + circuit.unverified_hop.dh_first_part, + exit_candidates))) _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_SENT, circuit.mid) return True @@ -603,7 +601,7 @@ def active_data_circuits(self, hops=None): (hops is None or hops == len(c.hops))} def is_relay(self, circuit_id): - return circuit_id > 0 and circuit_id in self.relay_from_to and not circuit_id in self.waiting_for + return circuit_id > 0 and circuit_id in self.relay_from_to and circuit_id not in self.waiting_for def send_cell(self, candidates, message_type, payload): meta = self.get_meta_message(message_type) @@ -686,7 +684,7 @@ def check_create(self, messages): if self.crypto.key.pub().key_to_bin() != message.payload.node_public_key: yield DropMessage(message, "TunnelCommunity: public keys do not match") continue - + yield message def check_extend(self, messages): @@ -772,18 +770,18 @@ def _ours_on_created_extended(self, circuit, message): if extend_hop_public_bin: extend_hop_public_key = self.dispersy.crypto.key_from_public_bin(extend_hop_public_bin) circuit.unverified_hop = Hop(extend_hop_public_key) - circuit.unverified_hop.dh_secret, circuit.unverified_hop.dh_first_part = self.crypto.generate_diffie_secret( - ) + circuit.unverified_hop.dh_secret, circuit.unverified_hop.dh_first_part = self.crypto.generate_diffie_secret() self._logger.info( "extending circuit %d with %s", circuit.circuit_id, extend_hop_public_bin[:20].encode('hex')) self.increase_bytes_sent( - circuit, self.send_cell([Candidate(circuit.first_hop, False)], u"extend", (circuit.circuit_id, - circuit.unverified_hop.node_id, - circuit.unverified_hop.node_public_key, - extend_hop_addr, - circuit.unverified_hop.dh_first_part, - exit_candidates))) + circuit, self.send_cell([Candidate(circuit.first_hop, False)], + u"extend", (circuit.circuit_id, + circuit.unverified_hop.node_id, + circuit.unverified_hop.node_public_key, + extend_hop_addr, + circuit.unverified_hop.dh_first_part, + exit_candidates))) else: self.remove_circuit(circuit.circuit_id, "no candidates to extend, bailing out.") @@ -810,21 +808,20 @@ def on_introduction_request(self, messages): super(TunnelCommunity, self).on_introduction_request(messages, extra_payload) for message in messages: pubkey = message.candidate.get_member().public_key - connectable = message.candidate.connection_type == u"public" - self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode, connectable) - + self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode) + def create_introduction_request(self, destination, allow_sync, forward=True, is_fast_walker=False): exitnode = self.settings.become_exitnode extra_payload = [exitnode] - super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, is_fast_walker, extra_payload) - + super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, + is_fast_walker, extra_payload) + def on_introduction_response(self, messages): super(TunnelCommunity, self).on_introduction_response(messages) for message in messages: pubkey = message.candidate.get_member().public_key - connectable = message.candidate.connection_type == u"public" - self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode, connectable) - + self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode) + def on_cell(self, messages): for message in messages: circuit_id = message.payload.circuit_id @@ -855,7 +852,7 @@ def on_create(self, messages): for message in messages: candidate = message.candidate circuit_id = message.payload.circuit_id - + if self.settings.max_relays_or_exits <= len(self.relay_from_to) + len(self.exit_sockets): self._logger.error('TunnelCommunity: ignoring create for circuit %d from %s (too many relays %d)', circuit_id, candidate.sock_addr, len(self.relay_from_to) + len(self.exit_sockets)) @@ -876,10 +873,13 @@ def on_create(self, messages): for c in self.dispersy_yield_verified_candidates(): pubkey = c.get_member().public_key exit_candidate = self.exit_candidates[pubkey] - if message.payload.exit_candidates and not exit_candidate.connectable: - # Next candidates need to be exit nodes, and this candidate isn't + if message.payload.exit_candidates and not (exit_candidate.become_exit and message.candidate.connection_type == u"public"): + # Next candidates need to be connectable exit nodes, and this candidate isn't continue - + if not message.payload.exit_candidates and (exit_candidate.become_exit): + # Reserve exit node candidates just for exiting data + continue + candidates[pubkey] = c if len(candidates) >= 4: break @@ -966,11 +966,12 @@ def on_extend(self, messages): self._logger.info("extending circuit, got candidate with IP %s:%d from cache", *extend_candidate.sock_addr) - self.increase_bytes_sent(new_circuit_id, self.send_cell([extend_candidate], u"create", (new_circuit_id, - message.payload.node_id, - message.payload.node_public_key, - message.payload.key, - message.payload.exit_candidates))) + self.increase_bytes_sent(new_circuit_id, self.send_cell([extend_candidate], + u"create", (new_circuit_id, + message.payload.node_id, + message.payload.node_public_key, + message.payload.key, + message.payload.exit_candidates))) def on_extended(self, messages): for message in messages: From 86d496974ba91ea071d6511725c644f5564aaa52 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 9 Apr 2015 10:37:31 +0200 Subject: [PATCH 08/11] initiating peer chooses exit node --- Tribler/community/tunnel/conversion.py | 131 ++++++++++----- Tribler/community/tunnel/hidden_community.py | 6 +- Tribler/community/tunnel/payload.py | 19 +-- Tribler/community/tunnel/tunnel_community.py | 160 +++++++++++-------- Tribler/dispersy | 2 +- 5 files changed, 197 insertions(+), 121 deletions(-) diff --git a/Tribler/community/tunnel/conversion.py b/Tribler/community/tunnel/conversion.py index fafb2e4f58d..5b525bda9cb 100644 --- a/Tribler/community/tunnel/conversion.py +++ b/Tribler/community/tunnel/conversion.py @@ -15,27 +15,84 @@ class TunnelConversion(BinaryConversion): def __init__(self, community): super(TunnelConversion, self).__init__(community, "\x02") - self.define_meta_message(chr(1), community.get_meta_message(u"cell"), self._encode_cell, self._decode_cell) - self.define_meta_message(chr(2), community.get_meta_message(u"create"), self._encode_create, self._decode_create) - self.define_meta_message(chr(3), community.get_meta_message(u"created"), self._encode_created, self._decode_created) - self.define_meta_message(chr(4), community.get_meta_message(u"extend"), self._encode_extend, self._decode_extend) - self.define_meta_message(chr(5), community.get_meta_message(u"extended"), self._encode_extended, self._decode_extended) - self.define_meta_message(chr(6), community.get_meta_message(u"ping"), self._encode_ping, self._decode_ping) - self.define_meta_message(chr(7), community.get_meta_message(u"pong"), self._encode_pong, self._decode_pong) - self.define_meta_message(chr(8), community.get_meta_message(u"stats-request"), self._encode_stats_request, self._decode_stats_request) - self.define_meta_message(chr(9), community.get_meta_message(u"stats-response"), self._encode_stats_response, self._decode_stats_response) - self.define_meta_message(chr(10), community.get_meta_message(u"destroy"), self._encode_destroy, self._decode_destroy) - self.define_meta_message(chr(11), community.get_meta_message(u"establish-intro"), self._encode_establish_intro, self._decode_establish_intro) - self.define_meta_message(chr(12), community.get_meta_message(u"intro-established"), self._encode_intro_established, self._decode_intro_established) - self.define_meta_message(chr(13), community.get_meta_message(u"key-request"), self._encode_keys_request, self._decode_keys_request) - self.define_meta_message(chr(14), community.get_meta_message(u"key-response"), self._encode_keys_response, self._decode_keys_response) - self.define_meta_message(chr(15), community.get_meta_message(u"establish-rendezvous"), self._encode_establish_rendezvous, self._decode_establish_rendezvous) - self.define_meta_message(chr(16), community.get_meta_message(u"rendezvous-established"), self._encode_rendezvous_established, self._decode_rendezvous_established) - self.define_meta_message(chr(17), community.get_meta_message(u"create-e2e"), self._encode_create_e2e, self._decode_create_e2e) - self.define_meta_message(chr(18), community.get_meta_message(u"created-e2e"), self._encode_created_e2e, self._decode_created_e2e) - self.define_meta_message(chr(19), community.get_meta_message(u"link-e2e"), self._encode_link_e2e, self._decode_link_e2e) - self.define_meta_message(chr(20), community.get_meta_message(u"linked-e2e"), self._encode_linked_e2e, self._decode_linked_e2e) - + self.define_meta_message(chr(1), + community.get_meta_message(u"cell"), + self._encode_cell, + self._decode_cell) + self.define_meta_message(chr(2), + community.get_meta_message(u"create"), + self._encode_create, + self._decode_create) + self.define_meta_message(chr(3), + community.get_meta_message(u"created"), + self._encode_created, + self._decode_created) + self.define_meta_message(chr(4), + community.get_meta_message(u"extend"), + self._encode_extend, + self._decode_extend) + self.define_meta_message(chr(5), + community.get_meta_message(u"extended"), + self._encode_extended, + self._decode_extended) + self.define_meta_message(chr(6), + community.get_meta_message(u"ping"), + self._encode_ping, + self._decode_ping) + self.define_meta_message(chr(7), + community.get_meta_message(u"pong"), + self._encode_pong, + self._decode_pong) + self.define_meta_message(chr(8), + community.get_meta_message(u"stats-request"), + self._encode_stats_request, self._decode_stats_request) + self.define_meta_message(chr(9), + community.get_meta_message(u"stats-response"), + self._encode_stats_response, + self._decode_stats_response) + self.define_meta_message(chr(10), + community.get_meta_message(u"destroy"), + self._encode_destroy, self._decode_destroy) + self.define_meta_message(chr(11), + community.get_meta_message(u"establish-intro"), + self._encode_establish_intro, + self._decode_establish_intro) + self.define_meta_message(chr(12), + community.get_meta_message(u"intro-established"), + self._encode_intro_established, + self._decode_intro_established) + self.define_meta_message(chr(13), + community.get_meta_message(u"key-request"), + self._encode_keys_request, + self._decode_keys_request) + self.define_meta_message(chr(14), + community.get_meta_message(u"key-response"), + self._encode_keys_response, + self._decode_keys_response) + self.define_meta_message(chr(15), + community.get_meta_message(u"establish-rendezvous"), + self._encode_establish_rendezvous, + self._decode_establish_rendezvous) + self.define_meta_message(chr(16), + community.get_meta_message(u"rendezvous-established"), + self._encode_rendezvous_established, + self._decode_rendezvous_established) + self.define_meta_message(chr(17), + community.get_meta_message(u"create-e2e"), + self._encode_create_e2e, + self._decode_create_e2e) + self.define_meta_message(chr(18), + community.get_meta_message(u"created-e2e"), + self._encode_created_e2e, + self._decode_created_e2e) + self.define_meta_message(chr(19), + community.get_meta_message(u"link-e2e"), + self._encode_link_e2e, + self._decode_link_e2e) + self.define_meta_message(chr(20), + community.get_meta_message(u"linked-e2e"), + self._encode_linked_e2e, + self._decode_linked_e2e) def _encode_introduction_response(self, message): payload = message.payload @@ -73,7 +130,7 @@ def _decode_cell(self, placeholder, offset, data): circuit_id, = unpack_from('!I', data, offset) offset += 4 - if not self._decode_message_map.has_key(data[offset]): + if not data[offset] in self._decode_message_map: raise DropPacket("Invalid message") message_type = self._decode_message_map[data[offset]].meta.name offset += 1 @@ -85,13 +142,13 @@ def _decode_cell(self, placeholder, offset, data): def _encode_create(self, message): payload = message.payload - packet = pack("!IHH20s?", payload.circuit_id, len(payload.node_public_key), - len(payload.key), payload.node_id, payload.exit_candidates) + payload.node_public_key + payload.key + packet = pack("!IHH20s", payload.circuit_id, len(payload.node_public_key), + len(payload.key), payload.node_id) + payload.node_public_key + payload.key return packet, def _decode_create(self, placeholder, offset, data): - circuit_id, len_pubic_key, len_key, nodeid, exit_candidates = unpack_from('!IHH20s?', data, offset) - offset += 29 + circuit_id, len_pubic_key, len_key, nodeid = unpack_from('!IHH20s', data, offset) + offset += 28 node_public_key = data[offset: offset + len_pubic_key] offset += len_pubic_key @@ -99,7 +156,7 @@ def _decode_create(self, placeholder, offset, data): key = data[offset:offset + len_key] offset += len_key - return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, key, exit_candidates) + return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, key) def _encode_created(self, message): payload = message.payload @@ -121,8 +178,8 @@ def _decode_created(self, placeholder, offset, data): def _encode_extend(self, message): payload = message.payload - packet = pack("!IHH20s?", payload.circuit_id, len(payload.node_public_key), len(payload.key), - payload.node_id, payload.exit_candidates) + payload.node_public_key + payload.key + packet = pack("!IHH20s", payload.circuit_id, len(payload.node_public_key), len(payload.key), + payload.node_id) + payload.node_public_key + payload.key if message.payload.node_addr: host, port = message.payload.node_addr @@ -130,8 +187,8 @@ def _encode_extend(self, message): return packet, def _decode_extend(self, placeholder, offset, data): - circuit_id, len_public_key, len_key, nodeid, exit_candidates = unpack_from('!IHH20s?', data, offset) - offset += 29 + circuit_id, len_public_key, len_key, nodeid = unpack_from('!IHH20s', data, offset) + offset += 28 node_public_key = data[offset:offset + len_public_key] offset += len_public_key @@ -145,11 +202,12 @@ def _decode_extend(self, placeholder, offset, data): offset += 6 node_addr = (inet_ntoa(host), port) - return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, node_addr, key, exit_candidates) + return offset, placeholder.meta.payload.implement(circuit_id, nodeid, node_public_key, node_addr, key) def _encode_extended(self, message): payload = message.payload - return pack("!IH32s", payload.circuit_id, len(payload.key), payload.auth) + payload.key + payload.candidate_list, + return pack("!IH32s", payload.circuit_id, len(payload.key), payload.auth) + \ + payload.key + payload.candidate_list, def _decode_extended(self, placeholder, offset, data): circuit_id, len_key, auth = unpack_from('!IH32s', data, offset) @@ -204,7 +262,8 @@ def _decode_stats_request(self, placeholder, offset, data): def _encode_stats_response(self, message): stats_list = [] - for key in ['uptime', 'bytes_up', 'bytes_down', 'bytes_relay_up', 'bytes_relay_down', 'bytes_enter', 'bytes_exit']: + for key in ['uptime', 'bytes_up', 'bytes_down', 'bytes_relay_up', 'bytes_relay_down', + 'bytes_enter', 'bytes_exit']: stats_list.append(message.payload.stats.get(key, 0)) return pack('!HIQQQQQQ', *([message.payload.identifier] + stats_list)), @@ -216,7 +275,8 @@ def _decode_stats_response(self, placeholder, offset, data): stats_list = unpack_from('!IQQQQQQ', data, offset) offset += 52 stats_dict = dict( - zip(['uptime', 'bytes_up', 'bytes_down', 'bytes_relay_up', 'bytes_relay_down', 'bytes_enter', 'bytes_exit'], stats_list)) + zip(['uptime', 'bytes_up', 'bytes_down', 'bytes_relay_up', 'bytes_relay_down', + 'bytes_enter', 'bytes_exit'], stats_list)) # Ignore the rest offset += len(data[offset:]) @@ -458,4 +518,3 @@ def is_allowed(data): TunnelConversion.could_be_udp_tracker(data) or TunnelConversion.could_be_dht(data) or TunnelConversion.could_be_dispersy(data)) - diff --git a/Tribler/community/tunnel/hidden_community.py b/Tribler/community/tunnel/hidden_community.py index ff94d830d1e..7d3d5b03470 100644 --- a/Tribler/community/tunnel/hidden_community.py +++ b/Tribler/community/tunnel/hidden_community.py @@ -354,8 +354,12 @@ def on_created_e2e(self, messages): _, rp_info = decode(self.crypto.decrypt_str( message.payload.rp_sock_addr, session_keys[EXIT_NODE], session_keys[EXIT_NODE_SALT])) + self.create_circuit(DEFAULT_HOPS, CIRCUIT_TYPE_RENDEZVOUS, callback=lambda circuit, cookie=rp_info[ - 1], session_keys=session_keys, info_hash=cache.info_hash, sock_addr=cache.sock_addr: self.create_link_e2e(circuit, cookie, session_keys, info_hash, sock_addr), max_retries=5, required_exit=rp_info[0]) + 1], session_keys=session_keys, info_hash=cache.info_hash, + sock_addr=cache.sock_addr: self.create_link_e2e(circuit, cookie, session_keys, + info_hash, sock_addr), + max_retries=5, required_exit=rp_info[0]) def create_link_e2e(self, circuit, cookie, session_keys, info_hash, sock_addr): self.my_download_points[circuit.circuit_id] = (info_hash, circuit.goal_hops, sock_addr) diff --git a/Tribler/community/tunnel/payload.py b/Tribler/community/tunnel/payload.py index 360dc84e88a..570c61d1ad5 100644 --- a/Tribler/community/tunnel/payload.py +++ b/Tribler/community/tunnel/payload.py @@ -62,19 +62,17 @@ class CreatePayload(Payload): class Implementation(Payload.Implementation): - def __init__(self, meta, circuit_id, node_id, node_public_key, key, exit_candidates): + def __init__(self, meta, circuit_id, node_id, node_public_key, key): assert isinstance(circuit_id, (int, long)), type(circuit_id) assert isinstance(node_id, basestring), type(node_id) assert isinstance(node_public_key, basestring), type(node_public_key) assert isinstance(key, basestring), type(key) - assert isinstance(exit_candidates, bool), type(exit_candidates) super(CreatePayload.Implementation, self).__init__(meta) self._circuit_id = circuit_id self._node_id = node_id self._node_public_key = node_public_key self._key = key - self._exit_candidates = exit_candidates @property def circuit_id(self): @@ -91,10 +89,6 @@ def node_public_key(self): @property def key(self): return self._key - - @property - def exit_candidates(self): - return self._exit_candidates class CreatedPayload(Payload): @@ -134,13 +128,12 @@ class ExtendPayload(Payload): class Implementation(Payload.Implementation): - def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key, exit_candidates): + def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key): assert isinstance(circuit_id, (int, long)), type(circuit_id) assert isinstance(node_id, basestring), type(node_id) assert isinstance(node_public_key, basestring), type(node_public_key) - assert node_addr == None or isinstance(node_addr, tuple), type(node_addr) + assert node_addr is None or isinstance(node_addr, tuple), type(node_addr) assert isinstance(key, basestring), type(key) - assert isinstance(exit_candidates, bool), type(exit_candidates) super(ExtendPayload.Implementation, self).__init__(meta) self._circuit_id = circuit_id @@ -148,7 +141,6 @@ def __init__(self, meta, circuit_id, node_id, node_public_key, node_addr, key, e self._node_public_key = node_public_key self._node_addr = node_addr self._key = key - self._exit_candidates = exit_candidates @property def circuit_id(self): @@ -170,11 +162,6 @@ def node_addr(self): def key(self): return self._key - @property - def exit_candidates(self): - return self._exit_candidates - - class ExtendedPayload(Payload): diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index c0f7662b927..fe1baa53e43 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -57,6 +57,7 @@ def on_timeout(self): if self.retry: self.retry() + class CreatedRequestCache(NumberCache): def __init__(self, community, circuit_id, candidate, candidates): @@ -124,7 +125,7 @@ def sendto(self, data, destination): if TunnelConversion.is_allowed(data): try: self.transport.write(data, destination) - except Exception, e: + except Exception, _: self._logger.error("Failed to write data to transport: %s", str(destination).encode("HEX")) raise @@ -321,28 +322,31 @@ def initiate_meta_messages(self): mm.check_callback, mm.handle_callback) return meta_messages + [Message(self, u"cell", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), CellPayload(), self._generic_timeline_check, self.on_cell), - Message(self, u"create", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), CreatePayload(), self.check_create, self.on_create), - Message(self, u"created", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), CreatedPayload(), self.check_created, self.on_created), - Message(self, u"extend", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), ExtendPayload(), self.check_extend, self.on_extend), - Message(self, u"extended", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), ExtendedPayload(), self.check_extended, self.on_extended), - Message(self, u"ping", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), PingPayload(), self._generic_timeline_check, self.on_ping), - Message(self, u"pong", NoAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), PongPayload(), self.check_pong, self.on_pong), - Message(self, u"destroy", MemberAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), DestroyPayload(), self._generic_timeline_check, - self.on_destroy), - Message(self, u"stats-request", MemberAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), StatsRequestPayload(), self._generic_timeline_check, - self.on_stats_request), - Message(self, u"stats-response", MemberAuthentication(), PublicResolution(), DirectDistribution(), - CandidateDestination(), StatsResponsePayload(), self._generic_timeline_check, - self.on_stats_response)] + CandidateDestination(), CellPayload(), self._generic_timeline_check, + self.on_cell), + Message(self, u"create", NoAuthentication(), PublicResolution(), DirectDistribution(), + CandidateDestination(), CreatePayload(), self.check_create, self.on_create), + Message(self, u"created", NoAuthentication(), PublicResolution(), DirectDistribution(), + CandidateDestination(), CreatedPayload(), self.check_created, self.on_created), + Message(self, u"extend", NoAuthentication(), PublicResolution(), DirectDistribution(), + CandidateDestination(), ExtendPayload(), self.check_extend, self.on_extend), + Message(self, u"extended", NoAuthentication(), PublicResolution(), DirectDistribution(), + CandidateDestination(), ExtendedPayload(), self.check_extended, + self.on_extended), + Message(self, u"ping", NoAuthentication(), PublicResolution(), DirectDistribution(), + CandidateDestination(), PingPayload(), self._generic_timeline_check, + self.on_ping), + Message(self, u"pong", NoAuthentication(), PublicResolution(), DirectDistribution(), + CandidateDestination(), PongPayload(), self.check_pong, self.on_pong), + Message(self, u"destroy", MemberAuthentication(), PublicResolution(), + DirectDistribution(), CandidateDestination(), DestroyPayload(), + self._generic_timeline_check, self.on_destroy), + Message(self, u"stats-request", MemberAuthentication(), PublicResolution(), + DirectDistribution(), CandidateDestination(), StatsRequestPayload(), + self._generic_timeline_check, self.on_stats_request), + Message(self, u"stats-response", MemberAuthentication(), PublicResolution(), + DirectDistribution(), CandidateDestination(), StatsResponsePayload(), + self._generic_timeline_check, self.on_stats_response)] def initiate_conversions(self): return [DefaultConversion(self), TunnelConversion(self)] @@ -425,7 +429,7 @@ def do_remove(self): self.remove_exit_socket(circuit_id, 'too old') elif exit_socket.bytes_up + exit_socket.bytes_down > self.settings.max_traffic: self.remove_exit_socket(circuit_id, 'traffic limit exceeded') - + # Remove exit_candidates that are not returned as dispersy verified candidates current_candidates = {} for c in self.dispersy_yield_verified_candidates(): @@ -437,7 +441,6 @@ def do_remove(self): self.exit_candidates.pop(pubkey) logging.debug("Removed candidate from exit_candidates dictionary") - def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_retries=0, required_exit=None): assert required_exit is None or isinstance(required_exit, tuple), type(required_exit) assert required_exit is None or len(required_exit) == 3, required_exit @@ -463,7 +466,8 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ return False circuit_id = self._generate_circuit_id(first_hop.sock_addr) - circuit = Circuit(circuit_id, goal_hops, first_hop.sock_addr, self, ctype, callback, required_exit, first_hop._association.mid.encode('hex')) + circuit = Circuit(circuit_id, goal_hops, first_hop.sock_addr, self, ctype, callback, + required_exit, first_hop.get_member().mid.encode('hex')) self.request_cache.add(CircuitRequestCache(self, circuit, retry_lambda)) @@ -477,14 +481,11 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ self.circuits[circuit_id] = circuit self.waiting_for.add(circuit_id) - exit_candidates = circuit.goal_hops - 1 == len(circuit.hops) + 1 - self.increase_bytes_sent(circuit, self.send_cell([first_hop], u"create", (circuit_id, circuit.unverified_hop.node_id, circuit.unverified_hop.node_public_key, - circuit.unverified_hop.dh_first_part, - exit_candidates))) + circuit.unverified_hop.dh_first_part))) return True @@ -739,17 +740,26 @@ def _ours_on_created_extended(self, circuit, message): if circuit.state == CIRCUIT_STATE_EXTENDING: become_exit = circuit.goal_hops - 1 == len(circuit.hops) - exit_candidates = circuit.goal_hops - 1 == len(circuit.hops) + 1 - if become_exit and circuit.required_exit: - host, port, pub_key = circuit.required_exit - extend_hop_public_bin = pub_key - extend_hop_addr = (host, port) + if become_exit: + if circuit.required_exit: + host, port, pub_key = circuit.required_exit + extend_hop_public_bin = pub_key + extend_hop_addr = (host, port) + else: + extend_hop_public_bin = None + for c in self.dispersy_yield_verified_candidates(): + pubkey = c.get_member().public_key + exit_candidate = self.exit_candidates[pubkey] + if exit_candidate.become_exit: + wan = c._wan_address + extend_hop_public_bin = pubkey + extend_hop_addr = wan + break else: candidate_list_enc = message.payload.candidate_list _, candidate_list = decode(self.crypto.decrypt_str( candidate_list_enc, hop.session_keys[EXIT_NODE], hop.session_keys[EXIT_NODE_SALT])) - ignore_candidates = [self.crypto.key_to_bin(hop.public_key) for hop in circuit.hops] + \ [self.my_member.public_key] if circuit.required_exit: @@ -769,7 +779,8 @@ def _ours_on_created_extended(self, circuit, message): if extend_hop_public_bin: extend_hop_public_key = self.dispersy.crypto.key_from_public_bin(extend_hop_public_bin) circuit.unverified_hop = Hop(extend_hop_public_key) - circuit.unverified_hop.dh_secret, circuit.unverified_hop.dh_first_part = self.crypto.generate_diffie_secret() + circuit.unverified_hop.dh_secret, circuit.unverified_hop.dh_first_part = \ + self.crypto.generate_diffie_secret() self._logger.info( "extending circuit %d with %s", circuit.circuit_id, extend_hop_public_bin[:20].encode('hex')) @@ -779,8 +790,7 @@ def _ours_on_created_extended(self, circuit, message): circuit.unverified_hop.node_id, circuit.unverified_hop.node_public_key, extend_hop_addr, - circuit.unverified_hop.dh_first_part, - exit_candidates))) + circuit.unverified_hop.dh_first_part))) else: self.remove_circuit(circuit.circuit_id, "no candidates to extend, bailing out.") @@ -800,7 +810,6 @@ def _ours_on_created_extended(self, circuit, message): from Tribler.Core.simpledefs import NTFY_TUNNEL, NTFY_CREATED, NTFY_EXTENDED self.notifier.notify(NTFY_TUNNEL, NTFY_CREATED if len(circuit.hops) == 1 else NTFY_EXTENDED, circuit) - def on_introduction_request(self, messages): exitnode = self.settings.become_exitnode extra_payload = [exitnode] @@ -841,7 +850,8 @@ def on_cell(self, messages): packet = plaintext + encrypted self.dispersy.on_incoming_packets( - [(message.candidate, TunnelConversion.convert_from_cell(packet))], False, source=u"circuit_%d" % circuit_id) + [(message.candidate, TunnelConversion.convert_from_cell(packet))], False, + source=u"circuit_%d" % circuit_id) if circuit_id in self.circuits: self.circuits[circuit_id].beat_heart() @@ -871,12 +881,9 @@ def on_create(self, messages): candidates = {} for c in self.dispersy_yield_verified_candidates(): pubkey = c.get_member().public_key - exit_candidate = self.exit_candidates[pubkey] - if message.payload.exit_candidates and not (exit_candidate.become_exit and message.candidate.connection_type == u"public"): - # Next candidates need to be connectable exit nodes, and this candidate isn't - continue - if not message.payload.exit_candidates and (exit_candidate.become_exit): - # Reserve exit node candidates just for exiting data + vc = self.exit_candidates[pubkey] + if vc.become_exit: + # Exit nodes are chosen by the circuit initiator, don't add them continue candidates[pubkey] = c @@ -884,8 +891,8 @@ def on_create(self, messages): break self.request_cache.add(CreatedRequestCache(self, circuit_id, candidate, candidates)) - if candidate._association is not None: - candidate_mid = candidate._association.mid.encode('hex') + if candidate.get_member() is not None: + candidate_mid = candidate.get_member().mid.encode('hex') else: candidate_mid = 0 self.exit_sockets[circuit_id] = TunnelExitSocket(circuit_id, self, candidate.sock_addr, candidate_mid) @@ -932,7 +939,7 @@ def on_extend(self, messages): circuit_id = message.payload.circuit_id request = self.request_cache.pop(u"anon-created", circuit_id) - if request.candidates.has_key(message.payload.node_public_key): + if message.payload.node_public_key in request.candidates: extend_candidate = request.candidates[message.payload.node_public_key] else: extend_candidate = Candidate(message.payload.node_addr, False) @@ -945,16 +952,24 @@ def on_extend(self, messages): if circuit_id in self.relay_from_to: current_relay = self.relay_from_to.pop(circuit_id) - assert not current_relay.online, "shouldn't be called whenever relay is online the extend message should have been forwarded" + assert not current_relay.online, "shouldn't be called whenever relay is online the extend message \ + should have been forwarded" # We will just forget the attempt and try again, possibly with another candidate. del self.relay_from_to[current_relay.circuit_id] new_circuit_id = self._generate_circuit_id(extend_candidate.sock_addr) + if extend_candidate.get_member() is not None: + candidate_extend_mid = extend_candidate.get_member().mid.encode('hex') + else: + candidate_extend_mid = 0 + self.waiting_for.add(new_circuit_id) - self.relay_from_to[new_circuit_id] = RelayRoute(circuit_id, candidate.sock_addr, mid=candidate.get_member().mid.encode('hex')) - self.relay_from_to[circuit_id] = RelayRoute(new_circuit_id, extend_candidate.sock_addr, mid=extend_candidate.get_member().mid.encode('hex')) + self.relay_from_to[new_circuit_id] = RelayRoute(circuit_id, candidate.sock_addr, + mid=candidate.get_member().mid.encode('hex')) + self.relay_from_to[circuit_id] = RelayRoute(new_circuit_id, extend_candidate.sock_addr, + mid=candidate_extend_mid) self.relay_session_keys[new_circuit_id] = self.relay_session_keys[circuit_id] @@ -969,8 +984,7 @@ def on_extend(self, messages): u"create", (new_circuit_id, message.payload.node_id, message.payload.node_public_key, - message.payload.key, - message.payload.exit_candidates))) + message.payload.key))) def on_extended(self, messages): for message in messages: @@ -978,7 +992,6 @@ def on_extended(self, messages): circuit = self.circuits[circuit_id] self._ours_on_created_extended(circuit, message) - @call_on_reactor_thread def on_data(self, sock_addr, packet): # If its our circuit, the messenger is the candidate assigned to that circuit and the DATA's destination @@ -1008,7 +1021,8 @@ def on_data(self, sock_addr, packet): if TunnelConversion.could_be_dispersy(data): self._logger.error("Giving incoming data packet to dispersy") self.dispersy.on_incoming_packets( - [(Candidate(origin, False), data[TUNNEL_PREFIX_LENGHT:])], False, source=u"circuit_%d" % circuit_id) + [(Candidate(origin, False), data[TUNNEL_PREFIX_LENGHT:])], + False, source=u"circuit_%d" % circuit_id) else: anon_seed = self.circuits[circuit_id].ctype == CIRCUIT_TYPE_RP self.socks_server.on_incoming_from_tunnel(self, self.circuits[circuit_id], origin, data, anon_seed) @@ -1132,7 +1146,8 @@ def crypto_out(self, circuit_id, content, is_data=False): content = self.crypto.encrypt_str(content, *self.get_session_keys(hop.session_keys, EXIT_NODE)) return content elif circuit_id in self.relay_session_keys: - return self.crypto.encrypt_str(content, *self.get_session_keys(self.relay_session_keys[circuit_id], ORIGINATOR)) + return self.crypto.encrypt_str(content, + *self.get_session_keys(self.relay_session_keys[circuit_id], ORIGINATOR)) raise CryptoException("Don't know how to encrypt outgoing message for circuit_id %d" % circuit_id) def crypto_in(self, circuit_id, content, is_data=False): @@ -1149,41 +1164,52 @@ def crypto_in(self, circuit_id, content, is_data=False): content, circuit.hs_session_keys[direction], circuit.hs_session_keys[direction_salt]) return content elif circuit_id in self.relay_session_keys: - return self.crypto.decrypt_str(content, self.relay_session_keys[circuit_id][EXIT_NODE], self.relay_session_keys[circuit_id][EXIT_NODE_SALT]) + return self.crypto.decrypt_str(content, + self.relay_session_keys[circuit_id][EXIT_NODE], + self.relay_session_keys[circuit_id][EXIT_NODE_SALT]) raise CryptoException("Don't know how to decrypt incoming message for circuit_id %d" % circuit_id) def crypto_relay(self, circuit_id, content): direction = self.directions[circuit_id] if direction == ORIGINATOR: - return self.crypto.encrypt_str(content, *self.get_session_keys(self.relay_session_keys[circuit_id], ORIGINATOR)) + return self.crypto.encrypt_str(content, + *self.get_session_keys(self.relay_session_keys[circuit_id], ORIGINATOR)) elif direction == EXIT_NODE: - return self.crypto.decrypt_str(content, self.relay_session_keys[circuit_id][EXIT_NODE], self.relay_session_keys[circuit_id][EXIT_NODE_SALT]) + return self.crypto.decrypt_str(content, + self.relay_session_keys[circuit_id][EXIT_NODE], + self.relay_session_keys[circuit_id][EXIT_NODE_SALT]) raise CryptoException("Direction must be either ORIGINATOR or EXIT_NODE") def increase_bytes_sent(self, obj, num_bytes): if isinstance(obj, Circuit): obj.bytes_up += num_bytes self.stats['bytes_up'] += num_bytes - _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_SENT, obj.mid, num_bytes) + _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_SENT, + obj.mid, num_bytes) elif isinstance(obj, RelayRoute): obj.bytes_up += num_bytes self.stats['bytes_relay_up'] += num_bytes - _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_RELAY_BYTES_SENT, obj.mid, num_bytes) + _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_RELAY_BYTES_SENT, + obj.mid, num_bytes) elif isinstance(obj, TunnelExitSocket): obj.bytes_up += num_bytes self.stats['bytes_exit'] += num_bytes - _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_EXIT_BYTES_SENT, obj.mid, num_bytes) + _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_EXIT_BYTES_SENT, + obj.mid, num_bytes) def increase_bytes_received(self, obj, num_bytes): if isinstance(obj, Circuit): obj.bytes_down += num_bytes self.stats['bytes_down'] += num_bytes - _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_RECEIVED, obj.mid, num_bytes) + _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_BYTES_RECEIVED, + obj.mid, num_bytes) elif isinstance(obj, RelayRoute): obj.bytes_down += num_bytes self.stats['bytes_relay_down'] += num_bytes - _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_RELAY_BYTES_RECEIVED, obj.mid, num_bytes) + _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_RELAY_BYTES_RECEIVED, + obj.mid, num_bytes) elif isinstance(obj, TunnelExitSocket): obj.bytes_down += num_bytes self.stats['bytes_enter'] += num_bytes - _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_EXIT_BYTES_RECEIVED, obj.mid, num_bytes) + _barter_statistics.dict_inc_bartercast(BartercastStatisticTypes.TUNNELS_EXIT_BYTES_RECEIVED, + obj.mid, num_bytes) diff --git a/Tribler/dispersy b/Tribler/dispersy index fec55be1708..2c6efd6d266 160000 --- a/Tribler/dispersy +++ b/Tribler/dispersy @@ -1 +1 @@ -Subproject commit fec55be1708ac9fd70bdde9714f71e58f88d7697 +Subproject commit 2c6efd6d2661f96dc0180d0acdc96afac5590382 From 28976a36e7d72e37189480f356a6badb5801a483 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 9 Apr 2015 18:32:28 +0200 Subject: [PATCH 09/11] Fix connectability constraint on exit-node --- Tribler/Test/test_tunnel_community.py | 31 ++++++------------ Tribler/community/tunnel/tunnel_community.py | 34 +++++++++++++------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 87977d52288..9510537f3ee 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -5,8 +5,6 @@ from threading import Event from traceback import print_exc -from nose.tools import timed - from Tribler.Core.DecentralizedTracking.pymdht.core.identifier import Id from Tribler.Core.Utilities.twisted_thread import reactor from Tribler.Core.simpledefs import dlstatus_strings @@ -19,7 +17,6 @@ class TestTunnelCommunity(TestGuiAsServer): - @timed(120) def test_anon_download(self): def take_second_screenshot(): self.screenshot() @@ -66,7 +63,6 @@ def do_create_local_torrent(_): self.startTest(do_create_local_torrent) - @timed(180) def test_anon_download_without_relays(self): def take_second_screenshot(): self.screenshot() @@ -98,10 +94,8 @@ def check_progress(download, start_time): self.CallConditional(1, lambda: download.get_progress() == 0.0, lambda: on_success(download, start_time), - 'Anonymous download without relays should not have any progress (%.1f%% downloaded)' % ( - download.get_progress() * 100), - on_fail - ) + 'Anonymous download without relays should not have any progress (%.1f%% downloaded)' % + (download.get_progress() * 100), on_fail) def do_create_local_torrent(_): tf = self.setupSeeder() @@ -115,7 +109,6 @@ def do_create_local_torrent(_): self.startTest(do_create_local_torrent, nr_exitnodes=5, nr_relays=0) - @timed(180) def test_anon_download_without_exitnodes(self): def take_second_screenshot(): self.screenshot() @@ -147,10 +140,8 @@ def check_progress(download, start_time): self.CallConditional(1, lambda: download.get_progress() == 0.0, lambda: on_success(download, start_time), - 'Anonymous download without exit nodes should not have any progress (%.1f%% downloaded)' % ( - download.get_progress() * 100), - on_fail - ) + 'Anonymous download without exit nodes should not make progress (%.1f%% downloaded)' % + (download.get_progress() * 100), on_fail) def do_create_local_torrent(_): tf = self.setupSeeder() @@ -164,7 +155,6 @@ def do_create_local_torrent(_): self.startTest(do_create_local_torrent, nr_exitnodes=0, nr_relays=5) - @timed(120) def test_anon_download_exitnode_changeofmind(self): def changed_my_mind(tunnel_communities): @@ -175,7 +165,8 @@ def changed_my_mind(tunnel_communities): def compare_progress(lastprogress, download): progress = download.get_progress() self.assert_(progress == lastprogress, - "Expected no progress, but actual progress was progress=%s, lastprogress=%s" % (progress, lastprogress)) + "Expected no progress, but actual progress was progress=%s, lastprogress=%s" % (progress, + lastprogress)) self.quit() def check_progress(download, start_time): @@ -195,7 +186,6 @@ def do_create_local_torrent(tunnel_communities): self.startTest(do_create_local_torrent, nr_exitnodes=4, nr_relays=6) - @timed(120) def test_anon_tunnel(self): got_data = Event() this = self @@ -222,10 +212,10 @@ def start_test(tunnel_communities): def replace_socks(tunnel_communities): for tunnel_community in tunnel_communities: socks_server = tunnel_community.socks_server - socks_server.on_incoming_from_tunnel = lambda community, circuit, origin, data, socks_server = socks_server: on_incoming_from_tunnel( - socks_server, community, circuit, origin, data) - tunnel_community.exit_data = lambda circuit_id, sock_addr, destination, data, community = tunnel_community: exit_data( - community, circuit_id, sock_addr, destination, data) + socks_server.on_incoming_from_tunnel = lambda community, circuit, origin, data, \ + socks_server = socks_server: on_incoming_from_tunnel(socks_server, community, circuit, origin, data) + tunnel_community.exit_data = lambda circuit_id, sock_addr, destination, data, \ + community = tunnel_community: exit_data(community, circuit_id, sock_addr, destination, data) tunnel_communities[-1].circuits_needed[3] = 4 self.CallConditional(30, lambda: len(tunnel_communities[-1].active_data_circuits()) == 4, @@ -233,7 +223,6 @@ def replace_socks(tunnel_communities): self.startTest(replace_socks) - @timed(180) def test_hidden_services(self): def take_second_screenshot(): self.screenshot() diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index fe1baa53e43..eb7d5b1df5f 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -257,7 +257,7 @@ def initialize(self, tribler_session=None, settings=None): self.trsession = tribler_session self.settings = settings if settings else TunnelSettings(tribler_session=tribler_session) - self._logger.debug("TunnelCommunity: become_exitnode = %s" % settings.become_exitnode) + self._logger.debug("TunnelCommunity: setting become_exitnode = %s" % self.settings.become_exitnode) super(TunnelCommunity, self).initialize() @@ -281,6 +281,15 @@ def initialize(self, tribler_session=None, settings=None): self.notifier = Notifier.getInstance() self.trsession.lm.tunnel_community = self + def self_is_connectable(self): + return self._dispersy._connection_type == u"public" + + def candidate_is_connectable(self, candidate): + return candidate.connection_type == u"public" + + def become_exitnode(self): + return self.settings.become_exitnode + def start_download_test(self): if self.trsession and self.trsession.get_libtorrent() and self.settings.do_test: self.libtorrent_test = LibtorrentTest(self, self.trsession) @@ -431,10 +440,7 @@ def do_remove(self): self.remove_exit_socket(circuit_id, 'traffic limit exceeded') # Remove exit_candidates that are not returned as dispersy verified candidates - current_candidates = {} - for c in self.dispersy_yield_verified_candidates(): - pubkey = c.get_member().public_key - current_candidates[pubkey] = pubkey + current_candidates = set(c.get_member().public_key for c in self.dispersy_yield_verified_candidates()) ckeys = self.exit_candidates.keys() for pubkey in ckeys: if pubkey not in current_candidates: @@ -740,28 +746,32 @@ def _ours_on_created_extended(self, circuit, message): if circuit.state == CIRCUIT_STATE_EXTENDING: become_exit = circuit.goal_hops - 1 == len(circuit.hops) + ignore_candidates = [self.crypto.key_to_bin(hop.public_key) for hop in circuit.hops] + \ + [self.my_member.public_key] + if become_exit: if circuit.required_exit: + # Set the required exit according to the circuit setting (e.g. for linking e2e circuits) host, port, pub_key = circuit.required_exit extend_hop_public_bin = pub_key extend_hop_addr = (host, port) else: + # The exit node is a verified connectable exit node peer chosen by the circuit initiator extend_hop_public_bin = None for c in self.dispersy_yield_verified_candidates(): pubkey = c.get_member().public_key exit_candidate = self.exit_candidates[pubkey] - if exit_candidate.become_exit: + if exit_candidate.become_exit and self.candidate_is_connectable(c) and pubkey not in ignore_candidates: wan = c._wan_address extend_hop_public_bin = pubkey extend_hop_addr = wan break else: + # The next candidate is chosen from the returned list of possible candidates candidate_list_enc = message.payload.candidate_list _, candidate_list = decode(self.crypto.decrypt_str( candidate_list_enc, hop.session_keys[EXIT_NODE], hop.session_keys[EXIT_NODE_SALT])) - ignore_candidates = [self.crypto.key_to_bin(hop.public_key) for hop in circuit.hops] + \ - [self.my_member.public_key] if circuit.required_exit: ignore_candidates.append(circuit.required_exit[2]) for ignore_candidate in ignore_candidates: @@ -811,7 +821,7 @@ def _ours_on_created_extended(self, circuit, message): self.notifier.notify(NTFY_TUNNEL, NTFY_CREATED if len(circuit.hops) == 1 else NTFY_EXTENDED, circuit) def on_introduction_request(self, messages): - exitnode = self.settings.become_exitnode + exitnode = self.become_exitnode() extra_payload = [exitnode] super(TunnelCommunity, self).on_introduction_request(messages, extra_payload) for message in messages: @@ -819,7 +829,7 @@ def on_introduction_request(self, messages): self.exit_candidates[pubkey] = ExitCandidate(message.payload.exitnode) def create_introduction_request(self, destination, allow_sync, forward=True, is_fast_walker=False): - exitnode = self.settings.become_exitnode + exitnode = self.become_exitnode() extra_payload = [exitnode] super(TunnelCommunity, self).create_introduction_request(destination, allow_sync, forward, is_fast_walker, extra_payload) @@ -883,7 +893,7 @@ def on_create(self, messages): pubkey = c.get_member().public_key vc = self.exit_candidates[pubkey] if vc.become_exit: - # Exit nodes are chosen by the circuit initiator, don't add them + # Exit nodes are chosen by the circuit initiator, we decided not to use exit nodes as normal relay continue candidates[pubkey] = c @@ -1120,7 +1130,7 @@ def tunnel_data_to_origin(self, circuit_id, sock_addr, source_address, data): self.send_data([Candidate(sock_addr, False)], u'data', packet) def exit_data(self, circuit_id, sock_addr, destination, data): - if not self.settings.become_exitnode: + if not self.become_exitnode(): self._logger.error("Dropping data packets, refusing to be an exit node") elif circuit_id in self.exit_sockets: if not self.exit_sockets[circuit_id].enabled: From 0fb9c083e0aa185e9781e98257cf13757099cfa6 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Wed, 15 Apr 2015 12:50:15 +0200 Subject: [PATCH 10/11] moved choice for exitnode to create_circuits --- Tribler/community/tunnel/hidden_community.py | 2 +- Tribler/community/tunnel/tunnel_community.py | 54 ++++++++++++-------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Tribler/community/tunnel/hidden_community.py b/Tribler/community/tunnel/hidden_community.py index 7d3d5b03470..4f262843010 100644 --- a/Tribler/community/tunnel/hidden_community.py +++ b/Tribler/community/tunnel/hidden_community.py @@ -51,7 +51,7 @@ def __init__(self, community, rp): self.rp = rp def on_timeout(self): - self._logger.debug("RPRequestCache: no response on establish-rendezvous (circuit %d)", self.circuit.circuit_id) + self._logger.debug("RPRequestCache: no response on establish-rendezvous (circuit %d)", self.rp.circuit_id) self.community.remove_circuit(self.circuit.circuit_id, 'establish-rendezvous timeout') diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index eb7d5b1df5f..2a6b80213b3 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -463,12 +463,34 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ first_hop = c break + if not required_exit: + self._logger.debug("Look for connectable exit node to set as required_exit for this circuit") + # Each circuit's exit node should be a verified connectable exit node peer chosen by the circuit initiator + for c in self.dispersy_yield_verified_candidates(): + pubkey = c.get_member().public_key + exit_candidate = self.exit_candidates[pubkey] + if exit_candidate.become_exit and self.candidate_is_connectable(c): + self._logger.debug("Valid exit node found for this circuit") + required_exit = (c.sock_addr[0], c.sock_addr[1], pubkey) + # Stop looking for a better alternative if the exit-node is not used for exiting in another circuit + if c.sock_addr not in hops: + self._logger.debug("Exit node not used in other circuits, best choice") + break + + if not required_exit: + if retry_lambda: + self._logger.info("could not create circuit, no available exit-nodes found, will retry in 5 seconds.") + self.register_task(retry_lambda, reactor.callLater(5, retry_lambda)) + else: + self._logger.info("could not create circuit, no available exit-nodes.") + return False + if not first_hop: if retry_lambda: - self._logger.info("could not create circuit, no available hops will retry in 5 seconds.") + self._logger.info("could not create circuit, no available relay for first hop, will retry in 5 seconds.") self.register_task(retry_lambda, reactor.callLater(5, retry_lambda)) else: - self._logger.info("could not create circuit, no available hops.") + self._logger.info("could not create circuit, no available relay for first hop.") return False circuit_id = self._generate_circuit_id(first_hop.sock_addr) @@ -745,27 +767,15 @@ def _ours_on_created_extended(self, circuit, message): circuit.unverified_hop = None if circuit.state == CIRCUIT_STATE_EXTENDING: - become_exit = circuit.goal_hops - 1 == len(circuit.hops) ignore_candidates = [self.crypto.key_to_bin(hop.public_key) for hop in circuit.hops] + \ [self.my_member.public_key] - if become_exit: - if circuit.required_exit: - # Set the required exit according to the circuit setting (e.g. for linking e2e circuits) - host, port, pub_key = circuit.required_exit - extend_hop_public_bin = pub_key - extend_hop_addr = (host, port) - else: - # The exit node is a verified connectable exit node peer chosen by the circuit initiator - extend_hop_public_bin = None - for c in self.dispersy_yield_verified_candidates(): - pubkey = c.get_member().public_key - exit_candidate = self.exit_candidates[pubkey] - if exit_candidate.become_exit and self.candidate_is_connectable(c) and pubkey not in ignore_candidates: - wan = c._wan_address - extend_hop_public_bin = pubkey - extend_hop_addr = wan - break + 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) + host, port, pub_key = circuit.required_exit + extend_hop_public_bin = pub_key + extend_hop_addr = (host, port) else: # The next candidate is chosen from the returned list of possible candidates @@ -782,8 +792,8 @@ def _ours_on_created_extended(self, circuit, message): public_key = self.crypto.key_from_public_bin(candidate_list[i]) if not self.crypto.is_key_compatible(public_key): candidate_list.pop(i) - - extend_hop_public_bin = next(iter(candidate_list), None) + pub_key = next(iter(candidate_list), None) + extend_hop_public_bin = pub_key extend_hop_addr = None if extend_hop_public_bin: From b3439a49b0b514f73cdf1338211790169bc39541 Mon Sep 17 00:00:00 2001 From: Rob Ruigrok Date: Thu, 16 Apr 2015 13:42:16 +0200 Subject: [PATCH 11/11] Bugfixes and refactoring on circuit creation --- .../Core/Libtorrent/LibtorrentDownloadImpl.py | 14 +++++++++-- Tribler/Core/Libtorrent/LibtorrentMgr.py | 16 ------------- Tribler/Main/vwxGUI/home.py | 12 ++++++---- Tribler/Test/test_as_server.py | 2 ++ Tribler/Test/test_tunnel_community.py | 13 +++++++---- Tribler/community/tunnel/hidden_community.py | 23 ++++++++++--------- Tribler/community/tunnel/tunnel_community.py | 23 +++++++++++++++---- 7 files changed, 59 insertions(+), 44 deletions(-) diff --git a/Tribler/Core/Libtorrent/LibtorrentDownloadImpl.py b/Tribler/Core/Libtorrent/LibtorrentDownloadImpl.py index f9ede424227..c4e75f4acd4 100644 --- a/Tribler/Core/Libtorrent/LibtorrentDownloadImpl.py +++ b/Tribler/Core/Libtorrent/LibtorrentDownloadImpl.py @@ -266,7 +266,13 @@ def create_engine_wrapper(self, lm_network_engine_wrapper_created_callback, psta if not self.cew_scheduled: self.ltmgr = self.session.lm.ltmgr dht_ok = not isinstance(self.tdef, TorrentDefNoMetainfo) or self.ltmgr.is_dht_ready() - session_ok = self.ltmgr.tunnels_ready(self) == 1 + tunnel_community = self.ltmgr.trsession.lm.tunnel_community + if tunnel_community: + tunnels_ready = tunnel_community.tunnels_ready(self.get_hops(), self.get_def().is_anonymous()) + else: + tunnels_ready = 1 + + session_ok = tunnels_ready == 1 if not self.ltmgr or not dht_ok or not session_ok: self._logger.info(u"LTMGR/DHT/session not ready, rescheduling create_engine_wrapper") @@ -892,7 +898,11 @@ def network_get_state(self, usercallback, getpeerlist, sessioncalling=False): if self.dlstate != DLSTATUS_CIRCUITS: progress = self.progressbeforestop else: - progress = self.ltmgr.tunnels_ready(self) + tunnel_community = self.ltmgr.trsession.lm.tunnel_community + if tunnel_community: + progress = tunnel_community.tunnels_ready(self.get_hops(), self.get_def().is_anonymous()) + else: + progress = 1 ds = DownloadState(self, self.dlstate, self.error, progress) else: (status, stats, seeding_stats, logmsgs) = self.network_get_stats(getpeerlist) diff --git a/Tribler/Core/Libtorrent/LibtorrentMgr.py b/Tribler/Core/Libtorrent/LibtorrentMgr.py index 7317c94abcb..a9a2c2e85e2 100644 --- a/Tribler/Core/Libtorrent/LibtorrentMgr.py +++ b/Tribler/Core/Libtorrent/LibtorrentMgr.py @@ -126,22 +126,6 @@ def get_session(self, hops=0): return self.ltsessions[hops] - def tunnels_ready(self, download): - hops = download.get_hops() - if hops > 0: - tunnel_community = self.trsession.lm.tunnel_community - if tunnel_community: - if download.get_def().is_anonymous(): - current_hops = tunnel_community.circuits_needed.get(hops, 0) - tunnel_community.circuits_needed[hops] = max(1, current_hops) - return bool(tunnel_community.active_data_circuits(hops)) - else: - tunnel_community.circuits_needed[hops] = tunnel_community.settings.max_circuits - return min(1, len(tunnel_community.active_data_circuits(hops)) / - float(tunnel_community.settings.min_circuits)) - return 0 - return 1 - def shutdown(self): # Save DHT state dhtstate_file = open(os.path.join(self.trsession.get_state_dir(), DHTSTATE_FILENAME), 'w') diff --git a/Tribler/Main/vwxGUI/home.py b/Tribler/Main/vwxGUI/home.py index 80f76e19566..40d80d37e78 100644 --- a/Tribler/Main/vwxGUI/home.py +++ b/Tribler/Main/vwxGUI/home.py @@ -648,8 +648,8 @@ def AddComponents(self): self.circuit_list.InsertColumn(0, 'ID', wx.LIST_FORMAT_LEFT, 25) self.circuit_list.InsertColumn(1, 'Online', wx.LIST_FORMAT_RIGHT, 50) self.circuit_list.InsertColumn(2, 'Hops', wx.LIST_FORMAT_RIGHT, 45) - self.circuit_list.InsertColumn(3, u'Bytes \u2191', wx.LIST_FORMAT_RIGHT, 63) - self.circuit_list.InsertColumn(4, u'Bytes \u2193', wx.LIST_FORMAT_RIGHT, 63) + self.circuit_list.InsertColumn(3, u'Bytes \u2191', wx.LIST_FORMAT_RIGHT, 83) + self.circuit_list.InsertColumn(4, u'Bytes \u2193', wx.LIST_FORMAT_RIGHT, 83) self.circuit_list.InsertColumn(5, 'Uptime', wx.LIST_FORMAT_RIGHT, 54) self.circuit_list.setResizeColumn(0) self.circuit_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) @@ -660,7 +660,8 @@ def AddComponents(self): self.log_text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.BORDER_SIMPLE | wx.HSCROLL & wx.VSCROLL) self.log_text.SetEditable(False) self.log_text.Show(self.fullscreen) - self.num_circuits_label = wx.StaticText(self, -1, "You have 0 circuit(s); 0 relay(s); 0 exit socket(s)") + self.num_circuits_label = wx.StaticText(self, -1, "You have 0 circuit(s); 0 relay(s); \ + 0 exit socket(s); 0 candidate(s)") self.vSizer = wx.BoxSizer(wx.VERTICAL) self.vSizer.Add(self.circuit_list, 1, wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0) @@ -697,10 +698,11 @@ def OnUpdateCircuits(self, event): return if self.fullscreen: - self.num_circuits_label.SetLabel("You have %d circuit(s); %d relay(s); %d exit socket(s)" % + self.num_circuits_label.SetLabel("You have %d circuit(s); %d relay(s); %d exit socket(s); %d candidate(s)" % (len(self.tunnel_community.circuits), len(self.tunnel_community.relay_from_to), - len(self.tunnel_community.exit_sockets))) + len(self.tunnel_community.exit_sockets), + sum(1 for _ in self.tunnel_community.dispersy_yield_verified_candidates()))) new_circuits = dict(self.tunnel_community.circuits) self.circuits = {k: v for k, v in new_circuits.iteritems() if v.goal_hops == self.hops or self.hops < 0} diff --git a/Tribler/Test/test_as_server.py b/Tribler/Test/test_as_server.py index f681982a648..e899161aa0d 100644 --- a/Tribler/Test/test_as_server.py +++ b/Tribler/Test/test_as_server.py @@ -99,6 +99,7 @@ def tearDown(self, annotate=True): for dc in delayed_calls: self._logger.debug("> %s" % dc) self.assertFalse(delayed_calls, "The reactor was dirty when tearing down the test") + self.assertFalse(Session.has_instance(), 'A session instance is still present when tearing down the test') def tearDownCleanup(self): self.setUpCleanup() @@ -301,6 +302,7 @@ class TestGuiAsServer(TestAsServer): """ def setUp(self): + self.assertFalse(Session.has_instance(), 'A session instance is already present when setting up the test') AbstractServer.setUp(self, annotate=False) self.app = wx.GetApp() diff --git a/Tribler/Test/test_tunnel_community.py b/Tribler/Test/test_tunnel_community.py index 9510537f3ee..04d5ac2bf40 100644 --- a/Tribler/Test/test_tunnel_community.py +++ b/Tribler/Test/test_tunnel_community.py @@ -13,6 +13,7 @@ from Tribler.dispersy.util import blockingCallFromThread from Tribler.community.tunnel.tunnel_community import TunnelSettings from Tribler.community.tunnel.hidden_community import HiddenTunnelCommunity +from Tribler.dispersy.crypto import NoCrypto class TestTunnelCommunity(TestGuiAsServer): @@ -328,9 +329,9 @@ def cb_dht(info_hash, peers, source): self.CallConditional(60, dht.is_set, lambda: self.Call(5, lambda: start_download(tf)), 'Introduction point did not get announced') - self.startTest(setup_seeder) + self.startTest(setup_seeder, nr_relays=6, nr_exitnodes=4) - def startTest(self, callback, min_timeout=5, nr_relays=5, nr_exitnodes=3): + def startTest(self, callback, min_timeout=5, nr_relays=5, nr_exitnodes=3, crypto_enabled=True): from Tribler.Main import tribler_main tribler_main.FORCE_ENABLE_TUNNEL_COMMUNITY = True tribler_main.TUNNEL_COMMUNITY_DO_TEST = False @@ -341,11 +342,11 @@ def setup_proxies(): tunnel_communities = [] baseindex = 3 for i in range(baseindex, baseindex + nr_relays): # Normal relays - tunnel_communities.append(create_proxy(i, False)) + tunnel_communities.append(create_proxy(i, False, crypto_enabled)) baseindex += nr_relays + 1 for i in range(baseindex, baseindex + nr_exitnodes): # Exit nodes - tunnel_communities.append(create_proxy(i, True)) + tunnel_communities.append(create_proxy(i, True, crypto_enabled)) # Connect the proxies to the Tribler instance for community in self.lm.dispersy.get_communities(): @@ -367,7 +368,7 @@ def setup_proxies(): callback(tunnel_communities) - def create_proxy(index, become_exit_node): + def create_proxy(index, become_exit_node, crypto_enabled): from Tribler.Core.Session import Session self.setUpPreSession() @@ -393,6 +394,8 @@ def load_community(session): dispersy_member = dispersy.get_member(private_key=dispersy.crypto.key_to_bin(keypair)) settings = TunnelSettings(tribler_session=session) settings.do_test = False + if not crypto_enabled: + settings.crypto = NoCrypto() settings.become_exitnode = become_exit_node return dispersy.define_auto_load(HiddenTunnelCommunity, dispersy_member, (session, settings), load=True)[0] diff --git a/Tribler/community/tunnel/hidden_community.py b/Tribler/community/tunnel/hidden_community.py index 4f262843010..4d2513ef8fa 100644 --- a/Tribler/community/tunnel/hidden_community.py +++ b/Tribler/community/tunnel/hidden_community.py @@ -15,7 +15,7 @@ from Tribler.community.tunnel.payload import ( EstablishIntroPayload, IntroEstablishedPayload, EstablishRendezvousPayload, - RendezvousEstablishedPayload, KeyResponsePayload, KeyRequestPayload, + RendezvousEstablishedPayload, KeyResponsePayload, KeyRequestPayload, CreateE2EPayload, CreatedE2EPayload, LinkE2EPayload, LinkedE2EPayload) from Tribler.community.tunnel.routing import RelayRoute, RendezvousPoint, Hop @@ -51,8 +51,9 @@ def __init__(self, community, rp): self.rp = rp def on_timeout(self): - self._logger.debug("RPRequestCache: no response on establish-rendezvous (circuit %d)", self.rp.circuit_id) - self.community.remove_circuit(self.circuit.circuit_id, 'establish-rendezvous timeout') + self._logger.debug("RPRequestCache: no response on establish-rendezvous (circuit %d)", + self.rp.circuit.circuit_id) + self.community.remove_circuit(self.rp.circuit.circuit_id, 'establish-rendezvous timeout') class KeyRequestCache(RandomNumberCache): @@ -251,11 +252,11 @@ def check_key_request(self, messages): for message in messages: info_hash = message.payload.info_hash if not message.source.startswith(u"circuit_"): - if not info_hash in self.intro_point_for: + if info_hash not in self.intro_point_for: yield DropMessage(message, "not an intro point for this infohash") continue else: - if not info_hash in self.session_keys: + if info_hash not in self.session_keys: yield DropMessage(message, "not seeding this infohash") continue @@ -314,18 +315,18 @@ def on_create_e2e(self, messages): relay_circuit.tunnel_data(message.candidate.sock_addr, TUNNEL_PREFIX + message.packet) else: - self.create_rendevous_point( - DEFAULT_HOPS, lambda rendevous_point, message=message: self.create_created_e2e(rendevous_point, message)) + self.create_rendezvous_point( + DEFAULT_HOPS, lambda rendezvous_point, message=message: self.create_created_e2e(rendezvous_point, message)) - def create_created_e2e(self, rendevous_point, message): + def create_created_e2e(self, rendezvous_point, message): info_hash = message.payload.info_hash key = self.session_keys[info_hash] circuit = self.circuits[int(message.source[8:])] shared_secret, Y, AUTH = self.crypto.generate_diffie_shared_secret(message.payload.key, key) - rendevous_point.circuit.hs_session_keys = self.crypto.generate_session_keys(shared_secret) + rendezvous_point.circuit.hs_session_keys = self.crypto.generate_session_keys(shared_secret) rp_info_enc = self.crypto.encrypt_str( - encode((rendevous_point.rp_info, rendevous_point.cookie)), *self.get_session_keys(rendevous_point.circuit.hs_session_keys, EXIT_NODE)) + encode((rendezvous_point.rp_info, rendezvous_point.cookie)), *self.get_session_keys(rendezvous_point.circuit.hs_session_keys, EXIT_NODE)) meta = self.get_meta_message(u'created-e2e') response = meta.impl(distribution=(self.global_time,), payload=( @@ -477,7 +478,7 @@ def on_intro_established(self, messages): self.request_cache.pop(u"establish-intro", message.payload.identifier) self._logger.info("Got intro-established from %s", message.candidate) - def create_rendevous_point(self, hops, finished_callback): + def create_rendezvous_point(self, hops, finished_callback): def callback(circuit): # We got a circuit, now let's create a rendezvous point circuit_id = circuit.circuit_id diff --git a/Tribler/community/tunnel/tunnel_community.py b/Tribler/community/tunnel/tunnel_community.py index 2a6b80213b3..397253ea0e0 100644 --- a/Tribler/community/tunnel/tunnel_community.py +++ b/Tribler/community/tunnel/tunnel_community.py @@ -39,6 +39,7 @@ from Tribler.dispersy.util import call_on_reactor_thread from Tribler.dispersy.requestcache import NumberCache, RandomNumberCache from Tribler.community.bartercast4.statistics import BartercastStatisticTypes, _barter_statistics +from Tribler.Core.CacheDB.sqlitecachedb import bin2str class CircuitRequestCache(NumberCache): @@ -413,6 +414,17 @@ def do_circuits(self): self.do_remove() + def tunnels_ready(self, hops, anonymous): + if hops > 0: + if anonymous: + current_hops = self.circuits_needed.get(hops, 0) + self.circuits_needed[hops] = max(1, current_hops) + return bool(self.active_data_circuits(hops)) + else: + self.circuits_needed[hops] = self.settings.max_circuits + return min(1, len(self.active_data_circuits(hops)) / float(self.settings.min_circuits)) + return 1 + def do_remove(self): # Remove circuits that are inactive / are too old / have transferred too many bytes. for key, circuit in self.circuits.items(): @@ -464,17 +476,17 @@ def create_circuit(self, goal_hops, ctype=CIRCUIT_TYPE_DATA, callback=None, max_ break if not required_exit: - self._logger.debug("Look for connectable exit node to set as required_exit for this circuit") + self._logger.debug("Look for exit node to set as required_exit for this circuit") # Each circuit's exit node should be a verified connectable exit node peer chosen by the circuit initiator for c in self.dispersy_yield_verified_candidates(): pubkey = c.get_member().public_key exit_candidate = self.exit_candidates[pubkey] - if exit_candidate.become_exit and self.candidate_is_connectable(c): - self._logger.debug("Valid exit node found for this circuit") + if exit_candidate.become_exit: + self._logger.debug("Valid exit candidate found for this circuit") required_exit = (c.sock_addr[0], c.sock_addr[1], pubkey) # Stop looking for a better alternative if the exit-node is not used for exiting in another circuit - if c.sock_addr not in hops: - self._logger.debug("Exit node not used in other circuits, best choice") + if c.sock_addr not in hops and self.candidate_is_connectable(c): + self._logger.debug("Exit node is connectable and not used in other circuits, that's prefered") break if not required_exit: @@ -1175,6 +1187,7 @@ def crypto_in(self, circuit_id, content, is_data=False): if circuit and len(circuit.hops) > 0: # Remove all the encryption layers for hop in self.circuits[circuit_id].hops: + self._logger.debug("Decrypting encryption layer for hop %s in circuit %s" % (hop.address, circuit_id)) content = self.crypto.decrypt_str( content, hop.session_keys[ORIGINATOR], hop.session_keys[ORIGINATOR_SALT]) if circuit and is_data and circuit.ctype in [CIRCUIT_TYPE_RENDEZVOUS, CIRCUIT_TYPE_RP]: