Jump to content

módulo Socks5.py - funcionalidades socks5 em custom apps


Recommended Posts

Posted

citação do RFC sobre SOCKS5

The implementation of the SOCKS protocol typically involves the recompilation or relinking of TCP-based client applications to use the appropriate encapsulation routines in the SOCKS library.

como vimos temos de introduzir certas funcionalidades especificas nas nossas aplicações para tirar proveito do Socks5...

fica aqui um modulo desenvovido com o intuito de dar essas funcionalidades a custmos apps...

disponibilizo esse modulo também para que melhor entendam os "internals" disto

Código de umja aplicação em Python disponibilizada por alguem especial...

desde já agradeçoa disponibilidade de Dmitry Litovchenko em me atender como têm feitoo autor do código a seguir apresentado

Criem um módulo socks5 para disponibilizar funcionalidades referentes a SOCKS5... depois na app cliente importam este módulo como no codigo apresentado...

exemplo de chamada do módulo

import socks5

factory = create here chained factory to use after socks connect

reactor.connectWith (socks5.ClientConnectorhost=remote_host,
 port=80sockshost=proxy.addrsocksport=proxy.port,
 otherFactory=factorytimeout=self.proxy_timeout,
 readableID="sometext")

Módulo Socks5 - socks5.py

from twisted.internet.interfaces import ITransport
from twisted.internet.base      import BaseConnector
from twisted.internet          import reactortcp
from twisted.internet          import protocol
from twisted.python            import logfailure
import structresocketsys

class SocksException (Exception):
   """ Class descendants are raised for every fatal error that leads to
       connection close.
   """

class UnexpectedDataError (SocksException):
   pass

class UnhandledStateError (SocksException):
   pass

class LoginTooLongError (SocksException):
   """ According to RFC1929 Login must be 1-255 chars. """

class PasswordTooLongError (SocksException):
   """ According to RFC1929 Password must be 1-255 chars. """

class UnknownMethod (SocksException):
   """ Method is invalid or not implemented. """

class ConnectError (SocksException):
   """ One of error replies after client issue CONNECT command. """

class UnhandledData (SocksException):
   """ Server returned data that was not handled properly in our impl. """

class GlobalTimeoutError (SocksException):
   """ Connection took too long and was interrupted unconditionally. """

# here are SOCKS error codes according to RFC1928
#
SOCKS_errors = [\
   "general SOCKS server failure",
   "connection not allowed by ruleset",
   "Network unreachable",
   "Host unreachable",
   "Connection refused",
   "TTL expired",
   "Command not supported",
   "Address type not supported"]

SOCKS4_errors = {\
   0x90: "No error",
   0x91: "Rejected or failed",
   0x92: "Connection to client ident refused",
   0x93: "Client login and ident reply mismatch"
}

# Used to distinguish IP address from domain name
# TODO: should this be optimized somehow?
#
_ip_regex = re.compile ("\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?")


class ClientProtocol (protocol.Protocol):
   """ This protocol that talks to SOCKS5 server from client side.
   """
   __implements__ = ITransport,
   disconnecting = 0

   def __init__(selfsockshostsocksporthostportfactoryotherProtocol,
       method="CONNECT"login=Nonepassword=None):
       """ Initializes SOCKS session

       @type sockshost: string
       @param sockshost: Domain name or ip address of intermediate SOCKS server.

       @type socksport: int
       @param socksport: Port number of intermediate server.

       @type host: string
       @param host: Domain name or ip address where should connect or bind.

       @type port: int
       @param port: Port number where to connect or bind.

       @type otherProtocol: object
       @param otherProtocol: Initialised protocol instancewhich will receive
           all I/O and events after SOCKS connected.

       @type login: string
       @param login: Sets user name if SOCKS server requires us to
           authenticate.

       @type password: string
       @param password: Sets user password if SOCKS server requires us
           to authenticate.

       @type method: string
       @param method: What to do: may be \"CONNECT\" only. Other
           methods are currently unsupported.
       """
       # login and password are limited to 256 chars
       #
       if login is not None and len (login) > 255:
           raise LoginTooLongError()

       if password is not None and len (password) > 255:
           raise PasswordTooLongError()

       # save information
       #
       self.method        = method
       self.host          = host
       self.port          = port
       self.login          = login
       self.password      = password
       self.state          = "mustNotReceiveData"
       self.otherProtocol  = otherProtocol
       self.factory        = factory

   def connectionMade(self):
       # prepare connection string with available authentication methods
       #
       #log.debug ("SOCKS5.connectionMade")
       methods = "\x00"
       if not self.login is None: methods += "\x02"

       connstring = struct.pack ("!BB"5len (methods))

       self.transport.write (connstring + methods)
       self.state = "gotHelloReply"

   def dataReceived (selfdata):
       #log.debug ("SOCKS state=" + self.state)
       method = getattr(self'socks_%s' % (self.state)
           self.socks_thisMustNeverHappen)
       method (data)

   def socks_thisMustNeverHappen (selfdata):
       self.transport.loseConnection()
       raise UnhandledStateError ("This SOCKS5 self.state (%s) "\
           "must never happen %s" % (self.stateself))

   def socks_mustNotReceiveData (selfdata):
       """ This error might occur when server tells something into connection
       right after connection is established. Server in this case is
       certainly not SOCKS.
       """
       self.transport.loseConnection()
       self.factory.clientConnectionFailed (selffailure.Failure (
           UnexpectedDataError ("Server must not send data before client %s" % self)))

   def socks_gotHelloReply (selfdata):
       """ Receive server greeting and send authentication or ask to
       execute requested method right now.
       """
       if data == "\x05\xFF":
           # No acceptable methods. We MUST close
           #
           self.transport.loseConnection()
           return

       elif data == "\x05\x00":
           # Anonymous access allowed - let's issue connect
           #
           self.sendCurrentMethod()

       elif data == "\x05\x02":
           # Authentication required
           #
           self.sendAuth()

       else:
           self.transport.loseConnection()
           self.factory.clientConnectionFailed (selffailure.Failure (
               UnhandledData ("Server returned unknown reply in gotHelloReply")))

       # From now on SOCKS server considered alive - we've got reply
       #
       self.factory.status = "connected"

   def socks_gotAuthReply (selfdata):
       """ Called when client received server authentication reply,
           we or close connection or issue "CONNECT" command
       """
       if data == "\x05\x00":
           self.sendCurrentMethod()

   def sendAuth (self):
       """ Prepare login/password pair and send it to the server
       """
       command = "\x05%s%s%s%s" % (chr (len (self.login))self.login,
           chr (len (self.password))self.password)
       self.transport.write (command)

       self.state = "gotAuthReply"

   def sendCurrentMethod (self):
       method = getattr(self'socks_method_%s' % (self.method)
           self.socks_method_UNKNOWNMETHOD)
       method()

   def socks_method_UNKNOWNMETHOD (self):
       self.transport.loseConnection()
       self.factory.clientConnectionFailed (selffailure.Failure (
           UnknownMethod ("Method %s is unknown %s" % (self.methodself))))

   def socks_method_CONNECT (self):
       # Check if we have ip address or domain name
       #
       if _ip_regex.match (self.host):
           # we have dotted quad IP address
           addressType = 1
           address = socket.inet_aton (self.host)
       else:
           # we have host name
           address = self.host
           addressType = 3

       # Protocol version=5Command=1 (CONNECT)Reserved=0
       command = struct.pack ("!BBBB"51addressType)
       portstr = struct.pack ("!H"self.port)

       self.transport.write (command + address + portstr)
       self.state = "gotConnectReply"

   def socks_gotConnectReply (selfdata):
       """ Called after server accepts or rejects CONNECT method.
       """
       if data[:2] == "\x05\x00":
           # No need to analyze other fields of replywe are done
           #
           self.state = "done"
           self.factory.status = "established"

           self.otherProtocol.transport = self
           self.otherProtocol.connectionMade()
           return

       errcode = ord (data[1])

       if errcode < len (SOCKS_errors):
           self.transport.loseConnection()
           self.factory.clientConnectionFailed (selffailure.Failure (
               ConnectError ("%s %s" % (SOCKS_errors[errcode]self))))
       else:
           self.transport.loseConnection()
           self.factory.clientConnectionFailed (selffailure.Failure (
               ConnectError ("Unknown SOCKS error after CONNECT request issued %s" % (self))))

   def socks_done (selfdata):
       """ Proxy received data to other protocol.
       """
       self.otherProtocol.dataReceived (data)
   #
   # Transport relaying
   #
   def write(selfdata):
       self.transport.write(data)

   def writeSequence(selfdata):
       self.transport.writeSequence(data)

   def loseConnection(self):
       self.disconnecting = 1
       self.transport.loseConnection()

   def getPeer(self):
       return self.transport.getPeer()

   def getHost(self):
       return self.transport.getHost()

   def registerProducer(selfproducerstreaming):
       self.transport.registerProducer(producerstreaming)

   def unregisterProducer(self):
       self.transport.unregisterProducer()

   def stopConsuming(self):
       self.transport.stopConsuming()

class ClientConnector (tcp.Connector):
   """Object used to connect to some host using intermediate server
   supporting SOCKS5 protocol.

   This IConnector manages one connection.
   """
   def __init__(selfsockshostsocksporthostportotherFactory,
       reactor=Nonemethod="CONNECT"login=Nonepassword=None,
       timeout=30readableID=None):
       """ Creates IConnector to connect through SOCKS

       @type sockshost: string
       @param sockshost: SOCKS5 compliant server address.

       @type socksport: int
       @param socksport: Port to use when connecting to SOCKS.

       @type timeout: float
       @param timeout: Time to wait until client connectsthen fail.

       @type readableID: string
       @param readableID: Some human readable ID for this connection.

       See ClientProtocol constructor for details on other params.
       """
       factory = ClientFactory (method=methodsockshost=sockshost,
           socksport=socksporthost=hostport=portlogin=login,
           password=passwordotherFactory=otherFactorytimeout=timeout,
           readableID=readableID)

       tcp.Connector.__init__ (selfhost=sockshostport=socksport,
           factory=factorytimeout=timeoutbindAddress=None,
           reactor=reactor)

class ClientFactory (protocol.ClientFactory):
   def __init__(selfsockshostsocksporthostportotherFactory,
       method="CONNECT"login=Nonepassword=Nonetimeout=60,
       readableID=None):
       """ Factory creates SOCKS5 client protocol to connect through it.
       See ClientProtocol constructor for details on params.

       @type globalTimeout: int
       @param globalTimeout: Seconds before connection is completely and
           unconditionally closed as is.

       @type readableID: string
       @param readableID: Some human readable ID for this connection.
       """
       self.sockshost      = sockshost
       self.socksport      = socksport
       self.host          = host
       self.port          = port
       self.method        = method
       self.login          = login
       self.password      = password
       self.otherFactory  = otherFactory
       self.timeout        = timeout
       self.readableID    = readableID

       # This variable contains current status of SOCKS connection,
       # useful for diagnosting connectionwithout knowing SOCKS
       # internal states. One of: "unconnected""connected" (SOCKS
       # server replied and is alive)"established"
       #
       self.status = "unconnected"

   def startedConnecting (selfconnector):
       # Set global timeout
       #
       #log.msg ("Set timeout %d sec" % self.timeout)
       delayedcall = reactor.callLater (self.timeoutself.onTimeout,
           connector)

       setattr (self"delayed_timeout_call"delayedcall)

       # inherited
       #
       protocol.ClientFactory.startedConnecting (selfconnector)

   def onTimeout (selfconnector):
       """ Timeout occuredcan't continue and should stop immediately
       and unconditionally in the whatever state I am.
       """
       connector.disconnect()
       log.msg ("%s timeout %d sec" % (selfself.timeout))
       self.clientConnectionFailed (selffailure.Failure (
           GlobalTimeoutError ("Timeout %s" % self)))

   def stopFactory(self):
       """ Do cleanups such as cancelling timeout
       """
       try:
           self.delayed_timeout_call.cancel()
       except:
           pass

       protocol.ClientFactory.stopFactory (self)

   def buildProtocol (selfa):
       """ Connection is successfulcreate protocol and let it talk to peer.
       """
       proto = ClientProtocol (sockshost=self.sockshost,
           socksport=self.socksporthost=self.hostport=self.port,
           method=self.methodlogin=self.loginpassword=self.password,
           otherProtocol=self.otherFactory.buildProtocol (self.sockshost),
           factory=self)

       proto.factory = self
       return proto

   def __repr__ (self):
       return "<SOCKS %s>" % self.readableID

   def clientConnectionLost(selfconnectorreason):
       # If flag indicates that connection may not be lost
       #
       rmap = {"reason": reason"socks": self.status}

       try:
           if self.status != "established":
               # Tell about error
               #
               log.msg ("Connection LOST before SOCKS established %s" % self)
               self.otherFactory.clientConnectionFailed (connectorrmap)
           else:
               self.otherFactory.clientConnectionLost (connectorrmap)
       except:
           ei = sys.exc_info()
           if not str (ei[0]).count ("AlreadyCalled"):
               raise

   def clientConnectionFailed(selfconnectorreason):
       # I can't know where to get deferredlet factory do this itself
       #
       rmap = {"reason": reason"socks": self.status}

       try:
           if self.status != "established":
               log.msg ("Connection FAILED before SOCKS established %s" % self)
               self.otherFactory.clientConnectionFailed (connectorrmap)
           else:
               self.otherFactory.clientConnectionFailed (connectorrmap)
       except:
           ei = sys.exc_info()
           if not str (ei[0]).count ("AlreadyCalled"):
               raise

#--- EOF ---

teckV com o contributo de Dmitry Litovchenko

h2k6 - on FIRE

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.