WebSockets are not as simple as you think...
- Beware of the following
- Resources
- Example payloads
- Server requesting the connection to close
- Client responding to server's request to close a connection
- Server responding with a CONNACK
- Notes
- WebSockets
- Base framing protocol
- Status code reference
- Opcode reference
- MQTT 3.1.1
- Table 2.1 - Control packet types
- Table 3.1 - Connect return code values
- AWS IoT
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
- WebSockets RFC - RFC6455
- Section 5.2 - Base Framing Protocol
- Section 11.8 - WebSocket Opcode Registry
- Section 7.4 - Status Codes
- Section 11.7 - WebSocket Close Code Number Registry
- MQTT version 3.1.1 OASIS standard
- Mozilla reference on writing a WebSocket server
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
.