Skip to content

Commit 1a40c29

Browse files
committed
Add Tor hidden service support
- PyBitmessage can now run as a hidden service on Tor - three new variables in keys.dat: onionhostname, onionport, onionbindip - you need to manually add a hidden service to tor
1 parent 33991f4 commit 1a40c29

File tree

6 files changed

+53
-16
lines changed

6 files changed

+53
-16
lines changed

‎src/class_outgoingSynSender.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ def _getPeer(self):
5050
if (random.random() <= priority):
5151
break
5252
time.sleep(0.01) # prevent CPU hogging if something is broken
53-
return peer
53+
try:
54+
return peer
55+
except NameError:
56+
return shared.Peer('127.0.0.1', 8444)
5457

5558
def stopThread(self):
5659
super(outgoingSynSender, self).stopThread()

‎src/class_receiveDataThread.py‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,10 +601,19 @@ def sendaddr(self):
601601
# the right child stream.
602602
with shared.knownNodesLock:
603603
if len(shared.knownNodes[self.streamNumber]) > 0:
604+
ownPosition = random.randint(0, 499)
605+
sentOwn = False
604606
for i in range(500):
605-
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
607+
# if current connection is over a proxy, sent our own onion address at a random position
608+
if ownPosition == i and ".onion" in shared.config.get("bitmessagesettings", "onionhostname") and self.sock.getproxytype() != 0 and not sentOwn:
609+
peer = shared.Peer(shared.config.get("bitmessagesettings", "onionhostname"), shared.config.getint("bitmessagesettings", "onionport"))
610+
else:
611+
# still may contain own onion address, but we don't change it
612+
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
606613
if isHostInPrivateIPRange(peer.host):
607614
continue
615+
if shared.config.get("bitmessagesettings", "onionhostname") == peer.host:
616+
sentOwn = True
608617
addrsInMyStream[peer] = shared.knownNodes[
609618
self.streamNumber][peer]
610619
if len(shared.knownNodes[self.streamNumber * 2]) > 0:

‎src/class_singleListener.py‎

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ def setup(self, selfInitiatedConnections):
2727

2828
def _createListenSocket(self, family):
2929
HOST = '' # Symbolic name meaning all available interfaces
30+
# If not sockslisten, but onionhostname defined, only listen on localhost
31+
if not shared.safeConfigGetBoolean('bitmessagesettings', 'sockslisten') and ".onion" in shared.config.get('bitmessagesettings', 'onionhostname'):
32+
HOST = shared.config.get('bitmessagesettings', 'onionbindip')
3033
PORT = shared.config.getint('bitmessagesettings', 'port')
3134
sock = socket.socket(family, socket.SOCK_STREAM)
3235
if family == socket.AF_INET6:
@@ -43,12 +46,14 @@ def _createListenSocket(self, family):
4346
def stopThread(self):
4447
super(singleListener, self).stopThread()
4548
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
46-
try:
47-
s.connect(('127.0.0.1', shared.config.getint('bitmessagesettings', 'port')))
48-
s.shutdown(socket.SHUT_RDWR)
49-
s.close()
50-
except:
51-
pass
49+
for ip in ('127.0.0.1', shared.config.get('bitmessagesettings', 'onionbindip')):
50+
try:
51+
s.connect((ip, shared.config.getint('bitmessagesettings', 'port')))
52+
s.shutdown(socket.SHUT_RDWR)
53+
s.close()
54+
break
55+
except:
56+
pass
5257

5358
def run(self):
5459
# If there is a trusted peer then we don't want to accept
@@ -62,8 +67,12 @@ def run(self):
6267
# We typically don't want to accept incoming connections if the user is using a
6368
# SOCKS proxy, unless they have configured otherwise. If they eventually select
6469
# proxy 'none' or configure SOCKS listening then this will start listening for
65-
# connections.
66-
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
70+
# connections. But if on SOCKS and have an onionhostname, listen
71+
# (socket is then only opened for localhost)
72+
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \
73+
(not shared.config.getboolean('bitmessagesettings', 'sockslisten') and \
74+
".onion" not in shared.config.get('bitmessagesettings', 'onionhostname')) and \
75+
shared.shutdown == 0:
6776
self.stop.wait(5)
6877

6978
logger.info('Listening for incoming connections.')
@@ -77,6 +86,7 @@ def run(self):
7786
if (isinstance(e.args, tuple) and
7887
e.args[0] in (errno.EAFNOSUPPORT,
7988
errno.EPFNOSUPPORT,
89+
errno.EADDRNOTAVAIL,
8090
errno.ENOPROTOOPT)):
8191
sock = self._createListenSocket(socket.AF_INET)
8292
else:
@@ -90,7 +100,7 @@ def run(self):
90100
# SOCKS proxy, unless they have configured otherwise. If they eventually select
91101
# proxy 'none' or configure SOCKS listening then this will start listening for
92102
# connections.
93-
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
103+
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and ".onion" not in shared.config.get('bitmessagesettings', 'onionhostname') and shared.shutdown == 0:
94104
self.stop.wait(10)
95105
while len(shared.connectedHostsList) > 220 and shared.shutdown == 0:
96106
logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.')
@@ -112,7 +122,9 @@ def run(self):
112122
# is already connected because the two computers will
113123
# share the same external IP. This is here to prevent
114124
# connection flooding.
115-
if HOST in shared.connectedHostsList:
125+
# permit repeated connections from Tor
126+
# FIXME: sockshostname may be a hostname rather than IP, in such a case this will break
127+
if HOST in shared.connectedHostsList and (".onion" not in shared.config.get('bitmessagesettings', 'onionhostname') or HOST != shared.config.get('bitmessagesettings', 'sockshostname')):
116128
socketObject.close()
117129
logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.')
118130
else:

‎src/class_sqlThread.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,13 @@ def run(self):
419419
logger.debug('In messages.dat database, done adding address field to the pubkeys table and removing the hash field.')
420420
self.cur.execute('''update settings set value=10 WHERE key='version';''')
421421

422+
if not shared.config.has_option('bitmessagesettings', 'onionhostname'):
423+
shared.config.set('bitmessagesettings', 'onionhostname', '')
424+
if not shared.config.has_option('bitmessagesettings', 'onionport'):
425+
shared.config.set('bitmessagesettings', 'onionport', '8444')
426+
if not shared.config.has_option('bitmessagesettings', 'onionbindip'):
427+
shared.config.set('bitmessagesettings', 'onionbindip', '127.0.0.1')
428+
shared.writeKeysFile()
422429

423430
# Are you hoping to add a new option to the keys.dat file of existing
424431
# Bitmessage users or modify the SQLite database? Add it right above this line!

‎src/helper_bootstrap.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ def knownNodes():
2121
shared.knownNodes[stream] = {}
2222
for node_tuple in nodes.items():
2323
try:
24-
host, (port, time) = node_tuple
24+
host, (port, lastseen) = node_tuple
2525
peer = shared.Peer(host, port)
2626
except:
27-
peer, time = node_tuple
28-
shared.knownNodes[stream][peer] = time
27+
peer, lastseen = node_tuple
28+
shared.knownNodes[stream][peer] = lastseen
2929
except:
3030
shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata)
31+
# your own onion address, if setup
32+
if shared.config.has_option('bitmessagesettings', 'onionhostname') and ".onion" in shared.config.get('bitmessagesettings', 'onionhostname'):
33+
shared.knownNodes[1][shared.Peer(shared.config.get('bitmessagesettings', 'onionhostname'), shared.config.getint('bitmessagesettings', 'onionport'))] = int(time.time())
3134
if shared.config.getint('bitmessagesettings', 'settingsversion') > 10:
3235
logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.')
3336
raise SystemExit

‎src/socks/__init__.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ def getpeername(self):
299299
"""
300300
return self.__proxypeername
301301

302+
def getproxytype(self):
303+
return self.__proxy[0]
304+
302305
def __negotiatesocks4(self,destaddr,destport):
303306
"""__negotiatesocks4(self,destaddr,destport)
304307
Negotiates a connection through a SOCKS4 server.
@@ -425,4 +428,4 @@ def resolve(self, host):
425428
self.__negotiatesocks5()
426429
return self.__resolvesocks5(host)
427430
else:
428-
return None
431+
return None

0 commit comments

Comments
 (0)