fix(multiplayer): harden websocket frame parser

This commit is contained in:
Josh Creek
2026-09-01 21:23:20 +01:00
parent 3eb47bb9d3
commit 30a22c366b
3 changed files with 28 additions and 1 deletions
+8 -1
View File
@@ -317,6 +317,10 @@ func readWebSocketFrame(reader *bufio.Reader) (byte, []byte, error) {
if first&0x70 != 0 || first&0x80 == 0 {
return 0, nil, errors.New("unsupported websocket frame")
}
opcode := first & 0x0f
if opcode != 0x8 && opcode != 0x9 && opcode != 0xA {
return 0, nil, errors.New("unsupported websocket opcode")
}
if second&0x80 == 0 {
return 0, nil, errors.New("unmasked websocket frame")
}
@@ -337,6 +341,9 @@ func readWebSocketFrame(reader *bufio.Reader) (byte, []byte, error) {
if length > maxWebSocketFrame {
return 0, nil, errors.New("websocket frame too large")
}
if opcode&0x8 != 0 && length > 125 {
return 0, nil, errors.New("websocket control frame too large")
}
var mask [4]byte
if _, err := io.ReadFull(reader, mask[:]); err != nil {
return 0, nil, err
@@ -348,7 +355,7 @@ func readWebSocketFrame(reader *bufio.Reader) (byte, []byte, error) {
for i := range payload {
payload[i] ^= mask[i%4]
}
return first & 0x0f, payload, nil
return opcode, payload, nil
}
func writeWebSocketFrame(connection net.Conn, opcode byte, payload []byte) error {
+18
View File
@@ -1,12 +1,30 @@
package api
import (
"bufio"
"bytes"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestWebSocketReaderRejectsClientDataAndReservedOpcodes(t *testing.T) {
for _, opcode := range []byte{0x0, 0x1, 0x2, 0x3, 0xB, 0xF} {
frame := append([]byte{0x80 | opcode, 0x80, 0, 0, 0, 0}, nil...)
if _, _, err := readWebSocketFrame(bufio.NewReader(bytes.NewReader(frame))); err == nil {
t.Fatalf("opcode 0x%x was accepted", opcode)
}
}
}
func TestWebSocketReaderRejectsOversizedControlFrame(t *testing.T) {
frame := append([]byte{0x89, 0xFE, 0, 126, 0, 0, 0, 0}, bytes.Repeat([]byte{0}, 126)...)
if _, _, err := readWebSocketFrame(bufio.NewReader(bytes.NewReader(frame))); err == nil {
t.Fatal("oversized ping control frame was accepted")
}
}
func TestWebSocketHandshakeRequiresRFC6455Version(t *testing.T) {
service := &Service{}
request := httptest.NewRequest(http.MethodGet, "/v1/events", nil)