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
This commit is contained in:
parent
33991f4598
commit
1a40c29d22
|
@ -50,7 +50,10 @@ class outgoingSynSender(threading.Thread, StoppableThread):
|
||||||
if (random.random() <= priority):
|
if (random.random() <= priority):
|
||||||
break
|
break
|
||||||
time.sleep(0.01) # prevent CPU hogging if something is broken
|
time.sleep(0.01) # prevent CPU hogging if something is broken
|
||||||
return peer
|
try:
|
||||||
|
return peer
|
||||||
|
except NameError:
|
||||||
|
return shared.Peer('127.0.0.1', 8444)
|
||||||
|
|
||||||
def stopThread(self):
|
def stopThread(self):
|
||||||
super(outgoingSynSender, self).stopThread()
|
super(outgoingSynSender, self).stopThread()
|
||||||
|
|
|
@ -601,10 +601,19 @@ class receiveDataThread(threading.Thread):
|
||||||
# the right child stream.
|
# the right child stream.
|
||||||
with shared.knownNodesLock:
|
with shared.knownNodesLock:
|
||||||
if len(shared.knownNodes[self.streamNumber]) > 0:
|
if len(shared.knownNodes[self.streamNumber]) > 0:
|
||||||
|
ownPosition = random.randint(0, 499)
|
||||||
|
sentOwn = False
|
||||||
for i in range(500):
|
for i in range(500):
|
||||||
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
|
# if current connection is over a proxy, sent our own onion address at a random position
|
||||||
|
if ownPosition == i and ".onion" in shared.config.get("bitmessagesettings", "onionhostname") and self.sock.getproxytype() != 0 and not sentOwn:
|
||||||
|
peer = shared.Peer(shared.config.get("bitmessagesettings", "onionhostname"), shared.config.getint("bitmessagesettings", "onionport"))
|
||||||
|
else:
|
||||||
|
# still may contain own onion address, but we don't change it
|
||||||
|
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
|
||||||
if isHostInPrivateIPRange(peer.host):
|
if isHostInPrivateIPRange(peer.host):
|
||||||
continue
|
continue
|
||||||
|
if shared.config.get("bitmessagesettings", "onionhostname") == peer.host:
|
||||||
|
sentOwn = True
|
||||||
addrsInMyStream[peer] = shared.knownNodes[
|
addrsInMyStream[peer] = shared.knownNodes[
|
||||||
self.streamNumber][peer]
|
self.streamNumber][peer]
|
||||||
if len(shared.knownNodes[self.streamNumber * 2]) > 0:
|
if len(shared.knownNodes[self.streamNumber * 2]) > 0:
|
||||||
|
|
|
@ -27,6 +27,9 @@ class singleListener(threading.Thread, StoppableThread):
|
||||||
|
|
||||||
def _createListenSocket(self, family):
|
def _createListenSocket(self, family):
|
||||||
HOST = '' # Symbolic name meaning all available interfaces
|
HOST = '' # Symbolic name meaning all available interfaces
|
||||||
|
# If not sockslisten, but onionhostname defined, only listen on localhost
|
||||||
|
if not shared.safeConfigGetBoolean('bitmessagesettings', 'sockslisten') and ".onion" in shared.config.get('bitmessagesettings', 'onionhostname'):
|
||||||
|
HOST = shared.config.get('bitmessagesettings', 'onionbindip')
|
||||||
PORT = shared.config.getint('bitmessagesettings', 'port')
|
PORT = shared.config.getint('bitmessagesettings', 'port')
|
||||||
sock = socket.socket(family, socket.SOCK_STREAM)
|
sock = socket.socket(family, socket.SOCK_STREAM)
|
||||||
if family == socket.AF_INET6:
|
if family == socket.AF_INET6:
|
||||||
|
@ -43,12 +46,14 @@ class singleListener(threading.Thread, StoppableThread):
|
||||||
def stopThread(self):
|
def stopThread(self):
|
||||||
super(singleListener, self).stopThread()
|
super(singleListener, self).stopThread()
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
try:
|
for ip in ('127.0.0.1', shared.config.get('bitmessagesettings', 'onionbindip')):
|
||||||
s.connect(('127.0.0.1', shared.config.getint('bitmessagesettings', 'port')))
|
try:
|
||||||
s.shutdown(socket.SHUT_RDWR)
|
s.connect((ip, shared.config.getint('bitmessagesettings', 'port')))
|
||||||
s.close()
|
s.shutdown(socket.SHUT_RDWR)
|
||||||
except:
|
s.close()
|
||||||
pass
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# If there is a trusted peer then we don't want to accept
|
# If there is a trusted peer then we don't want to accept
|
||||||
|
@ -62,8 +67,12 @@ class singleListener(threading.Thread, StoppableThread):
|
||||||
# We typically don't want to accept incoming connections if the user is using a
|
# We typically don't want to accept incoming connections if the user is using a
|
||||||
# SOCKS proxy, unless they have configured otherwise. If they eventually select
|
# SOCKS proxy, unless they have configured otherwise. If they eventually select
|
||||||
# proxy 'none' or configure SOCKS listening then this will start listening for
|
# proxy 'none' or configure SOCKS listening then this will start listening for
|
||||||
# connections.
|
# connections. But if on SOCKS and have an onionhostname, listen
|
||||||
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
|
# (socket is then only opened for localhost)
|
||||||
|
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:
|
||||||
self.stop.wait(5)
|
self.stop.wait(5)
|
||||||
|
|
||||||
logger.info('Listening for incoming connections.')
|
logger.info('Listening for incoming connections.')
|
||||||
|
@ -77,6 +86,7 @@ class singleListener(threading.Thread, StoppableThread):
|
||||||
if (isinstance(e.args, tuple) and
|
if (isinstance(e.args, tuple) and
|
||||||
e.args[0] in (errno.EAFNOSUPPORT,
|
e.args[0] in (errno.EAFNOSUPPORT,
|
||||||
errno.EPFNOSUPPORT,
|
errno.EPFNOSUPPORT,
|
||||||
|
errno.EADDRNOTAVAIL,
|
||||||
errno.ENOPROTOOPT)):
|
errno.ENOPROTOOPT)):
|
||||||
sock = self._createListenSocket(socket.AF_INET)
|
sock = self._createListenSocket(socket.AF_INET)
|
||||||
else:
|
else:
|
||||||
|
@ -90,7 +100,7 @@ class singleListener(threading.Thread, StoppableThread):
|
||||||
# SOCKS proxy, unless they have configured otherwise. If they eventually select
|
# SOCKS proxy, unless they have configured otherwise. If they eventually select
|
||||||
# proxy 'none' or configure SOCKS listening then this will start listening for
|
# proxy 'none' or configure SOCKS listening then this will start listening for
|
||||||
# connections.
|
# connections.
|
||||||
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
|
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:
|
||||||
self.stop.wait(10)
|
self.stop.wait(10)
|
||||||
while len(shared.connectedHostsList) > 220 and shared.shutdown == 0:
|
while len(shared.connectedHostsList) > 220 and shared.shutdown == 0:
|
||||||
logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.')
|
logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.')
|
||||||
|
@ -112,7 +122,9 @@ class singleListener(threading.Thread, StoppableThread):
|
||||||
# is already connected because the two computers will
|
# is already connected because the two computers will
|
||||||
# share the same external IP. This is here to prevent
|
# share the same external IP. This is here to prevent
|
||||||
# connection flooding.
|
# connection flooding.
|
||||||
if HOST in shared.connectedHostsList:
|
# permit repeated connections from Tor
|
||||||
|
# FIXME: sockshostname may be a hostname rather than IP, in such a case this will break
|
||||||
|
if HOST in shared.connectedHostsList and (".onion" not in shared.config.get('bitmessagesettings', 'onionhostname') or HOST != shared.config.get('bitmessagesettings', 'sockshostname')):
|
||||||
socketObject.close()
|
socketObject.close()
|
||||||
logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.')
|
logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.')
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -419,6 +419,13 @@ class sqlThread(threading.Thread):
|
||||||
logger.debug('In messages.dat database, done adding address field to the pubkeys table and removing the hash field.')
|
logger.debug('In messages.dat database, done adding address field to the pubkeys table and removing the hash field.')
|
||||||
self.cur.execute('''update settings set value=10 WHERE key='version';''')
|
self.cur.execute('''update settings set value=10 WHERE key='version';''')
|
||||||
|
|
||||||
|
if not shared.config.has_option('bitmessagesettings', 'onionhostname'):
|
||||||
|
shared.config.set('bitmessagesettings', 'onionhostname', '')
|
||||||
|
if not shared.config.has_option('bitmessagesettings', 'onionport'):
|
||||||
|
shared.config.set('bitmessagesettings', 'onionport', '8444')
|
||||||
|
if not shared.config.has_option('bitmessagesettings', 'onionbindip'):
|
||||||
|
shared.config.set('bitmessagesettings', 'onionbindip', '127.0.0.1')
|
||||||
|
shared.writeKeysFile()
|
||||||
|
|
||||||
# Are you hoping to add a new option to the keys.dat file of existing
|
# Are you hoping to add a new option to the keys.dat file of existing
|
||||||
# Bitmessage users or modify the SQLite database? Add it right above this line!
|
# Bitmessage users or modify the SQLite database? Add it right above this line!
|
||||||
|
|
|
@ -21,13 +21,16 @@ def knownNodes():
|
||||||
shared.knownNodes[stream] = {}
|
shared.knownNodes[stream] = {}
|
||||||
for node_tuple in nodes.items():
|
for node_tuple in nodes.items():
|
||||||
try:
|
try:
|
||||||
host, (port, time) = node_tuple
|
host, (port, lastseen) = node_tuple
|
||||||
peer = shared.Peer(host, port)
|
peer = shared.Peer(host, port)
|
||||||
except:
|
except:
|
||||||
peer, time = node_tuple
|
peer, lastseen = node_tuple
|
||||||
shared.knownNodes[stream][peer] = time
|
shared.knownNodes[stream][peer] = lastseen
|
||||||
except:
|
except:
|
||||||
shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata)
|
shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata)
|
||||||
|
# your own onion address, if setup
|
||||||
|
if shared.config.has_option('bitmessagesettings', 'onionhostname') and ".onion" in shared.config.get('bitmessagesettings', 'onionhostname'):
|
||||||
|
shared.knownNodes[1][shared.Peer(shared.config.get('bitmessagesettings', 'onionhostname'), shared.config.getint('bitmessagesettings', 'onionport'))] = int(time.time())
|
||||||
if shared.config.getint('bitmessagesettings', 'settingsversion') > 10:
|
if shared.config.getint('bitmessagesettings', 'settingsversion') > 10:
|
||||||
logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.')
|
logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.')
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
|
@ -299,6 +299,9 @@ class socksocket(socket.socket):
|
||||||
"""
|
"""
|
||||||
return self.__proxypeername
|
return self.__proxypeername
|
||||||
|
|
||||||
|
def getproxytype(self):
|
||||||
|
return self.__proxy[0]
|
||||||
|
|
||||||
def __negotiatesocks4(self,destaddr,destport):
|
def __negotiatesocks4(self,destaddr,destport):
|
||||||
"""__negotiatesocks4(self,destaddr,destport)
|
"""__negotiatesocks4(self,destaddr,destport)
|
||||||
Negotiates a connection through a SOCKS4 server.
|
Negotiates a connection through a SOCKS4 server.
|
||||||
|
|
Reference in New Issue
Block a user