DNS bootstrap over Tor

If proxy type is SOCKS5, it will try to perform DNS bootstrap using the
Tor RESOLVE extension.
This commit is contained in:
mailchuck 2015-11-26 23:45:44 +01:00 committed by Peter Surda
parent 1e89616c0f
commit d75533c6b6
2 changed files with 94 additions and 16 deletions

View File

@ -5,6 +5,7 @@ import pickle
import time import time
from debug import logger from debug import logger
import socks
def knownNodes(): def knownNodes():
try: try:
@ -37,7 +38,6 @@ def dns():
# defaultKnownNodes.py. Hopefully either they are up to date or the user # defaultKnownNodes.py. Hopefully either they are up to date or the user
# has run Bitmessage recently without SOCKS turned on and received good # has run Bitmessage recently without SOCKS turned on and received good
# bootstrap nodes using that method. # bootstrap nodes using that method.
with shared.printLock:
if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none': if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none':
try: try:
for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80):
@ -51,6 +51,38 @@ def dns():
shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time()) shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time())
except: except:
logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.')
elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
for port in [8080, 8444]:
logger.debug("Resolving %i through SOCKS...", port)
address_family = socket.AF_INET
sock = socks.socksocket(address_family, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(20)
proxytype = socks.PROXY_TYPE_SOCKS5
sockshostname = shared.config.get(
'bitmessagesettings', 'sockshostname')
socksport = shared.config.getint(
'bitmessagesettings', 'socksport')
rdns = True # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
socksusername = shared.config.get(
'bitmessagesettings', 'socksusername')
sockspassword = shared.config.get(
'bitmessagesettings', 'sockspassword')
sock.setproxy(
proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
else: else:
logger.info('DNS bootstrap skipped because SOCKS is used.') sock.setproxy(
proxytype, sockshostname, socksport, rdns)
try:
ip = sock.resolve("bootstrap" + str(port) + ".bitmessage.org")
sock.shutdown(socket.SHUT_RDWR)
sock.close()
except:
logger.error("SOCKS DNS resolving failed", exc_info=True)
if ip is not None:
logger.info ('Adding ' + ip + ' to knownNodes based on SOCKS DNS bootstrap method')
shared.knownNodes[1][shared.Peer(ip, port)] = time.time()
else:
logger.info('DNS bootstrap skipped because the proxy type does not support DNS resolution.')

View File

@ -155,7 +155,7 @@ class socksocket(socket.socket):
""" """
self.__proxy = (proxytype, addr, port, rdns, username, password) self.__proxy = (proxytype, addr, port, rdns, username, password)
def __negotiatesocks5(self, destaddr, destport): def __negotiatesocks5(self):
"""__negotiatesocks5(self,destaddr,destport) """__negotiatesocks5(self,destaddr,destport)
Negotiates a connection through a SOCKS5 server. Negotiates a connection through a SOCKS5 server.
""" """
@ -200,6 +200,8 @@ class socksocket(socket.socket):
raise Socks5AuthError((2, _socks5autherrors[2])) raise Socks5AuthError((2, _socks5autherrors[2]))
else: else:
raise GeneralProxyError((1, _generalerrors[1])) raise GeneralProxyError((1, _generalerrors[1]))
def __connectsocks5(self, destaddr, destport):
# Now we can request the actual connection # Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0x01, 0x00) req = struct.pack('BBB', 0x05, 0x01, 0x00)
# If the given destination address is an IP address, we'll # If the given destination address is an IP address, we'll
@ -247,6 +249,37 @@ class socksocket(socket.socket):
else: else:
self.__proxypeername = (destaddr, destport) self.__proxypeername = (destaddr, destport)
def __resolvesocks5(self, host):
# Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0xF0, 0x00)
req += chr(0x03).encode() + chr(len(host)).encode() + host
req = req + struct.pack(">H", 8444)
self.sendall(req)
# Get the response
ip = ""
resp = self.__recvall(4)
if resp[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
if ord(resp[1:2])<=8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
# Get the bound address/port
elif resp[3:4] == chr(0x01).encode():
ip = socket.inet_ntoa(self.__recvall(4))
elif resp[3:4] == chr(0x03).encode():
resp = resp + self.recv(1)
ip = self.__recvall(ord(resp[4:5]))
else:
self.close()
raise GeneralProxyError((1,_generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
return ip
def getproxysockname(self): def getproxysockname(self):
"""getsockname() -> address info """getsockname() -> address info
Returns the bound IP address and port number at the proxy. Returns the bound IP address and port number at the proxy.
@ -361,7 +394,8 @@ class socksocket(socket.socket):
else: else:
portnum = 1080 portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum)) _orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks5(destpair[0], destpair[1]) self.__negotiatesocks5()
self.__connectsocks5(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_SOCKS4: elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
if self.__proxy[2] != None: if self.__proxy[2] != None:
portnum = self.__proxy[2] portnum = self.__proxy[2]
@ -380,3 +414,15 @@ class socksocket(socket.socket):
_orgsocket.connect(self, (destpair[0], destpair[1])) _orgsocket.connect(self, (destpair[0], destpair[1]))
else: else:
raise GeneralProxyError((4, _generalerrors[4])) raise GeneralProxyError((4, _generalerrors[4]))
def resolve(self, host):
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks5()
return self.__resolvesocks5(host)
else:
return None