/ src / network / proxy.py
proxy.py
  1  """
  2  Set proxy if avaiable otherwise exception
  3  """
  4  # pylint: disable=protected-access
  5  import logging
  6  import socket
  7  import time
  8  
  9  import asyncore_pollchoose as asyncore
 10  from advanceddispatcher import AdvancedDispatcher
 11  from bmconfigparser import config
 12  from node import Peer
 13  
 14  logger = logging.getLogger('default')
 15  
 16  
 17  class ProxyError(Exception):
 18      """Base proxy exception class"""
 19      errorCodes = ("Unknown error",)
 20  
 21      def __init__(self, code=-1):
 22          self.code = code
 23          try:
 24              self.message = self.errorCodes[code]
 25          except IndexError:
 26              self.message = self.errorCodes[-1]
 27          super(ProxyError, self).__init__(self.message)
 28  
 29  
 30  class GeneralProxyError(ProxyError):
 31      """General proxy error class (not specfic to an implementation)"""
 32      errorCodes = (
 33          "Success",
 34          "Invalid data",
 35          "Not connected",
 36          "Not available",
 37          "Bad proxy type",
 38          "Bad input",
 39          "Timed out",
 40          "Network unreachable",
 41          "Connection refused",
 42          "Host unreachable"
 43      )
 44  
 45  
 46  class Proxy(AdvancedDispatcher):
 47      """Base proxy class"""
 48      # these are global, and if you change config during runtime,
 49      # all active/new instances should change too
 50      _proxy = ("127.0.0.1", 9050)
 51      _auth = None
 52      _onion_proxy = None
 53      _onion_auth = None
 54      _remote_dns = True
 55  
 56      @property
 57      def proxy(self):
 58          """Return proxy IP and port"""
 59          return self.__class__._proxy
 60  
 61      @proxy.setter
 62      def proxy(self, address):
 63          """Set proxy IP and port"""
 64          if (not isinstance(address, tuple) or len(address) < 2
 65                  or not isinstance(address[0], str)
 66                  or not isinstance(address[1], int)):
 67              raise ValueError
 68          self.__class__._proxy = address
 69  
 70      @property
 71      def auth(self):
 72          """Return proxy authentication settings"""
 73          return self.__class__._auth
 74  
 75      @auth.setter
 76      def auth(self, authTuple):
 77          """Set proxy authentication (username and password)"""
 78          self.__class__._auth = authTuple
 79  
 80      @property
 81      def onion_proxy(self):
 82          """
 83          Return separate proxy IP and port for use only with onion
 84          addresses. Untested.
 85          """
 86          return self.__class__._onion_proxy
 87  
 88      @onion_proxy.setter
 89      def onion_proxy(self, address):
 90          """Set onion proxy address"""
 91          if address is not None and (
 92              not isinstance(address, tuple) or len(address) < 2
 93              or not isinstance(address[0], str)
 94              or not isinstance(address[1], int)
 95          ):
 96              raise ValueError
 97          self.__class__._onion_proxy = address
 98  
 99      @property
100      def onion_auth(self):
101          """Return proxy authentication settings for onion hosts only"""
102          return self.__class__._onion_auth
103  
104      @onion_auth.setter
105      def onion_auth(self, authTuple):
106          """Set proxy authentication for onion hosts only. Untested."""
107          self.__class__._onion_auth = authTuple
108  
109      def __init__(self, address):
110          if not isinstance(address, Peer):
111              raise ValueError
112          AdvancedDispatcher.__init__(self)
113          self.destination = address
114          self.isOutbound = True
115          self.fullyEstablished = False
116          self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
117          if config.safeGetBoolean(
118                  "bitmessagesettings", "socksauthentication"):
119              self.auth = (
120                  config.safeGet(
121                      "bitmessagesettings", "socksusername"),
122                  config.safeGet(
123                      "bitmessagesettings", "sockspassword"))
124          else:
125              self.auth = None
126          self.connect(
127              self.onion_proxy
128              if address.host.endswith(".onion") and self.onion_proxy else
129              self.proxy
130          )
131  
132      def handle_connect(self):
133          """Handle connection event (to the proxy)"""
134          self.set_state("init")
135          try:
136              AdvancedDispatcher.handle_connect(self)
137          except socket.error as e:
138              if e.errno in asyncore._DISCONNECTED:
139                  logger.debug(
140                      "%s:%i: Connection failed: %s",
141                      self.destination.host, self.destination.port, e)
142                  return
143          self.state_init()
144  
145      def state_proxy_handshake_done(self):
146          """Handshake is complete at this point"""
147          self.connectedAt = time.time()
148          return False