mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-02-26 16:57:08 +00:00
chore: clean up duplicate code in sudoku
This commit is contained in:
@@ -5,41 +5,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
sudokuobfs "github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku"
|
sudokuobfs "github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku"
|
||||||
)
|
)
|
||||||
|
|
||||||
type discardConn struct{}
|
|
||||||
|
|
||||||
func (discardConn) Read([]byte) (int, error) { return 0, io.EOF }
|
|
||||||
func (discardConn) Write(p []byte) (int, error) { return len(p), nil }
|
|
||||||
func (discardConn) Close() error { return nil }
|
|
||||||
func (discardConn) LocalAddr() net.Addr { return nil }
|
|
||||||
func (discardConn) RemoteAddr() net.Addr { return nil }
|
|
||||||
func (discardConn) SetDeadline(time.Time) error { return nil }
|
|
||||||
func (discardConn) SetReadDeadline(time.Time) error { return nil }
|
|
||||||
func (discardConn) SetWriteDeadline(time.Time) error { return nil }
|
|
||||||
|
|
||||||
func TestSudokuObfsWriter_ReducesWriteAllocs(t *testing.T) {
|
|
||||||
table := sudokuobfs.NewTable("alloc-seed", "prefer_ascii")
|
|
||||||
w := newSudokuObfsWriter(discardConn{}, table, 0, 0)
|
|
||||||
|
|
||||||
payload := bytes.Repeat([]byte{0x42}, 2048)
|
|
||||||
if _, err := w.Write(payload); err != nil {
|
|
||||||
t.Fatalf("warmup write: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allocs := testing.AllocsPerRun(100, func() {
|
|
||||||
if _, err := w.Write(payload); err != nil {
|
|
||||||
t.Fatalf("write: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if allocs != 0 {
|
|
||||||
t.Fatalf("expected 0 allocs/run, got %.2f", allocs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCustomTablesRotation_ProbedByServer(t *testing.T) {
|
func TestCustomTablesRotation_ProbedByServer(t *testing.T) {
|
||||||
key := "rotate-test-key"
|
key := "rotate-test-key"
|
||||||
target := "8.8.8.8:53"
|
target := "8.8.8.8:53"
|
||||||
|
|||||||
@@ -68,6 +68,15 @@ type directionalConn struct {
|
|||||||
closers []func() error
|
closers []func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newDirectionalConn(base net.Conn, reader io.Reader, writer io.Writer, closers ...func() error) net.Conn {
|
||||||
|
return &directionalConn{
|
||||||
|
Conn: base,
|
||||||
|
reader: reader,
|
||||||
|
writer: writer,
|
||||||
|
closers: closers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *directionalConn) Read(p []byte) (int, error) {
|
func (c *directionalConn) Read(p []byte) (int, error) {
|
||||||
return c.reader.Read(p)
|
return c.reader.Read(p)
|
||||||
}
|
}
|
||||||
@@ -112,40 +121,21 @@ func downlinkMode(cfg *ProtocolConfig) byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildClientObfsConn(raw net.Conn, cfg *ProtocolConfig, table *sudoku.Table) net.Conn {
|
func buildClientObfsConn(raw net.Conn, cfg *ProtocolConfig, table *sudoku.Table) net.Conn {
|
||||||
baseReader := sudoku.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)
|
baseSudoku := sudoku.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)
|
||||||
baseWriter := newSudokuObfsWriter(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
|
||||||
if cfg.EnablePureDownlink {
|
if cfg.EnablePureDownlink {
|
||||||
return &directionalConn{
|
return baseSudoku
|
||||||
Conn: raw,
|
|
||||||
reader: baseReader,
|
|
||||||
writer: baseWriter,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
packed := sudoku.NewPackedConn(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
packed := sudoku.NewPackedConn(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
||||||
return &directionalConn{
|
return newDirectionalConn(raw, packed, baseSudoku)
|
||||||
Conn: raw,
|
|
||||||
reader: packed,
|
|
||||||
writer: baseWriter,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildServerObfsConn(raw net.Conn, cfg *ProtocolConfig, table *sudoku.Table, record bool) (*sudoku.Conn, net.Conn) {
|
func buildServerObfsConn(raw net.Conn, cfg *ProtocolConfig, table *sudoku.Table, record bool) (*sudoku.Conn, net.Conn) {
|
||||||
uplink := sudoku.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, record)
|
uplinkSudoku := sudoku.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, record)
|
||||||
if cfg.EnablePureDownlink {
|
if cfg.EnablePureDownlink {
|
||||||
downlink := &directionalConn{
|
return uplinkSudoku, uplinkSudoku
|
||||||
Conn: raw,
|
|
||||||
reader: uplink,
|
|
||||||
writer: newSudokuObfsWriter(raw, table, cfg.PaddingMin, cfg.PaddingMax),
|
|
||||||
}
|
|
||||||
return uplink, downlink
|
|
||||||
}
|
}
|
||||||
packed := sudoku.NewPackedConn(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
packed := sudoku.NewPackedConn(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
||||||
return uplink, &directionalConn{
|
return uplinkSudoku, newDirectionalConn(raw, uplinkSudoku, packed, packed.Flush)
|
||||||
Conn: raw,
|
|
||||||
reader: uplink,
|
|
||||||
writer: packed,
|
|
||||||
closers: []func() error{packed.Flush},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHandshakePayload(key string) [16]byte {
|
func buildHandshakePayload(key string) [16]byte {
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
package sudoku
|
|
||||||
|
|
||||||
import (
|
|
||||||
crypto_rand "crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku"
|
|
||||||
)
|
|
||||||
|
|
||||||
// perm4 matches github.com/saba-futai/sudoku/pkg/obfs/sudoku perm4.
|
|
||||||
var perm4 = [24][4]byte{
|
|
||||||
{0, 1, 2, 3},
|
|
||||||
{0, 1, 3, 2},
|
|
||||||
{0, 2, 1, 3},
|
|
||||||
{0, 2, 3, 1},
|
|
||||||
{0, 3, 1, 2},
|
|
||||||
{0, 3, 2, 1},
|
|
||||||
{1, 0, 2, 3},
|
|
||||||
{1, 0, 3, 2},
|
|
||||||
{1, 2, 0, 3},
|
|
||||||
{1, 2, 3, 0},
|
|
||||||
{1, 3, 0, 2},
|
|
||||||
{1, 3, 2, 0},
|
|
||||||
{2, 0, 1, 3},
|
|
||||||
{2, 0, 3, 1},
|
|
||||||
{2, 1, 0, 3},
|
|
||||||
{2, 1, 3, 0},
|
|
||||||
{2, 3, 0, 1},
|
|
||||||
{2, 3, 1, 0},
|
|
||||||
{3, 0, 1, 2},
|
|
||||||
{3, 0, 2, 1},
|
|
||||||
{3, 1, 0, 2},
|
|
||||||
{3, 1, 2, 0},
|
|
||||||
{3, 2, 0, 1},
|
|
||||||
{3, 2, 1, 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
type sudokuObfsWriter struct {
|
|
||||||
conn net.Conn
|
|
||||||
table *sudoku.Table
|
|
||||||
rng *rand.Rand
|
|
||||||
paddingRate float32
|
|
||||||
|
|
||||||
outBuf []byte
|
|
||||||
pads []byte
|
|
||||||
padLen int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSudokuObfsWriter(conn net.Conn, table *sudoku.Table, pMin, pMax int) *sudokuObfsWriter {
|
|
||||||
var seedBytes [8]byte
|
|
||||||
if _, err := crypto_rand.Read(seedBytes[:]); err != nil {
|
|
||||||
binary.BigEndian.PutUint64(seedBytes[:], uint64(rand.Int63()))
|
|
||||||
}
|
|
||||||
seed := int64(binary.BigEndian.Uint64(seedBytes[:]))
|
|
||||||
localRng := rand.New(rand.NewSource(seed))
|
|
||||||
|
|
||||||
min := float32(pMin) / 100.0
|
|
||||||
span := float32(pMax-pMin) / 100.0
|
|
||||||
rate := min + localRng.Float32()*span
|
|
||||||
|
|
||||||
w := &sudokuObfsWriter{
|
|
||||||
conn: conn,
|
|
||||||
table: table,
|
|
||||||
rng: localRng,
|
|
||||||
paddingRate: rate,
|
|
||||||
}
|
|
||||||
w.pads = table.PaddingPool
|
|
||||||
w.padLen = len(w.pads)
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *sudokuObfsWriter) Write(p []byte) (int, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Worst-case: 4 hints + up to 6 paddings per input byte.
|
|
||||||
needed := len(p)*10 + 1
|
|
||||||
if cap(w.outBuf) < needed {
|
|
||||||
w.outBuf = make([]byte, 0, needed)
|
|
||||||
}
|
|
||||||
out := w.outBuf[:0]
|
|
||||||
|
|
||||||
pads := w.pads
|
|
||||||
padLen := w.padLen
|
|
||||||
|
|
||||||
for _, b := range p {
|
|
||||||
if padLen > 0 && w.rng.Float32() < w.paddingRate {
|
|
||||||
out = append(out, pads[w.rng.Intn(padLen)])
|
|
||||||
}
|
|
||||||
|
|
||||||
puzzles := w.table.EncodeTable[b]
|
|
||||||
puzzle := puzzles[w.rng.Intn(len(puzzles))]
|
|
||||||
|
|
||||||
perm := perm4[w.rng.Intn(len(perm4))]
|
|
||||||
for _, idx := range perm {
|
|
||||||
if padLen > 0 && w.rng.Float32() < w.paddingRate {
|
|
||||||
out = append(out, pads[w.rng.Intn(padLen)])
|
|
||||||
}
|
|
||||||
out = append(out, puzzle[idx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if padLen > 0 && w.rng.Float32() < w.paddingRate {
|
|
||||||
out = append(out, pads[w.rng.Intn(padLen)])
|
|
||||||
}
|
|
||||||
|
|
||||||
w.outBuf = out
|
|
||||||
_, err := w.conn.Write(out)
|
|
||||||
return len(p), err
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user