def __init__( self ): self.recvBuffer = "" self.connectionCorrupted = False self.pduReadTimer = None self.enquireLinkTimer = None self.inactivityTimer = None self.dataRequestHandler = None self.lastSeqNum = 0 self.inTxns = {} self.outTxns = {} self.sessionState = SMPPSessionStates.NONE self.encoder = PDUEncoder() self.disconnectedDeferred = defer.Deferred() # Overriden in tests self.callLater = reactor.callLater self.port = None
class SMPPProtocolBase( protocol.Protocol ): """Short Message Peer to Peer Protocol v3.4 implementing ESME (client)""" version = 0x34 def __init__( self ): self.recvBuffer = "" self.connectionCorrupted = False self.pduReadTimer = None self.enquireLinkTimer = None self.inactivityTimer = None self.dataRequestHandler = None self.lastSeqNum = 0 self.inTxns = {} self.outTxns = {} self.sessionState = SMPPSessionStates.NONE self.encoder = PDUEncoder() self.disconnectedDeferred = defer.Deferred() # Overriden in tests self.callLater = reactor.callLater self.port = None def config(self): return self.factory.getConfig() def connectionMade(self): """When TCP connection is made """ protocol.Protocol.connectionMade(self) self.port = self.transport.getHost().port #Start the inactivity timer the connection is dropped if we receive no data self.activateInactivityTimer() self.sessionState = SMPPSessionStates.OPEN self.log.warning("SMPP connection established from %s to port %s", self.transport.getPeer().host, self.port) def connectionLost( self, reason ): protocol.Protocol.connectionLost( self, reason ) self.log.warning("SMPP %s disconnected from port %s: %s", self.transport.getPeer().host, self.port, reason) self.sessionState = SMPPSessionStates.NONE self.cancelEnquireLinkTimer() self.cancelInactivityTimer() self.disconnectedDeferred.callback(None) def dataReceived( self, data ): """ Looks for a full PDU (protocol data unit) and passes it from rawMessageReceived. """ # if self.log.isEnabledFor(logging.DEBUG): # self.log.debug("Received data [%s]" % _safelylogOutPdu(data)) self.recvBuffer = self.recvBuffer + data while True: if self.connectionCorrupted: return msg = self.readMessage() if msg is None: break self.endPDURead() self.rawMessageReceived(msg) if len(self.recvBuffer) > 0: self.incompletePDURead() def incompletePDURead(self): if self.pduReadTimer and self.pduReadTimer.active(): return self.pduReadTimer = self.callLater(self.config().pduReadTimerSecs, self.onPDUReadTimeout) def endPDURead(self): if self.pduReadTimer and self.pduReadTimer.active(): self.pduReadTimer.cancel() def readMessage(self): pduLen = self.getMessageLength() if pduLen is None: return None return self.getMessage(pduLen) def getMessageLength(self): if len(self.recvBuffer) < 4: return None return struct.unpack('!L', self.recvBuffer[:4])[0] def getMessage(self, pduLen): if len(self.recvBuffer) < pduLen: return None message = self.recvBuffer[:pduLen] self.recvBuffer = self.recvBuffer[pduLen:] return message def corruptDataRecvd(self, status=CommandStatus.ESME_RINVCMDLEN): self.sendPDU(GenericNack(status=status)) self.onCorruptConnection() def onCorruptConnection(self): """ Once the connection is corrupt, the PDU boundaries are lost and it's impossible to continue processing messages. - Set a flag to indicate corrupt connection - no more parse attempts should be made for inbound data - no more outbound requests should be attempted (they should errback immediately) - Cancel outstanding outbound requests (which have not yet been ack'ed) (removed from the list and errback called) - Shutdown """ self.log.critical("Connection is corrupt!!! Shutting down...") self.connectionCorrupted = True self.cancelOutboundTransactions(SMPPClientConnectionCorruptedError()) self.shutdown() def getHeader(self, message): try: return self.encoder.decodeHeader(StringIO.StringIO(message[:self.encoder.HEADER_LEN])) except: return {} def onPDUReadTimeout(self): self.log.critical('PDU read timed out. Buffer is now considered corrupt') self.corruptDataRecvd() def rawMessageReceived( self, message ): """Called once a PDU (protocol data unit) boundary is identified. Creates an SMPP PDU class from the data and calls PDUReceived dispatcher """ pdu = None try: pdu = self.encoder.decode(StringIO.StringIO(message)) pdu.raw_hex = _safelylogOutPdu(message) except PDUCorruptError, e: self.log.exception(e) self.log.critical("Received corrupt PDU %s" % _safelylogOutPdu(message)) self.corruptDataRecvd(status=e.status) except PDUParseError, e: self.log.exception(e) self.log.critical("Received unparsable PDU %s" % _safelylogOutPdu(message)) header = self.getHeader(message) seqNum = header.get('sequence_number', None) commandId = header.get('command_id', None) self.sendPDU(getPDUClass(commandId).requireAck(seqNum=seqNum, status=e.status))