[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [ooni-probe/master] implemented a SOCKv5 endpoint using the same _WrappingFactory used for TCP and SSL.
commit 4fad239939f250cdbd192e842ebcee3d3cbbebee
Author: Giovanni `evilaliv3` Pellerano <giovanni.pellerano@xxxxxxxxxxxxx>
Date: Thu Nov 15 23:58:05 2012 +0100
implemented a SOCKv5 endpoint using the same _WrappingFactory used for TCP and SSL.
Twisted Agent class extended to use SOCKv5 andpoint to permit HTTP connections throught Tor (shttp:// standing for socksified http)
---
ooni/lib/txagentwithsocks.py | 203 ++++++++++++++++++++++++++++++++++++++++++
ooni/templates/httpt.py | 15 ++--
2 files changed, 212 insertions(+), 6 deletions(-)
diff --git a/ooni/lib/txagentwithsocks.py b/ooni/lib/txagentwithsocks.py
new file mode 100644
index 0000000..fecd9fc
--- /dev/null
+++ b/ooni/lib/txagentwithsocks.py
@@ -0,0 +1,203 @@
+# -*- encoding: utf-8 -*-
+#
+# :authors: Giovanni Pellerano
+# :licence: see LICENSE
+
+import struct
+
+from zope.interface import implements
+from twisted.web import client
+from twisted.internet.protocol import ClientFactory, Protocol
+from twisted.internet.endpoints import TCP4ClientEndpoint, SSL4ClientEndpoint, _WrappingProtocol, _WrappingFactory
+from twisted.internet import interfaces, defer
+
+class SOCKSError(Exception):
+ def __init__(self, value):
+ Exception.__init__(self)
+ self.code = value
+
+class SOCKSv5ClientProtocol(_WrappingProtocol):
+ state = 0
+
+ def __init__(self, connectedDeferred, wrappedProtocol, host, port):
+ _WrappingProtocol.__init__(self, connectedDeferred, wrappedProtocol)
+ self._host = host
+ self._port = port
+ self.ready = False
+ self.buf = []
+
+ def socks_state_0(self, data):
+ # error state
+ self._connectedDeferred.errback(SOCKSError(0x00))
+ return
+
+ def socks_state_1(self, data):
+ if data != "\x05\x00":
+ self._connectedDeferred.errback(SOCKSError(0x00))
+ return
+
+ # Anonymous access allowed - let's issue connect
+ self.transport.write(struct.pack("!BBBBB", 5, 1, 0, 3,
+ len(self._host)) +
+ self._host +
+ struct.pack("!H", self._port))
+
+ def socks_state_2(self, data):
+ if data[:2] != "\x05\x00":
+ # Anonymous access denied
+
+ errcode = ord(data[1])
+ self._connectedDeferred.errback(SOCKSError(errcode))
+
+ return
+
+ self.ready = True
+ self._wrappedProtocol.transport = self.transport
+ self._wrappedProtocol.connectionMade()
+
+ if self.buf != []:
+ self.transport.write(''.join(self.buf))
+ self.buf = []
+
+ self._connectedDeferred.callback(self._wrappedProtocol)
+
+ def connectionMade(self):
+ # We implement only Anonymous access
+ self.transport.write(struct.pack("!BB", 5, len("\x00")) + "\x00")
+
+ self.state = self.state + 1
+
+ def connectionLost(self, reason):
+ pass
+
+ def write(self, data):
+ if self.ready:
+ self.transport.write(data)
+ else:
+ self.buf.append(data)
+
+ def dataReceived(self, data):
+ if self.state != 3:
+ getattr(self, 'socks_state_%s' % (self.state),
+ self.socks_state_0)(data)
+ self.state = self.state + 1
+ else:
+ self._wrappedProtocol.dataReceived(data)
+
+class SOCKSv5ClientFactory(_WrappingFactory):
+ protocol = SOCKSv5ClientProtocol
+
+ def __init__(self, wrappedFactory, host, port):
+ _WrappingFactory.__init__(self, wrappedFactory)
+ self._host, self._port = host, port
+
+ def buildProtocol(self, addr):
+ """
+ Proxy C{buildProtocol} to our C{self._wrappedFactory} or errback
+ the C{self._onConnection} L{Deferred}.
+
+ @return: An instance of L{_WrappingProtocol} or C{None}
+ """''
+ try:
+ proto = self._wrappedFactory.buildProtocol(addr)
+ except:
+ self._onConnection.errback()
+ else:
+ print self._host
+ return self.protocol(self._onConnection, proto,
+ self._host, self._port)
+
+class SOCKS5ClientEndpoint(object):
+ """
+ TCP client endpoint with an IPv4 configuration.
+ """
+ implements(interfaces.IStreamClientEndpoint)
+
+ def __init__(self, reactor, sockhost, sockport,
+ host, port, timeout=30,bindAddress=None):
+ """
+ @param reactor: An L{IReactorTCP} provider
+
+ @param host: A hostname, used when connecting
+ @type host: str
+
+ @param port: The port number, used when connecting
+ @type port: int
+
+ @param timeout: The number of seconds to wait before assuming the
+ connection has failed.
+ @type timeout: int
+
+ @param bindAddress: A (host, port) tuple of local address to bind to,
+ or None.
+ @type bindAddress: tuple
+ """
+ self._reactor = reactor
+ self._sockhost = sockhost
+ self._sockport = sockport
+ self._host = host
+ self._port = port
+ self._timeout = timeout
+ self._bindAddress = bindAddress
+
+ def connect(self, protocolFactory):
+ """
+ Implement L{IStreamClientEndpoint.connect} to connect via TCP.
+ """
+ try:
+ wf = SOCKSv5ClientFactory(protocolFactory, self._host, self._port)
+ self._reactor.connectTCP(
+ self._sockhost, self._sockport, wf,
+ timeout=self._timeout, bindAddress=self._bindAddress)
+ return wf._onConnection
+ except:
+ return defer.fail()
+
+class Agent(client.Agent):
+ def __init__(self, reactor,
+ contextFactory=client.WebClientContextFactory(),
+ connectTimeout=None, bindAddress=None,
+ pool=None, sockhost=None, sockport=None):
+ if pool is None:
+ pool = client.HTTPConnectionPool(reactor, False)
+ self._reactor = reactor
+ self._pool = pool
+ self._contextFactory = contextFactory
+ self._connectTimeout = connectTimeout
+ self._bindAddress = bindAddress
+ self._sockhost = sockhost
+ self._sockport = sockport
+
+ def _getEndpoint(self, scheme, host, port):
+ """
+ Get an endpoint for the given host and port, using a transport
+ selected based on scheme.
+
+ @param scheme: A string like C{'http'} or C{'https'} (the only two
+ supported values) to use to determine how to establish the
+ connection.
+
+ @param host: A C{str} giving the hostname which will be connected to in
+ order to issue a request.
+
+ @param port: An C{int} giving the port number the connection will be
+ on.
+
+ @return: An endpoint which can be used to connect to given address.
+ """
+ kwargs = {}
+ if self._connectTimeout is not None:
+ kwargs['timeout'] = self._connectTimeout
+ kwargs['bindAddress'] = self._bindAddress
+ if scheme == 'http':
+ return TCP4ClientEndpoint(self._reactor, host, port, **kwargs)
+ elif scheme == 'shttp':
+ return SOCKS5ClientEndpoint(self._reactor, self._sockhost,
+ self._sockport, host, port, **kwargs)
+ elif scheme == 'https':
+ return SSL4ClientEndpoint(self._reactor, host, port,
+ self._wrapContextFactory(host, port),
+ **kwargs)
+ else:
+ raise SchemeNotSupported("Unsupported scheme: %r" % (scheme,))
+
diff --git a/ooni/templates/httpt.py b/ooni/templates/httpt.py
index ee6af90..011ff5b 100644
--- a/ooni/templates/httpt.py
+++ b/ooni/templates/httpt.py
@@ -4,15 +4,13 @@
# :licence: see LICENSE
import copy
import random
-
-from zope.interface import implements
+import struct
from twisted.python import usage
from twisted.plugin import IPlugin
from twisted.internet import protocol, defer
from twisted.internet.ssl import ClientContextFactory
-from twisted.web.client import Agent
from twisted.internet import reactor
from twisted.internet.error import ConnectionRefusedError
@@ -23,6 +21,8 @@ from ooni.utils import log
from ooni.utils.net import BodyReceiver, StringProducer, userAgents
+from ooni.lib.txagentwithsocks import Agent, SOCKSError
+
class HTTPTest(NetTestCase):
"""
A utility class for dealing with HTTP based testing. It provides methods to
@@ -47,7 +47,7 @@ class HTTPTest(NetTestCase):
log.err("Warning! pyOpenSSL is not installed. https websites will"
"not work")
- self.agent = Agent(reactor)
+ self.agent = Agent(reactor, sockhost="127.0.0.1", sockport=9050)
if self.followRedirects:
try:
@@ -131,8 +131,11 @@ class HTTPTest(NetTestCase):
d = self.build_request(url, method, headers, body)
def errback(failure):
- failure.trap(ConnectionRefusedError)
- log.err("Connection refused. The backend may be down")
+ failure.trap(ConnectionRefusedError, SOCKSError)
+ if type(failure.value) is ConnectionRefusedError:
+ log.err("Connection refused. The backend may be down")
+ else:
+ log.err("Sock error. The SOCK proxy may be down")
self.report["failure"] = str(failure.value)
def finished(data):
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits