Files
2026-07-09 11:19:16 +08:00

150 lines
3.2 KiB
Go

package tlsspoof
import (
"net"
E "github.com/sagernet/sing/common/exceptions"
)
type Method int
const (
MethodWrongSequence Method = iota
MethodWrongChecksum
MethodWrongAcknowledgment
MethodWrongMD5Sig
MethodWrongTimestamp
)
const (
MethodNameWrongSequence = "wrong-sequence"
MethodNameWrongChecksum = "wrong-checksum"
MethodNameWrongAcknowledgment = "wrong-ack"
MethodNameWrongMD5Sig = "wrong-md5"
MethodNameWrongTimestamp = "wrong-timestamp"
)
func ParseOptions(spoof, method string) (string, Method, error) {
if spoof == "" {
if method != "" {
return "", 0, E.New("spoof_method requires spoof")
}
return "", 0, nil
}
if !PlatformSupported {
return "", 0, E.New("tls_spoof is not supported on this platform")
}
parsedMethod, err := ParseMethod(method)
if err != nil {
return "", 0, err
}
return spoof, parsedMethod, nil
}
func ParseMethod(s string) (Method, error) {
switch s {
case "", MethodNameWrongSequence:
return MethodWrongSequence, nil
case MethodNameWrongChecksum:
return MethodWrongChecksum, nil
case MethodNameWrongAcknowledgment:
return MethodWrongAcknowledgment, nil
case MethodNameWrongMD5Sig:
return MethodWrongMD5Sig, nil
case MethodNameWrongTimestamp:
return MethodWrongTimestamp, nil
default:
return 0, E.New("tls_spoof: unknown method: ", s)
}
}
func (m Method) String() string {
switch m {
case MethodWrongSequence:
return MethodNameWrongSequence
case MethodWrongChecksum:
return MethodNameWrongChecksum
case MethodWrongAcknowledgment:
return MethodNameWrongAcknowledgment
case MethodWrongMD5Sig:
return MethodNameWrongMD5Sig
case MethodWrongTimestamp:
return MethodNameWrongTimestamp
default:
return "unknown"
}
}
type rawSpoofer interface {
Inject(payload []byte) error
Close() error
}
type Conn struct {
net.Conn
spoofer rawSpoofer
fakeHello []byte
injected bool
}
func NewConn(conn net.Conn, method Method, fakeSNI string) (*Conn, error) {
spoofer, err := newRawSpoofer(conn, method)
if err != nil {
return nil, err
}
result, err := newConn(conn, spoofer, fakeSNI)
if err != nil {
spoofer.Close()
return nil, err
}
return result, nil
}
func newConn(conn net.Conn, spoofer rawSpoofer, fakeSNI string) (*Conn, error) {
fakeHello, err := buildFakeClientHello(fakeSNI)
if err != nil {
return nil, E.Cause(err, "tls_spoof: build fake ClientHello")
}
return &Conn{
Conn: conn,
spoofer: spoofer,
fakeHello: fakeHello,
}, nil
}
func (c *Conn) Write(b []byte) (n int, err error) {
if c.injected {
return c.Conn.Write(b)
}
defer func() {
closeErr := c.spoofer.Close()
if err == nil && closeErr != nil {
err = E.Cause(closeErr, "tls_spoof: close spoofer")
}
}()
err = c.spoofer.Inject(c.fakeHello)
if err != nil {
return 0, E.Cause(err, "tls_spoof: inject")
}
c.injected = true
return c.Conn.Write(b)
}
func (c *Conn) Close() error {
return E.Append(c.Conn.Close(), c.spoofer.Close(), func(e error) error {
return E.Cause(e, "tls_spoof: close spoofer")
})
}
func (c *Conn) ReaderReplaceable() bool {
return true
}
func (c *Conn) WriterReplaceable() bool {
return c.injected
}
func (c *Conn) Upstream() any {
return c.Conn
}