[Pkg-privacy-commits] [obfsproxy] 289/353: Add support for connecting via a HTTPS CONNECT proxy.
Ximin Luo
infinity0 at moszumanska.debian.org
Sat Aug 22 13:02:12 UTC 2015
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository obfsproxy.
commit 2477925050651940a40741b2c12910bb36250594
Author: Yawning Angel <yawning at torproject.org>
Date: Tue Apr 15 10:00:21 2014 +0000
Add support for connecting via a HTTPS CONNECT proxy.
This adds a HTTPS CONNECT client. It's been lightly tested with both
privoxy and apache2 and appears to work, both without authentication
and with Basic.
---
ChangeLog | 2 +
obfsproxy/managed/client.py | 5 --
obfsproxy/network/http.py | 136 +++++++++++++++++++++++++++++++++++++++++++
obfsproxy/network/network.py | 19 ++++--
obfsproxy/network/socks.py | 6 ++
obfsproxy/network/socks5.py | 7 ++-
obfsproxy/pyobfsproxy.py | 5 --
7 files changed, 162 insertions(+), 18 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 9fa0221..77e8062 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,6 @@
Changes in version 0.2.8 - UNRELEASED
+ - Support connecting over a HTTPS CONNECT proxy. Patch by Yawning Angel.
+ Fixes #11409.
- Support connecting over SOCKS4(a) and SOCKS5. Based on the patch by
Arturo Filastò with changes by Yawning Angel. Fixes #8956.
diff --git a/obfsproxy/managed/client.py b/obfsproxy/managed/client.py
index 1df6baf..1a49074 100644
--- a/obfsproxy/managed/client.py
+++ b/obfsproxy/managed/client.py
@@ -32,11 +32,6 @@ def do_managed_client():
# Apply the proxy settings if any
proxy = ptclient.config.getProxy()
if proxy:
- # XXX temporarily: till we implement HTTP
- if proxy.scheme == "http":
- log.error("HTTP CONNECT proxy not supported yet")
- ptclient.reportProxyError("HTTP CONNECT not supported yet")
- return
ptclient.reportProxySuccess()
for transport in ptclient.getTransports():
diff --git a/obfsproxy/network/http.py b/obfsproxy/network/http.py
new file mode 100644
index 0000000..9aa62d1
--- /dev/null
+++ b/obfsproxy/network/http.py
@@ -0,0 +1,136 @@
+from base64 import b64encode
+from twisted.internet.interfaces import IStreamClientEndpoint
+from twisted.internet.protocol import ClientFactory
+from twisted.internet.defer import Deferred
+from twisted.web.http import HTTPClient
+from zope.interface import implementer
+
+import obfsproxy.common.log as logging
+
+"""
+HTTP CONNECT Client:
+
+Next up on the list of things one would expect Twisted to provide, but does not
+is an endpoint for outgoing connections through a HTTP CONNECT proxy.
+
+Limitations:
+ * Only Basic Authentication is supported (RFC2617).
+"""
+
+log = logging.get_obfslogger()
+
+# Create the body of the RFC2617 Basic Authentication 'Authorization' header.
+def _makeBasicAuth(username, password):
+ if username and password:
+ return "Basic " + b64encode(username + ':' + password)
+ elif username or password:
+ raise ValueError("expecting both a username *and* password")
+ else:
+ return None
+
+class HTTPConnectClient(HTTPClient):
+ deferred = None
+ host = None
+ port = None
+ proxy_addr = None
+ auth = None
+ instance_factory = None
+ instance = None
+
+ def __init__(self, deferred, host, port, proxy_addr, auth, instance_factory):
+ self.deferred = deferred
+ self.host = host
+ self.port = port
+ self.proxy_addr = proxy_addr
+ self.auth = auth
+ self.instance_factory = instance_factory
+
+ def connectionMade(self):
+ log.debug("HTTPConnectClient: Proxy connection established: %s:%d" % (log.safe_addr_str(self.proxy_addr.host), self.proxy_addr.port))
+
+ self.sendCommand("CONNECT", "%s:%d" % (self.host, self.port))
+ if self.auth:
+ self.sendHeader("Proxy-Authorization", self.auth)
+ self.endHeaders()
+
+ def connectionLost(self, reason):
+ if self.instance:
+ self.instance.connectionLost(reason)
+ else:
+ self.onConnectionError(reason)
+
+ def handleEndHeaders(self):
+ log.info("HTTPConnectClient: Connected to %s:%d via %s:%d" % (log.safe_addr_str(self.host), self.port, log.safe_addr_str(self.proxy_addr.host), self.proxy_addr.port))
+
+ self.setRawMode()
+ self.instance = self.instance_factory.buildProtocol(self.proxy_addr)
+ self.instance.makeConnection(self.transport)
+ self.deferred.callback(self.instance)
+
+ tmp = self.clearLineBuffer()
+ if tmp:
+ self.instance.dataReceived(tmp)
+
+ def handleStatus(self, version, status, message):
+ if status != "200":
+ self.onConnectionError(IOError("HTTPConnectClient: Proxy returned status: %s" % status))
+
+ def rawDataReceived(self, data):
+ log.debug("HTTPConnectClient: Received %d bytes of proxied data" % len(data))
+ if self.instance:
+ self.instance.dataReceived(data)
+ else:
+ raise RuntimeError("HTTPConnectClient.rawDataReceived() called with no instance")
+
+ def onConnectionError(self, reason):
+ if self.deferred:
+ log.warning("HTTPConnectClient: Connect error: %s" % reason)
+ self.deferred.errback(reason)
+ self.deferred = None
+ self.transport.loseConnection()
+
+class HTTPConnectClientFactory(ClientFactory):
+ deferred = None
+ host = None
+ port = None
+ auth = None
+ instance_factory = None
+
+ def __init__(self, host, port, auth, instance_factory):
+ self.deferred = Deferred()
+ self.host = host
+ self.port = port
+ self.auth = auth
+ self.instance_factory = instance_factory
+
+ def buildProtocol(self, addr):
+ proto = HTTPConnectClient(self.deferred, self.host, self.port, addr, self.auth, self.instance_factory)
+ return proto
+
+ def startedConnecting(self, connector):
+ self.instance_factory.startedConnectiong(connector)
+
+ def clientConnectionFailed(self, connector, reason):
+ self.instance_factory.clientConnectionFailed(connector, reason)
+
+ def clientConnectionLost(self, connector, reason):
+ self.instance_factory.clientConnectionLost(connector, reason)
+
+ at implementer(IStreamClientEndpoint)
+class HTTPConnectClientEndpoint(object):
+ host = None
+ port = None
+ endpoint = None
+ auth = None
+
+ def __init__(self, host, port, endpoint, username=None, password=None):
+ self.host = host
+ self.port = port
+ self.endpoint = endpoint
+ self.auth = _makeBasicAuth(username, password)
+
+ def connect(self, instance_factory):
+ f = HTTPConnectClientFactory(self.host, self.port, self.auth, instance_factory)
+ d = self.endpoint.connect(f)
+ d.addCallback(lambda proto: f.deferred)
+ return d
diff --git a/obfsproxy/network/network.py b/obfsproxy/network/network.py
index a6c135e..2ecc08d 100644
--- a/obfsproxy/network/network.py
+++ b/obfsproxy/network/network.py
@@ -10,6 +10,8 @@ import obfsproxy.common.heartbeat as heartbeat
import obfsproxy.network.buffer as obfs_buf
import obfsproxy.transports.base as base
+from obfsproxy.network.http import HTTPConnectClientEndpoint
+
log = logging.get_obfslogger()
"""
@@ -396,10 +398,11 @@ def create_proxy_client(host, port, proxy_spec, instance):
log.debug("Connecting via %s proxy %s:%d" % (proxy_spec.scheme, log.safe_addr_str(proxy_spec.hostname), proxy_spec.port))
+ TCPPoint = HostnameEndpoint(reactor, proxy_spec.hostname, proxy_spec.port)
+ username = proxy_spec.username
+ password = proxy_spec.password
+
if proxy_spec.scheme in ["socks4a", "socks5"]:
- TCPPoint = HostnameEndpoint(reactor, proxy_spec.hostname, proxy_spec.port)
- username = proxy_spec.username
- password = proxy_spec.password
if proxy_spec.scheme == "socks4a":
if username:
assert(password == None)
@@ -416,8 +419,14 @@ def create_proxy_client(host, port, proxy_spec, instance):
d = SOCKSPoint.connect(instance)
return d
elif proxy_spec.scheme == "http":
- # TODO: This should be supported one day
- raise NotImplementedError("HTTP CONNECT proxy unsupported")
+ if username and password:
+ HTTPPoint = HTTPConnectClientEndpoint(host, port, TCPPoint,
+ username, password)
+ else:
+ assert(username == None and password == None)
+ HTTPPoint = HTTPConnectClientEndpoint(host, port, TCPPoint)
+ d = HTTPPoint.connect(instance)
+ return d
else:
# Should *NEVER* happen
raise RuntimeError("Invalid proxy scheme %s" % proxy_spec.scheme)
diff --git a/obfsproxy/network/socks.py b/obfsproxy/network/socks.py
index 268c1e8..dc09f91 100644
--- a/obfsproxy/network/socks.py
+++ b/obfsproxy/network/socks.py
@@ -76,6 +76,12 @@ class OBFSSOCKSv5OutgoingFactory(protocol.Factory):
def buildProtocol(self, addr):
return OBFSSOCKSv5Outgoing(self.socks)
+ def clientConnectionFailed(self, connector, reason):
+ self.socks.transport.loseConnection()
+
+ def clientConnectionLost(self, connector, reason):
+ self.socks.transport.loseConnection()
+
class OBFSSOCKSv5Protocol(socks5.SOCKSv5Protocol, network.GenericProtocol):
"""
Represents an upstream connection from a SOCKS client to our SOCKS
diff --git a/obfsproxy/network/socks5.py b/obfsproxy/network/socks5.py
index a02759a..5cc62a2 100644
--- a/obfsproxy/network/socks5.py
+++ b/obfsproxy/network/socks5.py
@@ -408,9 +408,6 @@ class SOCKSv5Protocol(protocol.Protocol):
def handleCmdConnectFailure(self, failure):
log.error("CMD CONNECT: %s" % failure.getErrorMessage())
- failure.trap(error.NoRouteError, error.ConnectionRefusedError,
- error.TCPTimedOutError, error.TimeoutError,
- error.UnsupportedAddressFamily)
# Map common twisted errors to SOCKS error codes
if failure.type == error.NoRouteError:
@@ -424,6 +421,10 @@ class SOCKSv5Protocol(protocol.Protocol):
else:
self.sendReply(SOCKSv5Reply.GeneralFailure)
+ failure.trap(error.NoRouteError, error.ConnectionRefusedError,
+ error.TCPTimedOutError, error.TimeoutError,
+ error.UnsupportedAddressFamily)
+
def processCmdBind(self, addr, port):
self.sendReply(SOCKSv5Reply.CommandNotSupported)
diff --git a/obfsproxy/pyobfsproxy.py b/obfsproxy/pyobfsproxy.py
index 6a31646..82a4b54 100755
--- a/obfsproxy/pyobfsproxy.py
+++ b/obfsproxy/pyobfsproxy.py
@@ -133,11 +133,6 @@ def consider_cli_args(args):
log.error("Failed to parse proxy specifier: %s" % e)
sys.exit(1)
- # XXX temporarily: till we implement HTTP
- if proxy.scheme == 'http':
- log.error("obfsproxy does not yet support HTTP CONNECT")
- sys.exit(1)
-
def run_transport_setup(pt_config):
"""Run the setup() method for our transports."""
for transport, transport_class in transports.transports.items():
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/obfsproxy.git
More information about the Pkg-privacy-commits
mailing list