Beispiel #1
0
    def _recv_handshake(self, key):
        pending = ''
        while True:
            if pending.find('\r\n\r\n') < 0:
                data = self.sock.recv(_buffer_size)
                logger.debug('received %d bytes\n%s', len(data), data)
                if not data:
                    raise Terminated()
                pending += data

            if pending.find('\r\n\r\n') >= 0:
                data, pending = pending.split('\r\n\r\n', 1)

                firstline, headers = data.split(
                    '\r\n', 1) if data.find('\r\n') >= 0 else (data, '')
                protocol, code, reason = firstline.split(' ', 2)
                if code != '101': raise HTTPError(code + ' ' + reason)

                headers = MIMEMessage(StringIO(headers))
                if headers.get('Upgrade', '').lower() != 'websocket':
                    raise HTTPError('invalid Upgrade header')
                if headers.get('Connection', '').lower() != 'upgrade':
                    raise HTTPError('invalid Connection header')

                accept = headers.get('Sec-WebSocket-Accept', '')
                digest = base64.b64encode(
                    hashlib.sha1(key + _magic).hexdigest().decode('hex'))
                if accept.lower() != digest.lower():
                    raise HTTPError('invalid Sec-WebSocket-Accept header')

                _callit(self, 'onopen', self)
                break
        return pending
def anonymise_mime_message(msg: MIMEMessage) -> MIMEMessage:
    """Replace all of the sensitive fields in the MIME message with messages
    indicating that this message has been anonymised.

    The fields that get replaced are "Subject", "Thread-Topic", as well as the
    body of the email. Attachments are preserved.
    """
    msg.replace_header("Subject", "The subject has been removed for anonymity")

    # the thread topic isn't always present
    if msg.get("Thread-Topic") is not None:
        msg.replace_header("Thread-Topic",
                           "The topic has been removed for anonymity")

    for part in msg.walk():
        # the only parts that contain the body are text/html and text/plain
        # TODO(sam): I think sometimes these can be encoded as base64?
        #            Might need special handling...
        if part.get_content_type() == "text/html" \
                or part.get_content_type() == "text/plain":
            if not is_file(part):
                part.set_payload("The body has been removed for anonymity")

    return msg
Beispiel #3
0
def receive_handshake(msg, verify_handshake=None, userdata=None):
    index1, index2 = msg.find('\n\n'), msg.find(
        '\n\r\n')  # handle both LFLF and CRLFCRLF
    if index2 > 0 and index1 > 0:
        index = (index1 + 2) if index1 < index2 else (index2 + 3)
    elif index1 > 0:
        index = index1 + 2
    elif index2 > 0:
        index = index2 + 3
    else:
        logger.debug('no CRLF found')
        return (None, msg, '')  # not enough header data yet

    # verify if enough data is available for content-length, if any
    match = re.search(r'content-length\s*:\s*(\d+)\r?\n', msg[:index].lower())
    length = int(match.group(1)) if match else 0
    if len(msg) < index + length:
        logger.debug('has more content %d < %d (%d+%d)', len(msg),
                     index + length, index, length)
        return (None, msg, '')  # pending further content.

    # extract the first HTTP request, and store remaining as pending
    data, body, msg = msg[:index], msg[index:index + length], msg[index +
                                                                  length:]
    try:
        firstline, data = data.split('\n', 1)
        firstline = firstline.rstrip()
        headers = MIMEMessage(StringIO(data))
        # validate firstline and some headers
        method, path, protocol = firstline.split(' ', 2)
        if method != 'GET': raise HTTPError('405 Method Not Allowed')
        if protocol != "HTTP/1.1":
            raise HTTPError('505 HTTP Version Not Supported')
        if headers.get('Upgrade', None) != 'websocket':
            raise HTTPError('403 Forbidden',
                            'missing or invalid Upgrade header')
        if headers.get('Connection', None) != 'Upgrade':
            raise HTTPError('400 Bad Request',
                            'missing or invalid Connection header')
        if 'Sec-WebSocket-Key' not in headers:
            raise HTTPError('400 Bad Request',
                            'missing Sec-WebSocket-Key header')
        if int(headers.get('Sec-WebSocket-Version', '0')) < 13:
            raise HTTPError('400 Bad Request',
                            'missing or unsupported Sec-WebSocket-Version')

        result = None  # invoke app below for result if needed
        if verify_handshake is not None and callable(verify_handshake):
            try:
                result = verify_handshake(userdata=userdata,
                                          path=path,
                                          headers=headers)
            except HTTPError:
                raise  # re-raise only HTTPError, and mask all others
            except:
                logger.exception('exception in server app: verify_handshake')
                raise HTTPError('500 Server Error',
                                'exception in server app: verify_handshake')

        # generate the response, and append result returned by onhandshake if applicable
        key = headers['Sec-WebSocket-Key']
        digest = base64.b64encode(
            hashlib.sha1(key + _magic).hexdigest().decode('hex'))
        response = [
            'HTTP/1.1 101 Switching Protocols', 'Upgrade: websocket',
            'Connection: Upgrade',
            'Sec-WebSocket-Accept: %s' % digest
        ]
        if result: response.extend(result)
        response = '\r\n'.join(
            response) + '\r\n\r\n'  # we always respond with CRLF line ending

        return (response, msg, path)
    except HTTPError as e:
        return (str(e), msg, '')  # send error response