teckV Posted April 26, 2006 at 03:37 PM Report #24434 Posted April 26, 2006 at 03:37 PM 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
vbmaster Posted April 26, 2006 at 04:43 PM Report #24442 Posted April 26, 2006 at 04:43 PM Já comentei no outro lado, mas digo também aqui... A aplicação cliente tem de ser opensource, não?
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now