sing-box/outbound/shadowsocksr.go
2022-09-12 18:35:36 +08:00

141 lines
4.4 KiB
Go

//go:build with_shadowsocksr
package outbound
import (
"context"
"net"
"strings"
"github.com/sagernet/shadowsocksr"
"github.com/sagernet/shadowsocksr/obfs"
"github.com/sagernet/shadowsocksr/protocol"
"github.com/sagernet/shadowsocksr/ssr"
"github.com/sagernet/shadowsocksr/streamCipher"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowimpl"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
var _ adapter.Outbound = (*ShadowsocksR)(nil)
type ShadowsocksR struct {
myOutboundAdapter
dialer N.Dialer
serverAddr M.Socksaddr
method shadowsocks.Method
cipher string
password string
obfs string
obfsParams *ssr.ServerInfo
protocol string
protocolParams *ssr.ServerInfo
}
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) {
outbound := &ShadowsocksR{
myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeShadowsocksR,
network: options.Network.Build(),
router: router,
logger: logger,
tag: tag,
},
dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(),
cipher: options.Method,
password: options.Password,
obfs: options.Obfs,
protocol: options.Protocol,
}
var err error
outbound.method, err = shadowimpl.FetchMethod(options.Method, options.Password)
if err != nil {
return nil, err
}
if _, err = streamCipher.NewStreamCipher(options.Method, options.Password); err != nil {
return nil, E.New(strings.ToLower(err.Error()))
}
if obfs.NewObfs(options.Obfs) == nil {
return nil, E.New("unknown obfs: " + options.Obfs)
}
outbound.obfsParams = &ssr.ServerInfo{
Host: outbound.serverAddr.AddrString(),
Port: outbound.serverAddr.Port,
TcpMss: 1460,
Param: options.ObfsParam,
}
if protocol.NewProtocol(options.Protocol) == nil {
return nil, E.New("unknown protocol: " + options.Protocol)
}
outbound.protocolParams = &ssr.ServerInfo{
Host: outbound.serverAddr.AddrString(),
Port: outbound.serverAddr.Port,
TcpMss: 1460,
Param: options.Protocol,
}
if outbound.method == nil {
outbound.network = common.Filter(outbound.network, func(it string) bool { return it == N.NetworkTCP })
}
return outbound, nil
}
func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch network {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound connection to ", destination)
conn, err := h.dialer.DialContext(ctx, network, h.serverAddr)
if err != nil {
return nil, err
}
cipher, err := streamCipher.NewStreamCipher(h.cipher, h.password)
if err != nil {
return nil, E.New(strings.ToLower(err.Error()))
}
ssConn := shadowsocksr.NewSSTCPConn(conn, cipher)
ssConn.IObfs = obfs.NewObfs(h.obfs)
ssConn.IObfs.SetServerInfo(h.obfsParams)
ssConn.IProtocol = protocol.NewProtocol(h.protocol)
ssConn.IProtocol.SetServerInfo(h.protocolParams)
err = M.SocksaddrSerializer.WriteAddrPort(ssConn, destination)
if err != nil {
return nil, E.Cause(err, "write request")
}
return ssConn, nil
case N.NetworkUDP:
conn, err := h.ListenPacket(ctx, destination)
if err != nil {
return nil, err
}
return &bufio.BindPacketConn{PacketConn: conn, Addr: destination}, nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (h *ShadowsocksR) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
if err != nil {
return nil, err
}
return h.method.DialPacketConn(outConn), nil
}
func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *ShadowsocksR) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}