Files
sing-box-extended/transport/snell/client.go

121 lines
2.7 KiB
Go

package snell
import (
"context"
"net"
"strconv"
"time"
obfs "github.com/sagernet/sing-box/transport/simple-obfs"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type ClientOptions struct {
Dialer N.Dialer
Server M.Socksaddr
PSK []byte
Version int
Reuse bool
ObfsMode string
ObfsHost string
}
type Client struct {
dialer N.Dialer
server M.Socksaddr
psk []byte
version int
reuse bool
obfsMode string
obfsHost string
pool *Pool
}
func NewClient(options ClientOptions) *Client {
c := &Client{
dialer: options.Dialer,
server: options.Server,
psk: options.PSK,
version: options.Version,
reuse: options.Reuse,
obfsMode: options.ObfsMode,
obfsHost: options.ObfsHost,
}
if c.reuse {
c.pool = NewPool(func(ctx context.Context) (*Snell, error) {
conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.server)
if err != nil {
return nil, err
}
return c.streamConn(conn), nil
})
}
return c
}
func (c *Client) streamConn(conn net.Conn) *Snell {
switch c.obfsMode {
case "tls":
conn = obfs.NewTLSObfs(conn, c.obfsHost)
case "http":
conn = obfs.NewHTTPObfs(conn, c.obfsHost, strconv.Itoa(int(c.server.Port)))
}
return StreamConn(conn, c.psk, c.version)
}
func (c *Client) writeHeader(ctx context.Context, conn net.Conn, destination M.Socksaddr, udp bool) (err error) {
if deadline, ok := ctx.Deadline(); ok {
_ = conn.SetWriteDeadline(deadline)
defer conn.SetWriteDeadline(time.Time{})
}
if udp {
err = WriteUDPHeader(conn, c.version)
if err == nil && c.version >= Version4 {
if sc, ok := conn.(*Snell); ok {
err = sc.ReadReply()
}
}
return
}
err = WriteHeaderWithReuse(conn, destination.AddrString(), uint(destination.Port), c.version, c.reuse)
return
}
func (c *Client) DialContext(ctx context.Context, destination M.Socksaddr) (net.Conn, error) {
if c.reuse {
conn, err := c.pool.Get()
if err != nil {
return nil, err
}
if err = c.writeHeader(ctx, conn, destination, false); err != nil {
_ = conn.Close()
return nil, err
}
return conn, nil
}
conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.server)
if err != nil {
return nil, err
}
stream := c.streamConn(conn)
if err = c.writeHeader(ctx, stream, destination, false); err != nil {
_ = conn.Close()
return nil, err
}
return stream, nil
}
func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.server)
if err != nil {
return nil, err
}
stream := c.streamConn(conn)
if err = c.writeHeader(ctx, stream, destination, true); err != nil {
_ = conn.Close()
return nil, err
}
return PacketConn(stream), nil
}