PyBitmessage-2021-04-27/src/network/proxy.py

149 lines
4.4 KiB
Python
Raw Normal View History

2019-09-06 15:34:52 +02:00
"""
src/network/proxy.py
====================
"""
# pylint: disable=protected-access
import socket
import time
import asyncore_pollchoose as asyncore
2018-07-17 13:28:56 +02:00
import state
from advanceddispatcher import AdvancedDispatcher
from bmconfigparser import BMConfigParser
from debug import logger
2018-07-17 13:28:56 +02:00
class ProxyError(Exception):
2019-07-08 16:20:29 +02:00
"""Base proxy exception class"""
2018-07-17 13:28:56 +02:00
errorCodes = ("Unknown error",)
def __init__(self, code=-1):
self.code = code
try:
2018-07-17 13:28:56 +02:00
self.message = self.errorCodes[code]
except IndexError:
2018-07-17 13:28:56 +02:00
self.message = self.errorCodes[-1]
super(ProxyError, self).__init__(self.message)
class GeneralProxyError(ProxyError):
2019-07-08 16:20:29 +02:00
"""General proxy error class (not specfic to an implementation)"""
2018-07-17 13:28:56 +02:00
errorCodes = (
"Success",
"Invalid data",
"Not connected",
"Not available",
"Bad proxy type",
"Bad input",
"Timed out",
"Network unreachable",
"Connection refused",
2018-07-17 13:28:56 +02:00
"Host unreachable"
)
class Proxy(AdvancedDispatcher):
2019-07-08 16:20:29 +02:00
"""Base proxy class"""
2018-07-17 13:28:56 +02:00
# these are global, and if you change config during runtime,
# all active/new instances should change too
_proxy = ("127.0.0.1", 9050)
_auth = None
_onion_proxy = None
_onion_auth = None
_remote_dns = True
@property
def proxy(self):
2019-07-08 16:20:29 +02:00
"""Return proxy IP and port"""
return self.__class__._proxy
@proxy.setter
def proxy(self, address):
2019-07-08 16:20:29 +02:00
"""Set proxy IP and port"""
2018-07-17 13:28:56 +02:00
if (not isinstance(address, tuple) or len(address) < 2 or
2019-09-06 15:34:52 +02:00
not isinstance(address[0], str) or
2018-07-17 13:28:56 +02:00
not isinstance(address[1], int)):
raise ValueError
self.__class__._proxy = address
@property
def auth(self):
2019-07-08 16:20:29 +02:00
"""Return proxy authentication settings"""
return self.__class__._auth
@auth.setter
def auth(self, authTuple):
2019-07-08 16:20:29 +02:00
"""Set proxy authentication (username and password)"""
self.__class__._auth = authTuple
@property
def onion_proxy(self):
2019-07-08 16:20:29 +02:00
"""
Return separate proxy IP and port for use only with onion
addresses. Untested.
"""
return self.__class__._onion_proxy
@onion_proxy.setter
def onion_proxy(self, address):
2019-07-08 16:20:29 +02:00
"""Set onion proxy address"""
2018-07-17 13:28:56 +02:00
if address is not None and (
2019-09-06 15:34:52 +02:00
not isinstance(address, tuple) or len(address) < 2 or
not isinstance(address[0], str) or
2018-07-17 13:28:56 +02:00
not isinstance(address[1], int)):
raise ValueError
self.__class__._onion_proxy = address
@property
def onion_auth(self):
2019-07-08 16:20:29 +02:00
"""Return proxy authentication settings for onion hosts only"""
return self.__class__._onion_auth
@onion_auth.setter
def onion_auth(self, authTuple):
2019-07-08 16:20:29 +02:00
"""Set proxy authentication for onion hosts only. Untested."""
self.__class__._onion_auth = authTuple
def __init__(self, address):
if not isinstance(address, state.Peer):
raise ValueError
AdvancedDispatcher.__init__(self)
self.destination = address
self.isOutbound = True
self.fullyEstablished = False
2019-09-06 15:34:52 +02:00
self.connectedAt = 0
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
2018-07-17 13:28:56 +02:00
if BMConfigParser().safeGetBoolean(
"bitmessagesettings", "socksauthentication"):
self.auth = (
BMConfigParser().safeGet(
"bitmessagesettings", "socksusername"),
BMConfigParser().safeGet(
"bitmessagesettings", "sockspassword")
)
else:
self.auth = None
2018-07-17 13:28:56 +02:00
self.connect(
self.onion_proxy
if address.host.endswith(".onion") and self.onion_proxy else
self.proxy
)
def handle_connect(self):
2019-07-08 16:20:29 +02:00
"""Handle connection event (to the proxy)"""
self.set_state("init")
try:
AdvancedDispatcher.handle_connect(self)
except socket.error as e:
if e.errno in asyncore._DISCONNECTED:
2018-07-17 13:28:56 +02:00
logger.debug(
"%s:%i: Connection failed: %s",
self.destination.host, self.destination.port, e)
return
self.state_init()
def state_proxy_handshake_done(self):
2019-07-08 16:20:29 +02:00
"""Handshake is complete at this point"""
2019-09-13 10:41:21 +02:00
self.connectedAt = time.time() # pylint: disable=attribute-defined-outside-init
return False