pax_global_header00006660000000000000000000000064122240665730014521gustar00rootroot0000000000000052 comment=b08617662d6a3a0a6be86f77aa1fff9fe23f388e txsocksx-1.13.0.0/000077500000000000000000000000001222406657300136275ustar00rootroot00000000000000txsocksx-1.13.0.0/COPYING000066400000000000000000000013611222406657300146630ustar00rootroot00000000000000Copyright (c) 2010-2013, Aaron Gallagher <_@habnab.it> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. txsocksx-1.13.0.0/MANIFEST.in000066400000000000000000000001071222406657300153630ustar00rootroot00000000000000graft examples include version.txt requirements.txt COPYING README.rst txsocksx-1.13.0.0/PKG-INFO000066400000000000000000000203711222406657300147270ustar00rootroot00000000000000Metadata-Version: 1.1 Name: txsocksx Version: 1.13.0.0 Summary: Twisted client endpoints for SOCKS{4,4a,5} Home-page: https://github.com/habnabit/txsocksx Author: Aaron Gallagher Author-email: _@habnab.it License: ISC Description: .. image:: https://travis-ci.org/habnabit/txsocksx.png :target: https://travis-ci.org/habnabit/txsocksx .. image:: https://coveralls.io/repos/habnabit/txsocksx/badge.png?branch=master :target: https://coveralls.io/r/habnabit/txsocksx?branch=master ======== txsocksx ======== |txsocksx| is SOCKS4/4a and SOCKS5 client endpoints for `Twisted`_ 10.1 or greater. The code is available on github: https://github.com/habnabit/txsocksx Examples ======== These examples assume familiarity with how to use `Twisted endpoints`_. For simplicity, most of the examples will use SOCKS5. Authenticating -------------- One specifies authentication methods to a |SOCKS5ClientEndpoint| via the *methods* parameter. For example, to connect using the username ``spam`` and password ``eggs``:: exampleEndpoint = SOCKS5ClientEndpoint( 'example.com', 6667, proxyEndpoint, methods={'login': ('spam', 'eggs')}) However, this will disable anonymous authentication. To use either login or anonymous authentication, specify both methods:: exampleEndpoint = SOCKS5ClientEndpoint( 'example.com', 6667, proxyEndpoint, methods={'login': ('spam', 'eggs'), 'anonymous': ()}) The ``methods`` dict must always map from a string to a tuple. SOCKS4 ~~~~~~ SOCKS4 has no authentication, but does have a configurable "user ID" which defaults to an empty string:: exampleEndpoint = SOCKS4ClientEndpoint( 'example.com', 6667, proxyEndpoint, user='spam') Connecting to a thing over tor ------------------------------ To connect to ``example.com`` on port 6667 over tor, one creates a |SOCKS5ClientEndpoint| wrapping the endpoint of the tor server:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) Establishing the connection from there proceeds like usual:: deferred = exampleEndpoint.connect(someFactory) |txsocksx| will not do any DNS resolution, so the hostname ``example.com`` will not leak; tor will receive the hostname directly and do the DNS lookup itself. Tor allows connections by SOCKS4 or SOCKS5, and does not expect a user ID to be sent when using the SOCKS4 client. Cancelling a connection ----------------------- Sometimes one tires of waiting and wants to abort the connection attempt. For example, to abort the whole connection attempt after ten seconds:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) deferred = exampleEndpoint.connect(someFactory) reactor.callLater(10, deferred.cancel) This is a trivial example; real code should cancel the `IDelayedCall`_ returned by ``reactor.callLater`` when the deferred fires. The code would then look like this:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) deferred = exampleEndpoint.connect(someFactory) canceler = reactor.callLater(10, deferred.cancel) def cancelCanceler(result): if canceler.active(): canceler.cancel() return result deferred.addBoth(cancelCanceler) Making HTTP requests -------------------- Twisted's builtin `Agent`_ HTTP client does not support being handed an arbitrary endpoint. (Yet. `Ticket #6634`_ was filed to make this an API directly supported by Twisted.) |txsocksx| provides an ``Agent`` as a workaround, but it uses a private API. There are no guarantees that this approach will run in newer versions of Twisted, but |txsocksx.http| will attempt to provide a consistent API. While |txsocksx| requires only Twisted 10.1, |txsocksx.http| requires Twisted 12.1 or greater. Its usage is almost identical to normal ``Agent`` usage:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torServerEndpoint) deferred = agent.request('GET', 'http://example.com/') Note that the ``proxyEndpoint`` parameter *must* be passed as a keyword argument. There is a second, optional, keyword-only argument for passing additional arguments to the |SOCKS5ClientEndpoint| as |SOCKS5Agent| constructs it:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torServerEndpoint, endpointArgs=dict(methods={'login': ('spam', 'eggs')})) deferred = agent.request('GET', 'http://example.com/') |SOCKS5Agent| transparently supports HTTPS via |TLSWrapClientEndpoint|. Upgrading to TLS ---------------- Sometimes one wants to switch to speaking TLS as soon as the proxy negotiation is finished. For that, there is |txsocksx.tls|. After wrapping an endpoint with |TLSWrapClientEndpoint|, the connection will be upgraded to using TLS immediately after proxy negotiation finishes:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) tlsEndpoint = TLSWrapClientEndpoint(exampleEndpoint) deferred = tlsEndpoint.connect(someFactory) Proxying over a proxy --------------------- Because of |txsocksx|'s composable design, it's trivial to connect from one SOCKS proxy to another:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) firstProxyEndpoint = SOCKS5ClientEndpoint( 'first-proxy.example.com', 1080, torServerEndpoint) secondProxyEndpoint = SOCKS4ClientEndpoint( 'second-proxy.example.com', 1080, firstProxyEndpoint) finalHop = SOCKS5ClientEndpoint( 'example.com', 113, secondProxyEndpoint) deferred = finalHop.connect(someFactory) .. _Twisted: http://twistedmatrix.com/ .. _Twisted endpoints: http://twistedmatrix.com/documents/current/core/howto/endpoints.html .. _IDelayedCall: http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IDelayedCall.html .. _Agent: http://twistedmatrix.com/documents/current/web/howto/client.html .. _Ticket #6634: https://twistedmatrix.com/trac/ticket/6634 .. |SOCKS5ClientEndpoint| replace:: ``SOCKS5ClientEndpoint`` .. |SOCKS5Agent| replace:: ``SOCKS5Agent`` .. |TLSWrapClientEndpoint| replace:: ``TLSWrapClientEndpoint`` .. |txsocksx| replace:: ``txsocksx`` .. |txsocksx.http| replace:: ``txsocksx.http`` .. |txsocksx.tls| replace:: ``txsocksx.tls`` Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Framework :: Twisted Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: ISC License (ISCL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2 :: Only Classifier: Topic :: Internet txsocksx-1.13.0.0/README.rst000066400000000000000000000144031222406657300153200ustar00rootroot00000000000000.. image:: https://travis-ci.org/habnabit/txsocksx.png :target: https://travis-ci.org/habnabit/txsocksx .. image:: https://coveralls.io/repos/habnabit/txsocksx/badge.png?branch=master :target: https://coveralls.io/r/habnabit/txsocksx?branch=master ======== txsocksx ======== |txsocksx| is SOCKS4/4a and SOCKS5 client endpoints for `Twisted`_ 10.1 or greater. The code is available on github: https://github.com/habnabit/txsocksx Examples ======== These examples assume familiarity with how to use `Twisted endpoints`_. For simplicity, most of the examples will use SOCKS5. Authenticating -------------- One specifies authentication methods to a |SOCKS5ClientEndpoint| via the *methods* parameter. For example, to connect using the username ``spam`` and password ``eggs``:: exampleEndpoint = SOCKS5ClientEndpoint( 'example.com', 6667, proxyEndpoint, methods={'login': ('spam', 'eggs')}) However, this will disable anonymous authentication. To use either login or anonymous authentication, specify both methods:: exampleEndpoint = SOCKS5ClientEndpoint( 'example.com', 6667, proxyEndpoint, methods={'login': ('spam', 'eggs'), 'anonymous': ()}) The ``methods`` dict must always map from a string to a tuple. SOCKS4 ~~~~~~ SOCKS4 has no authentication, but does have a configurable "user ID" which defaults to an empty string:: exampleEndpoint = SOCKS4ClientEndpoint( 'example.com', 6667, proxyEndpoint, user='spam') Connecting to a thing over tor ------------------------------ To connect to ``example.com`` on port 6667 over tor, one creates a |SOCKS5ClientEndpoint| wrapping the endpoint of the tor server:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) Establishing the connection from there proceeds like usual:: deferred = exampleEndpoint.connect(someFactory) |txsocksx| will not do any DNS resolution, so the hostname ``example.com`` will not leak; tor will receive the hostname directly and do the DNS lookup itself. Tor allows connections by SOCKS4 or SOCKS5, and does not expect a user ID to be sent when using the SOCKS4 client. Cancelling a connection ----------------------- Sometimes one tires of waiting and wants to abort the connection attempt. For example, to abort the whole connection attempt after ten seconds:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) deferred = exampleEndpoint.connect(someFactory) reactor.callLater(10, deferred.cancel) This is a trivial example; real code should cancel the `IDelayedCall`_ returned by ``reactor.callLater`` when the deferred fires. The code would then look like this:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) deferred = exampleEndpoint.connect(someFactory) canceler = reactor.callLater(10, deferred.cancel) def cancelCanceler(result): if canceler.active(): canceler.cancel() return result deferred.addBoth(cancelCanceler) Making HTTP requests -------------------- Twisted's builtin `Agent`_ HTTP client does not support being handed an arbitrary endpoint. (Yet. `Ticket #6634`_ was filed to make this an API directly supported by Twisted.) |txsocksx| provides an ``Agent`` as a workaround, but it uses a private API. There are no guarantees that this approach will run in newer versions of Twisted, but |txsocksx.http| will attempt to provide a consistent API. While |txsocksx| requires only Twisted 10.1, |txsocksx.http| requires Twisted 12.1 or greater. Its usage is almost identical to normal ``Agent`` usage:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torServerEndpoint) deferred = agent.request('GET', 'http://example.com/') Note that the ``proxyEndpoint`` parameter *must* be passed as a keyword argument. There is a second, optional, keyword-only argument for passing additional arguments to the |SOCKS5ClientEndpoint| as |SOCKS5Agent| constructs it:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torServerEndpoint, endpointArgs=dict(methods={'login': ('spam', 'eggs')})) deferred = agent.request('GET', 'http://example.com/') |SOCKS5Agent| transparently supports HTTPS via |TLSWrapClientEndpoint|. Upgrading to TLS ---------------- Sometimes one wants to switch to speaking TLS as soon as the proxy negotiation is finished. For that, there is |txsocksx.tls|. After wrapping an endpoint with |TLSWrapClientEndpoint|, the connection will be upgraded to using TLS immediately after proxy negotiation finishes:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) tlsEndpoint = TLSWrapClientEndpoint(exampleEndpoint) deferred = tlsEndpoint.connect(someFactory) Proxying over a proxy --------------------- Because of |txsocksx|'s composable design, it's trivial to connect from one SOCKS proxy to another:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) firstProxyEndpoint = SOCKS5ClientEndpoint( 'first-proxy.example.com', 1080, torServerEndpoint) secondProxyEndpoint = SOCKS4ClientEndpoint( 'second-proxy.example.com', 1080, firstProxyEndpoint) finalHop = SOCKS5ClientEndpoint( 'example.com', 113, secondProxyEndpoint) deferred = finalHop.connect(someFactory) .. _Twisted: http://twistedmatrix.com/ .. _Twisted endpoints: http://twistedmatrix.com/documents/current/core/howto/endpoints.html .. _IDelayedCall: http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IDelayedCall.html .. _Agent: http://twistedmatrix.com/documents/current/web/howto/client.html .. _Ticket #6634: https://twistedmatrix.com/trac/ticket/6634 .. |SOCKS5ClientEndpoint| replace:: ``SOCKS5ClientEndpoint`` .. |SOCKS5Agent| replace:: ``SOCKS5Agent`` .. |TLSWrapClientEndpoint| replace:: ``TLSWrapClientEndpoint`` .. |txsocksx| replace:: ``txsocksx`` .. |txsocksx.http| replace:: ``txsocksx.http`` .. |txsocksx.tls| replace:: ``txsocksx.tls`` txsocksx-1.13.0.0/examples/000077500000000000000000000000001222406657300154455ustar00rootroot00000000000000txsocksx-1.13.0.0/examples/get-ip-http.py000066400000000000000000000011051222406657300201560ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from __future__ import print_function from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.internet.task import react from twisted.web.client import readBody from txsocksx.http import SOCKS5Agent def main(reactor): torEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torEndpoint) d = agent.request('GET', 'http://api.externalip.net/ip/') d.addCallback(readBody) d.addCallback(print) return d react(main, []) txsocksx-1.13.0.0/examples/get-ip-socks4.py000066400000000000000000000022131222406657300204060ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from __future__ import print_function from twisted.internet.defer import Deferred from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.internet.protocol import Protocol, ClientFactory from twisted.internet.task import react from txsocksx.client import SOCKS4ClientEndpoint class TerribleHTTPClient(Protocol): def connectionMade(self): self.transport.write( "GET /ip/ HTTP/1.1\r\nHost: api.externalip.net\r\n\r\n") self.data = [] self.deferred = Deferred() def dataReceived(self, data): self.data.append(data) def connectionLost(self, reason): self.deferred.callback(''.join(self.data)) class TerribleHTTPClientFactory(ClientFactory): protocol = TerribleHTTPClient def main(reactor): torEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) proxiedEndpoint = SOCKS4ClientEndpoint('api.externalip.net', 80, torEndpoint) d = proxiedEndpoint.connect(TerribleHTTPClientFactory()) d.addCallback(lambda proto: proto.deferred) d.addCallback(print) return d react(main, []) txsocksx-1.13.0.0/examples/get-ip.py000066400000000000000000000022131222406657300172020ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from __future__ import print_function from twisted.internet.defer import Deferred from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.internet.protocol import Protocol, ClientFactory from twisted.internet.task import react from txsocksx.client import SOCKS5ClientEndpoint class TerribleHTTPClient(Protocol): def connectionMade(self): self.transport.write( "GET /ip/ HTTP/1.1\r\nHost: api.externalip.net\r\n\r\n") self.data = [] self.deferred = Deferred() def dataReceived(self, data): self.data.append(data) def connectionLost(self, reason): self.deferred.callback(''.join(self.data)) class TerribleHTTPClientFactory(ClientFactory): protocol = TerribleHTTPClient def main(reactor): torEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) proxiedEndpoint = SOCKS5ClientEndpoint('api.externalip.net', 80, torEndpoint) d = proxiedEndpoint.connect(TerribleHTTPClientFactory()) d.addCallback(lambda proto: proto.deferred) d.addCallback(print) return d react(main, []) txsocksx-1.13.0.0/examples/tor-irc.py000066400000000000000000000033771222406657300174100ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from twisted.internet.defer import Deferred from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.internet.protocol import ClientFactory from twisted.internet.task import react from twisted.words.protocols.irc import IRCClient from twisted.protocols.policies import SpewingFactory from txsocksx.client import SOCKS5ClientEndpoint class TorIRC(IRCClient): nickname = 'txsocksx-tor-irc' nickservPassword = '' def connectionMade(self): self.sendLine('CAP REQ :sasl') self.deferred = Deferred() IRCClient.connectionMade(self) def irc_CAP(self, prefix, params): if params[1] != 'ACK' or params[2].split() != ['sasl']: print 'sasl not available' self.quit('') sasl = ('{0}\0{0}\0{1}'.format(self.nickname, self.nickservPassword)).encode('base64').strip() self.sendLine('AUTHENTICATE PLAIN') self.sendLine('AUTHENTICATE ' + sasl) def irc_903(self, prefix, params): self.sendLine('CAP END') def irc_904(self, prefix, params): print 'sasl auth failed', params self.quit('') irc_905 = irc_904 def connectionLost(self, reason): self.deferred.errback(reason) def signedOn(self): print 'signed on successfully' self.quit('') class TorIRCFactory(ClientFactory): protocol = TorIRC def main(reactor): torEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) # freenode's tor endpoint ircEndpoint = SOCKS5ClientEndpoint('lgttsalmpw3qo4no.onion', 6667, torEndpoint) d = ircEndpoint.connect(SpewingFactory(TorIRCFactory())) d.addCallback(lambda proto: proto.wrappedProtocol.deferred) return d react(main, []) txsocksx-1.13.0.0/requirements.txt000066400000000000000000000000331222406657300171070ustar00rootroot00000000000000Twisted>=10.1 Parsley>=1.2 txsocksx-1.13.0.0/setup.cfg000066400000000000000000000000731222406657300154500ustar00rootroot00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 txsocksx-1.13.0.0/setup.py000066400000000000000000000022201222406657300153350ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from setuptools import setup with open('README.rst', 'rb') as infile: long_description = infile.read() with open('requirements.txt', 'rb') as infile: install_requires = infile.read().split() setup( name='txsocksx', description='Twisted client endpoints for SOCKS{4,4a,5}', long_description=long_description, author='Aaron Gallagher', author_email='_@habnab.it', url='https://github.com/habnabit/txsocksx', classifiers=[ 'Development Status :: 3 - Alpha', 'Framework :: Twisted', 'Intended Audience :: Developers', 'License :: OSI Approved :: ISC License (ISCL)', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2 :: Only', 'Topic :: Internet', ], license='ISC', setup_requires=['vcversioner'], vcversioner={ 'version_module_paths': ['txsocksx/_version.py'], }, install_requires=install_requires, packages=['txsocksx', 'txsocksx.test'], ) txsocksx-1.13.0.0/txsocksx.egg-info/000077500000000000000000000000001222406657300172075ustar00rootroot00000000000000txsocksx-1.13.0.0/txsocksx.egg-info/PKG-INFO000066400000000000000000000203711222406657300203070ustar00rootroot00000000000000Metadata-Version: 1.1 Name: txsocksx Version: 1.13.0.0 Summary: Twisted client endpoints for SOCKS{4,4a,5} Home-page: https://github.com/habnabit/txsocksx Author: Aaron Gallagher Author-email: _@habnab.it License: ISC Description: .. image:: https://travis-ci.org/habnabit/txsocksx.png :target: https://travis-ci.org/habnabit/txsocksx .. image:: https://coveralls.io/repos/habnabit/txsocksx/badge.png?branch=master :target: https://coveralls.io/r/habnabit/txsocksx?branch=master ======== txsocksx ======== |txsocksx| is SOCKS4/4a and SOCKS5 client endpoints for `Twisted`_ 10.1 or greater. The code is available on github: https://github.com/habnabit/txsocksx Examples ======== These examples assume familiarity with how to use `Twisted endpoints`_. For simplicity, most of the examples will use SOCKS5. Authenticating -------------- One specifies authentication methods to a |SOCKS5ClientEndpoint| via the *methods* parameter. For example, to connect using the username ``spam`` and password ``eggs``:: exampleEndpoint = SOCKS5ClientEndpoint( 'example.com', 6667, proxyEndpoint, methods={'login': ('spam', 'eggs')}) However, this will disable anonymous authentication. To use either login or anonymous authentication, specify both methods:: exampleEndpoint = SOCKS5ClientEndpoint( 'example.com', 6667, proxyEndpoint, methods={'login': ('spam', 'eggs'), 'anonymous': ()}) The ``methods`` dict must always map from a string to a tuple. SOCKS4 ~~~~~~ SOCKS4 has no authentication, but does have a configurable "user ID" which defaults to an empty string:: exampleEndpoint = SOCKS4ClientEndpoint( 'example.com', 6667, proxyEndpoint, user='spam') Connecting to a thing over tor ------------------------------ To connect to ``example.com`` on port 6667 over tor, one creates a |SOCKS5ClientEndpoint| wrapping the endpoint of the tor server:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) Establishing the connection from there proceeds like usual:: deferred = exampleEndpoint.connect(someFactory) |txsocksx| will not do any DNS resolution, so the hostname ``example.com`` will not leak; tor will receive the hostname directly and do the DNS lookup itself. Tor allows connections by SOCKS4 or SOCKS5, and does not expect a user ID to be sent when using the SOCKS4 client. Cancelling a connection ----------------------- Sometimes one tires of waiting and wants to abort the connection attempt. For example, to abort the whole connection attempt after ten seconds:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) deferred = exampleEndpoint.connect(someFactory) reactor.callLater(10, deferred.cancel) This is a trivial example; real code should cancel the `IDelayedCall`_ returned by ``reactor.callLater`` when the deferred fires. The code would then look like this:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) deferred = exampleEndpoint.connect(someFactory) canceler = reactor.callLater(10, deferred.cancel) def cancelCanceler(result): if canceler.active(): canceler.cancel() return result deferred.addBoth(cancelCanceler) Making HTTP requests -------------------- Twisted's builtin `Agent`_ HTTP client does not support being handed an arbitrary endpoint. (Yet. `Ticket #6634`_ was filed to make this an API directly supported by Twisted.) |txsocksx| provides an ``Agent`` as a workaround, but it uses a private API. There are no guarantees that this approach will run in newer versions of Twisted, but |txsocksx.http| will attempt to provide a consistent API. While |txsocksx| requires only Twisted 10.1, |txsocksx.http| requires Twisted 12.1 or greater. Its usage is almost identical to normal ``Agent`` usage:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torServerEndpoint) deferred = agent.request('GET', 'http://example.com/') Note that the ``proxyEndpoint`` parameter *must* be passed as a keyword argument. There is a second, optional, keyword-only argument for passing additional arguments to the |SOCKS5ClientEndpoint| as |SOCKS5Agent| constructs it:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) agent = SOCKS5Agent(reactor, proxyEndpoint=torServerEndpoint, endpointArgs=dict(methods={'login': ('spam', 'eggs')})) deferred = agent.request('GET', 'http://example.com/') |SOCKS5Agent| transparently supports HTTPS via |TLSWrapClientEndpoint|. Upgrading to TLS ---------------- Sometimes one wants to switch to speaking TLS as soon as the proxy negotiation is finished. For that, there is |txsocksx.tls|. After wrapping an endpoint with |TLSWrapClientEndpoint|, the connection will be upgraded to using TLS immediately after proxy negotiation finishes:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) exampleEndpoint = SOCKS5ClientEndpoint('example.com', 6667, torServerEndpoint) tlsEndpoint = TLSWrapClientEndpoint(exampleEndpoint) deferred = tlsEndpoint.connect(someFactory) Proxying over a proxy --------------------- Because of |txsocksx|'s composable design, it's trivial to connect from one SOCKS proxy to another:: torServerEndpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 9050) firstProxyEndpoint = SOCKS5ClientEndpoint( 'first-proxy.example.com', 1080, torServerEndpoint) secondProxyEndpoint = SOCKS4ClientEndpoint( 'second-proxy.example.com', 1080, firstProxyEndpoint) finalHop = SOCKS5ClientEndpoint( 'example.com', 113, secondProxyEndpoint) deferred = finalHop.connect(someFactory) .. _Twisted: http://twistedmatrix.com/ .. _Twisted endpoints: http://twistedmatrix.com/documents/current/core/howto/endpoints.html .. _IDelayedCall: http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IDelayedCall.html .. _Agent: http://twistedmatrix.com/documents/current/web/howto/client.html .. _Ticket #6634: https://twistedmatrix.com/trac/ticket/6634 .. |SOCKS5ClientEndpoint| replace:: ``SOCKS5ClientEndpoint`` .. |SOCKS5Agent| replace:: ``SOCKS5Agent`` .. |TLSWrapClientEndpoint| replace:: ``TLSWrapClientEndpoint`` .. |txsocksx| replace:: ``txsocksx`` .. |txsocksx.http| replace:: ``txsocksx.http`` .. |txsocksx.tls| replace:: ``txsocksx.tls`` Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Framework :: Twisted Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: ISC License (ISCL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2 :: Only Classifier: Topic :: Internet txsocksx-1.13.0.0/txsocksx.egg-info/SOURCES.txt000066400000000000000000000012071222406657300210730ustar00rootroot00000000000000COPYING MANIFEST.in README.rst requirements.txt setup.py version.txt examples/get-ip-http.py examples/get-ip-socks4.py examples/get-ip.py examples/tor-irc.py txsocksx/__init__.py txsocksx/_version.py txsocksx/client.py txsocksx/constants.py txsocksx/errors.py txsocksx/grammar.py txsocksx/http.py txsocksx/ssl.py txsocksx/tls.py txsocksx.egg-info/PKG-INFO txsocksx.egg-info/SOURCES.txt txsocksx.egg-info/dependency_links.txt txsocksx.egg-info/requires.txt txsocksx.egg-info/top_level.txt txsocksx/test/__init__.py txsocksx/test/test_client.py txsocksx/test/test_grammar.py txsocksx/test/test_http.py txsocksx/test/test_tls.py txsocksx/test/util.pytxsocksx-1.13.0.0/txsocksx.egg-info/dependency_links.txt000066400000000000000000000000011222406657300232550ustar00rootroot00000000000000 txsocksx-1.13.0.0/txsocksx.egg-info/requires.txt000066400000000000000000000000321222406657300216020ustar00rootroot00000000000000Twisted>=10.1 Parsley>=1.2txsocksx-1.13.0.0/txsocksx.egg-info/top_level.txt000066400000000000000000000000111222406657300217310ustar00rootroot00000000000000txsocksx txsocksx-1.13.0.0/txsocksx/000077500000000000000000000000001222406657300155155ustar00rootroot00000000000000txsocksx-1.13.0.0/txsocksx/__init__.py000066400000000000000000000003511222406657300176250ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. try: from txsocksx._version import __version__, __sha__ except ImportError: __version__ = __sha__ = None __author__ = 'Aaron Gallagher <_@habnab.it>' txsocksx-1.13.0.0/txsocksx/_version.py000066400000000000000000000001431222406657300177110ustar00rootroot00000000000000 # This file is automatically generated by setup.py. __version__ = '1.13.0.0' __sha__ = 'gc4d51fd' txsocksx-1.13.0.0/txsocksx/client.py000066400000000000000000000301671222406657300173540ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. """SOCKS4/4a and SOCKS5 client endpoints. """ import socket import struct from parsley import makeProtocol, stack from twisted.internet import protocol, defer, interfaces from twisted.python import failure from zope.interface import implements import txsocksx.constants as c, txsocksx.errors as e from txsocksx import grammar def socks_host(host): return chr(c.ATYP_DOMAINNAME) + chr(len(host)) + host def validateSOCKS4aHost(host): try: host = socket.inet_pton(socket.AF_INET, host) except socket.error: return if host[:3] == '\0\0\0' and host[3] != '\0': raise ValueError('SOCKS4a reserves addresses 0.0.0.1-0.0.0.255') class _SOCKSClientFactory(protocol.ClientFactory): currentCandidate = None canceled = False def _cancel(self, d): self.currentCandidate.sender.transport.abortConnection() self.canceled = True def buildProtocol(self, addr): proto = self.protocol() proto.factory = self self.currentCandidate = proto return proto def proxyConnectionFailed(self, reason): if not self.canceled: self.deferred.errback(reason) # this method is not called if an endpoint deferred errbacks def clientConnectionFailed(self, connector, reason): self.proxyConnectionFailed(reason) def proxyConnectionEstablished(self, proxyProtocol): proto = self.proxiedFactory.buildProtocol( proxyProtocol.sender.transport.getPeer()) if proto is None: self.deferred.cancel() return proxyProtocol.proxyEstablished(proto) self.deferred.callback(proto) class _SOCKSReceiver(object): def proxyEstablished(self, other): self.otherProtocol = other other.makeConnection(self.sender.transport) # a bit rude, but a huge performance increase if hasattr(self.sender.transport, 'protocol'): self.sender.transport.protocol = other def dataReceived(self, data): self.otherProtocol.dataReceived(data) def finishParsing(self, reason): if self.otherProtocol: self.otherProtocol.connectionLost(reason) else: self.factory.proxyConnectionFailed(reason) class SOCKS5Sender(object): def __init__(self, transport): self.transport = transport def sendAuthMethods(self, methods): self.transport.write( struct.pack('!BB', c.VER_SOCKS5, len(methods)) + ''.join(methods)) def sendLogin(self, username, password): self.transport.write( '\x01' + chr(len(username)) + username + chr(len(password)) + password) def sendRequest(self, command, host, port): data = struct.pack('!BBB', c.VER_SOCKS5, command, c.RSV) port = struct.pack('!H', port) self.transport.write(data + socks_host(host) + port) class SOCKS5AuthDispatcher(object): def __init__(self, wrapped): self.w = wrapped def __getattr__(self, attr): return getattr(self.w, attr) def authSelected(self, method): if method not in self.w.factory.methods: raise e.MethodsNotAcceptedError('no method proprosed was accepted', self.w.factory.methods, method) authMethod = getattr(self.w, 'auth_' + self.w.authMethodMap[method]) authMethod(*self.w.factory.methods[method]) class SOCKS5Receiver(_SOCKSReceiver): implements(interfaces.ITransport) otherProtocol = None currentRule = 'SOCKS5ClientState_initial' def __init__(self, sender): self.sender = sender def prepareParsing(self, parser): self.factory = parser.factory self.sender.sendAuthMethods(self.factory.methods) authMethodMap = { c.AUTH_ANONYMOUS: 'anonymous', c.AUTH_LOGIN: 'login', } def auth_anonymous(self): self._sendRequest() def auth_login(self, username, password): self.sender.sendLogin(username, password) self.currentRule = 'SOCKS5ClientState_readLoginResponse' def loginResponse(self, success): if not success: raise e.LoginAuthenticationFailed( 'username/password combination was rejected') self._sendRequest() def _sendRequest(self): self.sender.sendRequest( c.CMD_CONNECT, self.factory.host, self.factory.port) self.currentRule = 'SOCKS5ClientState_readResponse' def serverResponse(self, status, address, port): if status != c.SOCKS5_GRANTED: raise e.socks5ErrorMap.get(status)() self.factory.proxyConnectionEstablished(self) self.currentRule = 'SOCKSState_readData' SOCKS5Client = makeProtocol( grammar.grammarSource, SOCKS5Sender, stack(SOCKS5AuthDispatcher, SOCKS5Receiver), grammar.bindings) class SOCKS5ClientFactory(_SOCKSClientFactory): protocol = SOCKS5Client authMethodMap = { 'anonymous': c.AUTH_ANONYMOUS, 'login': c.AUTH_LOGIN, } def __init__(self, host, port, proxiedFactory, methods={'anonymous': ()}): if not methods: raise ValueError('no auth methods were specified') self.host = host self.port = port self.proxiedFactory = proxiedFactory self.methods = dict( (self.authMethodMap[method], value) for method, value in methods.iteritems()) self.deferred = defer.Deferred(self._cancel) class SOCKS5ClientEndpoint(object): """An endpoint which does SOCKS5 negotiation. :param host: The hostname to connect to through the SOCKS5 server. This will not be resolved by ``txsocksx`` but will be sent without modification to the SOCKS5 server to be resolved remotely. :param port: The port to connect to through the SOCKS5 server. :param proxyEndpoint: The endpoint of the SOCKS5 server. This must provide `IStreamClientEndpoint`__. :param methods: The authentication methods to try. Authentication methods are specified as a dict mapping from method names to tuples. By default, the only method tried is anonymous authentication, so the default *methods* is ``{'anonymous': ()}``. The ``anonymous`` auth method must map to an empty tuple if provided. The other method available by default is ``login``. ``login`` must map to a tuple of ``(username, password)``. __ http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IStreamClientEndpoint.html """ implements(interfaces.IStreamClientEndpoint) def __init__(self, host, port, proxyEndpoint, methods={'anonymous': ()}): if not methods: raise ValueError('no auth methods were specified') self.host = host self.port = port self.proxyEndpoint = proxyEndpoint self.methods = methods def connect(self, fac): """Connect over SOCKS5. The provided factory will have its ``buildProtocol`` method once a SOCKS5 connection has been successfully negotiated. Returns a ``Deferred`` which will fire with the resulting ``Protocol`` when negotiation finishes, or errback for a variety of reasons. For example: 1. If the ``Deferred`` returned by ``proxyEndpoint.connect`` errbacks (e.g. the connection to the SOCKS5 server was refused). 2. If the SOCKS5 server gave a non-success response. 3. If the SOCKS5 server did not reply with valid SOCKS5. 4. If the ``Deferred`` returned from ``connect`` was cancelled. The returned ``Deferred`` is cancelable during negotiation: the connection will immediately close and the ``Deferred`` will errback with a ``CancelledError``. The ``Deferred`` can be canceled before negotiation starts only if the ``Deferred`` returned by ``proxyEndpoint.connect`` is cancelable. If the factory's ``buildProtocol`` returns ``None``, the connection will immediately close. """ proxyFac = SOCKS5ClientFactory(self.host, self.port, fac, self.methods) d = self.proxyEndpoint.connect(proxyFac) d.addCallback(lambda proto: proxyFac.deferred) return d class SOCKS4Sender(object): def __init__(self, transport): self.transport = transport def sendRequest(self, host, port, user): data = struct.pack('!BBH', c.VER_SOCKS4, c.CMD_CONNECT, port) try: host = socket.inet_pton(socket.AF_INET, host) except socket.error: host, suffix = '\0\0\0\1', host + '\0' else: suffix = '' self.transport.write(data + host + user + '\0' + suffix) class SOCKS4Receiver(_SOCKSReceiver): implements(interfaces.ITransport) otherProtocol = None currentRule = 'SOCKS4ClientState_initial' def __init__(self, sender): self.sender = sender def prepareParsing(self, parser): self.factory = parser.factory self.sender.sendRequest(self.factory.host, self.factory.port, self.factory.user) def serverResponse(self, status, host, port): if status != c.SOCKS4_GRANTED: raise e.socks4ErrorMap.get(status)() self.factory.proxyConnectionEstablished(self) self.currentRule = 'SOCKSState_readData' SOCKS4Client = makeProtocol( grammar.grammarSource, SOCKS4Sender, SOCKS4Receiver, grammar.bindings) class SOCKS4ClientFactory(_SOCKSClientFactory): protocol = SOCKS4Client def __init__(self, host, port, proxiedFactory, user=''): validateSOCKS4aHost(host) self.host = host self.port = port self.user = user self.proxiedFactory = proxiedFactory self.deferred = defer.Deferred(self._cancel) class SOCKS4ClientEndpoint(object): """An endpoint which does SOCKS4 or SOCKS4a negotiation. :param host: The hostname or IP to connect to through the SOCKS4 server. If this is a valid IPv4 address, it will be sent to the server as a SOCKS4 request. Otherwise, *host* will be sent as a hostname in a SOCKS4a request. In the SOCKS4a case, the hostname will not be resolved by ``txsocksx`` but will be sent without modification to the SOCKS4 server to be resolved remotely. :param port: The port to connect to through the SOCKS4 server. :param proxyEndpoint: The endpoint of the SOCKS4 server. This must provide `IStreamClientEndpoint`__. :param user: The user ID to send to the SOCKS4 server. __ http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IStreamClientEndpoint.html """ implements(interfaces.IStreamClientEndpoint) def __init__(self, host, port, proxyEndpoint, user=''): validateSOCKS4aHost(host) self.host = host self.port = port self.proxyEndpoint = proxyEndpoint self.user = user def connect(self, fac): """Connect over SOCKS4. The provided factory will have its ``buildProtocol`` method once a SOCKS4 connection has been successfully negotiated. Returns a ``Deferred`` which will fire with the resulting ``Protocol`` when negotiation finishes, or errback for a variety of reasons. For example: 1. If the ``Deferred`` returned by ``proxyEndpoint.connect`` errbacks (e.g. the connection to the SOCKS4 server was refused). 2. If the SOCKS4 server gave a non-success response. 3. If the SOCKS4 server did not reply with valid SOCKS4. 4. If the ``Deferred`` returned from ``connect`` was cancelled. The returned ``Deferred`` is cancelable during negotiation: the connection will immediately close and the ``Deferred`` will errback with a ``CancelledError``. The ``Deferred`` can be canceled before negotiation starts only if the ``Deferred`` returned by ``proxyEndpoint.connect`` is cancelable. If the factory's ``buildProtocol`` returns ``None``, the connection will immediately close. """ proxyFac = SOCKS4ClientFactory(self.host, self.port, fac, self.user) d = self.proxyEndpoint.connect(proxyFac) d.addCallback(lambda proto: proxyFac.deferred) return d txsocksx-1.13.0.0/txsocksx/constants.py000066400000000000000000000012071222406657300201030ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. VER_SOCKS4, VER_SOCKS5 = 4, 5 AUTH_ANONYMOUS, AUTH_LOGIN = '\x00', '\x02' ATYP_IPV4, ATYP_DOMAINNAME, ATYP_IPV6 = 1, 3, 4 CMD_CONNECT, CMD_BIND, CMD_UDP_ASSOCIATE = 1, 2, 3 NO_ACCEPTABLE_METHODS = 255 RSV = 0 SOCKS5_GRANTED, SOCKS5_GENERAL_FAILURE, SOCKS5_REJECTED = 0, 1, 2 SOCKS5_NETWORK_UNREACHABLE, SOCKS5_HOST_UNREACHABLE = 3, 4 SOCKS5_CONNECTION_REFUSED, SOCKS5_TTL_EXPIRED = 5, 6 SOCKS5_COMMAND_NOT_SUPPORTED, SOCKS5_ADDRESS_NOT_SUPPORTED = 7, 8 SOCKS4_GRANTED, SOCKS4_REJECTED_OR_FAILED = 0x5a, 0x5b SOCKS4_IDENTD_UNREACHABLE, SOCKS4_IDENTD_MISMATCH = 0x5c, 0x5d txsocksx-1.13.0.0/txsocksx/errors.py000066400000000000000000000045741222406657300174150ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. import txsocksx.constants as c class SOCKSError(Exception): pass class ConnectionLostEarly(SOCKSError): """ XXX this is actually no longer being used, but is needed for backward compatibility. One day this will be removed. """ pass class MethodsNotAcceptedError(SOCKSError): pass class ConnectionError(SOCKSError): pass class LoginAuthenticationFailed(SOCKSError): pass class ParsingError(Exception): pass class InvalidServerVersion(Exception): pass class InvalidServerReply(Exception): pass class SOCKSError(Exception): pass class StateError(Exception): """ There was a problem with the State. """ pass class NoAcceptableMethods(SOCKSError): """ No Acceptable Methods ( FF ) """ class ServerFailure(SOCKSError): """ General SOCKS server failure ( 1 ) """ class ConnectionNotAllowed(SOCKSError): """ Connection not allowed ( 2 ) """ class NetworkUnreachable(SOCKSError): """ Network unreachable ( 3 ) """ class HostUnreachable(SOCKSError): """ Host unreachable ( 4 ) """ class ConnectionRefused(SOCKSError): """ Connection refused ( 5 ) """ class TTLExpired(SOCKSError): """ TTL expired ( 6 ) """ class CommandNotSupported(SOCKSError): """ Command Not Supported ( 7 ) """ class AddressNotSupported(SOCKSError): """ Address type not supported ( 8 ) """ socks5ErrorMap = { c.SOCKS5_GENERAL_FAILURE: ServerFailure, c.SOCKS5_REJECTED: ConnectionNotAllowed, c.SOCKS5_NETWORK_UNREACHABLE: NetworkUnreachable, c.SOCKS5_HOST_UNREACHABLE: HostUnreachable, c.SOCKS5_CONNECTION_REFUSED: ConnectionRefused, c.SOCKS5_TTL_EXPIRED: TTLExpired, c.SOCKS5_COMMAND_NOT_SUPPORTED: CommandNotSupported, c.SOCKS5_ADDRESS_NOT_SUPPORTED: AddressNotSupported, } class RequestRejectedOrFailed(SOCKSError): """ Request rejected or failed (0x5b) """ class IdentdUnreachable(SOCKSError): """ Identd not running or unreachable (0x5c) """ class IdentdMismatch(SOCKSError): """ Identd could not confirm the request's user ID (0x5a) """ socks4ErrorMap = { c.SOCKS4_REJECTED_OR_FAILED: RequestRejectedOrFailed, c.SOCKS4_IDENTD_UNREACHABLE: IdentdUnreachable, c.SOCKS4_IDENTD_MISMATCH: IdentdMismatch, } txsocksx-1.13.0.0/txsocksx/grammar.py000066400000000000000000000046441222406657300175250ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. import socket grammarSource = r""" byte = anything:b -> ord(b) short = byte:high byte:low -> (high << 8) | low cstring = <(~'\x00' anything)*>:string '\x00' -> string ipv4Address = :packed -> socket.inet_ntop(socket.AF_INET, packed) ipv6Address = :packed -> socket.inet_ntop(socket.AF_INET6, packed) SOCKS4Command = ( '\x01' -> 'tcp-connect' | '\x02' -> 'tcp-bind' ) SOCKS4HostUser = ipv4Address:host cstring:user -> (host, user) SOCKS4aHostUser = ( '\x00'{3} ~'\x00' anything cstring:user cstring:host -> (host, user) | SOCKS4HostUser ) SOCKS4Request = '\x04' SOCKS4Command:command short:port SOCKS4aHostUser:hostuser -> (command, port) + hostuser SOCKS4Response = '\x00' byte:status short:port ipv4Address:address -> (status, address, port) SOCKS4ServerState_initial = SOCKS4Request:request -> receiver.clientRequest(*request) SOCKS4ClientState_initial = SOCKS4Response:response -> receiver.serverResponse(*response) SOCKS5Command = (SOCKS4Command | '\x03' -> 'udp-associate') SOCKS5Hostname = byte:length :host -> host SOCKS5Address = ( '\x01' ipv4Address:address -> address | '\x03' SOCKS5Hostname:host -> host | '\x04' ipv6Address:address -> address ) SOCKS5ServerAuthSelection = '\x05' anything SOCKS5ServerLoginResponse = anything anything:status -> status == '\x00' SOCKS5ServerResponse = '\x05' byte:status '\x00' SOCKS5Address:address short:port -> (status, address, port) SOCKS5ClientGreeting = '\x05' byte:authMethodCount byte{authMethodCount}:authMethods -> authMethods or [] SOCKS5ClientRequest = '\x05' SOCKS5Command:command '\x00' SOCKS5Address:address short:port -> (command, address, port) SOCKS5ServerState_initial = SOCKS5ClientGreeting:authMethods -> receiver.authRequested(authMethods) SOCKS5ServerState_readRequest = SOCKS5ClientRequest:request -> receiver.clientRequest(*request) SOCKS5ClientState_initial = SOCKS5ServerAuthSelection:selection -> receiver.authSelected(selection) SOCKS5ClientState_readLoginResponse = SOCKS5ServerLoginResponse:response -> receiver.loginResponse(response) SOCKS5ClientState_readResponse = SOCKS5ServerResponse:response -> receiver.serverResponse(*response) SOCKSState_readData = anything:data -> receiver.dataReceived(data) """ bindings = {'socket': socket} txsocksx-1.13.0.0/txsocksx/http.py000066400000000000000000000043731222406657300170550ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. """``twisted.web.client`` adapters for SOCKS4/4a and SOCKS5 connections. This requires Twisted 12.1 or greater to use. """ from twisted.web.client import Agent, SchemeNotSupported from txsocksx.client import SOCKS4ClientEndpoint, SOCKS5ClientEndpoint from txsocksx.tls import TLSWrapClientEndpoint class _SOCKSAgent(Agent): endpointFactory = None _tlsWrapper = TLSWrapClientEndpoint def __init__(self, *a, **kw): self.proxyEndpoint = kw.pop('proxyEndpoint') self.endpointArgs = kw.pop('endpointArgs', {}) super(_SOCKSAgent, self).__init__(*a, **kw) def _getEndpoint(self, scheme, host, port): if scheme not in ('http', 'https'): raise SchemeNotSupported('unsupported scheme', scheme) endpoint = self.endpointFactory( host, port, self.proxyEndpoint, **self.endpointArgs) if scheme == 'https': endpoint = self._tlsWrapper( self._wrapContextFactory(host, port), endpoint) return endpoint class SOCKS4Agent(_SOCKSAgent): """An `Agent`__ which connects over SOCKS4. See |SOCKS5Agent| for details. __ http://twistedmatrix.com/documents/current/api/twisted.web.client.Agent.html .. |SOCKS5Agent| replace:: ``SOCKS5Agent`` """ endpointFactory = SOCKS4ClientEndpoint class SOCKS5Agent(_SOCKSAgent): """An ``Agent`` which connects over SOCKS5. :param proxyEndpoint: The same as *proxyEndpoint* for |SOCKS5ClientEndpoint|: the endpoint of the SOCKS5 proxy server. This argument must be passed as a keyword argument. :param endpointArgs: A dict of keyword arguments which will be passed when constructing the |SOCKS5ClientEndpoint|. For example, this could be ``{'methods': {'anonymous': ()}}``. The rest of the parameters, methods, and overall behavior is identical to `Agent`__. The ``connectTimeout`` and ``bindAddress`` arguments will be ignored and should be specified when constructing the *proxyEndpoint*. __ http://twistedmatrix.com/documents/current/api/twisted.web.client.Agent.html .. |SOCKS5ClientEndpoint| replace:: ``SOCKS5ClientEndpoint`` """ endpointFactory = SOCKS5ClientEndpoint txsocksx-1.13.0.0/txsocksx/ssl.py000066400000000000000000000040421222406657300166700ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. """SSL/TLS convenience wrappers for endpoints. This module is deprecated; please use ``txsocksx.tls`` instead. """ from twisted.protocols import tls from twisted.internet import interfaces from zope.interface import implements class SSLWrapClientEndpoint(object): implements(interfaces.IStreamClientEndpoint) def __init__(self, contextFactory, wrappedEndpoint): self.contextFactory = contextFactory self.wrappedEndpoint = wrappedEndpoint def connect(self, fac): fac = tls.TLSMemoryBIOFactory(self.contextFactory, True, fac) return self.wrappedEndpoint.connect(fac) class TLSStarterClientEndpointWrapper(object): """An endpoint which automatically starts TLS. :param contextFactory: A `ContextFactory`__ instance. :param wrappedEndpoint: The endpoint to wrap. __ http://twistedmatrix.com/documents/current/api/twisted.internet.protocol.ClientFactory.html """ implements(interfaces.IStreamClientEndpoint) def __init__(self, contextFactory, wrappedEndpoint): self.contextFactory = contextFactory self.wrappedEndpoint = wrappedEndpoint def _startTLS(self, proto): proto.transport.startTLS(self.contextFactory) return proto def connect(self, fac): """Connect to the wrapped endpoint, then start TLS. ``wrappedEndpoint.connect(fac)`` must return a ``Deferred`` which will fire with a ``Protocol`` whose transport implements `ITLSTransport`__ for the ``startTLS`` method. ``startTLS`` will be called immediately after the ``Deferred`` fires. :returns: A ``Deferred`` which fires with the same ``Protocol`` as ``wrappedEndpoint.connect(fac)`` fires with. If that ``Deferred`` errbacks, so will the returned deferred. __ http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.ITLSTransport.html """ return self.wrappedEndpoint.connect(fac).addCallback(self._startTLS) txsocksx-1.13.0.0/txsocksx/test/000077500000000000000000000000001222406657300164745ustar00rootroot00000000000000txsocksx-1.13.0.0/txsocksx/test/__init__.py000066400000000000000000000001111222406657300205760ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. txsocksx-1.13.0.0/txsocksx/test/test_client.py000066400000000000000000000545541222406657300214000ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from parsley import makeProtocol, stack from twisted.internet.error import ConnectionLost, ConnectionRefusedError from twisted.internet import defer, protocol from twisted.python import failure, log from twisted.trial import unittest from twisted.test import proto_helpers from txsocksx.test.util import FakeEndpoint from txsocksx import client, errors, grammar import txsocksx.constants as c connectionLostFailure = failure.Failure(ConnectionLost()) connectionRefusedFailure = failure.Failure(ConnectionRefusedError()) class FakeSOCKS5ClientFactory(protocol.ClientFactory): protocol = client.SOCKS5Client def __init__(self, host='', port=0, methods={c.AUTH_ANONYMOUS: ()}): self.host = host self.port = port self.methods = methods self.reason = None self.accum = proto_helpers.AccumulatingProtocol() self.expectingReason = False def proxyConnectionFailed(self, reason): if self.expectingReason: self.reason = reason else: log.err(reason) def proxyConnectionEstablished(self, proxyProtocol): proxyProtocol.proxyEstablished(self.accum) class FakeSOCKS4ClientFactory(protocol.ClientFactory): protocol = client.SOCKS4Client def __init__(self, host='', port=0, user=''): self.host = host self.port = port self.user = user self.reason = None self.accum = proto_helpers.AccumulatingProtocol() self.expectingReason = False def proxyConnectionFailed(self, reason): if self.expectingReason: self.reason = reason else: log.err(reason) def proxyConnectionEstablished(self, proxyProtocol): proxyProtocol.proxyEstablished(self.accum) authAdditionGrammar = """ authAddition = 'addition' anything:x -> receiver.authedAddition(x) """ class AuthAdditionWrapper(object): def __init__(self, wrapped): self.w = wrapped def __getattr__(self, attr): return getattr(self.w, attr) authMethodMap = { c.AUTH_ANONYMOUS: 'anonymous', c.AUTH_LOGIN: 'login', 'A': 'addition', } additionArgs = additionParsed = None def auth_addition(self, *a): self.additionArgs = a self.sender.transport.write('addition!') self.currentRule = 'authAddition' def authedAddition(self, x): self.additionParsed = x del self.currentRule self.w._sendRequest() AdditionAuthSOCKS5Client = makeProtocol( grammar.grammarSource + authAdditionGrammar, client.SOCKS5Sender, stack(client.SOCKS5AuthDispatcher, AuthAdditionWrapper, client.SOCKS5Receiver), grammar.bindings) class TestSOCKS5Client(unittest.TestCase): def makeProto(self, *a, **kw): protoClass = kw.pop('_protoClass', client.SOCKS5Client) fac = FakeSOCKS5ClientFactory(*a, **kw) fac.protocol = protoClass proto = fac.buildProtocol(None) transport = proto_helpers.StringTransport() transport.abortConnection = lambda: None proto.makeConnection(transport) return fac, proto def test_initialHandshake(self): fac, proto = self.makeProto() self.assertEqual(proto.transport.value(), '\x05\x01\x00') fac, proto = self.makeProto(methods={c.AUTH_ANONYMOUS: (), c.AUTH_LOGIN: ()}) self.assertEqual(proto.transport.value(), '\x05\x02\x00\x02') fac, proto = self.makeProto(methods={c.AUTH_LOGIN: ()}) self.assertEqual(proto.transport.value(), '\x05\x01\x02') def test_failedMethodSelection(self): fac, proto = self.makeProto() fac.expectingReason = True proto.dataReceived('\x05\xff') self.failIfEqual(fac.reason, None) self.failUnlessIsInstance( fac.reason.value, errors.MethodsNotAcceptedError) self.assertEqual(fac.reason.value.args[2], '\xff') def test_loginAuth(self): fac, proto = self.makeProto(methods={c.AUTH_LOGIN: ('spam', 'eggs')}) proto.transport.clear() proto.dataReceived('\x05\x02') self.assertEqual(proto.transport.value(), '\x01\x04spam\x04eggs') def test_loginAuthAccepted(self): fac, proto = self.makeProto(methods={c.AUTH_LOGIN: ('spam', 'eggs')}) proto.dataReceived('\x05\x02') proto.transport.clear() proto.dataReceived('\x01\x00') self.assert_(proto.transport.value()) def test_loginAuthFailed(self): fac, proto = self.makeProto(methods={c.AUTH_LOGIN: ('spam', 'eggs')}) fac.expectingReason = True proto.dataReceived('\x05\x02\x01\x01') self.failIfEqual(fac.reason, None) self.failUnlessIsInstance( fac.reason.value, errors.LoginAuthenticationFailed) def test_connectionRequest(self): fac, proto = self.makeProto('host', 0x47) proto.transport.clear() proto.dataReceived('\x05\x00') self.assertEqual(proto.transport.value(), '\x05\x01\x00\x03\x04host\x00\x47') fac, proto = self.makeProto('longerhost', 0x9494) proto.transport.clear() proto.dataReceived('\x05\x00') self.assertEqual(proto.transport.value(), '\x05\x01\x00\x03\x0alongerhost\x94\x94') def test_handshakeEatsEnoughBytes(self): fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x01444422xxxxx') self.assertEqual(fac.accum.data, 'xxxxx') fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x04666666666666666622xxxxx') self.assertEqual(fac.accum.data, 'xxxxx') fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x03\x08somehost22xxxxx') self.assertEqual(fac.accum.data, 'xxxxx') fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x03\x0022xxxxx') self.assertEqual(fac.accum.data, 'xxxxx') def test_connectionRequestError(self): fac, proto = self.makeProto() fac.expectingReason = True proto.dataReceived('\x05\x00\x05\x01\x00\x03\x0022') self.failIfEqual(fac.reason, None) self.failUnlessIsInstance(fac.reason.value, errors.ServerFailure) def test_buffering(self): fac, proto = self.makeProto() for c in '\x05\x00\x05\x00\x00\x01444422xxxxx': proto.dataReceived(c) self.assertEqual(fac.accum.data, 'xxxxx') def test_connectionLostEarly(self): wholeRequest = '\x05\x00\x05\x00\x00\x01444422' for e in xrange(len(wholeRequest)): partialRequest = wholeRequest[:e] fac, proto = self.makeProto() fac.expectingReason = True if partialRequest: proto.dataReceived(partialRequest) proto.connectionLost(connectionLostFailure) self.failUnlessIsInstance(fac.reason.value, ConnectionLost) def test_connectionLostAfterNegotiation(self): fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') proto.connectionLost(connectionLostFailure) self.assertEqual(fac.accum.closedReason, connectionLostFailure) fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x01444422xxxxx') proto.connectionLost(connectionLostFailure) self.assertEqual(fac.accum.closedReason, connectionLostFailure) self.assertEqual(fac.accum.data, 'xxxxx') def test_authAddition(self): fac, proto = self.makeProto( _protoClass=AdditionAuthSOCKS5Client, methods={'A': ('x', 'y')}) proto.transport.clear() proto.dataReceived('\x05A') self.assertEqual(proto.transport.value(), 'addition!') self.assertEqual(proto.receiver.additionArgs, ('x', 'y')) proto.dataReceived('additionz') self.assertEqual(proto.receiver.additionParsed, 'z') proto.dataReceived('\x05\x00\x00\x01444422xxxxx') self.assertEqual(fac.accum.data, 'xxxxx') def test_dataSentByPeer(self): fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') proto.transport.clear() fac.accum.transport.write('xxxxx') self.assertEqual(proto.transport.value(), 'xxxxx') def test_protocolSwitchingWithoutAProtocolAttribute(self): fac, proto = self.makeProto() proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') self.assertFalse(hasattr(proto.transport, 'protocol')) def test_protocolSwitching(self): fac, proto = self.makeProto() proto.transport.protocol = None proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') self.assertEqual(proto.transport.protocol, fac.accum) class TestSOCKS4Client(unittest.TestCase): def makeProto(self, *a, **kw): protoClass = kw.pop('_protoClass', client.SOCKS4Client) fac = FakeSOCKS4ClientFactory(*a, **kw) fac.protocol = protoClass proto = fac.buildProtocol(None) transport = proto_helpers.StringTransport() transport.abortConnection = lambda: None proto.makeConnection(transport) return fac, proto def test_initialHandshake(self): fac, proto = self.makeProto(host='0.0.0.0', port=0x1234) self.assertEqual(proto.transport.value(), '\x04\x01\x12\x34\x00\x00\x00\x00\x00') def test_initialHandshakeWithHostname(self): fac, proto = self.makeProto(host='example.com', port=0x4321) self.assertEqual(proto.transport.value(), '\x04\x01\x43\x21\x00\x00\x00\x01\x00example.com\x00') def test_initialHandshakeWithUser(self): fac, proto = self.makeProto(host='0.0.0.0', port=0x1234, user='spam') self.assertEqual(proto.transport.value(), '\x04\x01\x12\x34\x00\x00\x00\x00spam\x00') def test_initialHandshakeWithUserAndHostname(self): fac, proto = self.makeProto(host='spam.com', port=0x1234, user='spam') self.assertEqual(proto.transport.value(), '\x04\x01\x12\x34\x00\x00\x00\x01spam\x00spam.com\x00') def test_handshakeEatsEnoughBytes(self): fac, proto = self.makeProto() proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00xxxxx') self.assertEqual(fac.accum.data, 'xxxxx') def test_connectionRequestError(self): fac, proto = self.makeProto() fac.expectingReason = True proto.dataReceived('\x00\x5b\x00\x00\x00\x00\x00\x00xxxxx') self.failIfEqual(fac.reason, None) self.failUnlessIsInstance(fac.reason.value, errors.RequestRejectedOrFailed) def test_buffering(self): fac, proto = self.makeProto() for c in '\x00\x5a\x00\x00\x00\x00\x00\x00xxxxx': proto.dataReceived(c) self.assertEqual(fac.accum.data, 'xxxxx') def test_connectionLostEarly(self): wholeRequest = '\x00\x5a\x00\x00\x00\x00\x00\x00' for e in xrange(len(wholeRequest)): partialRequest = wholeRequest[:e] fac, proto = self.makeProto() fac.expectingReason = True if partialRequest: proto.dataReceived(partialRequest) proto.connectionLost(connectionLostFailure) self.failUnlessIsInstance(fac.reason.value, ConnectionLost) def test_connectionLostAfterNegotiation(self): fac, proto = self.makeProto() proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') proto.connectionLost(connectionLostFailure) self.assertEqual(fac.accum.closedReason, connectionLostFailure) def test_connectionLostAfterNegotiationWithSomeBytes(self): fac, proto = self.makeProto() proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00xxxxx') proto.connectionLost(connectionLostFailure) self.assertEqual(fac.accum.closedReason, connectionLostFailure) self.assertEqual(fac.accum.data, 'xxxxx') def test_dataSentByPeer(self): fac, proto = self.makeProto() proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') proto.transport.clear() fac.accum.transport.write('xxxxx') self.assertEqual(proto.transport.value(), 'xxxxx') def test_protocolSwitchingWithoutAProtocolAttribute(self): fac, proto = self.makeProto() proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') self.assertFalse(hasattr(proto.transport, 'protocol')) def test_protocolSwitching(self): fac, proto = self.makeProto() proto.transport.protocol = None proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') self.assertEqual(proto.transport.protocol, fac.accum) class FakeFactory(protocol.ClientFactory): protocol = proto_helpers.AccumulatingProtocol def __init__(self, returnNoProtocol=False): self.returnNoProtocol = returnNoProtocol self.protocolConnectionMade = defer.Deferred() def buildProtocol(self, addr): if self.returnNoProtocol: return None self.proto = protocol.ClientFactory.buildProtocol(self, addr) return self.proto class _TestSOCKSClientFactoryCommon(object): def setUp(self): self.aborted = [] def makeProto(self, *a, **kw): fac = self.factory(*a, **kw) proto = fac.buildProtocol(None) transport = proto_helpers.StringTransport() transport.abortConnection = lambda: self.aborted.append(True) proto.makeConnection(transport) return fac, proto def test_cancellation(self): fac, proto = self.makeProto('', 0, None) fac.deferred.cancel() self.assert_(self.aborted) return self.assertFailure(fac.deferred, defer.CancelledError) def test_cancellationBeforeFailure(self): fac, proto = self.makeProto('', 0, None) fac.deferred.cancel() proto.connectionLost(connectionLostFailure) self.assert_(self.aborted) return self.assertFailure(fac.deferred, defer.CancelledError) def test_cancellationAfterFailure(self): fac, proto = self.makeProto('', 0, None) proto.connectionLost(connectionLostFailure) fac.deferred.cancel() self.assertFalse(self.aborted) return self.assertFailure(fac.deferred, ConnectionLost) def test_clientConnectionFailed(self): fac, proto = self.makeProto('', 0, None) fac.clientConnectionFailed(None, connectionRefusedFailure) return self.assertFailure(fac.deferred, ConnectionRefusedError) class TestSOCKS5ClientFactory(_TestSOCKSClientFactoryCommon, unittest.TestCase): factory = client.SOCKS5ClientFactory def test_defaultFactory(self): fac, proto = self.makeProto('', 0, None) self.assertEqual(proto.transport.value(), '\x05\x01\x00') def test_anonymousAndLoginAuth(self): fac, proto = self.makeProto('', 0, None, methods={'anonymous': (), 'login': ()}) self.assertEqual(proto.transport.value(), '\x05\x02\x00\x02') def test_justLoginAuth(self): fac, proto = self.makeProto('', 0, None, methods={'login': ()}) self.assertEqual(proto.transport.value(), '\x05\x01\x02') def test_noAuthMethodsFails(self): self.assertRaises( ValueError, client.SOCKS5ClientFactory, None, None, None, methods={}) def test_loginAuth(self): fac, proto = self.makeProto('', 0, None, methods={'login': ('spam', 'eggs')}) proto.transport.clear() proto.dataReceived('\x05\x02') self.assertEqual(proto.transport.value(), '\x01\x04spam\x04eggs') def test_loginAuthAccepted(self): fac, proto = self.makeProto('', 0, None, methods={'login': ('spam', 'eggs')}) proto.dataReceived('\x05\x02') proto.transport.clear() proto.dataReceived('\x01\x00') self.assert_(proto.transport.value()) def test_buildingWrappedFactory(self): wrappedFac = FakeFactory() fac, proto = self.makeProto('', 0, wrappedFac) proto.dataReceived('\x05\x00\x05\x00\x00\x01444422xxxxx') self.assertEqual(wrappedFac.proto.data, 'xxxxx') def test_noProtocolFromWrappedFactory(self): wrappedFac = FakeFactory(returnNoProtocol=True) fac, proto = self.makeProto('', 0, wrappedFac) proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') self.assert_(self.aborted) return self.assertFailure(fac.deferred, defer.CancelledError) def test_dataSentByPeer(self): wrappedFac = FakeFactory() fac, proto = self.makeProto('', 0, wrappedFac) proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') proto.transport.clear() wrappedFac.proto.transport.write('xxxxx') self.assertEqual(proto.transport.value(), 'xxxxx') class TestSOCKS4ClientFactory(_TestSOCKSClientFactoryCommon, unittest.TestCase): factory = client.SOCKS4ClientFactory def test_defaultFactory(self): fac, proto = self.makeProto('127.0.0.1', 0, None) self.assertEqual(proto.transport.value(), '\x04\x01\x00\x00\x7f\x00\x00\x01\x00') def test_hostname(self): fac, proto = self.makeProto('spam.com', 0, None) self.assertEqual(proto.transport.value(), '\x04\x01\x00\x00\x00\x00\x00\x01\x00spam.com\x00') def test_differentUser(self): fac, proto = self.makeProto('127.0.0.1', 0, None, 'spam') self.assertEqual(proto.transport.value(), '\x04\x01\x00\x00\x7f\x00\x00\x01spam\x00') def test_buildingWrappedFactory(self): wrappedFac = FakeFactory() fac, proto = self.makeProto('127.0.0.1', 0, wrappedFac) proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00xxxxx') self.assertEqual(wrappedFac.proto.data, 'xxxxx') def test_noProtocolFromWrappedFactory(self): wrappedFac = FakeFactory(returnNoProtocol=True) fac, proto = self.makeProto('', 0, wrappedFac) proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00xxxxx') self.assert_(self.aborted) return self.assertFailure(fac.deferred, defer.CancelledError) def test_dataSentByPeer(self): wrappedFac = FakeFactory() fac, proto = self.makeProto('', 0, wrappedFac) proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') proto.transport.clear() wrappedFac.proto.transport.write('xxxxx') self.assertEqual(proto.transport.value(), 'xxxxx') def test_invalidIPs(self): self.assertRaises(ValueError, client.SOCKS4ClientFactory, '0.0.0.1', 0, None) self.assertRaises(ValueError, client.SOCKS4ClientFactory, '0.0.0.255', 0, None) class TestSOCKS5ClientEndpoint(unittest.TestCase): def test_clientConnectionFailed(self): proxy = FakeEndpoint(failure=connectionRefusedFailure) endpoint = client.SOCKS5ClientEndpoint('', 0, proxy) d = endpoint.connect(None) return self.assertFailure(d, ConnectionRefusedError) def test_defaultFactory(self): proxy = FakeEndpoint() endpoint = client.SOCKS5ClientEndpoint('', 0, proxy) endpoint.connect(None) self.assertEqual(proxy.transport.value(), '\x05\x01\x00') def test_anonymousAndLoginAuth(self): proxy = FakeEndpoint() endpoint = client.SOCKS5ClientEndpoint('', 0, proxy, methods={'anonymous': (), 'login': ()}) endpoint.connect(None) self.assertEqual(proxy.transport.value(), '\x05\x02\x00\x02') def test_justLoginAuth(self): proxy = FakeEndpoint() endpoint = client.SOCKS5ClientEndpoint('', 0, proxy, methods={'login': ()}) endpoint.connect(None) self.assertEqual(proxy.transport.value(), '\x05\x01\x02') def test_noAuthMethodsFails(self): self.assertRaises( ValueError, client.SOCKS5ClientEndpoint, None, None, None, methods={}) def test_buildingWrappedFactory(self): wrappedFac = FakeFactory() proxy = FakeEndpoint() endpoint = client.SOCKS5ClientEndpoint('', 0, proxy) d = endpoint.connect(wrappedFac) proxy.proto.dataReceived('\x05\x00\x05\x00\x00\x01444422xxxxx') d.addCallback(self.assertEqual, wrappedFac.proto) self.assertEqual(wrappedFac.proto.data, 'xxxxx') return d def test_dataSentByPeer(self): wrappedFac = FakeFactory() proxy = FakeEndpoint() endpoint = client.SOCKS5ClientEndpoint('', 0, proxy) endpoint.connect(wrappedFac) proxy.proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') proxy.proto.transport.clear() wrappedFac.proto.transport.write('xxxxx') self.assertEqual(proxy.proto.transport.value(), 'xxxxx') class TestSOCKS4ClientEndpoint(unittest.TestCase): def test_clientConnectionFailed(self): proxy = FakeEndpoint(failure=connectionRefusedFailure) endpoint = client.SOCKS4ClientEndpoint('', 0, proxy) d = endpoint.connect(None) return self.assertFailure(d, ConnectionRefusedError) def test_defaultFactory(self): proxy = FakeEndpoint() endpoint = client.SOCKS4ClientEndpoint('127.0.0.1', 0, proxy) endpoint.connect(None) self.assertEqual(proxy.transport.value(), '\x04\x01\x00\x00\x7f\x00\x00\x01\x00') def test_hostname(self): proxy = FakeEndpoint() endpoint = client.SOCKS4ClientEndpoint('spam.com', 0, proxy) endpoint.connect(None) self.assertEqual(proxy.transport.value(), '\x04\x01\x00\x00\x00\x00\x00\x01\x00spam.com\x00') def test_differentUser(self): proxy = FakeEndpoint() endpoint = client.SOCKS4ClientEndpoint('127.0.0.1', 0, proxy, 'spam') endpoint.connect(None) self.assertEqual(proxy.transport.value(), '\x04\x01\x00\x00\x7f\x00\x00\x01spam\x00') def test_buildingWrappedFactory(self): wrappedFac = FakeFactory() proxy = FakeEndpoint() endpoint = client.SOCKS4ClientEndpoint('', 0, proxy) d = endpoint.connect(wrappedFac) proxy.proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00xxxxx') d.addCallback(self.assertEqual, wrappedFac.proto) self.assertEqual(wrappedFac.proto.data, 'xxxxx') return d def test_dataSentByPeer(self): wrappedFac = FakeFactory() proxy = FakeEndpoint() endpoint = client.SOCKS4ClientEndpoint('', 0, proxy) endpoint.connect(wrappedFac) proxy.proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') proxy.proto.transport.clear() wrappedFac.proto.transport.write('xxxxx') self.assertEqual(proxy.proto.transport.value(), 'xxxxx') def test_invalidIPs(self): self.assertRaises(ValueError, client.SOCKS4ClientEndpoint, '0.0.0.1', 0, None) self.assertRaises(ValueError, client.SOCKS4ClientEndpoint, '0.0.0.255', 0, None) txsocksx-1.13.0.0/txsocksx/test/test_grammar.py000066400000000000000000000100361222406657300215330ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. import unittest from parsley import makeGrammar, ParseError from txsocksx.grammar import grammarSource, bindings grammar = makeGrammar(grammarSource, bindings) def stringParserFromRule(rule): def parseString(s): return getattr(grammar(s), rule)() return parseString class TestGrammar(unittest.TestCase): def test_SOCKS4aHostUser(self): parse = stringParserFromRule('SOCKS4aHostUser') self.assertEqual(parse('\x7f\x00\x00\x01spam\x00'), ('127.0.0.1', 'spam')) self.assertEqual(parse('\x00\x00\x00\x00egggs\x00'), ('0.0.0.0', 'egggs')) self.assertEqual(parse('\x00\x00\x00\x01spam\x00example.com\x00'), ('example.com', 'spam')) def test_SOCKS4Command(self): parse = stringParserFromRule('SOCKS4Command') self.assertEqual(parse('\x01'), 'tcp-connect') self.assertEqual(parse('\x02'), 'tcp-bind') self.assertRaises(ParseError, parse, '\x00') self.assertRaises(ParseError, parse, '\x03') def test_SOCKS4Request(self): parse = stringParserFromRule('SOCKS4Request') self.assertEqual(parse('\x04\x01\x01\x00\x7f\x00\x00\x01spam\x00'), ('tcp-connect', 256, '127.0.0.1', 'spam')) self.assertEqual(parse('\x04\x02\x00\xff\x00\x00\x00\x01spam\x00eggs.com\x00'), ('tcp-bind', 255, 'eggs.com', 'spam')) def test_SOCKS4Response(self): parse = stringParserFromRule('SOCKS4Response') self.assertEqual(parse('\x00' * 8), (0, '0.0.0.0', 0)) self.assertEqual(parse('\x00\x01' + '\x00' * 6), (1, '0.0.0.0', 0)) self.assertEqual(parse('\x00\x01' + '\xff' * 6), (1, '255.255.255.255', 65535)) def test_SOCKS5Command(self): parse = stringParserFromRule('SOCKS5Command') self.assertEqual(parse('\x01'), 'tcp-connect') self.assertEqual(parse('\x02'), 'tcp-bind') self.assertEqual(parse('\x03'), 'udp-associate') self.assertRaises(ParseError, parse, '\x00') self.assertRaises(ParseError, parse, '\x04') def test_SOCKS5Address(self): parse = stringParserFromRule('SOCKS5Address') self.assertEqual(parse('\x01\x00\x00\x00\x00'), '0.0.0.0') self.assertEqual(parse('\x01\x7f\x00\x00\x01'), '127.0.0.1') self.assertEqual(parse('\x01\xff\xff\xff\xff'), '255.255.255.255') self.assertEqual(parse('\x03\x00'), '') self.assertEqual(parse('\x03\x0bexample.com'), 'example.com') self.assertEqual(parse('\x04' + '\x00' * 16), '::') self.assertEqual(parse('\x04' + '\x00' * 15 + '\x01'), '::1') self.assertEqual(parse('\x04\xfe\x80' + '\x00' * 14), 'fe80::') self.assertEqual(parse('\x04\xfe\x80' + '\x00' * 13 + '\x01'), 'fe80::1') def test_SOCKS5ServerAuthSelection(self): parse = stringParserFromRule('SOCKS5ServerAuthSelection') self.assertEqual(parse('\x05\x00'), '\x00') self.assertEqual(parse('\x05\x01'), '\x01') self.assertEqual(parse('\x05\xff'), '\xff') def test_SOCKS5ServerLoginResponse(self): parse = stringParserFromRule('SOCKS5ServerLoginResponse') self.assertEqual(parse('\x00\x00'), True) self.assertEqual(parse('\x00\x01'), False) self.assertEqual(parse('\x01\x00'), True) self.assertEqual(parse('\x01\x01'), False) def test_SOCKS5ServerResponse(self): parse = stringParserFromRule('SOCKS5ServerResponse') self.assertEqual(parse('\x05\x00\x00\x03\x00\x00\x00'), (0, '', 0)) self.assertEqual(parse('\x05\x01\x00\x01\x7f\x00\x00\x01\x01\x00'), (1, '127.0.0.1', 256)) self.assertEqual(parse('\x05\x02\x00\x04' + '\x00' * 15 + '\x01\x00\xff'), (2, '::1', 255)) def test_SOCKS5ClientGreeting(self): parse = stringParserFromRule('SOCKS5ClientGreeting') self.assertEqual(parse('\x05\x00'), []) self.assertEqual(parse('\x05\x01\x01'), [1]) self.assertEqual(parse('\x05\x02\x00\x02'), [0, 2]) txsocksx-1.13.0.0/txsocksx/test/test_http.py000066400000000000000000000063451222406657300210740ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from twisted.python.versions import Version from twisted.trial import unittest import twisted from txsocksx.test.util import FakeEndpoint, UppercaseWrapperFactory from txsocksx.http import SOCKS4Agent, SOCKS5Agent from txsocksx.tls import TLSWrapClientEndpoint if twisted.version < Version('twisted', 12, 1, 0): skip = 'txsocksx.http requires Twisted 12.1 or newer' else: skip = None class AgentTestCase(unittest.TestCase): def setUp(self): self.endpoint = FakeEndpoint() self.agent = self.agentType(None, proxyEndpoint=self.endpoint) self.agent._tlsWrapper = self._tlsWrapper def _tlsWrapper(self, *a): wrapper = TLSWrapClientEndpoint(*a) wrapper._wrapper = UppercaseWrapperFactory return wrapper class TestSOCKS5Agent(AgentTestCase): skip = skip agentType = SOCKS5Agent def test_HTTPRequest(self): self.agent.request('GET', 'http://spam.com/eggs') self.endpoint.proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') received = self.endpoint.transport.value() self.assertEqual(received[:18], '\x05\x01\x00\x05\x01\x00\x03\x08spam.com\x00\x50') request = received[18:].splitlines() self.assert_('GET /eggs HTTP/1.1' in request) self.assert_('Host: spam.com' in request) def test_HTTPSRequest(self): self.agent.request('GET', 'https://spam.com/eggs') self.endpoint.proto.dataReceived('\x05\x00\x05\x00\x00\x01444422') received = self.endpoint.transport.value() self.assertEqual(received[:18], '\x05\x01\x00\x05\x01\x00\x03\x08spam.com\x01\xbb') request = received[18:].splitlines() self.assert_('GET /EGGS HTTP/1.1' in request) self.assert_('HOST: SPAM.COM' in request) class TestSOCKS4Agent(AgentTestCase): skip = skip agentType = SOCKS4Agent def test_HTTP4Request(self): self.agent.request('GET', 'http://127.0.0.1/eggs') self.endpoint.proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') received = self.endpoint.transport.value() self.assertEqual(received[:9], '\x04\x01\x00\x50\x7f\x00\x00\x01\x00') request = received[9:].splitlines() self.assert_('GET /eggs HTTP/1.1' in request) self.assert_('Host: 127.0.0.1' in request) def test_HTTP4aRequest(self): self.agent.request('GET', 'http://spam.com/eggs') self.endpoint.proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') received = self.endpoint.transport.value() self.assertEqual(received[:18], '\x04\x01\x00\x50\x00\x00\x00\x01\x00spam.com\x00') request = received[18:].splitlines() self.assert_('GET /eggs HTTP/1.1' in request) self.assert_('Host: spam.com' in request) def test_HTTPSRequest(self): self.agent.request('GET', 'https://spam.com/eggs') self.endpoint.proto.dataReceived('\x00\x5a\x00\x00\x00\x00\x00\x00') received = self.endpoint.transport.value() self.assertEqual(received[:18], '\x04\x01\x01\xbb\x00\x00\x00\x01\x00spam.com\x00') request = received[18:].splitlines() self.assert_('GET /EGGS HTTP/1.1' in request) self.assert_('HOST: SPAM.COM' in request) txsocksx-1.13.0.0/txsocksx/test/test_tls.py000066400000000000000000000051471222406657300207160ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from twisted.internet import defer, protocol from twisted.protocols.basic import NetstringReceiver from txsocksx.test.util import ( FakeEndpoint, SyncDeferredsTestCase, UppercaseWrapperFactory) from txsocksx.tls import TLSWrapClientEndpoint class NetstringTracker(NetstringReceiver): def __init__(self): self.strings = [] def stringReceived(self, string): self.strings.append(string) class NetstringFactory(protocol.ClientFactory): protocol = NetstringTracker class FakeError(Exception): pass class TLSWrapClientEndpointTestCase(SyncDeferredsTestCase): def setUp(self): self.endpoint = FakeEndpoint() self.context = object() self.wrapper = TLSWrapClientEndpoint(self.context, self.endpoint) self.wrapper._wrapper = UppercaseWrapperFactory self.factory = NetstringFactory() def test_wrappingBehavior(self): """ Any modifications performed by the underlying ProtocolWrapper propagate through to the wrapped Protocol. """ proto = self.successResultOf(self.wrapper.connect(self.factory)) self.endpoint.proto.dataReceived('5:hello,') self.assertEqual(proto.strings, ['HELLO']) def test_methodsAvailable(self): """ Methods defined on the Protocol are accessible from the Protocol returned from connect's Deferred. """ proto = self.successResultOf(self.wrapper.connect(self.factory)) proto.sendString('spam') self.assertEqual(self.endpoint.transport.value(), '4:SPAM,') def test_connectionFailure(self): """ Connection failures propagate upward to connect's Deferred. """ self.endpoint.deferred = defer.Deferred() d = self.wrapper.connect(self.factory) self.assertNoResult(d) self.endpoint.deferred.errback(FakeError()) self.failureResultOf(d, FakeError) def test_connectionCancellation(self): """ Cancellation propagates upward to connect's Deferred. """ canceled = [] self.endpoint.deferred = defer.Deferred(canceled.append) d = self.wrapper.connect(self.factory) self.assertNoResult(d) d.cancel() self.assert_(canceled) self.failureResultOf(d, defer.CancelledError) def test_contextPassing(self): """ The SSL context object is passed along to the wrapper. """ self.successResultOf(self.wrapper.connect(self.factory)) self.assertIdentical(self.context, self.endpoint.factory.context) txsocksx-1.13.0.0/txsocksx/test/util.py000066400000000000000000000146541222406657300200350ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. from twisted.internet import defer from twisted.protocols import policies from twisted.python import failure from twisted.test import proto_helpers from twisted.trial import unittest class FakeEndpoint(object): def __init__(self, failure=None): self.failure = failure self.deferred = None def connect(self, fac): self.factory = fac if self.deferred: return self.deferred if self.failure: return defer.fail(self.failure) self.proto = fac.buildProtocol(None) transport = proto_helpers.StringTransport() self.aborted = [] transport.abortConnection = lambda: self.aborted.append(True) self.tlsStarts = [] transport.startTLS = lambda ctx: self.tlsStarts.append(ctx) self.proto.makeConnection(transport) self.transport = transport return defer.succeed(self.proto) class UppercaseWrapperProtocol(policies.ProtocolWrapper): def dataReceived(self, data): policies.ProtocolWrapper.dataReceived(self, data.upper()) def write(self, data): policies.ProtocolWrapper.write(self, data.upper()) def writeSequence(self, seq): for data in seq: self.write(data) class UppercaseWrapperFactory(policies.WrappingFactory): protocol = UppercaseWrapperProtocol def __init__(self, context, ign, factory): self.context = context policies.WrappingFactory.__init__(self, factory) class SyncDeferredsTestCase(unittest.TestCase): def successResultOf(self, deferred): """ Return the current success result of C{deferred} or raise C{self.failException}. @param deferred: A L{Deferred} which has a success result. This means L{Deferred.callback} or L{Deferred.errback} has been called on it and it has reached the end of its callback chain and the last callback or errback returned a non-L{failure.Failure}. @type deferred: L{Deferred} @raise SynchronousTestCase.failureException: If the L{Deferred} has no result or has a failure result. @return: The result of C{deferred}. """ result = [] deferred.addBoth(result.append) if not result: self.fail( "Success result expected on %r, found no result instead" % ( deferred,)) elif isinstance(result[0], failure.Failure): self.fail( "Success result expected on %r, " "found failure result instead:\n%s" % ( deferred, result[0].getTraceback())) else: return result[0] def failureResultOf(self, deferred, *expectedExceptionTypes): """ Return the current failure result of C{deferred} or raise C{self.failException}. @param deferred: A L{Deferred} which has a failure result. This means L{Deferred.callback} or L{Deferred.errback} has been called on it and it has reached the end of its callback chain and the last callback or errback raised an exception or returned a L{failure.Failure}. @type deferred: L{Deferred} @param expectedExceptionTypes: Exception types to expect - if provided, and the the exception wrapped by the failure result is not one of the types provided, then this test will fail. @raise SynchronousTestCase.failureException: If the L{Deferred} has no result, has a success result, or has an unexpected failure result. @return: The failure result of C{deferred}. @rtype: L{failure.Failure} """ result = [] deferred.addBoth(result.append) if not result: self.fail( "Failure result expected on %r, found no result instead" % ( deferred,)) elif not isinstance(result[0], failure.Failure): self.fail( "Failure result expected on %r, " "found success result (%r) instead" % (deferred, result[0])) elif (expectedExceptionTypes and not result[0].check(*expectedExceptionTypes)): expectedString = " or ".join([ '.'.join((t.__module__, t.__name__)) for t in expectedExceptionTypes]) self.fail( "Failure of type (%s) expected on %r, " "found type %r instead: %s" % ( expectedString, deferred, result[0].type, result[0].getTraceback())) else: return result[0] def assertNoResult(self, deferred): """ Assert that C{deferred} does not have a result at this point. If the assertion succeeds, then the result of C{deferred} is left unchanged. Otherwise, any L{failure.Failure} result is swallowed. @param deferred: A L{Deferred} without a result. This means that neither L{Deferred.callback} nor L{Deferred.errback} has been called, or that the L{Deferred} is waiting on another L{Deferred} for a result. @type deferred: L{Deferred} @raise SynchronousTestCase.failureException: If the L{Deferred} has a result. """ result = [] def cb(res): result.append(res) return res deferred.addBoth(cb) if result: # If there is already a failure, the self.fail below will # report it, so swallow it in the deferred deferred.addErrback(lambda _: None) self.fail( "No result expected on %r, found %r instead" % ( deferred, result[0])) txsocksx-1.13.0.0/txsocksx/tls.py000066400000000000000000000027711222406657300167000ustar00rootroot00000000000000# Copyright (c) Aaron Gallagher <_@habnab.it> # See COPYING for details. """TLS convenience wrappers for endpoints. """ from twisted.protocols import tls from twisted.internet import interfaces from zope.interface import implementer @implementer(interfaces.IStreamClientEndpoint) class TLSWrapClientEndpoint(object): """An endpoint which automatically starts TLS. :param contextFactory: A `ContextFactory`__ instance. :param wrappedEndpoint: The endpoint to wrap. __ http://twistedmatrix.com/documents/current/api/twisted.internet.protocol.ClientFactory.html """ _wrapper = tls.TLSMemoryBIOFactory def __init__(self, contextFactory, wrappedEndpoint): self.contextFactory = contextFactory self.wrappedEndpoint = wrappedEndpoint def connect(self, fac): """Connect to the wrapped endpoint, then start TLS. The TLS negotiation is done by way of wrapping the provided factory with `TLSMemoryBIOFactory`__ during connection. :returns: A ``Deferred`` which fires with the same ``Protocol`` as ``wrappedEndpoint.connect(fac)`` fires with. If that ``Deferred`` errbacks, so will the returned deferred. __ http://twistedmatrix.com/documents/current/api/twisted.protocols.tls.html """ fac = self._wrapper(self.contextFactory, True, fac) return self.wrappedEndpoint.connect(fac).addCallback(self._unwrapProtocol) def _unwrapProtocol(self, proto): return proto.wrappedProtocol txsocksx-1.13.0.0/version.txt000066400000000000000000000000231222406657300160500ustar00rootroot000000000000001.13.0.0-0-gc4d51fd