WebSocket debugging notes

WebSockets are not as simple as you think...

Beware of the following

  • All payloads are wrapped in the Base Framing Protocol
  • Payloads from the client to the server MUST have their mask bit set, MUST include a 32-bit mask value, and the payload MUST be XORed with the mask. This is to prevent very broken intermediate servers from interpreting the payloads and trying to cache them.

Resources

Example payloads

Server requesting the connection to close

If a server sends a payload like this:

0x88 0x00

It translates to this:

0x8_ - FIN flag (e.g. "the final fragment in a message")
0x_8 - CLOSE opcode

Hex   :       8       8       0       0
Binary: 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0

Reference:
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-------+-+-------------+-------------------------------+
       |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
       |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
       |N|V|V|V|       |S|             |   (if payload len==126/127)   |
       | |1|2|3|       |K|             |                               |
       +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +

Which means that it wants to close the connection.

Client responding to server's request to close a connection

If a client sends a payload like this:

0000: 88 82 41 42 D9 21 42 AA

Decoding it with the base framing protocol looks like this:

0x8___ - FIN flag (e.g. "the final fragment in a message")
0x_8__ - CLOSE opcode
0x__8_ - Payload is masked
0x___2 - Payload is two bytes long

Hex   :       8       8       8       2
Binary: 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0

Reference:
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-------+-+-------------+-------------------------------+
       |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
       |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
       |N|V|V|V|       |S|             |   (if payload len==126/127)   |
       | |1|2|3|       |K|             |                               |
       +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +

The mask is the next four bytes: 0x41 0x42 0xD9 0x21

The masked payload is: 0x42 0xAA

To unmask the payload we XOR it with the mask bytes one by one:

Unmasked byte 1 = MASK[index % 4] ^ MASKED[index] = 0x41 ^ 0x42 = 0x03
Unmasked byte 2 = MASK[index % 4] ^ MASKED[index] = 0xAA ^ 0x42 = 0xE8

The final raw payload is:

0x03 0xE8

0x03E8 is equal to 1000 in decimal. This value is the status code. This payload means the server requested to close the WebSocket and the client closed it and indicated that the transaction was complete.

Server responding with a CONNACK

If the server sends a payload like this:

0000: 82 04 20 02 00 00

Decoding it with the base framing protocol looks like this:

0x8___ - FIN flag (e.g. "the final fragment in a message")
0x_2__ - BINARY FRAME opcode
0x__0_ - Payload is NOT masked
0x___4 - Payload is four bytes long

Hex   :       8       2       0       4
Binary: 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0

Reference:
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-------+-+-------------+-------------------------------+
       |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
       |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
       |N|V|V|V|       |S|             |   (if payload len==126/127)   |
       | |1|2|3|       |K|             |                               |
       +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +

The payload is: 0x20 0x02 0x00 0x00

Decoding the MQTT payload:

Byte 0
Hex   : 0x20
Binary: 0  0  1  0  0  0  0  0

        0  1  2  3  4  5  6  7
       +-----------+-----------+
       |Control    | Reserved  |
       |Packet Type|           |
       +-----------+-----------+

Control packet type 2 is CONNACK

Byte 1
Hex   : 0x02
Binary: 0  0  0  0  0  0  0  0
        0  1  2  3  4  5  6  7
       +-----------------------+
       |  Remaining length     |
       +-----------------------+

Remaining length for CONNACK in MQTT 3.1.1 is always 2

Byte 2
Hex   : 0x00
Binary: 0  0  0  0  0  0  0  0
        0  1  2  3  4  5  6  7
       +------------------+----+
       |  Reserved        | SP |
       +-----------------------+

SP is the session present flag. No session is present here.

Byte 3
Hex   : 0x00
Binary: 0  0  0  0  0  0  0  0
        0  1  2  3  4  5  6  7
       +-----------------------+
       |  Connect return code  |
       +-----------------------+

Connection accepted

Notes

These notes below are taken directly from the standards documents. They are left here so they can be copied and pasted into the examples easily.

WebSockets

Base framing protocol

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

Status code reference

     |Status Code | Meaning         | Contact       | Reference |
    -+------------+-----------------+---------------+-----------|
     | 1000       | Normal Closure  | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1001       | Going Away      | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1002       | Protocol error  | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1003       | Unsupported Data| hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1004       | ---Reserved---- | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1005       | No Status Rcvd  | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1006       | Abnormal Closure| hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1007       | Invalid frame   | hybi@ietf.org | RFC 6455  |
     |            | payload data    |               |           |
    -+------------+-----------------+---------------+-----------|
     | 1008       | Policy Violation| hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1009       | Message Too Big | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1010       | Mandatory Ext.  | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|
     | 1011       | Internal Server | hybi@ietf.org | RFC 6455  |
     |            | Error           |               |           |
    -+------------+-----------------+---------------+-----------|
     | 1015       | TLS handshake   | hybi@ietf.org | RFC 6455  |
    -+------------+-----------------+---------------+-----------|

Opcode reference

     |Opcode  | Meaning                             | Reference |
    -+--------+-------------------------------------+-----------|
     | 0      | Continuation Frame                  | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 1      | Text Frame                          | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 2      | Binary Frame                        | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 8      | Connection Close Frame              | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 9      | Ping Frame                          | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 10     | Pong Frame                          | RFC 6455  |
    -+--------+-------------------------------------+-----------|

MQTT 3.1.1

Table 2.1 - Control packet types

     |Opcode  | Meaning                             | Reference |
    -+--------+-------------------------------------+-----------|
     | 0      | Continuation Frame                  | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 1      | Text Frame                          | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 2      | Binary Frame                        | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 8      | Connection Close Frame              | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 9      | Ping Frame                          | RFC 6455  |
    -+--------+-------------------------------------+-----------|
     | 10     | Pong Frame                          | RFC 6455  |
    -+--------+-------------------------------------+-----------|

Table 3.1 - Connect return code values

     |Value   | Meaning                       |
    -+--------+-------------------------------|
     | 0      | Connection accepted           |
    -+--------+-------------------------------|
     | 1      | Connection refused            |
     | 0      | Unacceptable protocol version |
    -+--------+-------------------------------|
     | 2      | Connection refused            |
     |        | Client ID rejected            |
     |        | UTF-8 not allowed by server   |
    -+--------+-------------------------------|
     | 3      | Connection refused            |
     |        | Server unavailable            |
    -+--------+-------------------------------|
     | 4      | Connection refused            |
     |        | Bad username or password      |
    -+--------+-------------------------------|
     | 5      | Connection refused            |
     |        | Not authorized                |
    -+--------+-------------------------------|

AWS IoT

AWS IoT will upgrade a connection to a WebSocket connection if the headers look valid but will not actually validate the credentials and signature until a device tries to send some data over the connection.

You can validate this by replacing the entire X-Amz-Signature value in the URI to a single character (z for example) and the system will still respond with HTTP/1.1 101 Switching Protocols.