.. image:: https://img.shields.io/travis/habnabit/txsocksx/master.svg?style=flat-square
   :target: https://travis-ci.org/habnabit/txsocksx

.. image:: https://img.shields.io/coveralls/habnabit/txsocksx/master.svg?style=flat-square
   :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 did not support being handed an
arbitrary endpoint before 15.0, so |txsocksx| provides an ``Agent`` for maximum
compatibility.

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|.

For users with Twisted 15.0 or greater, |SOCKS5Agent| also implements
`IAgentEndpointFactory`_.


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
.. _IAgentEndpointFactory: http://twistedmatrix.com/documents/current/api/twisted.web.iweb.IAgentEndpointFactory.html

.. |SOCKS5ClientEndpoint| replace:: ``SOCKS5ClientEndpoint``
.. |SOCKS5Agent| replace:: ``SOCKS5Agent``
.. |TLSWrapClientEndpoint| replace:: ``TLSWrapClientEndpoint``
.. |txsocksx| replace:: ``txsocksx``
.. |txsocksx.http| replace:: ``txsocksx.http``
.. |txsocksx.tls| replace:: ``txsocksx.tls``
