예제 #1
0
 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
예제 #2
0
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))