diff options
Diffstat (limited to 'src/xmpppy-0.5.0rc1/xmpp/transports.py')
-rw-r--r-- | src/xmpppy-0.5.0rc1/xmpp/transports.py | 339 |
1 files changed, 0 insertions, 339 deletions
diff --git a/src/xmpppy-0.5.0rc1/xmpp/transports.py b/src/xmpppy-0.5.0rc1/xmpp/transports.py deleted file mode 100644 index 0e3eec90..00000000 --- a/src/xmpppy-0.5.0rc1/xmpp/transports.py +++ /dev/null @@ -1,339 +0,0 @@ -## transports.py -## -## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2, or (at your option) -## any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -# $Id: transports.py,v 1.35 2009/04/07 08:34:09 snakeru Exp $ - -""" -This module contains the low-level implementations of xmpppy connect methods or -(in other words) transports for xmpp-stanzas. -Currently here is three transports: -direct TCP connect - TCPsocket class -proxied TCP connect - HTTPPROXYsocket class (CONNECT proxies) -TLS connection - TLS class. Can be used for SSL connections also. - -Transports are stackable so you - f.e. TLS use HTPPROXYsocket or TCPsocket as more low-level transport. - -Also exception 'error' is defined to allow capture of this module specific exceptions. -""" - -import socket,select,base64,dispatcher,sys -from simplexml import ustr -from client import PlugIn -from protocol import * - -# determine which DNS resolution library is available -HAVE_DNSPYTHON = False -HAVE_PYDNS = False -try: - import dns.resolver # http://dnspython.org/ - HAVE_DNSPYTHON = True -except ImportError: - try: - import DNS # http://pydns.sf.net/ - HAVE_PYDNS = True - except ImportError: - pass - -DATA_RECEIVED='DATA RECEIVED' -DATA_SENT='DATA SENT' - -class error: - """An exception to be raised in case of low-level errors in methods of 'transports' module.""" - def __init__(self,comment): - """Cache the descriptive string""" - self._comment=comment - - def __str__(self): - """Serialise exception into pre-cached descriptive string.""" - return self._comment - -BUFLEN=1024 -class TCPsocket(PlugIn): - """ This class defines direct TCP connection method. """ - def __init__(self, server=None, use_srv=True): - """ Cache connection point 'server'. 'server' is the tuple of (host, port) - absolutely the same as standard tcp socket uses. However library will lookup for - ('_xmpp-client._tcp.' + host) SRV record in DNS and connect to the found (if it is) - server instead - """ - PlugIn.__init__(self) - self.DBG_LINE='socket' - self._exported_methods=[self.send,self.disconnect] - self._server, self.use_srv = server, use_srv - - def srv_lookup(self, server): - " SRV resolver. Takes server=(host, port) as argument. Returns new (host, port) pair " - if HAVE_DNSPYTHON or HAVE_PYDNS: - host, port = server - possible_queries = ['_xmpp-client._tcp.' + host] - - for query in possible_queries: - try: - if HAVE_DNSPYTHON: - answers = [x for x in dns.resolver.query(query, 'SRV')] - if answers: - host = str(answers[0].target) - port = int(answers[0].port) - break - elif HAVE_PYDNS: - # ensure we haven't cached an old configuration - DNS.DiscoverNameServers() - response = DNS.Request().req(query, qtype='SRV') - answers = response.answers - if len(answers) > 0: - # ignore the priority and weight for now - _, _, port, host = answers[0]['data'] - del _ - port = int(port) - break - except: - self.DEBUG('An error occurred while looking up %s' % query, 'warn') - server = (host, port) - else: - self.DEBUG("Could not load one of the supported DNS libraries (dnspython or pydns). SRV records will not be queried and you may need to set custom hostname/port for some servers to be accessible.\n",'warn') - # end of SRV resolver - return server - - def plugin(self, owner): - """ Fire up connection. Return non-empty string on success. - Also registers self.disconnected method in the owner's dispatcher. - Called internally. """ - if not self._server: self._server=(self._owner.Server,5222) - if self.use_srv: server=self.srv_lookup(self._server) - else: server=self._server - if not self.connect(server): return - self._owner.Connection=self - self._owner.RegisterDisconnectHandler(self.disconnected) - return 'ok' - - def getHost(self): - """ Return the 'host' value that is connection is [will be] made to.""" - return self._server[0] - def getPort(self): - """ Return the 'port' value that is connection is [will be] made to.""" - return self._server[1] - - def connect(self,server=None): - """ Try to connect to the given host/port. Does not lookup for SRV record. - Returns non-empty string on success. """ - try: - if not server: server=self._server - self._sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._sock.connect((server[0], int(server[1]))) - self._send=self._sock.sendall - self._recv=self._sock.recv - self.DEBUG("Successfully connected to remote host %s"%`server`,'start') - return 'ok' - except socket.error, (errno, strerror): - self.DEBUG("Failed to connect to remote host %s: %s (%s)"%(`server`, strerror, errno),'error') - except: pass - - def plugout(self): - """ Disconnect from the remote server and unregister self.disconnected method from - the owner's dispatcher. """ - self._sock.close() - if self._owner.__dict__.has_key('Connection'): - del self._owner.Connection - self._owner.UnregisterDisconnectHandler(self.disconnected) - - def receive(self): - """ Reads all pending incoming data. - In case of disconnection calls owner's disconnected() method and then raises IOError exception.""" - try: received = self._recv(BUFLEN) - except socket.sslerror,e: - self._seen_data=0 - if e[0]==socket.SSL_ERROR_WANT_READ: return '' - if e[0]==socket.SSL_ERROR_WANT_WRITE: return '' - self.DEBUG('Socket error while receiving data','error') - sys.exc_clear() - self._owner.disconnected() - raise IOError("Disconnected from server") - except: received = '' - - while self.pending_data(0): - try: add = self._recv(BUFLEN) - except: add='' - received +=add - if not add: break - - if len(received): # length of 0 means disconnect - self._seen_data=1 - self.DEBUG(received,'got') - if hasattr(self._owner, 'Dispatcher'): - self._owner.Dispatcher.Event('', DATA_RECEIVED, received) - else: - self.DEBUG('Socket error while receiving data','error') - self._owner.disconnected() - raise IOError("Disconnected from server") - return received - - def send(self,raw_data): - """ Writes raw outgoing data. Blocks until done. - If supplied data is unicode string, encodes it to utf-8 before send.""" - if type(raw_data)==type(u''): raw_data = raw_data.encode('utf-8') - elif type(raw_data)<>type(''): raw_data = ustr(raw_data).encode('utf-8') - try: - self._send(raw_data) - # Avoid printing messages that are empty keepalive packets. - if raw_data.strip(): - self.DEBUG(raw_data,'sent') - if hasattr(self._owner, 'Dispatcher'): # HTTPPROXYsocket will send data before we have a Dispatcher - self._owner.Dispatcher.Event('', DATA_SENT, raw_data) - except: - self.DEBUG("Socket error while sending data",'error') - self._owner.disconnected() - - def pending_data(self,timeout=0): - """ Returns true if there is a data ready to be read. """ - return select.select([self._sock],[],[],timeout)[0] - - def disconnect(self): - """ Closes the socket. """ - self.DEBUG("Closing socket",'stop') - self._sock.close() - - def disconnected(self): - """ Called when a Network Error or disconnection occurs. - Designed to be overidden. """ - self.DEBUG("Socket operation failed",'error') - -DBG_CONNECT_PROXY='CONNECTproxy' -class HTTPPROXYsocket(TCPsocket): - """ HTTP (CONNECT) proxy connection class. Uses TCPsocket as the base class - redefines only connect method. Allows to use HTTP proxies like squid with - (optionally) simple authentication (using login and password). """ - def __init__(self,proxy,server,use_srv=True): - """ Caches proxy and target addresses. - 'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address) - and optional keys 'user' and 'password' to use for authentication. - 'server' argument is a tuple of host and port - just like TCPsocket uses. """ - TCPsocket.__init__(self,server,use_srv) - self.DBG_LINE=DBG_CONNECT_PROXY - self._proxy=proxy - - def plugin(self, owner): - """ Starts connection. Used interally. Returns non-empty string on success.""" - owner.debug_flags.append(DBG_CONNECT_PROXY) - return TCPsocket.plugin(self,owner) - - def connect(self,dupe=None): - """ Starts connection. Connects to proxy, supplies login and password to it - (if were specified while creating instance). Instructs proxy to make - connection to the target server. Returns non-empty sting on success. """ - if not TCPsocket.connect(self,(self._proxy['host'],self._proxy['port'])): return - self.DEBUG("Proxy server contacted, performing authentification",'start') - connector = ['CONNECT %s:%s HTTP/1.0'%self._server, - 'Proxy-Connection: Keep-Alive', - 'Pragma: no-cache', - 'Host: %s:%s'%self._server, - 'User-Agent: HTTPPROXYsocket/v0.1'] - if self._proxy.has_key('user') and self._proxy.has_key('password'): - credentials = '%s:%s'%(self._proxy['user'],self._proxy['password']) - credentials = base64.encodestring(credentials).strip() - connector.append('Proxy-Authorization: Basic '+credentials) - connector.append('\r\n') - self.send('\r\n'.join(connector)) - try: reply = self.receive().replace('\r','') - except IOError: - self.DEBUG('Proxy suddenly disconnected','error') - self._owner.disconnected() - return - try: proto,code,desc=reply.split('\n')[0].split(' ',2) - except: raise error('Invalid proxy reply') - if code<>'200': - self.DEBUG('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error') - self._owner.disconnected() - return - while reply.find('\n\n') == -1: - try: reply += self.receive().replace('\r','') - except IOError: - self.DEBUG('Proxy suddenly disconnected','error') - self._owner.disconnected() - return - self.DEBUG("Authentification successfull. Jabber server contacted.",'ok') - return 'ok' - - def DEBUG(self,text,severity): - """Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy".""" - return self._owner.DEBUG(DBG_CONNECT_PROXY,text,severity) - -class TLS(PlugIn): - """ TLS connection used to encrypts already estabilished tcp connection.""" - def PlugIn(self,owner,now=0): - """ If the 'now' argument is true then starts using encryption immidiatedly. - If 'now' in false then starts encryption as soon as TLS feature is - declared by the server (if it were already declared - it is ok). - """ - if owner.__dict__.has_key('TLS'): return # Already enabled. - PlugIn.PlugIn(self,owner) - DBG_LINE='TLS' - if now: return self._startSSL() - if self._owner.Dispatcher.Stream.features: - try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features) - except NodeProcessed: pass - else: self._owner.RegisterHandlerOnce('features',self.FeaturesHandler,xmlns=NS_STREAMS) - self.starttls=None - - def plugout(self,now=0): - """ Unregisters TLS handler's from owner's dispatcher. Take note that encription - can not be stopped once started. You can only break the connection and start over.""" - self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) - self._owner.UnregisterHandler('proceed',self.StartTLSHandler,xmlns=NS_TLS) - self._owner.UnregisterHandler('failure',self.StartTLSHandler,xmlns=NS_TLS) - - def FeaturesHandler(self, conn, feats): - """ Used to analyse server <features/> tag for TLS support. - If TLS is supported starts the encryption negotiation. Used internally""" - if not feats.getTag('starttls',namespace=NS_TLS): - self.DEBUG("TLS unsupported by remote server.",'warn') - return - self.DEBUG("TLS supported by remote server. Requesting TLS start.",'ok') - self._owner.RegisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS) - self._owner.RegisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS) - self._owner.Connection.send('<starttls xmlns="%s"/>'%NS_TLS) - raise NodeProcessed - - def pending_data(self,timeout=0): - """ Returns true if there possible is a data ready to be read. """ - return self._tcpsock._seen_data or select.select([self._tcpsock._sock],[],[],timeout)[0] - - def _startSSL(self): - """ Immidiatedly switch socket to TLS mode. Used internally.""" - """ Here we should switch pending_data to hint mode.""" - tcpsock=self._owner.Connection - tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None) - tcpsock._sslIssuer = tcpsock._sslObj.issuer() - tcpsock._sslServer = tcpsock._sslObj.server() - tcpsock._recv = tcpsock._sslObj.read - tcpsock._send = tcpsock._sslObj.write - - tcpsock._seen_data=1 - self._tcpsock=tcpsock - tcpsock.pending_data=self.pending_data - tcpsock._sock.setblocking(0) - - self.starttls='success' - - def StartTLSHandler(self, conn, starttls): - """ Handle server reply if TLS is allowed to process. Behaves accordingly. - Used internally.""" - if starttls.getNamespace()<>NS_TLS: return - self.starttls=starttls.getName() - if self.starttls=='failure': - self.DEBUG("Got starttls response: "+self.starttls,'error') - return - self.DEBUG("Got starttls proceed response. Switching to TLS/SSL...",'ok') - self._startSSL() - self._owner.Dispatcher.PlugOut() - dispatcher.Dispatcher().PlugIn(self._owner) |