Showing posts with label socks. Show all posts
Showing posts with label socks. Show all posts

adding SOCKS5 support to python3 http client using just standard libs

I had a need for a simple way of running some HTTP queries through SOCKS5 (think OpenSSH -D) proxy. All the examples I could find on the internet required usage of external libraries, I prefer to just use the standard ones, makes things simpler in the end.

Here is the code I come up with, I borrowed some code from a different tool that I wrote sometime ago. Gist can be found here.

#!/usr/bin/env python3

import http.client
import socket
from struct import pack, unpack

class HTTPSocks5(http.client.HTTPConnection):

  def setsocksproxy(self, host, port):
    self.socks5host = host
    self.socks5port = port

  def connect(self):
    if hasattr(self, "socks5host") and self.socks5host:
      self.sock = self._create_connection(
        (self.socks5host,self.socks5port), self.timeout, self.source_address)

      error = ["succeeded", "general SOCKS server failure",\
        "connection not allowed by ruleset", "Network unreachable",\
        "Host unreachable", "Connection refused", "TTL expired",\
        "Command not supported", "Address type not supported", "unassigned"]

      data = pack('!3B',5,1,0) # lets connect to socks5 server
      self.sock.send(data)
      data = self.sock.recv(2)
      auth = unpack('2B',data)[1] # do we need to authenticate
      if auth != 255:
        nport = pack('!H',self.port)
        try:
          if ":" in self.host: # we most likely have IPv6 here
            data = pack('!4B',5,1,0,4)+\
              socket.inet_pton(socket.AF_INET6,self.host)+nport
          else: # IPv4
            data = pack('!4B',5,1,0,1)+\
              socket.inet_pton(socket.AF_INET,self.host)+nport
        except socket.error: # or just a hostname to resolve by the SOCKS srv
          data = pack('!5B',5,1,0,3,len(self.host))+\
            bytearray(self.host,'UTF-8')+nport

        self.sock.send(data)
        data = self.sock.recv(256) # getting the status code
        try:
          code = unpack('BBB',data[:3])[1]
        except:
          raise("socks server sent a wrong replay")

        if code != 0:
          if code > 9:
            code = 9
          raise("socks server sent an error: %s" % (error[code],))
      else:
        raise("socks server requires authentication")
    else:
      self.sock = self._create_connection(
        (self.host,self.port), self.timeout, self.source_address)


Nagle in OpenSSH

OpenSSH by default enables NODELAY flags on the TCP socket, so each key stroke that you make is send as a separate packet. In some situation it is not a very good idea and you would like to have Nagle algorithm enabled.

Nothing ground braking, you can "enable" it by using a simple script (version written in C which is using TCP_CORK socket option) and ProxyCommand option.

The usage, run ssh command like that (or change your ~/.ssh/config):

ssh -o "proxycommand nagle.py %h %p" user@host

and read man ssh :)

you can modify the Nagle parameters by changing those variables:


  n_l    = 5    # how many timeout do we wait for
  n_tout = 0.1  # actual max wait is n_tout*n_l
  n_size = 1024 # minimum size of the packet

maybe this will be useful for somebody :)


PS. Just to add, there is also a SOCKS client that has Nagle support.

PS2. Interesting socket options in Linux and FreeBSD TCP_CORK and TCP_NOPUSH

PS3. Of course you could also just use socat like this, it also works nicely:
ProxyCommand /usr/bin/socat - TCP:%h:%p,cork

Similar post: SSH over SSL