Skip to content
Snippets Groups Projects
Commit fcb2f4be authored by George Kadianakis's avatar George Kadianakis
Browse files

Improve the code a bit.

* Make better use of exceptions instead of relying on retvals.
* If Tor asks us for a transport we don't support, we now send back an
  {S,C}METHOD-ERROR.
parent 98e7b503
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/python
# -*- coding: utf-8 -*-
""" The pyptlib.easy.client module includes a convenient API for writing pluggable transport clients. """
"""
This module provides a convenient API for writing pluggable transport clients.
"""
from pyptlib.config import EnvException
from pyptlib.client_config import ClientConfig
def init(transports):
def init(supported_transports):
"""
Initialize the pluggable transport by parsing the environment
variables and generating output to report any errors. The given
......@@ -20,45 +22,44 @@ def init(transports):
'state_loc' : Directory where the managed proxy should dump its
state files (if needed).
'transports' : The names of the transports that must be launched.
'transports' : The names of the transports that must be
launched. The list can be empty.
Returns None if something went wrong.
Throws EnvException.
"""
supportedTransportVersion = '1'
try:
config = ClientConfig()
except EnvException:
return None
config = ClientConfig()
if config.checkManagedTransportVersion(supportedTransportVersion):
config.writeVersion(supportedTransportVersion)
else:
config.writeVersionError()
return None
matchedTransports = []
for transport in transports:
if config.checkTransportEnabled(transport):
matchedTransports.append(transport)
# XXX the XXXs from server.py are valid here too.
raise EnvException("Unsupported managed proxy protocol version (%s)" %
str(config.getManagedTransportVersions()))
retval = {}
retval['state_loc'] = config.getStateLocation()
retval['transports'] = matchedTransports
retval['transports'] = getTransportsList(supported_transports, config)
return retval
def getTransportsList(supported_transports, config):
transports = []
if config.getAllTransportsEnabled():
return supported_transports
for transport in config.getClientTransports():
if transport in supported_transports:
transports.append(transport)
else:
config.writeMethodError(transport, "not supported")
return transports
def reportSuccess(
name,
socksVersion,
address,
args,
optArgs,
):
def reportSuccess(name, socksVersion, address, args, optArgs):
"""
This method should be called to report when a transport has been successfully launched.
It generates output to Tor informing that the transport launched successfully and can be used.
......
......@@ -2,53 +2,50 @@
# -*- coding: utf-8 -*-
"""
The pyptlib.client module contains a low-level API which closely follows the Tor Proposal 180: Pluggable transports for circumvention.
This module inherits from pyptlib.config and contains just the parts of the API which are specific to the client implementations of the protocol.
This module inherits from pyptlib.config and contains just the parts
of the API which are specific to the client implementations of the
protocol.
"""
import os
from pyptlib.config import Config
__docformat__ = 'restructuredtext'
class ClientConfig(Config):
"""
The ClientConfig class contains a low-level API which closely follows the Tor Proposal 180: Pluggable transports for circumvention.
This class inherits from pyptlib.config.Config and contains just the parts of the API which are specific to the client implementations of the protocol.
This class inherits from pyptlib.config.Config and contains just
the parts of the API which are specific to the client
implementations of the protocol.
"""
# Public methods
def __init__(self): # throws EnvError
def __init__(self):
"""
Initialize the ClientConfig object.
This causes the state location, managed transport, and transports version to be set.
Throws EnvException.
"""
Config.__init__(self)
self.transports = self.get('TOR_PT_CLIENT_TRANSPORTS').split(','
)
self.transports = self.get('TOR_PT_CLIENT_TRANSPORTS').split(',')
if '*' in self.transports:
self.allTransportsEnabled = True
self.transports.remove('*')
def getClientTransports(self):
""" Returns a list of strings representing the client transports reported by Tor. If present, '*' is stripped from this list and used to set allTransportsEnabled to True. """
def getClientTransports(self): # XXX why is this client-specific ???
"""
Returns a list of strings representing the client transports
reported by Tor. If present, '*' is stripped from this list
and used to set allTransportsEnabled to True.
"""
return self.transports
def writeMethod( # CMETHOD
self,
name,
socksVersion,
address,
args,
optArgs,
):
def writeMethod(self, name, socksVersion, address, args, optArgs):
"""
Write a message to stdout specifying a supported transport
Takes: str, int, (str, int), [str], [str]
......@@ -71,7 +68,10 @@ class ClientConfig(Config):
self.emit('CMETHOD-ERROR %s %s' % (name, message))
def writeMethodEnd(self): # CMETHODS DONE
""" Write a message to stdout specifying that the list of supported transports has ended """
"""
Write a message to stdout specifying that the list of
supported transports has ended.
"""
self.emit('CMETHODS DONE')
......
......@@ -2,24 +2,14 @@
# -*- coding: utf-8 -*-
"""
The pyptlib.config module contains a low-level API which closely follows the Tor Proposal 180: Pluggable transports for circumvention.
This module contains the parts of the API which are shared by both client and server implementations of the protocol.
This module contains parts of the managed proxy specification which
are shared by both client and server implementations of the protocol.
"""
import os, sys
__docformat__ = 'restructuredtext'
"""Receive "<addr>:<port>" in 'string', and return [<addr>,<port>]."""
def parse_addrport(string):
addrport = string.split(':')
if (len(addrport) != 2):
return None
addrport[1] = int(addrport[1]) # XXX lame integer check
return addrport
class Config:
"""
......@@ -34,12 +24,16 @@ class Config:
# Public methods
def __init__(self): # throws EnvError
""" Initialize the Config object. this causes the state location and managed transport version to be set. """
def __init__(self):
"""
Initialize the Config object. this causes the state location
and managed transport version to be set.
Throws EnvException.
"""
self.stateLocation = self.get('TOR_PT_STATE_LOCATION')
self.managedTransportVer = \
self.get('TOR_PT_MANAGED_TRANSPORT_VER').split(',')
self.managedTransportVer = self.get('TOR_PT_MANAGED_TRANSPORT_VER').split(',')
def checkClientMode(self):
""" Check to see if the daemon is being run as a client or a server. This is determined by looking for the presence of the TOR_PT_CLIENT_TRANSPORTS environment variable. """
......@@ -105,7 +99,11 @@ class Config:
return key in os.environ
def get(self, key):
""" Attempts to fetch the given key from the environment variables. If it is present, it is returned, otherwise an EnvException is thrown. """
"""
Attempts to fetch the given key from the environment
variables. If it is present, it is returned, otherwise an
EnvException is thrown.
"""
if key in os.environ:
return os.environ[key]
......@@ -118,15 +116,9 @@ class Config:
print msg
sys.stdout.flush()
# Exception thrown when there is an error parsing the configuration parameters provided by Tor in environment variables
class EnvException(Exception):
""" The EnvException exception is thrown whenever a required environment variable is not presented or cannot be parsed. """
message = None
def __init__(self, message):
self.message = message
"""
Exception thrown when there is an error parsing managed proxy
environment variables. Also sends an ENV-ERROR to Tor.
"""
class EnvException(Exception): pass
......@@ -7,7 +7,7 @@ from pyptlib.config import EnvException
from pyptlib.server_config import ServerConfig
def init(transports):
def init(supported_transports):
"""
Initialize the pluggable transport by parsing the environment
variables and generating output to report any errors. The
......@@ -24,47 +24,59 @@ def init(transports):
of Tor's ORPort.
'ext_orport' : [<addr>, <port>] tuple containing the address and
port of Tor's Extended ORPort.
port of Tor's Extended ORPort, or None if the Extended ORPort it's
not supported.
'transports' : A dictionary {<transport> : [<addr>, <port>]},
where <transport> is the name of the transport that must be
spawned, and [<addr>, <port>] is a list containing the location
where that transport should bind.
where that transport should bind. The dictionary can be empty.
Returns None if something went wrong.
Throws EnvException.
"""
supportedTransportVersion = '1'
try:
config = ServerConfig()
except EnvException: # don't throw exceptions; return None
return None
config = ServerConfig()
if config.checkManagedTransportVersion(supportedTransportVersion):
config.writeVersion(supportedTransportVersion)
else:
config.writeVersionError()
return None
matchedTransports = []
for transport in transports:
if config.checkTransportEnabled(transport):
matchedTransports.append(transport)
# XXX Must issue SMETHOD-ERROR when Tor asked us to spawn a
# XXX transport but we don't support it!!!!
# XXX what to do if matchedTransports is empty ???
raise EnvException("Unsupported managed proxy protocol version (%s)" %
str(config.getManagedTransportVersions()))
retval = {}
retval['state_loc'] = config.getStateLocation()
retval['orport'] = config.getORPort()
retval['ext_orport'] = config.getExtendedORPort()
retval['transports'] = config.getServerBindAddresses()
retval['transports'] = getTransportsDict(supported_transports, config)
return retval
def getTransportsDict(supported_transports, config):
"""
Given the transport names that the managed proxy support in
'transports', and Tor's configuration in 'config', figure out
which transports Tor wants us to spawn and create the appropriate
dictionary.
"""
transports = {}
if config.getAllTransportsEnabled():
return config.getServerBindAddresses()
for transport in config.getServerTransports():
if transport in supported_transports:
assert(transport in config.getServerBindAddresses())
transports[transport] = config.getServerBindAddresses()[transport]
else:
# Issue SMETHOD-ERROR when Tor asks us to spawn a
# transport that we do not support.
config.writeMethodError(transport, "not supported")
return transports
def reportSuccess(name, address, options):
"""
This method should be called to report when a transport has been successfully launched.
......
......@@ -2,8 +2,9 @@
# -*- coding: utf-8 -*-
"""
The pyptlib.client module contains a low-level API which closely follows the Tor Proposal 180: Pluggable transports for circumvention.
This module inherits from pyptlib.config and contains just the parts of the API which are specific to the server implementations of the protocol.
This module inherits from pyptlib.config and contains just the parts
of the API which are specific to the server implementations of the
protocol.
"""
import os
......@@ -13,36 +14,37 @@ import pyptlib.config as config
__docformat__ = 'restructuredtext'
class ServerConfig(config.Config):
"""
The ServerConfig class contains a low-level API which closely follows the Tor Proposal 180: Pluggable transports for circumvention.
This class inherits from pyptlib.config.Config and contains just the parts of the API which are specific to the client implementations of the protocol.
This class inherits from pyptlib.config.Config and contains just
the parts of the API which are specific to the client
implementations of the protocol.
"""
extendedServerPort = None # TOR_PT_EXTENDED_SERVER_PORT
ORPort = None # TOR_PT_ORPORT
serverBindAddr = {} # TOR_PT_SERVER_BINADDR
# Public methods
def __init__(self): # throws EnvError
def __init__(self):
"""
Initialize the ClientConfig object.
This causes the state location, managed transport, and transports version to be set.
Initialize the ClientConfig object.
This causes the state location, managed transport, and transports version to be set.
Throws EnvException.
"""
config.Config.__init__(self)
# extendedORPort can also be 'None'
self.extendedORPort = config.parse_addrport(self.get('TOR_PT_EXTENDED_SERVER_PORT'))
self.ORPort = config.parse_addrport(self.get('TOR_PT_ORPORT'))
if self.ORPort is None:
raise config.EnvException("ORPort was corrupted")
# TOR_PT_EXTENDED_SERVER_PORT is optional; tor uses the empty
# string as its value if it does not support the Extended
# ORPort.
ext_orport_tmp = self.get('TOR_PT_EXTENDED_SERVER_PORT')
if ext_orport_tmp == '':
self.extendedORPort = None
else:
self.extendedORPort = self.get_addrport('TOR_PT_EXTENDED_SERVER_PORT')
self.ORPort = self.get_addrport('TOR_PT_ORPORT')
binds = self.get('TOR_PT_SERVER_BINDADDR').split(',')
for bind in binds:
self.serverBindAddr = {}
bindaddrs = self.get('TOR_PT_SERVER_BINDADDR').split(',')
for bind in bindaddrs:
(key, value) = bind.split('-')
self.serverBindAddr[key] = value.split(":") # XXX ugly code
self.serverBindAddr[key][1] = int(self.serverBindAddr[key][1]) # XXX ugly code
......@@ -68,16 +70,15 @@ class ServerConfig(config.Config):
return self.serverBindAddr
def getServerTransports(self):
""" Returns a list of strings representing the server transports reported by Tor. If present, '*' is stripped from this list and used to set allTransportsEnabled to True. """
"""
Returns a list of strings representing the server
transports reported by Tor. If present, '*' is stripped from
this list and used to set allTransportsEnabled to True.
"""
return self.transports
def writeMethod( # SMETHOD
self,
name,
address,
options,
):
def writeMethod(self, name, address, options):
"""
Write a message to stdout specifying a supported transport
Takes: str, (str, int), MethodOptions
......@@ -103,6 +104,28 @@ class ServerConfig(config.Config):
self.emit('SMETHODS DONE')
def get_addrport(self, key):
"""
Given an environment variable name in 'key' with an
'<addr>:<port>' value, return [<addr>,<port>].
Throws EnvException.
"""
string = self.get(key)
addrport = string.split(':')
if (len(addrport) != 2) or (not addrport[1].isdigit()):
message = '%s: Parsing error (%s).' % (key, string)
self.writeEnvError(message)
raise config.EnvException(message)
if (not 0 <= int(addrport[1]) < 65536):
message = '%s: Port out of range (%s).' % (key, string)
self.writeEnvError(message)
raise config.EnvException(message)
return addrport
class MethodOptions:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment