mihomo/transport/sudoku/httpmask_strategy.go
saba-futai a06097c2c4
Some checks are pending
Test / test (1.20, macos-15-intel) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-15-intel) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, ubuntu-latest) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-15-intel) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, ubuntu-latest) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-15-intel) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, ubuntu-latest) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-15-intel) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, ubuntu-latest) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.25, macos-15-intel) (push) Waiting to run
Test / test (1.25, macos-latest) (push) Waiting to run
Test / test (1.25, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.25, ubuntu-latest) (push) Waiting to run
Test / test (1.25, windows-latest) (push) Waiting to run
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
chore: add xvp rotation andd new header generation strategy for sudoku (#2437)
2025-12-16 18:39:39 +08:00

180 lines
4.7 KiB
Go

package sudoku
import (
"encoding/base64"
"fmt"
"io"
"math/rand"
"net"
"strconv"
"sync"
"time"
"github.com/saba-futai/sudoku/pkg/obfs/httpmask"
)
var (
httpMaskUserAgents = []string{
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
}
httpMaskAccepts = []string{
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"application/json, text/plain, */*",
"application/octet-stream",
"*/*",
}
httpMaskAcceptLanguages = []string{
"en-US,en;q=0.9",
"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
}
httpMaskAcceptEncodings = []string{
"gzip, deflate, br",
"gzip, deflate",
}
httpMaskPaths = []string{
"/api/v1/upload",
"/data/sync",
"/v1/telemetry",
"/session",
"/ws",
}
httpMaskContentTypes = []string{
"application/octet-stream",
"application/json",
}
)
var (
httpMaskRngPool = sync.Pool{
New: func() any { return rand.New(rand.NewSource(time.Now().UnixNano())) },
}
httpMaskBufPool = sync.Pool{
New: func() any {
b := make([]byte, 0, 1024)
return &b
},
}
)
func trimPortForHost(host string) string {
if host == "" {
return host
}
h, _, err := net.SplitHostPort(host)
if err == nil && h != "" {
return h
}
return host
}
func appendCommonHeaders(buf []byte, host string, r *rand.Rand) []byte {
ua := httpMaskUserAgents[r.Intn(len(httpMaskUserAgents))]
accept := httpMaskAccepts[r.Intn(len(httpMaskAccepts))]
lang := httpMaskAcceptLanguages[r.Intn(len(httpMaskAcceptLanguages))]
enc := httpMaskAcceptEncodings[r.Intn(len(httpMaskAcceptEncodings))]
buf = append(buf, "Host: "...)
buf = append(buf, host...)
buf = append(buf, "\r\nUser-Agent: "...)
buf = append(buf, ua...)
buf = append(buf, "\r\nAccept: "...)
buf = append(buf, accept...)
buf = append(buf, "\r\nAccept-Language: "...)
buf = append(buf, lang...)
buf = append(buf, "\r\nAccept-Encoding: "...)
buf = append(buf, enc...)
buf = append(buf, "\r\nConnection: keep-alive\r\n"...)
buf = append(buf, "Cache-Control: no-cache\r\nPragma: no-cache\r\n"...)
return buf
}
// WriteHTTPMaskHeader writes an HTTP/1.x request header as a mask, according to strategy.
// Supported strategies: ""/"random", "post", "websocket".
func WriteHTTPMaskHeader(w io.Writer, host string, strategy string) error {
switch normalizeHTTPMaskStrategy(strategy) {
case "random":
return httpmask.WriteRandomRequestHeader(w, host)
case "post":
return writeHTTPMaskPOST(w, host)
case "websocket":
return writeHTTPMaskWebSocket(w, host)
default:
return fmt.Errorf("unsupported http-mask-strategy: %s", strategy)
}
}
func writeHTTPMaskPOST(w io.Writer, host string) error {
r := httpMaskRngPool.Get().(*rand.Rand)
defer httpMaskRngPool.Put(r)
path := httpMaskPaths[r.Intn(len(httpMaskPaths))]
ctype := httpMaskContentTypes[r.Intn(len(httpMaskContentTypes))]
bufPtr := httpMaskBufPool.Get().(*[]byte)
buf := *bufPtr
buf = buf[:0]
defer func() {
if cap(buf) <= 4096 {
*bufPtr = buf
httpMaskBufPool.Put(bufPtr)
}
}()
const minCL = int64(4 * 1024)
const maxCL = int64(10 * 1024 * 1024)
contentLength := minCL + r.Int63n(maxCL-minCL+1)
buf = append(buf, "POST "...)
buf = append(buf, path...)
buf = append(buf, " HTTP/1.1\r\n"...)
buf = appendCommonHeaders(buf, host, r)
buf = append(buf, "Content-Type: "...)
buf = append(buf, ctype...)
buf = append(buf, "\r\nContent-Length: "...)
buf = strconv.AppendInt(buf, contentLength, 10)
buf = append(buf, "\r\n\r\n"...)
_, err := w.Write(buf)
return err
}
func writeHTTPMaskWebSocket(w io.Writer, host string) error {
r := httpMaskRngPool.Get().(*rand.Rand)
defer httpMaskRngPool.Put(r)
path := httpMaskPaths[r.Intn(len(httpMaskPaths))]
bufPtr := httpMaskBufPool.Get().(*[]byte)
buf := *bufPtr
buf = buf[:0]
defer func() {
if cap(buf) <= 4096 {
*bufPtr = buf
httpMaskBufPool.Put(bufPtr)
}
}()
hostNoPort := trimPortForHost(host)
var keyBytes [16]byte
for i := 0; i < len(keyBytes); i++ {
keyBytes[i] = byte(r.Intn(256))
}
var wsKey [24]byte
base64.StdEncoding.Encode(wsKey[:], keyBytes[:])
buf = append(buf, "GET "...)
buf = append(buf, path...)
buf = append(buf, " HTTP/1.1\r\n"...)
buf = appendCommonHeaders(buf, host, r)
buf = append(buf, "Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: "...)
buf = append(buf, wsKey[:]...)
buf = append(buf, "\r\nOrigin: https://"...)
buf = append(buf, hostNoPort...)
buf = append(buf, "\r\n\r\n"...)
_, err := w.Write(buf)
return err
}