mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-02-26 08:47:09 +00:00
chore: remove unreachable code in sudoku
This commit is contained in:
@@ -265,7 +265,7 @@ func (s *Sudoku) dialAndHandshake(ctx context.Context, cfg *sudoku.ProtocolConfi
|
||||
}
|
||||
|
||||
upgrade := func(raw net.Conn) (net.Conn, error) {
|
||||
return sudoku.ClientHandshakeWithOptions(raw, &handshakeCfg, sudoku.ClientHandshakeOptions{})
|
||||
return sudoku.ClientHandshake(raw, &handshakeCfg)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -303,7 +303,7 @@ func (s *Sudoku) dialAndHandshake(ctx context.Context, cfg *sudoku.ProtocolConfi
|
||||
}
|
||||
|
||||
if !handshakeDone {
|
||||
c, err = sudoku.ClientHandshakeWithOptions(c, &handshakeCfg, sudoku.ClientHandshakeOptions{})
|
||||
c, err = sudoku.ClientHandshake(c, &handshakeCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -40,78 +40,6 @@ func TestSudokuObfsWriter_ReducesWriteAllocs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPMaskStrategy_WebSocketAndPost(t *testing.T) {
|
||||
key := "mask-test-key"
|
||||
target := "1.1.1.1:80"
|
||||
table := sudokuobfs.NewTable("mask-seed", "prefer_ascii")
|
||||
|
||||
base := DefaultConfig()
|
||||
base.Key = key
|
||||
base.AEADMethod = "chacha20-poly1305"
|
||||
base.Table = table
|
||||
base.PaddingMin = 0
|
||||
base.PaddingMax = 0
|
||||
base.EnablePureDownlink = true
|
||||
base.HandshakeTimeoutSeconds = 5
|
||||
base.DisableHTTPMask = false
|
||||
base.ServerAddress = "example.com:443"
|
||||
|
||||
cases := []string{"post", "websocket"}
|
||||
for _, strategy := range cases {
|
||||
t.Run(strategy, func(t *testing.T) {
|
||||
serverConn, clientConn := net.Pipe()
|
||||
defer serverConn.Close()
|
||||
defer clientConn.Close()
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
session, err := ServerHandshake(serverConn, base)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
defer session.Conn.Close()
|
||||
if session.Type != SessionTypeTCP {
|
||||
errCh <- io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
if session.Target != target {
|
||||
errCh <- io.ErrClosedPipe
|
||||
return
|
||||
}
|
||||
_, _ = session.Conn.Write([]byte("ok"))
|
||||
}()
|
||||
|
||||
cConn, err := ClientHandshakeWithOptions(clientConn, base, ClientHandshakeOptions{HTTPMaskStrategy: strategy})
|
||||
if err != nil {
|
||||
t.Fatalf("client handshake: %v", err)
|
||||
}
|
||||
defer cConn.Close()
|
||||
|
||||
addrBuf, err := EncodeAddress(target)
|
||||
if err != nil {
|
||||
t.Fatalf("encode addr: %v", err)
|
||||
}
|
||||
if _, err := cConn.Write(addrBuf); err != nil {
|
||||
t.Fatalf("write addr: %v", err)
|
||||
}
|
||||
|
||||
buf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(cConn, buf); err != nil {
|
||||
t.Fatalf("read: %v", err)
|
||||
}
|
||||
if string(buf) != "ok" {
|
||||
t.Fatalf("unexpected payload: %q", buf)
|
||||
}
|
||||
|
||||
if err := <-errCh; err != nil {
|
||||
t.Fatalf("server: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomTablesRotation_ProbedByServer(t *testing.T) {
|
||||
key := "rotate-test-key"
|
||||
target := "8.8.8.8:53"
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/transport/sudoku/crypto"
|
||||
@@ -192,19 +191,8 @@ func ClientAEADSeed(key string) string {
|
||||
return key
|
||||
}
|
||||
|
||||
type ClientHandshakeOptions struct {
|
||||
// HTTPMaskStrategy controls how the client generates the HTTP mask header when DisableHTTPMask=false.
|
||||
// Supported: ""/"random" (default), "post", "websocket".
|
||||
HTTPMaskStrategy string
|
||||
}
|
||||
|
||||
// ClientHandshake performs the client-side Sudoku handshake (without sending target address).
|
||||
func ClientHandshake(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, error) {
|
||||
return ClientHandshakeWithOptions(rawConn, cfg, ClientHandshakeOptions{})
|
||||
}
|
||||
|
||||
// ClientHandshakeWithOptions performs the client-side Sudoku handshake (without sending target address).
|
||||
func ClientHandshakeWithOptions(rawConn net.Conn, cfg *ProtocolConfig, opt ClientHandshakeOptions) (net.Conn, error) {
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("config is required")
|
||||
}
|
||||
@@ -213,7 +201,7 @@ func ClientHandshakeWithOptions(rawConn net.Conn, cfg *ProtocolConfig, opt Clien
|
||||
}
|
||||
|
||||
if !cfg.DisableHTTPMask {
|
||||
if err := WriteHTTPMaskHeader(rawConn, cfg.ServerAddress, cfg.HTTPMaskPathRoot, opt.HTTPMaskStrategy); err != nil {
|
||||
if err := httpmask.WriteRandomRequestHeaderWithPathRoot(rawConn, cfg.ServerAddress, cfg.HTTPMaskPathRoot); err != nil {
|
||||
return nil, fmt.Errorf("write http mask failed: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -363,18 +351,6 @@ func GenKeyPair() (privateKey, publicKey string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func normalizeHTTPMaskStrategy(strategy string) string {
|
||||
s := strings.TrimSpace(strings.ToLower(strategy))
|
||||
switch s {
|
||||
case "", "random":
|
||||
return "random"
|
||||
case "ws":
|
||||
return "websocket"
|
||||
default:
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func userHashFromHandshake(handshakeBuf []byte) string {
|
||||
if len(handshakeBuf) < 16 {
|
||||
return ""
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/transport/sudoku/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, pathRoot string, strategy string) error {
|
||||
switch normalizeHTTPMaskStrategy(strategy) {
|
||||
case "random":
|
||||
return httpmask.WriteRandomRequestHeaderWithPathRoot(w, host, pathRoot)
|
||||
case "post":
|
||||
return writeHTTPMaskPOST(w, host, pathRoot)
|
||||
case "websocket":
|
||||
return writeHTTPMaskWebSocket(w, host, pathRoot)
|
||||
default:
|
||||
return fmt.Errorf("unsupported http-mask-strategy: %s", strategy)
|
||||
}
|
||||
}
|
||||
|
||||
func writeHTTPMaskPOST(w io.Writer, host string, pathRoot string) error {
|
||||
r := httpMaskRngPool.Get().(*rand.Rand)
|
||||
defer httpMaskRngPool.Put(r)
|
||||
|
||||
path := joinPathRoot(pathRoot, 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, pathRoot string) error {
|
||||
r := httpMaskRngPool.Get().(*rand.Rand)
|
||||
defer httpMaskRngPool.Put(r)
|
||||
|
||||
path := joinPathRoot(pathRoot, 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
|
||||
}
|
||||
|
||||
func normalizePathRoot(root string) string {
|
||||
root = strings.TrimSpace(root)
|
||||
root = strings.Trim(root, "/")
|
||||
if root == "" {
|
||||
return ""
|
||||
}
|
||||
for i := 0; i < len(root); i++ {
|
||||
c := root[i]
|
||||
switch {
|
||||
case c >= 'a' && c <= 'z':
|
||||
case c >= 'A' && c <= 'Z':
|
||||
case c >= '0' && c <= '9':
|
||||
case c == '_' || c == '-':
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return "/" + root
|
||||
}
|
||||
|
||||
func joinPathRoot(root, path string) string {
|
||||
root = normalizePathRoot(root)
|
||||
if root == "" {
|
||||
return path
|
||||
}
|
||||
if path == "" {
|
||||
return root
|
||||
}
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
return root + path
|
||||
}
|
||||
Reference in New Issue
Block a user