|
|
|
// Copyright 2021, Chef. All rights reserved.
|
|
|
|
// https://github.com/q191201771/lal
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a MIT-style license
|
|
|
|
// that can be found in the License file.
|
|
|
|
//
|
|
|
|
// Author: joestarzxh
|
|
|
|
|
|
|
|
package base
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/q191201771/naza/pkg/bele"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WsOpcode The WebSocket Protocol
|
|
|
|
// https://tools.ietf.org/html/rfc6455
|
|
|
|
//
|
|
|
|
// 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 ... |
|
|
|
|
// +---------------------------------------------------------------+
|
|
|
|
// opcode:
|
|
|
|
// * %x0 denotes a continuation frame
|
|
|
|
// * %x1 denotes a text frame
|
|
|
|
// * %x2 denotes a binary frame
|
|
|
|
// * %x3-7 are reserved for further non-control frames
|
|
|
|
// * %x8 denotes a connection close
|
|
|
|
// * %x9 denotes a ping
|
|
|
|
// * %xA denotes a pong
|
|
|
|
// * %xB-F are reserved for further control frames
|
|
|
|
// Payload length: 7 bits, 7+16 bits, or 7+64 bits
|
|
|
|
// Masking-key: 0 or 4 bytes
|
|
|
|
// mark 加密
|
|
|
|
//
|
|
|
|
// for i := 0; i < datalen; i {
|
|
|
|
// m := markingkeys[i%4]
|
|
|
|
// data[i] = msg[i] ^ m
|
|
|
|
// }
|
|
|
|
type WsOpcode = uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
Wso_Continuous WsOpcode = iota //连续消息片断
|
|
|
|
Wso_Text //文本消息片断,
|
|
|
|
Wso_Binary //二进制消息片断,
|
|
|
|
|
|
|
|
// Wso_Rsv3 非控制消息片断保留的操作码,
|
|
|
|
Wso_Rsv3
|
|
|
|
Wso_Rsv4
|
|
|
|
Wso_Rsv5
|
|
|
|
Wso_Rsv6
|
|
|
|
Wso_Rsv7
|
|
|
|
Wso_Close //连接关闭,
|
|
|
|
Wso_Ping //心跳检查的ping,
|
|
|
|
Wso_Pong //心跳检查的pong,
|
|
|
|
|
|
|
|
// Wso_RsvB 为将来的控制消息片断的保留操作码
|
|
|
|
Wso_RsvB
|
|
|
|
Wso_RsvC
|
|
|
|
Wso_RsvD
|
|
|
|
Wso_RsvE
|
|
|
|
Wso_RsvF
|
|
|
|
)
|
|
|
|
|
|
|
|
type WsHeader struct {
|
|
|
|
Fin bool
|
|
|
|
Rsv1 bool
|
|
|
|
Rsv2 bool
|
|
|
|
Rsv3 bool
|
|
|
|
Opcode WsOpcode
|
|
|
|
|
|
|
|
PayloadLength uint64
|
|
|
|
|
|
|
|
Masked bool
|
|
|
|
MaskKey uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
const WsMagicStr = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
|
|
|
|
func MakeWsFrameHeader(wsHeader WsHeader) (buf []byte) {
|
|
|
|
headerSize := 2
|
|
|
|
payload := uint64(0)
|
|
|
|
switch {
|
|
|
|
case wsHeader.PayloadLength < 126:
|
|
|
|
payload = wsHeader.PayloadLength
|
|
|
|
case wsHeader.PayloadLength <= math.MaxUint16:
|
|
|
|
payload = 126
|
|
|
|
headerSize += 2
|
|
|
|
case wsHeader.PayloadLength > math.MaxUint16:
|
|
|
|
payload = 127
|
|
|
|
headerSize += 8
|
|
|
|
}
|
|
|
|
if wsHeader.Masked {
|
|
|
|
headerSize += 4
|
|
|
|
}
|
|
|
|
buf = make([]byte, headerSize, headerSize)
|
|
|
|
if wsHeader.Fin {
|
|
|
|
buf[0] |= 1 << 7
|
|
|
|
}
|
|
|
|
if wsHeader.Rsv1 {
|
|
|
|
buf[0] |= 1 << 6
|
|
|
|
}
|
|
|
|
if wsHeader.Rsv2 {
|
|
|
|
buf[0] |= 1 << 5
|
|
|
|
}
|
|
|
|
if wsHeader.Rsv3 {
|
|
|
|
buf[0] |= 1 << 4
|
|
|
|
}
|
|
|
|
buf[0] |= wsHeader.Opcode
|
|
|
|
|
|
|
|
if wsHeader.Masked {
|
|
|
|
buf[1] |= 1 << 7
|
|
|
|
}
|
|
|
|
buf[1] |= (uint8(payload) & 0x7F)
|
|
|
|
if payload == 126 {
|
|
|
|
bele.BePutUint16(buf[2:], uint16(wsHeader.PayloadLength))
|
|
|
|
} else if payload == 127 {
|
|
|
|
bele.BePutUint64(buf[2:], wsHeader.PayloadLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
if wsHeader.Masked {
|
|
|
|
bele.LePutUint32(buf[headerSize-4:], wsHeader.MaskKey)
|
|
|
|
}
|
|
|
|
return buf
|
|
|
|
}
|
|
|
|
|
|
|
|
func UpdateWebSocketHeader(secWebSocketKey, protocol string) []byte {
|
|
|
|
firstLine := "HTTP/1.1 101 Switching Protocol\r\n"
|
|
|
|
sha1Sum := sha1.Sum([]byte(secWebSocketKey + WsMagicStr))
|
|
|
|
secWebSocketAccept := base64.StdEncoding.EncodeToString(sha1Sum[:])
|
|
|
|
|
|
|
|
var webSocketResponseHeaderStr string
|
|
|
|
if protocol == "" {
|
|
|
|
webSocketResponseHeaderStr = firstLine +
|
|
|
|
"Server: " + LalHttpflvSubSessionServer + "\r\n" +
|
|
|
|
"Sec-WebSocket-Accept:" + secWebSocketAccept + "\r\n" +
|
|
|
|
"Keep-Alive: timeout=15, max=100\r\n" +
|
|
|
|
"Connection: Upgrade\r\n" +
|
|
|
|
"Upgrade: websocket\r\n" +
|
|
|
|
CorsHeaders +
|
|
|
|
"\r\n"
|
|
|
|
} else {
|
|
|
|
webSocketResponseHeaderStr = firstLine +
|
|
|
|
"Server: " + LalHttpflvSubSessionServer + "\r\n" +
|
|
|
|
"Sec-WebSocket-Accept:" + secWebSocketAccept + "\r\n" +
|
|
|
|
"Keep-Alive: timeout=15, max=100\r\n" +
|
|
|
|
"Connection: Upgrade\r\n" +
|
|
|
|
"Upgrade: websocket\r\n" +
|
|
|
|
CorsHeaders +
|
|
|
|
"Sec-WebSocket-Protocol:" + protocol + "\r\n" +
|
|
|
|
"\r\n"
|
|
|
|
}
|
|
|
|
return []byte(webSocketResponseHeaderStr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReadWsPayload(r *bufio.Reader) ([]byte, error) {
|
|
|
|
var h WsHeader
|
|
|
|
|
|
|
|
buf := make([]byte, 2)
|
|
|
|
_, err := io.ReadFull(r, buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
h.Fin = (buf[0] & 0x80) != 0
|
|
|
|
h.Rsv1 = (buf[0] & 0x40) != 0
|
|
|
|
h.Rsv2 = (buf[0] & 0x20) != 0
|
|
|
|
h.Rsv3 = (buf[0] & 0x10) != 0
|
|
|
|
h.Opcode = buf[0] & 0x0f
|
|
|
|
|
|
|
|
if buf[1]&0x80 != 0 {
|
|
|
|
h.Masked = true
|
|
|
|
}
|
|
|
|
|
|
|
|
length := buf[1] & 0x7f
|
|
|
|
switch {
|
|
|
|
case length < 126:
|
|
|
|
h.PayloadLength = uint64(length)
|
|
|
|
case length == 126:
|
|
|
|
buf = make([]byte, 2)
|
|
|
|
_, err := io.ReadFull(r, buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
h.PayloadLength = uint64(binary.BigEndian.Uint16(buf))
|
|
|
|
case length == 127:
|
|
|
|
buf = make([]byte, 8)
|
|
|
|
_, err := io.ReadFull(r, buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
h.PayloadLength = binary.BigEndian.Uint64(buf)
|
|
|
|
|
|
|
|
default:
|
|
|
|
err = fmt.Errorf("header error: the most significant bit must be 0")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if h.Masked {
|
|
|
|
buf = make([]byte, 4)
|
|
|
|
_, err := io.ReadFull(r, buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
h.MaskKey = bele.BeUint32(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
payload := make([]byte, h.PayloadLength)
|
|
|
|
_, err = io.ReadFull(r, payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if h.Masked {
|
|
|
|
mask := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint32(mask, h.MaskKey)
|
|
|
|
cipher(payload, mask, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return payload, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func cipher(payload []byte, mask []byte, offset int) {
|
|
|
|
n := len(payload)
|
|
|
|
if n < 8 {
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
payload[i] ^= mask[(offset+i)%4]
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate position in mask due to previously processed bytes number.
|
|
|
|
mpos := offset % 4
|
|
|
|
// Count number of bytes will processed one by one from the beginning of payload.
|
|
|
|
ln := remain[mpos]
|
|
|
|
// Count number of bytes will processed one by one from the end of payload.
|
|
|
|
// This is done to process payload by 8 bytes in each iteration of main loop.
|
|
|
|
rn := (n - ln) % 8
|
|
|
|
|
|
|
|
for i := 0; i < ln; i++ {
|
|
|
|
payload[i] ^= mask[(mpos+i)%4]
|
|
|
|
}
|
|
|
|
for i := n - rn; i < n; i++ {
|
|
|
|
payload[i] ^= mask[(mpos+i)%4]
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: we use here binary.LittleEndian regardless of what is real
|
|
|
|
// endianness on machine is. To do so, we have to use binary.LittleEndian in
|
|
|
|
// the masking loop below as well.
|
|
|
|
var (
|
|
|
|
m = binary.LittleEndian.Uint32((mask[:]))
|
|
|
|
m2 = uint64(m)<<32 | uint64(m)
|
|
|
|
)
|
|
|
|
// Skip already processed right part.
|
|
|
|
// Get number of uint64 parts remaining to process.
|
|
|
|
n = (n - ln - rn) >> 3
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
var (
|
|
|
|
j = ln + (i << 3)
|
|
|
|
chunk = payload[j : j+8]
|
|
|
|
)
|
|
|
|
p := binary.LittleEndian.Uint64(chunk)
|
|
|
|
p = p ^ m2
|
|
|
|
binary.LittleEndian.PutUint64(chunk, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// remain maps position in masking key [0,4) to number
|
|
|
|
// of bytes that need to be processed manually inside Cipher().
|
|
|
|
var remain = [4]int{0, 3, 2, 1}
|