def _errorMessage(self, source):
        (errorCode, length) = struct.unpack('>lH', source['data'][0:6])
        offset = 6

        expectedLength = offset + length
        actualLength = len(source['data'])

        if expectedLength != actualLength:
            raise ParsingException(
                'Expected error message length is {0} but actual is {1}'.
                format(expectedLength, actualLength))

        value = source['data'][offset:(offset + length)]

        try:
            # Most of the time value will be a string which means it will be UTF8
            value = value.decode('utf-8')

            # Since here, remove nulls
            value = value.replace('\0', '')

        except UnicodeDecodeError:
            # If this is happening here then something seriously bad is going on
            value = binascii.hexlify(value)

        self.record = {'code': errorCode, 'text': value}

        self.offset = actualLength
        self.isParsed = True
    def __init__(self, source):
        self.source = source
        self.logger = logging.getLogger(__name__)
        self.data = None
        self.length = 0
        self.recordType = 0
        self.blockType = 0
        self.offset = 0
        self.record = None
        self.isParsed = False

        self.inetNtop = socket.inet_ntop
        if os.name == 'nt':
            self.inetNtop = win_inet_pton.inet_ntop

        # Do not touch source. Leave it alone.
        if 'data' not in source:
            self.logger.info('loads(): data not in response')
            self.logger.info(source)

        else:
            self.data = source['data']

        if source['messageType'] == definitions.MESSAGE_TYPE_EVENT_DATA:
            self._eventHeader(self.data)

        elif source['messageType'] == definitions.MESSAGE_TYPE_ERROR:
            self._errorMessage(source)

        else:
            raise ParsingException('Unexpected message type: {0}'.format(
                source['messageType']))
    def _eventHeader(self, data):
        (recordType, recordLength) = struct.unpack('>LL', data[0:8])

        self.recordType = recordType

        offset = 0
        record = {'recordType': recordType, 'recordLength': recordLength}

        if len(data) == (8 + recordLength):
            record['archiveTimestamp'] = 0
            record['checksum'] = 0
            offset = 8
            #record[ 'record' ] = data[ 8 : 8 + recordLength ]

        elif len(data) == (16 + recordLength):
            (archiveTimestamp, checksum) = struct.unpack('>LL', data[8:16])
            record['archiveTimestamp'] = archiveTimestamp
            record['checksum'] = checksum
            offset = 16
            #record[ 'record' ] = data[ 16 : 16 + recordLength ]

        else:
            raise ParsingException('Invalid length')

        self.offset = offset
        self.record = record
    def _parse( self, data, offset, record ):
        recordType = record[ 'recordType' ]
        recordLength = len( data )

        try:
            #if recordType == 95:
            attributes = RECORDS[ recordType ][ 'attributes' ]
            offset = self._parseAttributes( data, offset, attributes, record )

        except (AttributeError, ValueError) as ex:
            hexRecord = binascii.hexlify( data )
            self.logger.error( 'AttributeError: Record={0}'.format( hexRecord ) )
            self.logger.exception(ex)
            return

        if offset != recordLength:
            msg = '__parse(): Offset ({0}) != recordLength ({1}) for recordType {2}'.format(
                offset,
                recordLength,
                recordType)

            if offset < recordLength:
                self.logger.warning( msg )

            else:
                raise ParsingException( msg )
    def _blockDefinition(key):
        if key is None:
            raise ParsingException('Unknown block definition: {0}', key)

        if not key & BLOCK_SERIES_2_SHIM:
            if key not in BLOCKS_SERIES_1:
                raise ParsingException('Unknown block definition: {0}', key)

            else:
                return BLOCKS_SERIES_1[key]

        else:
            if key not in BLOCKS_SERIES_2:
                raise ParsingException('Unknown block definition: {0}', key)

            else:
                return BLOCKS_SERIES_2[key]
    def _eventHeader(self, data):
        (recordType, recordLength) = struct.unpack('>LL', data[0:8])

        self.recordType = recordType
        blockType = 0
        #struct.unpack('>'+TYPE_UINT32, data[8:12])[0] epoch time for IPS events
        #struct.unpack('>'+TYPE_UINT32, data[12:16])[0]  reserved IPS events

        if self.logger.isEnabledFor(logging.TRACE):
            self.logger.log(
                logging.TRACE,
                '_eventHeader recordType={0} blockType={1} | data={2} | hex={3}'
                .format(recordType, blockType, data, binascii.hexlify(data)))

        offset = 0

        record = {
            'recordType': recordType,
            'recordLength': recordLength,
            'blockType': blockType
        }

        if len(data) == (8 + recordLength):
            record['archiveTimestamp'] = 0
            record['checksum'] = 0
            offset = 8
            #record[ 'record' ] = data[ 8 : 8 + recordLength ]

        elif len(data) == (16 + recordLength):
            (archiveTimestamp, checksum) = struct.unpack('>LL', data[8:16])
            record['archiveTimestamp'] = archiveTimestamp
            record['checksum'] = checksum
            offset = 16
            #record[ 'record' ] = data[ 16 : 16 + recordLength ]

        else:
            raise ParsingException('Invalid length')

#       if self.recordType == 400 :
#           (blockType, ) = struct.unpack('>'+TYPE_UINT32, data[16:20])
#       else :
#           (blockType, ) = struct.unpack('>'+TYPE_UINT32, data[offset: (offset + 4) ])

#        self.blockType = blockType

        if self.logger.isEnabledFor(logging.TRACE):
            self.logger.log(
                logging.TRACE,
                '_eventHeader recordType={0} blockType={1} | data={2} | hex={3}'
                .format(recordType, blockType, data, binascii.hexlify(data)))


#        record[ 'blockType' ]  = blockType

        self.offset = offset
        self.record = record
    def _parseVariable(self, data, offset, attribute, context):

        lengthSource = attribute['length']
        blockLength = context[lengthSource]

        if self.logger.isEnabledFor(logging.TRACE):
            binhex = data[offset:blockLength]
            self.logger.log(
                logging.TRACE,
                'offset= {0}/{1} |  attribute={1} | context={2} | data[{0}:{1}]={3}'
                .format(offset, blockLength, attribute, context, binhex))

        attributeName = attribute['name']

        if 'adjustment' in attribute:
            lengthAdjustment = attribute['adjustment']

            if self.logger.isEnabledFor(logging.TRACE):
                binhex = data[offset:(offset + 8)]
                self.logger.log(
                    logging.TRACE,
                    'length adjustment= {0} | block size={1} | attribute={2} | blockLength={3}'
                    .format(lengthAdjustment, binhex, attribute, blockLength))
        else:
            lengthAdjustment = 0

        length = blockLength + lengthAdjustment

        if length > 0:
            value = data[offset:(offset + length)]

            try:
                # Most of the time value will be a string which means it will be UTF8
                value = value.decode('utf-8')

                # Since here, remove nulls
                context[attributeName] = value.replace('\0', '')

            except UnicodeDecodeError:
                # But sometimes we have blobs or just "variable" data as in a packet
                context[attributeName] = binascii.hexlify(value)

            offset += length

        elif length == 0:
            context[attributeName] = ''

        else:
            value = data[offset:(offset + length)]
            raise ParsingException(
                'Invalid block length ({0}) length2: {1} at pos {7}. RecordType={2}, BlockType={3}, Field={4} Length={5} Value={6}'
                .format(blockLength, length, self.recordType, self.blockType,
                        attribute['name'], attribute['length'], value, offset))

        return offset
Example #8
0
    def _parseVariable(self, data, offset, attribute, context):
        lengthSource = attribute['length']
        blockLength = context[lengthSource]
        attributeName = attribute['name']

        if self.logger.isEnabledFor(logging.TRACE):
            self.logger.log(
                logging.TRACE,
                '_parseVariable - offset={0}:attributre={1}:context={2}:data={3}'
                .format(offset, attribute, context, data))

        if 'adjustment' in attribute:
            lengthAdjustment = attribute['adjustment']

        else:
            lengthAdjustment = 0

        length = blockLength + lengthAdjustment

        if length > 0:
            value = data[offset:(offset + length)]

            try:
                # Most of the time value will be a string which means it will be UTF8
                value = value.decode('utf-8')

                # Since here, remove nulls
                context[attributeName] = value.replace('\0', '')

            except UnicodeDecodeError:
                # But sometimes we have blobs or just "variable" data as in a packet
                context[attributeName] = binascii.hexlify(value)

            offset += length

        elif length == 0:
            context[attributeName] = ''

        else:
            raise ParsingException(
                'Invalid block length ({0}). RecordType={1}, Field={2}'.format(
                    blockLength, self.recordType, attribute['name']))

        return offset
Example #9
0
    def _parse(self, data, offset, record):
        recordType = record['recordType']
        recordLength = len(data)

        if recordType == 62:
            record['length'] = struct.unpack('>L', data[4:8])[0]
            record['id'] = struct.unpack('>L', data[8:12])[0]
            record['name'] = data[12:recordLength].decode('utf-8')

            offset = recordLength

        else:
            attributes = RECORDS[recordType]['attributes']
            offset = self._parseAttributes(data, offset, attributes, record)

        if offset != recordLength:
            msg = '__parse(): Offset ({0}) != recordLength ({1}) for recordType {2}'.format(
                offset, recordLength, recordType)

            if offset < recordLength:
                self.logger.warning(msg)

            else:
                raise ParsingException(msg)
    def _parse(self, data, offset, record):
        recordType = record['recordType']
        blockType = self.blockType
        recordLength = len(data)

        if self.logger.isEnabledFor(logging.TRACE):
            self.logger.log(
                logging.TRACE,
                '_parse offset={0}/{1} | recordType={2}  '.format(
                    offset, recordLength, recordType))
        try:
            attributes = RECORDS[recordType]['attributes']

            #Dynamic according to blocktype
            if recordType == 71:
                blockSubType = struct.unpack('>' + TYPE_UINT32, data[72:76])[0]

                self.logger.log(
                    logging.TRACE,
                    'parsing start {0} offset: {1} parsing: {2}'.format(
                        data, offset, data[72:76]))
                self.logger.log(logging.TRACE,
                                'parsing blockType {0}'.format(blockSubType))

                if blockSubType == 160:
                    attributes = RECORDS[1060]['attributes']

                    self.logger.log(
                        logging.TRACE, 'IPS BLOCK {0} attributes={1}'.format(
                            blockType, attributes))

                elif blockSubType == 163:
                    attributes = RECORDS[1061]['attributes']

                    self.logger.log(
                        logging.TRACE,
                        'parsing IPS event {0} : attributes={1}'.format(
                            blockType, attributes))

                elif blockSubType == 168:
                    attributes = RECORDS[1067]['attributes']

                    self.logger.log(
                        logging.TRACE,
                        'parsing IPS event {0} : attributes={1}'.format(
                            blockType, attributes))

                elif blockSubType == 173:
                    attributes = RECORDS[1070]['attributes']

                    self.logger.log(
                        logging.TRACE,
                        'parsing IPS event {0} : attributes={1}'.format(
                            blockType, attributes))

                elif blockSubType == 174:
                    attributes = RECORDS[1071]['attributes']

                    self.logger.log(
                        logging.TRACE,
                        'parsing IPS event {0} : attributes={1}'.format(
                            blockType, attributes))
                else:
                    attributes = RECORDS[1071]['attributes']

                    self.logger.error(
                        'Unsupported Record/Block Type: Record={0} BlockType={1}'
                        .format(recordType, blockType))

            elif recordType == 400:
                blockSubType = struct.unpack('>' + TYPE_UINT32, data[16:20])[0]
                self.logger.log(logging.TRACE,
                                'parsing blockType {0}'.format(blockSubType))

                if blockSubType == 60:
                    attributes = RECORDS[401]['attributes']

                    self.logger.log(
                        logging.TRACE, 'IPS BLOCK {0} attributes={1}'.format(
                            blockType, attributes))

                elif blockSubType == 81:
                    attributes = RECORDS[402]['attributes']

                    self.logger.log(
                        logging.TRACE,
                        'parsing IPS event {0} : attributes={1}'.format(
                            blockType, attributes))

                elif blockSubType == 85:
                    attributes = RECORDS[400]['attributes']

                    self.logger.log(
                        logging.TRACE,
                        'parsing IPS event {0} : attributes={1}'.format(
                            blockType, attributes))

                else:
                    attributes = RECORDS[recordType]['attributes']

                    self.logger.error(
                        'Unsupported Record/Block Type: Record={0} BlockType={1}'
                        .format(recordType, blockType))

            offset = self._parseAttributes(data, offset, attributes, record)

        except (AttributeError, ValueError) as ex:
            hexRecord = binascii.hexlify(data)
            self.logger.error('AttributeError: Record={0}'.format(hexRecord))
            self.logger.exception(ex)
            return

        if offset != recordLength:
            msg = '__parse(): Offset ({0}) != recordLength ({1}) for recordType {2} blockType {3}'.format(
                offset, recordLength, recordType, blockType)

            if offset < recordLength:
                self.logger.warning(msg)

            else:
                raise ParsingException(msg)
    def _parseAttributes(self, data, offset, attributes, context):
        recordType = self.recordType
        blockType = self.blockType
        recordLength = len(data)

        for attribute in attributes:
            attributeName = attribute['name'] if 'name' in attribute else None

            if self.logger.isEnabledFor(logging.TRACE):
                binhex = data[offset:recordLength]
                self.logger.log(
                    logging.TRACE,
                    'offset={0}/{1} | attribute={2} | data[{0}:{1}]={3}'.
                    format(offset, recordLength, attribute, binhex))

            if offset > recordLength:
                raise ParsingException(
                    '_attributes() | offset ({0}) > length ({1}) | blockType={2} recordType={3}'
                    .format(offset, recordLength, blockType, recordType))

            elif offset == recordLength:
                if 'type' in attribute and attribute['type'] == TYPE_VARIABLE:
                    context[attributeName] = ''

                return offset

            if 'discovery' in attribute:
                offset = self._parseDiscoveryHeader(data, offset, context)

            elif 'type' in attribute:
                attributeType = attribute['type']

                if attributeType == TYPE_UUID:
                    byteLength = 16
                    guid = uuid.UUID(bytes=data[offset:offset + byteLength])
                    context[attributeName] = str(guid)
                    offset += byteLength

                elif attributeType == TYPE_IPV6:
                    byteLength = 16
                    context[attributeName] = self._ip2str(
                        socket.AF_INET6, data[offset:offset + byteLength])

                    offset += byteLength

                elif attributeType == TYPE_IPV4:
                    byteLength = 4
                    context[attributeName] = self._ip2str(
                        socket.AF_INET, data[offset:offset + byteLength])

                    offset += byteLength

                elif attributeType == TYPE_MAC:
                    byteLength = 6
                    macX = Binary.unpackMac(data[offset:offset + byteLength])

                    context[attributeName] = Binary._formatMacAddress(*macX)
                    offset += byteLength

                elif attributeType == TYPE_VARIABLE:
                    if self.logger.isEnabledFor(logging.TRACE):
                        self.logger.log(
                            logging.TRACE,
                            'attributeVariable: data={0} | offset={1} | attribute={2} | context={3}'
                            .format(data, offset, attribute, context))

                    offset = self._parseVariable(data, offset, attribute,
                                                 context)

                elif attributeType == TYPE_UINT128 or \
                    attributeType == TYPE_UINT160 or \
                    attributeType == TYPE_UINT256:

                    byteLength = len(attributeType)

                    #Unpack as network big-endian
                    value = struct.unpack('>' + attributeType,
                                          data[offset:offset + byteLength])

                    if self.logger.isEnabledFor(logging.TRACE):
                        self.logger.log(
                            logging.TRACE,
                            'offset={0}/{1} | attribute={2} | value={3} | data={4}'
                            .format(offset, byteLength, attribute, value,
                                    data[offset:offset + byteLength]))

                    # repack native. This step is probably not necessary as
                    # endianness should only apply to bytes, not bits and we're
                    # pulling out raw groups of bytes. TODO
                    value = struct.pack(attributeType, *value)

                    context[attributeName] = binascii.hexlify(value)
                    offset += byteLength

                else:
                    if attributeType == TYPE_BYTE:
                        byteLength = 1

                    elif attributeType == TYPE_UINT8:
                        byteLength = 1

                    elif attributeType == TYPE_UINT16:
                        byteLength = 2

                    elif attributeType == TYPE_UINT32:
                        byteLength = 4

                    elif attributeType == TYPE_UINT64:
                        byteLength = 8

                    else:
                        raise ParsingException(
                            'Unknown type: {0}'.format(attributeType))

                    if recordType == 98:
                        recLen = int(context['recordLength'])
                        maxLen = len(data)
                        size = len(data[recLen:maxLen])

                        context['id'] = struct.unpack('>' + TYPE_UINT32,
                                                      data[12:16])[0]
                        context['protocol'] = struct.unpack(
                            '>' + TYPE_UINT32, data[16:20])[0]
                        name = struct.unpack('>' + str(size) + 's',
                                             data[recLen:maxLen])[0]
                        context['name'] = name.decode('utf-8', 'ignore')
                        offset = maxLen

                    else:
                        try:
                            self.logger.log(
                                logging.TRACE,
                                'unpacking binary data {0}'.format(
                                    attributeName))
                            context[attributeName] = struct.unpack(
                                '>' + attributeType,
                                data[offset:offset + byteLength])[0]

                            offset += byteLength

                            if attributeName == 'blockType' and blockType == 0:
                                self.blockType = context[attributeName]

                        except struct.error:
                            hData = binascii.hexlify(data[offset:offset +
                                                          byteLength])
                            hexData = binascii.hexlify(data)
                            raise ParsingException(
                                'Error Decoding binary for rec_type={0} attr={1} type={2} data={3} data_full={4}'
                                .format(recordType, attributeName,
                                        attributeType, hData, hexData))

            elif 'list' in attribute:
                oldOffset = offset

                (listType,
                 listLength) = struct.unpack('>LL', data[offset:(offset + 8)])

                offset += 8

                container = {
                    'listType': listType,
                    'listLength': listLength,
                    'items': []
                }

                while (offset - oldOffset) < listLength:
                    block = {}
                    offset = self._parseBlock(data, offset, attribute, block)
                    container['items'].append(block)

                context[attributeName] = container

            elif 'block' in attribute:
                block = context
                if attributeName is not None:
                    context[attributeName] = {}
                    block = context[attributeName]

                self.logger.log(
                    logging.TRACE,
                    'Parsing Attribute (Block Type) :-: attr name={0}:attr={1}:attr_type={2}:value={3}'
                    .format(attributeName, attribute, block, offset))
                offset = self._parseBlock(data, offset, attribute, block)

        return offset
    def _parseAttributes(self, data, offset, attributes, context):
        recordType = self.recordType
        recordLength = len(data)

        for attribute in attributes:
            attributeName = attribute['name'] if 'name' in attribute else None

            if self.logger.isEnabledFor(logging.TRACE):
                self.logger.log(
                    logging.TRACE, 'offset={0}/{1} | attribute={2}'.format(
                        offset, recordLength, attribute))

            if offset > recordLength:
                raise ParsingException(
                    '_attributes() | offset ({0}) > length ({1}) | recordType={2}'
                    .format(offset, recordLength, recordType))

            elif offset == recordLength:
                if 'type' in attribute and attribute['type'] == TYPE_VARIABLE:
                    context[attributeName] = ''

                return offset

            if 'discovery' in attribute:
                offset = self._parseDiscoveryHeader(data, offset, context)

            elif 'type' in attribute:
                attributeType = attribute['type']

                if attributeType == TYPE_UUID:
                    byteLength = 16
                    guid = uuid.UUID(bytes=data[offset:offset + byteLength])
                    context[attributeName] = str(guid)
                    offset += byteLength

                elif attributeType == TYPE_IPV6:
                    byteLength = 16
                    context[attributeName] = self._ip2str(
                        socket.AF_INET6, data[offset:offset + byteLength])

                    offset += byteLength

                elif attributeType == TYPE_IPV4:
                    byteLength = 4
                    context[attributeName] = self._ip2str(
                        socket.AF_INET, data[offset:offset + byteLength])

                    offset += byteLength

                elif attributeType == TYPE_MAC:
                    byteLength = 6
                    macX = Binary.unpackMac(data[offset:offset + byteLength])

                    context[attributeName] = Binary._formatMacAddress(*macX)
                    offset += byteLength

                elif attributeType == TYPE_VARIABLE:
                    offset = self._parseVariable(data, offset, attribute,
                                                 context)

                elif attributeType == TYPE_UINT128 or \
                    attributeType == TYPE_UINT160 or \
                    attributeType == TYPE_UINT256:

                    byteLength = len(attributeType)

                    #Unpack as network big-endian
                    value = struct.unpack('>' + attributeType,
                                          data[offset:offset + byteLength])

                    # repack native. This step is probably not necessary as
                    # endianness should only apply to bytes, not bits and we're
                    # pulling out raw groups of bytes. TODO
                    value = struct.pack(attributeType, *value)

                    context[attributeName] = binascii.hexlify(value)
                    offset += byteLength

                else:
                    if attributeType == TYPE_BYTE:
                        byteLength = 1

                    elif attributeType == TYPE_UINT16:
                        byteLength = 2

                    elif attributeType == TYPE_UINT32:
                        byteLength = 4

                    elif attributeType == TYPE_UINT64:
                        byteLength = 8

                    else:
                        raise ParsingException(
                            'Unknown type: {0}'.format(attributeType))

                    context[attributeName] = struct.unpack(
                        '>' + attributeType,
                        data[offset:offset + byteLength])[0]

                    offset += byteLength

            elif 'list' in attribute:
                oldOffset = offset

                (listType,
                 listLength) = struct.unpack('>LL', data[offset:(offset + 8)])

                offset += 8

                container = {
                    'listType': listType,
                    'listLength': listLength,
                    'items': []
                }

                while (offset - oldOffset) < listLength:
                    block = {}
                    offset = self._parseBlock(data, offset, attribute, block)
                    container['items'].append(block)

                context[attributeName] = container

            elif 'block' in attribute:
                block = context
                if attributeName is not None:
                    context[attributeName] = {}
                    block = context[attributeName]

                offset = self._parseBlock(data, offset, attribute, block)

        return offset