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)


No comments:

Post a Comment