Beispiel #1
0
    def _unmarshal_30(self, data):
        if not data.endswith(b'END\r\n'):
            raise MarshalError('Invalid SNP 3.0 request',
                                'Request.unmarshal')

        data = data[:-2]
        lines = data.split(b'\r\n')
        header = lines[0]

        try:
            items = header.split(b' ')
            if len(items) >= 2:
                if items[1] != b'NONE':
                    self._encryption = tuple(items[1].split(b':'))
                    self.use_encryption = True

            if len(items) >= 3:
                hash_and_salt = items[2]
                htype, rest = hash_and_salt.split(b':')
                hkey, hsalt = rest.split(b'.')
                self._hash = HashInfo(htype.upper(), unhexlify(hkey), unhexlify(hsalt))

                if self.password is not None:
                    validate_hash(self.password, self._hash)

                self.use_hash = True
        except ValueError:
            raise MarshalError('Invalid SNP body format: %s' % str(header),
                                'Request.unmarshal')

        self.commands = []
        for line in lines[1:-1]:
            self.commands.append(self._extract_command(line))

        self.version = '3.0'
Beispiel #2
0
    def marshal(self):
        """marshal the request ready to send over the wire."""

        if self.use_hash or self.use_encryption:
            if self.password is None:
                raise MarshalError('Password required to generate hash for marshal of request.',
                                    'Request.marshal')

            self._hash = generate_hash(self.password.encode('UTF-8'))

        if self.use_encryption:
            if not PY_CRYPTO:
                raise MarshalError('Unable to encrypt message. PyCrypto not available',
                                    'Request.marshal')

            if ENCRYPTION_ALGORITHM == 'AES':
                iv = urandom(16)
            else:
                iv = urandom(8)

            self._encryption = (ENCRYPTION_ALGORITHM, iv)

        if self.version == '2.0':
            return self._marshal_20()
        elif self.version == '3.0':
            return self._marshal_30()
        elif self.version == '1.0':
            raise MarshalError('SNP protocol version 1.0 is unsupported.',
                                'Request.marshal')
Beispiel #3
0
    def unmarshal(self, data):
        """Unmarshal data received over the wire into a valid request"""

        if data.startswith(b'snp://'):
            self._unmarshal_20(data)
        elif data.startswith(b'SNP/3.0'):
            self._unmarshal_30(data)
        else:
            raise MarshalError('Invalid SNP Request.',
                                'Request.unmarshal')
Beispiel #4
0
    def _extract_command(self, data):
        try:
            command, rest = data.split(b'?', 1)

            params = {}
            items = rest.split(b'&')
            for item in items:
                name, value = item.split(b'=', 1)
                value = self._unescape(value)

                params[name] = value

            return SNPCommand(command, params)
        except ValueError:
            raise MarshalError('Invalid command format found: %s', str(data),
                                'Request.unmarshal')
Beispiel #5
0
    def marshal(self, encoding='UTF-8'):
        """marshal the request ready to send over the wire."""

        if self.use_hash or self.use_encryption:
            if self.password is None:
                raise MarshalError(
                    'Password required to generate hash for marshaling of request.',
                    'Request.marshal')

            self._hash = generate_hash(self.password.encode('UTF-8'))

        if self.use_encryption:
            if not PY_CRYPTO:
                raise MarshalError(
                    'Unable to encrypt message. PyCrypto not available')

            if ENCRYPTION_ALGORITHM == 'AES':
                iv = urandom(16)
            else:
                iv = urandom(8)

            self._encryption = (ENCRYPTION_ALGORITHM, iv)

        header = 'GNTP/%s %s' % (self.version, self.command)
        if self._encryption is not None:
            header += ' %s:%s' % self._encryption
        else:
            header += ' NONE'

        if self._hash is not None:
            header += ' %s:%s.%s' % self._hash

        header += '\r\n'
        data = bytearray(header.encode(encoding))

        body_data = bytearray()
        for name, value in iter(sorted(self.body.items())):
            body_data.extend(('%s: %s\r\n' % (name, value)).encode(encoding))

        body_data.extend(('\r\n').encode(encoding))

        if self._encryption is not None:
            body_data = self._encrypt(body_data)

        data.extend(body_data)

        for section in self.sections:
            section_data = bytearray()
            for name, value in section.items():
                if isinstance(value, self._IdReference):
                    section_data.extend(('%s: x-growl-resource//%s\r\n' %
                                         (name, str(value))).encode(encoding))
                else:
                    section_data.extend(
                        ('%s: %s\r\n' % (name, value)).encode(encoding))

            section_data.extend(('\r\n').encode(encoding))

            if self._encryption is not None:
                section_data = self._encrypt(section_data)

            data.extend(section_data)

        for identifier in self.identifiers:
            identifier_data = bytearray()
            identifier_data.extend(('Identifier: %s\r\n' %
                                    identifier['Identifier']).encode(encoding))
            idata = identifier['Data']
            identifier_data.extend(
                ('Length: %d\r\n\r\n' % len(idata)).encode(encoding))

            if self._encryption is not None:
                idata = self._encrypt(idata)

            identifier_data.extend(idata)
            identifier_data.extend(('\r\n\r\n').encode(encoding))

            data.extend(identifier_data)

        return data
Beispiel #6
0
    def unmarshal(self, data):
        """Unmarshal data received over the wire into a valid response"""

        if self._encryption is not None and not PY_CRYPTO:
            raise MarshalError(
                'Unable to decrypt message. PyCrypto not available')

        header, data = data.split(b'\r\n', maxsplit=1)

        header_re = (
            'GNTP\/(?P<version>\d\.\d) (?P<responsetype>\-?[A-Z]+)'
            ' ((?P<encryptionAlgorithmID>\w+)\:(?P<ivValue>[a-fA-F0-9]+)|NONE)'
        )
        m = re.match(header_re, header.decode('UTF-8'))
        if m is not None:
            d = m.groupdict()
            self.version = d['version']
            self.status = d['responsetype'].lstrip('-')

            if self.status == 'OK':
                self.status_code = 0

            if d['encryptionAlgorithmID'] is None:
                self.use_encryption = False
                self._encryption = None
            else:
                self.use_encryption = True
                self._encryption = (d['encryptionAlgorithmID'], d['ivValue'])
                data = self._decrypt(data)

            for line in [l for l in data.split(b'\r\n') if len(l) != 0]:
                try:
                    name, value = line.split(b':', 1)
                    name = name.decode('UTF-8').strip()
                    value = value.decode('UTF-8').strip()

                    # All
                    if name == 'Response-Action':
                        self.command = value.lower()

                    elif name == 'X-Timestamp':
                        self.timestamp = parse_datetime(value)
                    elif name == 'X-Message-Daemon':
                        self.daemon = value
                    elif name == 'Origin-Machine-Name':
                        self.origin = value
                    elif name == 'Origin-Software-Name':
                        self.origin_software_name = value
                    elif name == 'Origin-Software-Version':
                        self.origin_software_version = value
                    elif name == 'Origin-Platform-Name':
                        self.origin_platform_name = value
                    elif name == 'Origin-Platform-Version':
                        self.origin_platform_version = value

                    elif name == 'Error-Code':
                        self.status_code = int(value)
                    elif name == 'Error-Description':
                        self.reason = value

                    # Notify
                    elif name == 'Application-Name':
                        self.notifier_name = value
                    elif name == 'Notification-ID':
                        self.nid = value
                    elif name == 'Notification-Callback-Result':
                        self.callback_status = value
                    elif name == 'Notification-Callback-Context':
                        self.callback_context = value
                    elif name == 'Notification-Callback-Context-Type':
                        self.callback_context_type = value
                    elif name == 'Notification-Callback-Timestamp':
                        self.callback_timestamp = parse_datetime(value)

                    # Subscribe
                    elif name == 'Subscription-TTL':
                        self.ttl = int(value)

                    else:
                        if name not in self.body:
                            self.body[name] = value
                        else:
                            if not isinstance(self.body[name], list):
                                self.body[name] = [self.body[name]]

                            self.body[name].append(value)
                except ValueError:
                    logging.debug(
                        'hiss.handler.GNTP.Response.unmarshal - Error splitting %s'
                        % line)
                    raise
Beispiel #7
0
    def unmarshal(self, data, encoding='UTF-8'):
        """Unmarshal data received over the wire into a valid request"""

        if self._encryption is not None and not PY_CRYPTO:
            raise MarshalError('PyCrypto required to decrypt message')

        sections = data.split(b'\r\n\r\n')

        header_info_re = b'GNTP\/(?P<version>\d\.\d) (?P<command>[A-Z]+)( ((?P<encryptionAlgorithmID>\w+)\:(?P<ivValue>[a-fA-F0-9]+)|NONE)( (?P<keyHashAlgorithmID>\w+)\:(?P<keyHash>[a-fA-F0-9]+)\.(?P<salt>[a-fA-F0-9]+))?)?'
        header_section = sections.pop(0)
        header_info, *headers = header_section.split(b'\r\n')
        m = re.match(header_info_re, header_info)

        if m is not None:
            d = m.groupdict()
            self.version = d['version'].decode(encoding)
            self.command = d['command'].decode(encoding)

            if d['encryptionAlgorithmID'] is None:
                self.use_encryption = False
                self._encryption = None
            else:
                self.use_encryption = True
                self._encryption = (
                    d['encryptionAlgorithmID'].decode(encoding),
                    unhexlify(d['ivValue']))

            if d['keyHashAlgorithmID'] is None:
                self.use_hash = False
                self._hash = None
            else:
                if self.password is None:
                    raise MarshalError(
                        'Password required to validate hash for unmarshal of request.'
                    )

                self._hash = HashInfo(d['keyHashAlgorithmID'].decode(encoding),
                                      unhexlify(d['keyHash']),
                                      unhexlify(d['salt']))
                self.use_hash = True
                validate_hash(self.password, self._hash)

            if len(headers[0]) > 0:
                self._unmarshal_section(headers, self.body)

            if len(sections) > 0:
                info = None
                next_section_is_data = False
                for section in sections:
                    if not next_section_is_data:
                        info = {}
                        self._unmarshal_section(section.split(b'\r\n'), info)

                        if 'Identifier' in info:
                            next_section_is_data = True
                            self.identifiers.append(info)
                        else:
                            self.sections.append(info)
                    elif info is not None:
                        length = int(info['Length'])
                        info['Data'] = section[:length]
                        next_section_is_data = False
        else:
            raise MarshalError('Response.unmarshal: Invalid GNTP message')