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..885d1b54f02 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 = 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" % (