text: #!/usr/bin/env python
  
  """Minimal non-feature complete socks proxy"""
  from __future__ import print_function
  
  import socket
  from struct import pack, unpack
  import threading
  import sys
  
  # Python 3 renamed SocketServer to socketserver
  try:
      from socketserver import StreamRequestHandler, ThreadingTCPServer
  except Exception:
      from SocketServer import StreamRequestHandler, ThreadingTCPServer
  
  
  def debug(s):
      print('socks.py: ', s)
      sys.stdout.flush()
  
  
  def error(s):
      print('socks.py, ERROR: ', s)
      sys.stdout.flush()
  
  
  class MyTCPServer(ThreadingTCPServer):
      allow_reuse_address = True
  
      def handle_timeout(self):
          raise Exception('socks.py: timeout')
  
  
  CLOSE = object()
  
  VERSION = '\x05'
  NOAUTH = '\x00'
  USERPASS = '\x02'
  CONNECT = '\x01'
  UDP_ASSOCIATE = '\x03'
  IPV4 = '\x01'
  IPV6 = '\x04'
  DOMAIN_NAME = '\x03'
  SUCCESS = '\x00'
  
  password = None
  username = None
  allow_v4 = False
  
  
  def send(dest, msg):
      if msg == CLOSE:
          try:
              dest.shutdown(socket.SHUT_WR)
          except Exception:
              pass
          dest.close()
          return 0
      else:
          return dest.sendall(msg)
  
  
  def recv(source, buffer):
      data = source.recv(buffer)
      if data == '':
          return CLOSE
      else:
          return data
  
  
  def forward(source, dest, name):
      while True:
          data = recv(source, 4000)
          if data == CLOSE:
              send(dest, CLOSE)
              debug('%s hung up' % name)
              return
  #        debug('Forwarding (%d) %r' % (len(data), data))
          send(dest, data)
  
  
  def spawn_forwarder(source, dest, name):
      t = threading.Thread(target=forward, args=(source, dest, name))
      t.daemon = True
      t.start()
  
  
  class SocksHandler(StreamRequestHandler):
      """Highly feature incomplete SOCKS 5 implementation"""
  
      def close_request(self):
          self.server.close_request(self.request)
  
      def read(self, n):
          data = ''
          while len(data) < n:
              extra = self.rfile.read(n)
              if extra == '':
                  raise Exception('Connection closed')
              data += extra
          return data
  
      def handle(self):
          # IMRPOVEMENT: Report who requests are from in logging
          # IMPROVEMENT: Timeout on client
          debug('Connection - authenticating')
          version = self.read(1)
  
          if allow_v4 and version == '\x04':
              cmd = self.read(1)
              if cmd != CONNECT and cmd != UDP_ASSOCIATE:
                  error('Only supports connect and udp-associate method not (%r) closing' % cmd)
                  self.close_request()
                  return
  
              raw_dest_port = self.read(2)
              dest_port, = unpack('>H', raw_dest_port)
  
              raw_dest_address = self.read(4)
              dest_address = '.'.join(map(str, unpack('>4B', raw_dest_address)))
  
              user_id = ''
              c = self.read(1)
              while c != '\0':
                  user_id += c
                  c = self.read(1)
  
              outbound_sock = socket.socket(socket.AF_INET)
              out_address = socket.getaddrinfo(dest_address, dest_port)[0][4]
              debug("Creating forwarder connection to %s:%d" % (out_address[0], out_address[1]))
              outbound_sock.connect(out_address)
  
              self.send_reply_v4(outbound_sock.getsockname())
  
              spawn_forwarder(outbound_sock, self.request, 'destination')
              forward(self.request, outbound_sock, 'client')
              return
  
          if version != '\x05':
              error('Wrong version number (%r) closing...' % version)
              self.close_request()
              return
  
          nmethods = ord(self.read(1))
          method_list = self.read(nmethods)
  
          global password
          global username
  
          if password is None and NOAUTH in method_list:
              self.send_no_auth_method()
              debug('Authenticated (no-auth)')
          elif USERPASS in method_list:
              self.send_user_pass_auth_method()
              auth_version = self.read(1)
              if auth_version != '\x01':
                  error('Wrong sub-negotiation version number (%r) closing...' % version)
                  self.close_request()
                  return
              usr_len = ord(self.read(1))
              usr_name = self.read(usr_len)
              pwd_len = ord(self.read(1))
              pwd = self.read(pwd_len)
  
              if usr_name != username or pwd != password:
                  error('Invalid username or password')
                  self.close_request()
                  return
              debug('Authenticated (user/password)')
              self.send_authenticated()
          else:
              error('Server only supports NOAUTH and user/pass')
              self.send_no_method()
              return
  
          # If we were authenticating it would go here
          version, cmd, zero, address_type = self.read(4)
          if version != '\x05':
              error('Wrong version number (%r) closing...' % version)
              self.close_request()
          elif cmd != CONNECT and cmd != UDP_ASSOCIATE:
              error('Only supports connect and udp-associate method not (%r) closing' % cmd)
              self.close_request()
          elif zero != '\x00':
              error('Mangled request. Reserved field (%r) is not null' % zero)
              self.close_request()
  
          if address_type == IPV4:
              raw_dest_address = self.read(4)
              dest_address = '.'.join(map(str, unpack('>4B', raw_dest_address)))
          elif address_type == IPV6:
              raw_dest_address = self.read(16)
              dest_address = ":".join([hex(x)[2:] for x in unpack('>8H', raw_dest_address)])
          elif address_type == DOMAIN_NAME:
              dns_length = ord(self.read(1))
              dns_name = self.read(dns_length)
              dest_address = dns_name
          else:
              error('Unknown addressing (%r)' % address_type)
              self.close_request()
  
          raw_dest_port = self.read(2)
          dest_port, = unpack('>H', raw_dest_port)
  
          if address_type == IPV6:
              outbound_sock = socket.socket(socket.AF_INET6)
          else:
              outbound_sock = socket.socket(socket.AF_INET)
          try:
              out_address = socket.getaddrinfo(dest_address, dest_port)[0][4]
          except Exception as e:
              error('%s' % e)
              return
  
          if cmd == UDP_ASSOCIATE:
              debug("no UDP support yet, closing")
              return
  
          debug("Creating forwarder connection to %s:%d" % (out_address[0], out_address[1]))
  
          try:
              outbound_sock.connect(out_address)
          except Exception as e:
              error('%s' % e)
              return
  
          if address_type == IPV6:
              self.send_reply6(outbound_sock.getsockname())
          else:
              self.send_reply(outbound_sock.getsockname())
  
          spawn_forwarder(outbound_sock, self.request, 'destination')
          try:
              forward(self.request, outbound_sock, 'client')
          except Exception as e:
              error('%s' % e)
  
      def send_reply_v4(self, xxx_todo_changeme):
          (bind_addr, bind_port) = xxx_todo_changeme
          self.wfile.write('\0\x5a\0\0\0\0\0\0')
          self.wfile.flush()
  
      def send_reply(self, xxx_todo_changeme1):
          (bind_addr, bind_port) = xxx_todo_changeme1
          bind_tuple = tuple(map(int, bind_addr.split('.')))
          full_address = bind_tuple + (bind_port,)
          debug('Setting up forwarding port %r' % (full_address,))
          msg = pack('>cccc4BH', VERSION, SUCCESS, '\x00', IPV4, *full_address)
          self.wfile.write(msg)
  
      def send_reply6(self, xxx_todo_changeme2):
          (bind_addr, bind_port, unused1, unused2) = xxx_todo_changeme2
          bind_tuple = tuple([int(x, 16) for x in bind_addr.split(':')])
          full_address = bind_tuple + (bind_port,)
          debug('Setting up forwarding port %r' % (full_address,))
          msg = pack('>cccc8HH', VERSION, SUCCESS, '\x00', IPV6, *full_address)
          self.wfile.write(msg)
  
      def send_no_method(self):
          self.wfile.write('\x05\xff')
          self.close_request()
  
      def send_no_auth_method(self):
          self.wfile.write('\x05\x00')
          self.wfile.flush()
  
      def send_user_pass_auth_method(self):
          self.wfile.write('\x05\x02')
          self.wfile.flush()
  
      def send_authenticated(self):
          self.wfile.write('\x01\x00')
          self.wfile.flush()
  
  
  if __name__ == '__main__':
  
      debug('starting socks.py %s' % " ".join(sys.argv))
      debug('python version: %s' % sys.version_info.__str__())
      listen_port = 8002
      i = 1
      while i < len(sys.argv):
          if sys.argv[i] == '--username':
              username = sys.argv[i + 1]
              i += 1
          elif sys.argv[i] == '--password':
              password = sys.argv[i + 1]
              i += 1
          elif sys.argv[i] == '--port':
              listen_port = int(sys.argv[i + 1])
              i += 1
          elif sys.argv[i] == '--allow-v4':
              allow_v4 = True
          else:
              if sys.argv[i] != '--help':
                  debug('unknown option "%s"' % sys.argv[i])
              print('usage: socks.py [--username <user> --password <password>] [--port <listen-port>]')
              sys.stdout.flush()
              sys.exit(1)
          i += 1
  
      debug('Listening on port %d...' % listen_port)
      server = MyTCPServer(('localhost', listen_port), SocksHandler)
      server.timeout = 190
      while True:
          server.handle_request()
  