Exemple #1
0
 def do_bind_conversion_test(self, pduBindKlass, reqCommandIdHex,
                             respCommandIdHex):
     reqPdu = pduBindKlass(
         2,
         CommandStatus.ESME_ROK,
         system_id='test',
         password='******',
         system_type='OTA',
         interface_version=0x34,
         addr_ton=AddrTon.NATIONAL,
         addr_npi=AddrNpi.LAND_MOBILE,
         address_range='127.0.0.*',
     )
     self.do_conversion_test(
         PDUEncoder(), reqPdu,
         b'0000002d%s00000000000000027465737400736563726574004f5441003402063132372e302e302e2a00'
         % reqCommandIdHex)
     respPdu = reqPdu.require_ack(1,
                                  CommandStatus.ESME_ROK,
                                  system_id='TSI7588',
                                  sc_interface_version=0x34)
     self.do_conversion_test(
         PDUEncoder(), respPdu,
         b'0000001d%s000000000000000154534937353838000210000134' %
         respCommandIdHex)
class BlackHoleSMSC( protocol.Protocol ):

    responseMap = {}

    def debug_func(self, stack):
        self.log.debug('-start---------------------------------------------------------------------')
        self.log.debug(stack[1])
        self.log.debug('-stop----------------------------------------------------------------------')
        
    def __init__( self ):
        self.log = logging.getLogger(LOG_CATEGORY)
        self.debug_func(inspect.stack())
        self.recvBuffer = ""
        self.lastSeqNum = 0
        self.encoder = PDUEncoder()

    def dataReceived( self, data ):
        self.debug_func(inspect.stack())
        self.recvBuffer = self.recvBuffer + data

        while len( self.recvBuffer ) > 3:
            ( length, ) = struct.unpack( '!L', self.recvBuffer[:4] )
            if len( self.recvBuffer ) < length:
                break
            message = self.recvBuffer[:length]
            self.recvBuffer = self.recvBuffer[length:]
            self.rawMessageReceived( message )

    def rawMessageReceived( self, message ):
        self.debug_func(inspect.stack())
        return self.PDUReceived( self.encoder.decode( StringIO.StringIO(message) ) )

    def PDUReceived( self, pdu ):
        self.debug_func(inspect.stack())
        if pdu.__class__ in self.responseMap:
            self.responseMap[pdu.__class__](pdu)
            
    def sendSuccessResponse(self, reqPDU):
        self.debug_func(inspect.stack())
        self.sendResponse(reqPDU, CommandStatus.ESME_ROK)
        
    def sendResponse(self, reqPDU, status):
        self.debug_func(inspect.stack())
        respPDU = reqPDU.requireAck(reqPDU.seqNum)
        respPDU.status = status
        self.sendPDU(respPDU)

    def sendPDU(self, pdu):
        self.debug_func(inspect.stack())
        if isinstance(pdu, PDURequest) and pdu.seqNum is None:
            self.lastSeqNum += 1
            pdu.seqNum = self.lastSeqNum
        # self.log.debug("Sending PDU: %s" % pdu)
        encoded = self.encoder.encode(pdu)
        # self.log.debug("Sending data [%s]" % binascii.b2a_hex(encoded))
        self.transport.write( encoded )
Exemple #3
0
 def test_BindTransceiverResp_error_has_no_body_status_set_later(self):
     hex = b'00000010800000090000000e00000d80'
     pdu = BindTransceiverResp(3456, system_id="XYZ")
     pdu.status = CommandStatus.ESME_RINVPASWD
     # Even though the system_id param was set, it will not be encoded
     self.do_encode_test(PDUEncoder(), pdu, hex)
     # It will decode with no params set
     pduExpected = BindTransceiverResp(3456,
                                       status=CommandStatus.ESME_RINVPASWD)
     self.assertEqual(0, len(pduExpected.params))
     self.do_decode_test(PDUEncoder(), pduExpected, hex)
Exemple #4
0
    def _send_requests(self, requests: List[PDURequest], merge=True):
        if not self.is_bound:
            raise Exception('Cannot send request to unbound client')

        encoder = PDUEncoder()

        if merge:
            buffer_list = [encoder.encode(pdu) for pdu in requests]
            self._transport.write(b''.join(buffer_list))
        else:
            for pdu in requests:
                self._transport.write(encoder.encode(pdu))
Exemple #5
0
 def __init__(self):
     self.log = logging.getLogger(LOG_CATEGORY)
     self.recvBuffer = ""
     self.connectionCorrupted = False
     self.pduReadTimer = None
     self.enquireLinkTimer = None
     self.inactivityTimer = None
     self.lastSeqNum = 0
     self.dataRequestHandler = None
     self.alertNotificationHandler = None
     self.inTxns = {}
     self.outTxns = {}
     self.sessionState = SMPPSessionStates.NONE
     self.encoder = PDUEncoder()
     self.disconnectedDeferred = defer.Deferred()
Exemple #6
0
 def test_DeliverSM_with_subaddress(self):
     pdu = DeliverSM(
         1,
         service_type='BM8',
         source_addr_ton=AddrTon.INTERNATIONAL,
         source_addr_npi=AddrNpi.ISDN,
         source_addr='46123456789',
         dest_addr_ton=AddrTon.INTERNATIONAL,
         dest_addr_npi=AddrNpi.ISDN,
         destination_addr='14046653410',
         esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT),
         protocol_id=0,
         priority_flag=PriorityFlag.LEVEL_0,
         registered_delivery=RegisteredDelivery(
             RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED),
         replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
         data_coding=DataCoding(
             DataCodingScheme.GSM_MESSAGE_CLASS,
             DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET,
                              DataCodingGsmMsgClass.CLASS_2)),
         short_message=b"Hello I'm a bigg fan of you",
         source_subaddress=Subaddress(SubaddressTypeTag.USER_SPECIFIED,
                                      b'742'),
         dest_subaddress=Subaddress(SubaddressTypeTag.USER_SPECIFIED,
                                    b'4131'),
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'00000066000000050000000000000001424d38000101343631323334353637383900010131343034363635333431300000000000000000f2001b48656c6c6f2049276d206120626967672066616e206f6620796f7502020004a037343202030005a034313331'
     )
Exemple #7
0
 def test_DeliverSM_sybase_MO_conversion(self):
     pdu = DeliverSM(
         1,
         service_type='CMT',
         source_addr_ton=AddrTon.INTERNATIONAL,
         source_addr_npi=AddrNpi.UNKNOWN,
         source_addr='3411149500001',
         dest_addr_ton=AddrTon.INTERNATIONAL,
         dest_addr_npi=AddrNpi.UNKNOWN,
         destination_addr='12345455',
         esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT),
         protocol_id=0,
         priority_flag=PriorityFlag.LEVEL_0,
         registered_delivery=RegisteredDelivery(
             RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED),
         replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
         data_coding=DataCoding(
             DataCodingScheme.GSM_MESSAGE_CLASS,
             DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET,
                              DataCodingGsmMsgClass.CLASS_2)),
         short_message=b'HELLO\x00',
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'0000003f000000050000000000000001434d540001003334313131343935303030303100010031323334353435350000000000000000f2000648454c4c4f00'
     )
Exemple #8
0
 def test_DeliverSM_handset_ack_conversion(self):
     pdu = DeliverSM(
         10,
         service_type='CMT',
         source_addr_ton=AddrTon.INTERNATIONAL,
         source_addr_npi=AddrNpi.UNKNOWN,
         source_addr='6515555678',
         dest_addr_ton=AddrTon.INTERNATIONAL,
         dest_addr_npi=AddrNpi.UNKNOWN,
         destination_addr='123',
         esm_class=EsmClass(EsmClassMode.DEFAULT,
                            EsmClassType.SMSC_DELIVERY_RECEIPT),
         protocol_id=0,
         priority_flag=PriorityFlag.LEVEL_0,
         registered_delivery=RegisteredDelivery(
             RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED),
         replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
         data_coding=DataCoding(
             scheme_data=DataCodingDefault.SMSC_DEFAULT_ALPHABET),
         short_message=
         b'id:1891273321 sub:001 dlvrd:001 submit date:1305050826 done date:1305050826 stat:DELIVRD err:000 Text:DLVRD TO MOBILE\x00',
         message_state=MessageState.DELIVERED,
         receipted_message_id='70BA8A69',
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'000000b900000005000000000000000a434d5400010036353135353535363738000100313233000400000000000000007669643a31383931323733333231207375623a30303120646c7672643a303031207375626d697420646174653a3133303530353038323620646f6e6520646174653a3133303530353038323620737461743a44454c49565244206572723a30303020546578743a444c56524420544f204d4f42494c45000427000102001e0009373042413841363900'
     )
Exemple #9
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
Exemple #10
0
 def test_SubmitSM_conversion(self):
     pdu = SubmitSM(
         9284,
         service_type='',
         source_addr_ton=AddrTon.ALPHANUMERIC,
         source_addr_npi=AddrNpi.UNKNOWN,
         source_addr='mobileway',
         dest_addr_ton=AddrTon.INTERNATIONAL,
         dest_addr_npi=AddrNpi.ISDN,
         destination_addr='1208230',
         esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT),
         protocol_id=0,
         priority_flag=PriorityFlag.LEVEL_0,
         registered_delivery=RegisteredDelivery(
             RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED),
         replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
         data_coding=DataCoding(
             DataCodingScheme.GSM_MESSAGE_CLASS,
             DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET,
                              DataCodingGsmMsgClass.CLASS_2)),
         short_message=b'HELLO',
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'000000360000000400000000000024440005006d6f62696c65776179000101313230383233300000000000000100f2000548454c4c4f'
     )
Exemple #11
0
 def test_SubmitSM_ringtone_conversion(self):
     pdu = SubmitSM(
         455569,
         service_type='',
         source_addr_ton=AddrTon.ALPHANUMERIC,
         source_addr_npi=AddrNpi.UNKNOWN,
         source_addr='mobileway',
         dest_addr_ton=AddrTon.INTERNATIONAL,
         dest_addr_npi=AddrNpi.ISDN,
         destination_addr='3369809342',
         esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT,
                            [EsmClassGsmFeatures.UDHI_INDICATOR_SET]),
         protocol_id=0,
         priority_flag=PriorityFlag.LEVEL_0,
         registered_delivery=RegisteredDelivery(
             RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED),
         replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
         data_coding=DataCoding(
             DataCodingScheme.GSM_MESSAGE_CLASS,
             DataCodingGsmMsg(DataCodingGsmMsgCoding.DATA_8BIT,
                              DataCodingGsmMsgClass.CLASS_1)),
         short_message=binascii.a2b_hex(
             b'06050415811581024a3a5db5a5cdcda5bdb8040084d8c51381481381481381481381481381381481581681781881881061881061b81081181081881061881061681081781081881061881061b81081181081881061881061681081781081b81881321081b81881221081b818811210824dc1446000'
         ))
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'000000a900000004000000000006f3910005006d6f62696c65776179000101333336393830393334320040000000000100f5007506050415811581024a3a5db5a5cdcda5bdb8040084d8c51381481381481381481381481381381481581681781881881061881061b81081181081881061881061681081781081881061881061b81081181081881061881061681081781081b81881321081b81881221081b818811210824dc1446000'
     )
Exemple #12
0
class BlackHoleSMSC(protocol.Protocol):

    responseMap = {}

    def __init__(self):
        self.log = logging.getLogger(LOG_CATEGORY)
        self.recvBuffer = ""
        self.lastSeqNum = 0
        self.encoder = PDUEncoder()

    def dataReceived(self, data):
        self.recvBuffer = self.recvBuffer + data

        while len(self.recvBuffer) > 3:
            (length, ) = struct.unpack('!L', self.recvBuffer[:4])
            if len(self.recvBuffer) < length:
                break
            message = self.recvBuffer[:length]
            self.recvBuffer = self.recvBuffer[length:]
            self.rawMessageReceived(message)

    def rawMessageReceived(self, message):
        return self.PDUReceived(self.encoder.decode(
            StringIO.StringIO(message)))

    def PDUReceived(self, pdu):
        if pdu.__class__ in self.responseMap:
            self.responseMap[pdu.__class__](pdu)

    def sendSuccessResponse(self, reqPDU):
        self.sendResponse(reqPDU, CommandStatus.ESME_ROK)

    def sendSuccessResponseWithStatus(self, reqPDU, status):
        self.sendResponse(reqPDU, CommandStatus.ESME_ROK)

    def sendResponse(self, reqPDU, status):
        respPDU = reqPDU.requireAck(reqPDU.seqNum, status=status)
        self.sendPDU(respPDU)

    def sendPDU(self, pdu):
        if isinstance(pdu, PDURequest) and pdu.seqNum is None:
            self.lastSeqNum += 1
            pdu.seqNum = self.lastSeqNum
        # self.log.debug("Sending PDU: %s" % pdu)
        encoded = self.encoder.encode(pdu)
        # self.log.debug("Sending data [%s]" % binascii.b2a_hex(encoded))
        self.transport.write(encoded)
Exemple #13
0
 def __init__(self):
     self.recvBuffer = b""
     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
     #pylint: disable=no-member
     self.callLater = reactor.callLater
     self.port = None
     self.log = logging.getLogger(LOG_CATEGORY)
Exemple #14
0
 def test_QuerySMResp_conversion(self):
     pdu = QuerySMResp(
         message_id='Smsc2003',
         final_date=None,
         message_state=MessageState.UNKNOWN,
         error_code=None,
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'0000001c800000030000000000000000536d73633230303300000700')
Exemple #15
0
 def __init__( self ):
     self.log = logging.getLogger(LOG_CATEGORY)
     self.recvBuffer = ""
     self.connectionCorrupted = False
     self.pduReadTimer = None
     self.enquireLinkTimer = None
     self.inactivityTimer = None
     self.lastSeqNum = 0
     self.dataRequestHandler = None
     self.alertNotificationHandler = None
     self.inTxns = {}
     self.outTxns = {}
     self.sessionState = SMPPSessionStates.NONE
     self.encoder = PDUEncoder()
     self.disconnectedDeferred = defer.Deferred()
Exemple #16
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
Exemple #17
0
 def test_AlertNotification_conversion(self):
     pdu = AlertNotification(
         source_addr_ton=AddrTon.NATIONAL,
         source_addr_npi=AddrNpi.ISDN,
         source_addr=
         'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
         esme_addr_ton=AddrTon.INTERNATIONAL,
         esme_addr_npi=AddrNpi.LAND_MOBILE,
         esme_addr=
         'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY',
         ms_availability_status=MsAvailabilityStatus.DENIED,
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'0000008900000102000000000000000002015858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858580001065959595959595959595959595959595959595959595959595959595959595959595959595959595959595959595959595959595959595959000422000101'
     )
Exemple #18
0
 def test_DeliverSM_syniverse_MO_conversion(self):
     pdu = DeliverSM(
         2676551972,
         service_type='AWSBD',
         source_addr_ton=AddrTon.INTERNATIONAL,
         source_addr_npi=AddrNpi.ISDN,
         source_addr='16505551234',
         dest_addr_ton=AddrTon.INTERNATIONAL,
         dest_addr_npi=AddrNpi.ISDN,
         destination_addr='17735554070',
         esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT),
         protocol_id=0,
         priority_flag=PriorityFlag.LEVEL_0,
         registered_delivery=RegisteredDelivery(
             RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED),
         replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
         data_coding=DataCoding(scheme_data=DataCodingDefault.LATIN_1),
         short_message=b'there is no spoon',
     )
     self.do_conversion_test(
         PDUEncoder(), pdu,
         b'0000004d00000005000000009f88f12441575342440001013136353035353531323334000101313737333535353430373000000000000000000300117468657265206973206e6f2073706f6f6e'
     )
Exemple #19
0
 def getPDU(self, hexStr):
     return PDUEncoder().decode(StringIO.StringIO(binascii.a2b_hex(hexStr)))
Exemple #20
0
 def _pdu2bin(self, pdu):
     return PDUEncoder().encode(pdu)
Exemple #21
0
 def _bin2pdu(self, bindata):
     io_pdu = StringIO(bindata)
     return PDUEncoder().decode(io_pdu)
Exemple #22
0
class SMPPClientProtocol(protocol.Protocol):
    """Short Message Peer to Peer Protocol v3.4 implementing ESME (client)"""
    version = 0x34

    def __init__(self):
        self.log = logging.getLogger(LOG_CATEGORY)
        self.recvBuffer = ""
        self.connectionCorrupted = False
        self.pduReadTimer = None
        self.enquireLinkTimer = None
        self.inactivityTimer = None
        self.lastSeqNum = 0
        self.dataRequestHandler = None
        self.alertNotificationHandler = None
        self.inTxns = {}
        self.outTxns = {}
        self.sessionState = SMPPSessionStates.NONE
        self.encoder = PDUEncoder()
        self.disconnectedDeferred = defer.Deferred()

    def config(self):
        return self.factory.getConfig()

    def connectionMade(self):
        """When TCP connection is made
        """
        protocol.Protocol.connectionMade(self)

        self.sessionState = SMPPSessionStates.OPEN
        self.log.warning("Connection established")

    def connectionLost(self, reason):
        protocol.Protocol.connectionLost(self, reason)
        self.log.warning("Disconnected: %s" % 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]" % binascii.b2a_hex(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 = reactor.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))
        except PDUCorruptError, e:
            self.log.exception(e)
            self.log.critical("Received corrupt PDU %s" %
                              binascii.b2a_hex(message))
            self.corruptDataRecvd(status=e.status)
        except PDUParseError, e:
            self.log.exception(e)
            self.log.critical("Received unparsable PDU %s" %
                              binascii.b2a_hex(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))
Exemple #23
0
 def __init__(self):
     self.log = logging.getLogger(LOG_CATEGORY)
     self.recvBuffer = ""
     self.lastSeqNum = 0
     self.encoder = PDUEncoder()
Exemple #24
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))
        except PDUCorruptError as e:
            self.log.exception(e)
            self.log.critical("Received corrupt PDU %s" % _safelylogOutPdu(message))
            self.corruptDataRecvd(status=e.status)
        except PDUParseError as 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))
        else:
            self.PDUReceived(pdu)

    def PDUReceived(self, pdu):
        """Dispatches incoming PDUs
        """
        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Received PDU: %s" % pdu)

        encoded = self.encoder.encode(pdu)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Receiving data [%s]" % _safelylogOutPdu(encoded))

        # Signal SMPP operation
        self.onSMPPOperation()

        if isinstance(pdu, PDURequest):
            self.PDURequestReceived(pdu)
        elif isinstance(pdu, PDUResponse):
            self.PDUResponseReceived(pdu)
        else:
            getattr(self, "onPDU_%s" % str(pdu.id))(pdu)

    def PDURequestReceived(self, reqPDU):
        """Handle incoming request PDUs
        """
        if isinstance(reqPDU, PDUDataRequest):
            self.PDUDataRequestReceived(reqPDU)
            return

        getattr(self, "onPDURequest_%s" % str(reqPDU.id))(reqPDU)

    def onPDURequest_enquire_link(self, reqPDU):
        self.sendResponse(reqPDU)

    def onPDURequest_unbind(self, reqPDU):
        # Allow no more outbound data requests
        # Accept no more inbound requests
        self.sessionState = SMPPSessionStates.UNBIND_RECEIVED
        self.cancelEnquireLinkTimer()
        # Cancel outbound requests
        self.cancelOutboundTransactions(SMPPClientSessionStateError('Unbind received'))
        # Wait for inbound requests to finish then ack and disconnect
        self.finishInboundTxns().addCallback(lambda r: (self.sendResponse(reqPDU) or True) and self.disconnect())

    def sendResponse(self, reqPDU, status=CommandStatus.ESME_ROK, **params):
        self.sendPDU(reqPDU.requireAck(reqPDU.seqNum, status, **params))

    def PDUDataRequestReceived(self, reqPDU):
        if self.sessionState == SMPPSessionStates.UNBIND_PENDING:
            self.log.info("Unbind is pending...Ignoring data request PDU %s" % reqPDU)
            return

        if not self.isBound():
            errMsg = 'Received data request when not bound %s' % reqPDU
            self.cancelOutboundTransactions(SessionStateError(errMsg, CommandStatus.ESME_RINVBNDSTS))
            return self.fatalErrorOnRequest(reqPDU, errMsg, CommandStatus.ESME_RINVBNDSTS)

        if self.dataRequestHandler is None:
            return self.fatalErrorOnRequest(reqPDU, 'Missing dataRequestHandler', CommandStatus.ESME_RX_T_APPN)

        self.doPDURequest(reqPDU, self.dataRequestHandler)

    def fatalErrorOnRequest(self, reqPDU, errMsg, status):
        self.log.critical(errMsg)
        self.sendResponse(reqPDU, status)
        self.shutdown()

    def doPDURequest(self, reqPDU, handler):
        self.startInboundTransaction(reqPDU)

        handlerCall = defer.maybeDeferred(handler, self, reqPDU)
        handlerCall.addCallback(self.PDURequestSucceeded, reqPDU)
        handlerCall.addErrback(self.PDURequestFailed, reqPDU)
        handlerCall.addBoth(self.PDURequestFinished, reqPDU)

    def PDURequestSucceeded(self, dataHdlrResp, reqPDU):
        if reqPDU.requireAck:
            status = CommandStatus.ESME_ROK
            params = {}
            if dataHdlrResp:
                if dataHdlrResp in CommandStatus:
                    status = dataHdlrResp
                elif isinstance(dataHdlrResp, DataHandlerResponse):
                    status = dataHdlrResp.status
                    params = dataHdlrResp.params
                else:
                    self.log.critical("Invalid response type returned from data handler %s" % type(dataHdlrResp))
                    status = CommandStatus.ESME_RX_T_APPN
                    self.shutdown()

            self.sendResponse(reqPDU, status, **params)

    def PDURequestFailed(self, error, reqPDU):
        if error.check(SMPPProtocolError):
            # Get the original error
            try:
                error.raiseException()
            except SMPPProtocolError as validation_error:
                self.log.debug("Application raised error '%s', forwarding to client. Inbound PDU was [%s], hex[%s]" % (
                validation_error, reqPDU, _safelylogOutPdu(self.encoder.encode(reqPDU))))
                return_cmd_status = validation_error.commandStatusName
                shutdown = False
        else:
            self.log.critical('Exception raised handling inbound PDU [%s] hex[%s]: %s' % (
            reqPDU, _safelylogOutPdu(self.encoder.encode(reqPDU)), error))
            return_cmd_status = CommandStatus.ESME_RX_T_APPN
            shutdown = True

        if reqPDU.requireAck:
            self.sendResponse(reqPDU, return_cmd_status)

        if shutdown:
            self.shutdown()

    def PDURequestFinished(self, result, reqPDU):
        self.endInboundTransaction(reqPDU)
        return result

    def finishTxns(self):
        return defer.DeferredList([self.finishInboundTxns(), self.finishOutboundTxns()])

    def finishInboundTxns(self):
        return defer.DeferredList(self.inTxns.values())

    def finishOutboundTxns(self):
        return defer.DeferredList([txn.ackDeferred for txn in self.outTxns.values()])

    def PDUResponseReceived(self, pdu):
        """Handle incoming response PDUs
        """
        if isinstance(pdu, GenericNack):
            self.log.critical("Recevied generic_nack %s" % pdu)
            if pdu.seqNum is None:
                self.onCorruptConnection()
                return

        if pdu.seqNum not in self.outTxns:
            self.log.critical('Response PDU received with unknown outbound transaction sequence number %s' % pdu)
            return

        self.endOutboundTransaction(pdu)

    def sendPDU(self, pdu):
        """Send a SMPP PDU
        """
        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Sending PDU: %s" % pdu)
        encoded = self.encoder.encode(pdu)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Sending data [%s]" % _safelylogOutPdu(encoded))

        self.transport.write(encoded)
        self.onSMPPOperation()

    def sendBindRequest(self, pdu):
        return self.sendRequest(pdu, self.config().sessionInitTimerSecs)

    def sendRequest(self, pdu, timeout):
        return defer.maybeDeferred(self.doSendRequest, pdu, timeout)

    def doSendRequest(self, pdu, timeout):
        if self.connectionCorrupted:
            raise SMPPClientConnectionCorruptedError()
        if not isinstance(pdu, PDURequest) or pdu.requireAck is None:
            raise SMPPClientError("Invalid PDU to send: %s" % pdu)

        pdu.seqNum = self.claimSeqNum()
        self.sendPDU(pdu)
        return self.startOutboundTransaction(pdu, timeout)

    def onSMPPOperation(self):
        """Called whenever an SMPP PDU is sent or received
        """
        if self.isBound():
            self.activateEnquireLinkTimer()

        self.activateInactivityTimer()

    def activateEnquireLinkTimer(self):
        if self.enquireLinkTimer and self.enquireLinkTimer.active():
            self.enquireLinkTimer.reset(self.config().enquireLinkTimerSecs)
        elif self.config().enquireLinkTimerSecs:
            self.enquireLinkTimer = self.callLater(self.config().enquireLinkTimerSecs, self.enquireLinkTimerExpired)

    def activateInactivityTimer(self):
        if self.inactivityTimer and self.inactivityTimer.active():
            self.inactivityTimer.reset(self.config().inactivityTimerSecs)
        elif self.config().inactivityTimerSecs:
            self.inactivityTimer = self.callLater(self.config().inactivityTimerSecs, self.inactivityTimerExpired)

    def cancelEnquireLinkTimer(self):
        if self.enquireLinkTimer and self.enquireLinkTimer.active():
            self.enquireLinkTimer.cancel()
            self.enquireLinkTimer = None

    def cancelInactivityTimer(self):
        if self.inactivityTimer and self.inactivityTimer.active():
            self.inactivityTimer.cancel()
            self.inactivityTimer = None

    def enquireLinkTimerExpired(self):
        txn = self.sendRequest(EnquireLink(), self.config().responseTimerSecs)
        txn.addErrback(self.enquireLinkErr)

    def enquireLinkErr(self, failure):
        # Unbinding already anyway. No need to raise another error
        failure.trap(SMPPError)

    def inactivityTimerExpired(self):
        self.log.critical("Inactivity timer expired...shutting down")
        self.shutdown()

    def isBound(self):
        return self.sessionState in (
        SMPPSessionStates.BOUND_TX, SMPPSessionStates.BOUND_RX, SMPPSessionStates.BOUND_TRX)

    def shutdown(self):
        """ Unbind if appropriate and disconnect """

        if self.isBound() and not self.connectionCorrupted:
            self.log.warning("Shutdown requested...unbinding")
            self.unbind().addBoth(lambda result: self.disconnect())
        elif self.sessionState not in (SMPPSessionStates.UNBIND_RECEIVED, SMPPSessionStates.UNBIND_PENDING):
            self.log.warning("Shutdown requested...disconnecting")
            self.disconnect()
        else:
            self.log.debug("Shutdown already in progress")

    def startInboundTransaction(self, reqPDU):
        if reqPDU.seqNum in self.inTxns:
            raise SMPPProtocolError('Duplicate message id [%s] received.  Already in progess.' % reqPDU.seqNum,
                                    CommandStatus.ESME_RUNKNOWNERR)
        txnDeferred = defer.Deferred()
        self.inTxns[reqPDU.seqNum] = txnDeferred
        self.log.debug("Inbound transaction started with message id %s" % reqPDU.seqNum)
        return txnDeferred

    def endInboundTransaction(self, reqPDU):
        if not reqPDU.seqNum in self.inTxns:
            raise ValueError('Unknown inbound sequence number in transaction for request PDU %s' % reqPDU)

        self.log.debug("Inbound transaction finished with message id %s" % reqPDU.seqNum)
        self.inTxns[reqPDU.seqNum].callback(reqPDU)
        del self.inTxns[reqPDU.seqNum]

    def startOutboundTransaction(self, reqPDU, timeout):
        if reqPDU.seqNum in self.outTxns:
            raise ValueError('Seq number [%s] is already in progess.' % reqPDU.seqNum)

        # Create callback deferred
        ackDeferred = defer.Deferred()
        # Create response timer
        timer = self.callLater(timeout, self.onResponseTimeout, reqPDU, timeout)
        # Save transaction
        self.outTxns[reqPDU.seqNum] = SMPPOutboundTxn(reqPDU, timer, ackDeferred)
        self.log.debug("Outbound transaction started with message id %s" % reqPDU.seqNum)
        return ackDeferred

    def closeOutboundTransaction(self, seqNum):
        self.log.debug("Outbound transaction finished with message id %s" % seqNum)

        txn = self.outTxns[seqNum]
        # Remove txn
        del self.outTxns[seqNum]
        # Cancel response timer
        if txn.timer.active():
            txn.timer.cancel()
        return txn

    def endOutboundTransaction(self, respPDU):
        txn = self.closeOutboundTransaction(respPDU.seqNum)

        if respPDU.status == CommandStatus.ESME_ROK:
            if not isinstance(respPDU, txn.request.requireAck):
                txn.ackDeferred.errback(SMPPProtocolError(
                    "Invalid PDU response type [%s] returned for request type [%s]" % (
                    type(respPDU), type(txn.request))))
                return
            # Do callback
            txn.ackDeferred.callback(SMPPOutboundTxnResult(self, txn.request, respPDU))
            return

        if isinstance(respPDU, GenericNack):
            txn.ackDeferred.errback(SMPPGenericNackTransactionError(respPDU, txn.request))
            return

        errCode = respPDU.status
        txn.ackDeferred.errback(SMPPTransactionError(respPDU, txn.request))

    def endOutboundTransactionErr(self, reqPDU, error):
        self.log.error(error)
        txn = self.closeOutboundTransaction(reqPDU.seqNum)
        # Do errback
        txn.ackDeferred.errback(error)

    def cancelOutboundTransactions(self, error):
        for txn in self.outTxns.values():
            self.endOutboundTransactionErr(txn.request, error)

    def onResponseTimeout(self, reqPDU, timeout):
        errMsg = 'Request timed out after %s secs: %s' % (timeout, reqPDU)
        self.endOutboundTransactionErr(reqPDU, SMPPRequestTimoutError(errMsg))
        self.shutdown()

    def claimSeqNum(self):
        self.lastSeqNum += 1
        return self.lastSeqNum

    def unbindSucceeded(self, result):
        self.sessionState = SMPPSessionStates.UNBOUND
        self.log.warning("Unbind succeeded")
        return result

    def unbindFailed(self, reason):
        self.log.error("Unbind failed [%s]. Disconnecting..." % reason)
        self.disconnect()
        if reason.check(SMPPRequestTimoutError):
            raise SMPPSessionInitTimoutError(str(reason))
        return reason

    def unbindAfterInProgressTxnsFinished(self, result, unbindDeferred):
        self.log.warning('Issuing unbind request')
        self.sendBindRequest(Unbind()).addCallbacks(self.unbindSucceeded, self.unbindFailed).chainDeferred(
            unbindDeferred)

    ############################################################################
    # Public command functions
    ############################################################################
    def unbind(self):
        """Unbind from SMSC
        
        Result is a Deferred object
        """
        if not self.isBound():
            return defer.fail(
                SMPPClientSessionStateError('unbind called with illegal session state: %s' % self.sessionState))

        self.cancelEnquireLinkTimer()

        self.log.info('Waiting for in-progress transactions to finish...')

        # Signal that
        #   - no new data requests should be sent
        #   - no new incoming data requests should be accepted
        self.sessionState = SMPPSessionStates.UNBIND_PENDING

        unbindDeferred = defer.Deferred()
        # Wait for any in-progress txns to finish
        self.finishTxns().addCallback(self.unbindAfterInProgressTxnsFinished, unbindDeferred)
        # Result is the deferred for the unbind txn
        return unbindDeferred

    def unbindAndDisconnect(self):
        """Unbind from SMSC and disconnect
        
        Result is a Deferred object
        """
        return self.unbind().addBoth(lambda result: self.disconnect())

    def disconnect(self):
        """Disconnect from SMSC
        """
        if self.isBound():
            self.log.warning("Disconnecting while bound to SMSC...")
        else:
            self.log.warning("Disconnecting...")
        self.sessionState = SMPPSessionStates.UNBOUND
        self.transport.loseConnection()

    def getDisconnectedDeferred(self):
        """Get a Deferred so you can be notified on disconnect
        """
        return self.disconnectedDeferred

    def sendDataRequest(self, pdu):
        """Send a SMPP Request Message

        Argument is an SMPP PDUDataRequest (protocol data unit).
        Result is a Deferred object
        """
        if not isinstance(pdu, PDUDataRequest):
            return defer.fail(SMPPClientError("Invalid PDU passed to sendDataRequest(): %s" % pdu))
        if not self.isBound():
            return defer.fail(SMPPClientSessionStateError('Not bound'))
        return self.sendRequest(pdu, self.config().responseTimerSecs)
Exemple #25
0
source_addr_npi=AddrNpi.UNKNOWN,
source_addr='sarafu',
dest_addr_ton=AddrTon.INTERNATIONAL,
dest_addr_npi=AddrNpi.ISDN,
destination_addr='07727',
esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT),
protocol_id=0,
priority_flag=PriorityFlag.LEVEL_0,
registered_delivery=RegisteredDelivery(RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED),
replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE,
data_coding=DataCoding(DataCodingScheme.GSM_MESSAGE_CLASS, DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET, DataCodingGsmMsgClass.CLASS_2)),
short_message='HELLO',
)
print "PDU: %s" % pdu

binary = PDUEncoder().encode(pdu)
hexStr = binascii.b2a_hex(binary)
print "HEX: %s" % hexStr

# r may represent request 




hex = '0000004d00000005000000009f88f12441575342440001013136353035353531323334000101313737333535353430373000000000000000000300117468657265206973206e6f2073706f6f6e'
binary = binascii.a2b_hex(hex)
file = StringIO.StringIO(binary)

pdu = PDUEncoder().decode(file)
print "PDU: %s" % pdu
Exemple #26
0
 def test_decode_bad_message_ends_in_middle_of_option(self):
     self.do_decode_corrupt_data_error_test(
         PDUEncoder().decode, CommandStatus.ESME_RINVMSGLEN,
         b'0000001b8000000900000000000000015453493735383800021000')
Exemple #27
0
 def test_SubmitSMResp_error_has_no_body(self):
     pdu = SubmitSMResp(1234, status=CommandStatus.ESME_RMSGQFUL)
     self.assertTrue(len(SubmitSMResp.mandatory_params) > 0)
     self.assertEqual(0, len(pdu.params))
     self.do_conversion_test(PDUEncoder(), pdu,
                             b'000000108000000400000014000004d2')
Exemple #28
0
    async def handle_data_received(self, data: bytes):
        self.app.logger.debug(f'Received {len(data)} bytes.')
        self.app.logger.debug(data)

        file = io.BytesIO(data)

        pdu = PDUEncoder().decode(file)

        self.app.logger.debug(f'Command received: {pdu.command_id}')

        if pdu.command_id == CommandId.submit_sm:
            submit_sm = SubmitSM(sequence_number=pdu.sequence_number,
                                 **pdu.params)
            sms = SMStringEncoder().decode_SM(submit_sm).str

            self._send_response(
                submit_sm.require_ack(
                    sequence_number=submit_sm.sequence_number))

            # Check if the message is only a part from a series
            while (submit_sm.params.get('more_messages_to_send') ==
                   MoreMessagesToSend.MORE_MESSAGES):

                pdu = PDUEncoder().decode(file)
                if pdu.command_id != CommandId.submit_sm:
                    raise Exception(
                        f'Received {pdu.command_id} instead '
                        f'of {CommandId.submit_sm} for concatenated messages')
                submit_sm = SubmitSM(sequence_number=pdu.sequence_number,
                                     **pdu.params)
                sms += SMStringEncoder().decode_SM(submit_sm).str

                self._send_response(
                    submit_sm.require_ack(
                        sequence_number=submit_sm.sequence_number))

            await self.app.handle_sms_received(
                client=self._client,
                source_number=submit_sm.params['source_addr'],
                dest_number=submit_sm.params['destination_addr'],
                text=sms)
        elif pdu.command_id == CommandId.bind_transceiver:
            request = BindTransceiver(sequence_number=pdu.sequence_number,
                                      **pdu.params)

            if self.is_bound:
                smpp_status = CommandStatus.ESME_RALYBND
            else:
                client = SmppClient(
                    protocol=self,
                    system_id=request.params['system_id'],
                    password=request.params['password'],
                    system_type=request.params['system_type'],
                    interface_version=request.params['interface_version'],
                    addr_ton=request.params['addr_ton'],
                    addr_npi=request.params['addr_npi'])

                self._client = client
                try:
                    client = await self.app.handle_bound_client(client=client)
                except Exception as e:
                    self.app.logger.error(
                        f'Exception in handle_bound_client: {e}')
                    smpp_status = CommandStatus.ESME_RBINDFAIL
                else:
                    if client:
                        self.is_bound = True
                        smpp_status = CommandStatus.ESME_ROK
                    else:
                        self.is_bound = False
                        # Generic bind error
                        smpp_status = CommandStatus.ESME_RBINDFAIL

            resp = BindTransceiverResp(sequence_number=request.sequence_number,
                                       status=smpp_status,
                                       system_id=self.app.name)
            self._send_response(resp)

        elif pdu.command_id == CommandId.unbind:
            self.is_bound = False

            request = Unbind(sequence_number=pdu.sequence_number, **pdu.params)
            resp = UnbindResp(sequence_number=request.sequence_number)

            self._send_response(resp)

            await self.app.handle_unbound_client(self._client)

        else:
            await self.request_handler(pdu)
 def __init__( self ):
     self.log = logging.getLogger(LOG_CATEGORY)
     self.debug_func(inspect.stack())
     self.recvBuffer = ""
     self.lastSeqNum = 0
     self.encoder = PDUEncoder()
Exemple #30
0
class SMPPClientProtocol( protocol.Protocol ):
    """Short Message Peer to Peer Protocol v3.4 implementing ESME (client)"""
    version = 0x34

    def __init__( self ):
        self.log = logging.getLogger(LOG_CATEGORY)
        self.recvBuffer = ""
        self.connectionCorrupted = False
        self.pduReadTimer = None
        self.enquireLinkTimer = None
        self.inactivityTimer = None
        self.lastSeqNum = 0
        self.dataRequestHandler = None
        self.alertNotificationHandler = None
        self.inTxns = {}
        self.outTxns = {}
        self.sessionState = SMPPSessionStates.NONE
        self.encoder = PDUEncoder()
        self.disconnectedDeferred = defer.Deferred()

    def config(self):
        return self.factory.getConfig()

    def connectionMade(self):
        """When TCP connection is made
        """
        protocol.Protocol.connectionMade(self)

        self.sessionState = SMPPSessionStates.OPEN
        self.log.warning("Connection established")

    def connectionLost( self, reason ):
        protocol.Protocol.connectionLost( self, reason )
        self.log.warning("Disconnected: %s" % 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]" % binascii.b2a_hex(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 = reactor.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))
        except PDUCorruptError, e:
            self.log.exception(e)
            self.log.critical("Received corrupt PDU %s" % binascii.b2a_hex(message))
            self.corruptDataRecvd(status=e.status)
        except PDUParseError, e:
            self.log.exception(e)
            self.log.critical("Received unparsable PDU %s" % binascii.b2a_hex(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))
Exemple #31
0
class SMPPProtocolBase(Protocol):
    """Short Message Peer to Peer Protocol v3.4 implementing ESME (client)"""
    version = 0x34

    def __init__(self):
        self.recvBuffer = b""
        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
        #pylint: disable=no-member
        self.callLater = reactor.callLater
        self.port = None
        self.log = logging.getLogger(LOG_CATEGORY)

    def config(self):
        #pylint: disable=no-member
        return self.factory.getConfig()

    def connectionMade(self):
        """When TCP connection is made
        """
        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.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(BytesIO(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(BytesIO(message))
        except PDUCorruptError as e:
            self.log.exception(e)
            self.log.critical("Received corrupt PDU %s" % _safelylogOutPdu(message))
            self.corruptDataRecvd(status=e.status)
        except PDUParseError as 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))
        else:
            self.PDUReceived(pdu)

    def PDUReceived(self, pdu):
        """Dispatches incoming PDUs
        """
        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Received PDU: %s" % pdu)

        encoded = self.encoder.encode(pdu)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Receiving data [%s]" % _safelylogOutPdu(encoded))

        # Signal SMPP operation
        self.onSMPPOperation()

        if isinstance(pdu, PDURequest):
            self.PDURequestReceived(pdu)
        elif isinstance(pdu, PDUResponse):
            self.PDUResponseReceived(pdu)
        else:
            getattr(self, "onPDU_%s" % pdu.id.name)(pdu)

    def PDURequestReceived(self, reqPDU):
        """Handle incoming request PDUs
        """
        if isinstance(reqPDU, PDUDataRequest):
            self.PDUDataRequestReceived(reqPDU)
            return

        getattr(self, "onPDURequest_%s" % reqPDU.id.name)(reqPDU)

    def onPDURequest_enquire_link(self, reqPDU):
        self.sendResponse(reqPDU)

    def onPDURequest_unbind(self, reqPDU):
        # Allow no more outbound data requests
        # Accept no more inbound requests
        self.sessionState = SMPPSessionStates.UNBIND_RECEIVED
        self.cancelEnquireLinkTimer()
        # Cancel outbound requests
        self.cancelOutboundTransactions(SMPPClientSessionStateError('Unbind received'))
        # Wait for inbound requests to finish then ack and disconnect
        self.finishInboundTxns().addCallback(lambda r: (self.sendResponse(reqPDU) or True) and self.disconnect())

    def sendResponse(self, reqPDU, status=CommandStatus.ESME_ROK, **params):
        self.sendPDU(reqPDU.requireAck(reqPDU.seqNum, status, **params))

    def PDUDataRequestReceived(self, reqPDU):
        if self.sessionState == SMPPSessionStates.UNBIND_PENDING:
            self.log.info("Unbind is pending...Ignoring data request PDU %s" % reqPDU)
            return

        if not self.isBound():
            errMsg = 'Received data request when not bound %s' % reqPDU
            self.cancelOutboundTransactions(SessionStateError(errMsg, CommandStatus.ESME_RINVBNDSTS))
            return self.fatalErrorOnRequest(reqPDU, errMsg, CommandStatus.ESME_RINVBNDSTS)

        if self.dataRequestHandler is None:
            return self.fatalErrorOnRequest(reqPDU, 'Missing dataRequestHandler', CommandStatus.ESME_RX_T_APPN)

        self.doPDURequest(reqPDU, self.dataRequestHandler)

    def fatalErrorOnRequest(self, reqPDU, errMsg, status):
        self.log.critical(errMsg)
        self.sendResponse(reqPDU, status)
        self.shutdown()

    def doPDURequest(self, reqPDU, handler):
        self.startInboundTransaction(reqPDU)

        handlerCall = defer.maybeDeferred(handler, self, reqPDU)
        handlerCall.addCallback(self.PDURequestSucceeded, reqPDU)
        handlerCall.addErrback(self.PDURequestFailed, reqPDU)
        handlerCall.addBoth(self.PDURequestFinished, reqPDU)

    def PDURequestSucceeded(self, dataHdlrResp, reqPDU):
        if reqPDU.requireAck:
            status = CommandStatus.ESME_ROK
            params = {}
            if dataHdlrResp:
                if dataHdlrResp in list(CommandStatus):
                    status = dataHdlrResp
                elif isinstance(dataHdlrResp, DataHandlerResponse):
                    status = dataHdlrResp.status
                    params = dataHdlrResp.params
                else:
                    self.log.critical("Invalid response type returned from data handler %s" % type(dataHdlrResp))
                    status = CommandStatus.ESME_RX_T_APPN
                    self.shutdown()

            self.sendResponse(reqPDU, status, **params)

    def PDURequestFailed(self, error, reqPDU):
        if error.check(SMPPProtocolError):
            # Get the original error
            try:
                error.raiseException()
            except SMPPProtocolError as validation_error:
                self.log.info("Application raised error '%s', forwarding to client. Inbound PDU was [%s], hex[%s]" % (
                validation_error, reqPDU, _safelylogOutPdu(self.encoder.encode(reqPDU))))
                # Jasmin update: validation_error have attribute named commandStatusName
                # return_cmd_status = validation_error.commandStatusName
                return_cmd_status = validation_error.status
                shutdown = False
        else:
            self.log.critical('Exception raised handling inbound PDU [%s] hex[%s]: %s' % (
            reqPDU, _safelylogOutPdu(self.encoder.encode(reqPDU)), error))
            return_cmd_status = CommandStatus.ESME_RX_T_APPN
            shutdown = True

        if reqPDU.requireAck:
            self.sendResponse(reqPDU, return_cmd_status)

        if shutdown:
            self.shutdown()

    def PDURequestFinished(self, result, reqPDU):
        self.endInboundTransaction(reqPDU)
        return result

    def finishTxns(self):
        return defer.DeferredList([self.finishInboundTxns(), self.finishOutboundTxns()])

    def finishInboundTxns(self):
        return defer.DeferredList(self.inTxns.values())

    def finishOutboundTxns(self):
        return defer.DeferredList([txn.ackDeferred for txn in list(self.outTxns.values())])

    def PDUResponseReceived(self, pdu):
        """Handle incoming response PDUs
        """
        if isinstance(pdu, GenericNack):
            self.log.critical("Recevied generic_nack %s" % pdu)
            if pdu.seqNum is None:
                self.onCorruptConnection()
                return

        if pdu.seqNum not in self.outTxns:
            self.log.critical('Response PDU received with unknown outbound transaction sequence number %s' % pdu)
            return

        self.endOutboundTransaction(pdu)

    def sendPDU(self, pdu):
        """Send a SMPP PDU
        """
        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Sending PDU: %s" % pdu)
        encoded = self.encoder.encode(pdu)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Sending data [%s]" % _safelylogOutPdu(encoded))

        self.transport.write(encoded)
        self.onSMPPOperation()

    def sendBindRequest(self, pdu):
        return self.sendRequest(pdu, self.config().sessionInitTimerSecs)

    def sendRequest(self, pdu, timeout):
        return defer.maybeDeferred(self.doSendRequest, pdu, timeout)

    def doSendRequest(self, pdu, timeout):
        if self.connectionCorrupted:
            raise SMPPClientConnectionCorruptedError()
        if not isinstance(pdu, PDURequest) or pdu.requireAck is None:
            raise SMPPClientError("Invalid PDU to send: %s" % pdu)

        pdu.seqNum = self.claimSeqNum()
        self.sendPDU(pdu)
        return self.startOutboundTransaction(pdu, timeout)

    def onSMPPOperation(self):
        """Called whenever an SMPP PDU is sent or received
        """
        if self.isBound():
            self.activateEnquireLinkTimer()

        self.activateInactivityTimer()

    def activateEnquireLinkTimer(self):
        if self.enquireLinkTimer and self.enquireLinkTimer.active():
            self.enquireLinkTimer.reset(self.config().enquireLinkTimerSecs)
        elif self.config().enquireLinkTimerSecs:
            self.enquireLinkTimer = self.callLater(self.config().enquireLinkTimerSecs, self.enquireLinkTimerExpired)

    def activateInactivityTimer(self):
        if self.inactivityTimer and self.inactivityTimer.active():
            self.inactivityTimer.reset(self.config().inactivityTimerSecs)
        elif self.config().inactivityTimerSecs:
            self.inactivityTimer = self.callLater(self.config().inactivityTimerSecs, self.inactivityTimerExpired)

    def cancelEnquireLinkTimer(self):
        if self.enquireLinkTimer and self.enquireLinkTimer.active():
            self.enquireLinkTimer.cancel()
            self.enquireLinkTimer = None

    def cancelInactivityTimer(self):
        if self.inactivityTimer and self.inactivityTimer.active():
            self.inactivityTimer.cancel()
            self.inactivityTimer = None

    def enquireLinkTimerExpired(self):
        txn = self.sendRequest(EnquireLink(), self.config().responseTimerSecs)
        txn.addErrback(self.enquireLinkErr)

    def enquireLinkErr(self, failure):
        # Unbinding already anyway. No need to raise another error
        failure.trap(SMPPError)

    def inactivityTimerExpired(self):
        self.log.critical("Inactivity timer expired...shutting down")
        self.shutdown()

    def isBound(self):
        return self.sessionState in (
        SMPPSessionStates.BOUND_TX, SMPPSessionStates.BOUND_RX, SMPPSessionStates.BOUND_TRX)

    def shutdown(self):
        """ Unbind if appropriate and disconnect """

        if self.isBound() and not self.connectionCorrupted:
            self.log.warning("Shutdown requested...unbinding")
            self.unbind().addBoth(lambda result: self.disconnect())
        elif self.sessionState not in (SMPPSessionStates.UNBIND_RECEIVED, SMPPSessionStates.UNBIND_PENDING):
            self.log.warning("Shutdown requested...disconnecting")
            self.disconnect()
        else:
            self.log.debug("Shutdown already in progress")

    def startInboundTransaction(self, reqPDU):
        if reqPDU.seqNum in self.inTxns:
            raise SMPPProtocolError('Duplicate message id [%s] received.  Already in progess.' % reqPDU.seqNum,
                                    CommandStatus.ESME_RUNKNOWNERR)
        txnDeferred = defer.Deferred()
        self.inTxns[reqPDU.seqNum] = txnDeferred
        self.log.debug("Inbound transaction started with message id %s" % reqPDU.seqNum)
        return txnDeferred

    def endInboundTransaction(self, reqPDU):
        if not reqPDU.seqNum in self.inTxns:
            raise ValueError('Unknown inbound sequence number in transaction for request PDU %s' % reqPDU)

        self.log.debug("Inbound transaction finished with message id %s" % reqPDU.seqNum)
        self.inTxns[reqPDU.seqNum].callback(reqPDU)
        del self.inTxns[reqPDU.seqNum]

    def startOutboundTransaction(self, reqPDU, timeout):
        if reqPDU.seqNum in self.outTxns:
            raise ValueError('Seq number [%s] is already in progess.' % reqPDU.seqNum)

        # Create callback deferred
        ackDeferred = defer.Deferred()
        # Create response timer
        timer = self.callLater(timeout, self.onResponseTimeout, reqPDU, timeout)
        # Save transaction
        self.outTxns[reqPDU.seqNum] = SMPPOutboundTxn(reqPDU, timer, ackDeferred)
        self.log.debug("Outbound transaction started with message id %s" % reqPDU.seqNum)
        return ackDeferred

    def closeOutboundTransaction(self, seqNum):
        self.log.debug("Outbound transaction finished with message id %s" % seqNum)

        if seqNum in self.outTxns:
            txn = self.outTxns[seqNum]
            # Remove txn
            del self.outTxns[seqNum]
            # Cancel response timer
            if txn.timer.active():
                txn.timer.cancel()
            return txn
        else:
            self.log.critical('Cannot close outbound transaction: trx id [%s] not found !', seqNum)
            return None

    def endOutboundTransaction(self, respPDU):
        txn = self.closeOutboundTransaction(respPDU.seqNum)

        if txn is not None:
            if respPDU.status == CommandStatus.ESME_ROK:
                if not isinstance(respPDU, txn.request.requireAck):
                    txn.ackDeferred.errback(SMPPProtocolError(respPDU,
                        "Invalid PDU response type [%s] returned for request type [%s]" % (
                        type(respPDU), type(txn.request))))
                    return
                # Do callback
                txn.ackDeferred.callback(SMPPOutboundTxnResult(self, txn.request, respPDU))
                return

            if isinstance(respPDU, GenericNack):
                txn.ackDeferred.errback(SMPPGenericNackTransactionError(respPDU, txn.request))
                return

            errCode = respPDU.status
            txn.ackDeferred.errback(SMPPTransactionError(respPDU, txn.request))

    def endOutboundTransactionErr(self, reqPDU, error):
        self.log.error(error)
        txn = self.closeOutboundTransaction(reqPDU.seqNum)

        if txn is not None:
            # Do errback
            txn.ackDeferred.errback(error)

    def cancelOutboundTransactions(self, error):
        for txn in list(self.outTxns.values()):
            self.endOutboundTransactionErr(txn.request, error)

    def onResponseTimeout(self, reqPDU, timeout):
        errMsg = 'Request timed out after %s secs: %s' % (timeout, reqPDU)
        self.endOutboundTransactionErr(reqPDU, SMPPRequestTimoutError(errMsg))
        self.shutdown()

    def claimSeqNum(self):
        self.lastSeqNum += 1
        return self.lastSeqNum

    def unbindSucceeded(self, result):
        self.sessionState = SMPPSessionStates.UNBOUND
        self.log.warning("Unbind succeeded")
        return result

    def unbindFailed(self, reason):
        self.log.error("Unbind failed [%s]. Disconnecting..." % reason)
        self.disconnect()
        if reason.check(SMPPRequestTimoutError):
            raise SMPPSessionInitTimoutError(str(reason))
        return reason

    def unbindAfterInProgressTxnsFinished(self, result, unbindDeferred):
        self.log.warning('Issuing unbind request')
        self.sendBindRequest(Unbind()).addCallbacks(self.unbindSucceeded, self.unbindFailed).chainDeferred(
            unbindDeferred)

    ############################################################################
    # Public command functions
    ############################################################################
    def unbind(self):
        """Unbind from SMSC

        Result is a Deferred object
        """
        if not self.isBound():
            return defer.fail(
                SMPPClientSessionStateError('unbind called with illegal session state: %s' % self.sessionState))

        self.cancelEnquireLinkTimer()

        self.log.info('Waiting for in-progress transactions to finish...')

        # Signal that
        #   - no new data requests should be sent
        #   - no new incoming data requests should be accepted
        self.sessionState = SMPPSessionStates.UNBIND_PENDING

        unbindDeferred = defer.Deferred()
        # Wait for any in-progress txns to finish
        self.finishTxns().addCallback(self.unbindAfterInProgressTxnsFinished, unbindDeferred)
        # Result is the deferred for the unbind txn
        return unbindDeferred

    def unbindAndDisconnect(self):
        """Unbind from SMSC and disconnect

        Result is a Deferred object
        """
        return self.unbind().addBoth(lambda result: self.disconnect())

    def disconnect(self):
        """Disconnect from SMSC
        """
        if self.isBound():
            self.log.warning("Disconnecting while bound to SMSC...")
        else:
            self.log.warning("Disconnecting...")
        self.sessionState = SMPPSessionStates.UNBOUND
        self.transport.loseConnection()

    def getDisconnectedDeferred(self):
        """Get a Deferred so you can be notified on disconnect
        """
        return self.disconnectedDeferred

    def sendDataRequest(self, pdu):
        """Send a SMPP Request Message

        Argument is an SMPP PDUDataRequest (protocol data unit).
        Result is a Deferred object
        """
        if not isinstance(pdu, PDUDataRequest):
            return defer.fail(SMPPClientError("Invalid PDU passed to sendDataRequest(): %s" % pdu))
        if not self.isBound():
            return defer.fail(SMPPClientSessionStateError('Not bound'))
        return self.sendRequest(pdu, self.config().responseTimerSecs)
Exemple #32
0
 def test_decodeHeader_command_length_too_short(self):
     self.do_decode_corrupt_data_error_test(
         PDUEncoder().decodeHeader, CommandStatus.ESME_RINVCMDLEN,
         b'0000000f000000060000000000000000')
Exemple #33
0
 def test_decode_bad_message_length_msg_too_long(self):
     self.do_decode_corrupt_data_error_test(
         PDUEncoder().decode, CommandStatus.ESME_RINVCMDLEN,
         b'0000001c80000009000000000000000154534937353838000210000134')
 def __init__(self):
     self.log = logging.getLogger(LOG_CATEGORY)
     self.recvBuffer = ""
     self.lastSeqNum = 0
     self.encoder = PDUEncoder()
Exemple #35
0
 def test_BindTransceiverResp_error_has_no_body(self):
     pdu = BindTransceiverResp(3456, status=CommandStatus.ESME_RINVPASWD)
     self.assertEqual(0, len(pdu.params))
     self.do_conversion_test(PDUEncoder(), pdu,
                             b'00000010800000090000000e00000d80')
Exemple #36
0
 def getPDU(self, hexStr):
     return PDUEncoder().decode(BytesIO(binascii.a2b_hex(hexStr)))
Exemple #37
0
 def _send_PDU(self, pdu: PDU):
     self._transport.write(PDUEncoder().encode(pdu))