mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 10:01:30 +00:00
Add multiplexer
This commit is contained in:
parent
83154eadd3
commit
457de86819
|
@ -3,7 +3,7 @@ linters:
|
||||||
enable:
|
enable:
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- govet
|
- govet
|
||||||
# - gci
|
- gci
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- paralleltest
|
- paralleltest
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Note: for proxy protocols, outbound creates early connections by default.
|
||||||
|
|
||||||
type Outbound interface {
|
type Outbound interface {
|
||||||
Type() string
|
Type() string
|
||||||
Tag() string
|
Tag() string
|
||||||
|
|
3
box.go
3
box.go
|
@ -124,6 +124,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
tag = F.ToString(i)
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
out, err = outbound.New(
|
out, err = outbound.New(
|
||||||
|
ctx,
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
|
||||||
outboundOptions)
|
outboundOptions)
|
||||||
|
@ -133,7 +134,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
outbounds = append(outbounds, out)
|
outbounds = append(outbounds, out)
|
||||||
}
|
}
|
||||||
err = router.Initialize(outbounds, func() adapter.Outbound {
|
err = router.Initialize(outbounds, func() adapter.Outbound {
|
||||||
out, oErr := outbound.New(router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"})
|
out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"})
|
||||||
common.Must(oErr)
|
common.Must(oErr)
|
||||||
outbounds = append(outbounds, out)
|
outbounds = append(outbounds, out)
|
||||||
return out
|
return out
|
||||||
|
|
87
common/debugio/log.go
Normal file
87
common/debugio/log.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package debugio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
logger log.Logger
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogConn(conn net.Conn, logger log.Logger, prefix string) N.ExtendedConn {
|
||||||
|
return &LogConn{bufio.NewExtendedConn(conn), logger, prefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogConn) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = c.ExtendedConn.Read(p)
|
||||||
|
if n > 0 {
|
||||||
|
c.logger.Debug(c.prefix, " read ", buf.EncodeHexString(p[:n]))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogConn) Write(p []byte) (n int, err error) {
|
||||||
|
c.logger.Debug(c.prefix, " write ", buf.EncodeHexString(p))
|
||||||
|
return c.ExtendedConn.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||||
|
err := c.ExtendedConn.ReadBuffer(buffer)
|
||||||
|
if err == nil {
|
||||||
|
c.logger.Debug(c.prefix, " read buffer ", buf.EncodeHexString(buffer.Bytes()))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
c.logger.Debug(c.prefix, " write buffer ", buf.EncodeHexString(buffer.Bytes()))
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogConn) Upstream() any {
|
||||||
|
return c.ExtendedConn
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogPacketConn struct {
|
||||||
|
N.NetPacketConn
|
||||||
|
logger log.Logger
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogPacketConn(conn net.PacketConn, logger log.Logger, prefix string) N.NetPacketConn {
|
||||||
|
return &LogPacketConn{bufio.NewPacketConn(conn), logger, prefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
n, addr, err = c.NetPacketConn.ReadFrom(p)
|
||||||
|
if n > 0 {
|
||||||
|
c.logger.Debug(c.prefix, " read from ", addr, " ", buf.EncodeHexString(p[:n]))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
c.logger.Debug(c.prefix, " write to ", addr, " ", buf.EncodeHexString(p))
|
||||||
|
return c.NetPacketConn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||||
|
destination, err = c.NetPacketConn.ReadPacket(buffer)
|
||||||
|
if err == nil {
|
||||||
|
c.logger.Debug(c.prefix, " read packet from ", destination, " ", buf.EncodeHexString(buffer.Bytes()))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
c.logger.Debug(c.prefix, " write packet to ", destination, " ", buf.EncodeHexString(buffer.Bytes()))
|
||||||
|
return c.NetPacketConn.WritePacket(buffer, destination)
|
||||||
|
}
|
48
common/debugio/race.go
Normal file
48
common/debugio/race.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package debugio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RaceConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
readAccess sync.Mutex
|
||||||
|
writeAccess sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRaceConn(conn net.Conn) N.ExtendedConn {
|
||||||
|
return &RaceConn{ExtendedConn: bufio.NewExtendedConn(conn)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RaceConn) Read(p []byte) (n int, err error) {
|
||||||
|
c.readAccess.Lock()
|
||||||
|
defer c.readAccess.Unlock()
|
||||||
|
return c.ExtendedConn.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RaceConn) Write(p []byte) (n int, err error) {
|
||||||
|
c.writeAccess.Lock()
|
||||||
|
defer c.writeAccess.Unlock()
|
||||||
|
return c.ExtendedConn.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RaceConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||||
|
c.readAccess.Lock()
|
||||||
|
defer c.readAccess.Unlock()
|
||||||
|
return c.ExtendedConn.ReadBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RaceConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
c.writeAccess.Lock()
|
||||||
|
defer c.writeAccess.Unlock()
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RaceConn) Upstream() any {
|
||||||
|
return c.ExtendedConn
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/database64128/tfo-go"
|
"github.com/database64128/tfo-go"
|
||||||
)
|
)
|
||||||
|
@ -124,7 +125,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
return d.ListenConfig.ListenPacket(ctx, C.NetworkUDP, "")
|
return d.ListenConfig.ListenPacket(ctx, N.NetworkUDP, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) Upstream() any {
|
func (d *DefaultDialer) Upstream() any {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
@ -23,7 +22,7 @@ func (d *RouterDialer) DialContext(ctx context.Context, network string, destinat
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *RouterDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *RouterDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
return d.router.DefaultOutbound(C.NetworkUDP).ListenPacket(ctx, destination)
|
return d.router.DefaultOutbound(N.NetworkUDP).ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *RouterDialer) Upstream() any {
|
func (d *RouterDialer) Upstream() any {
|
||||||
|
|
|
@ -112,7 +112,7 @@ func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOpt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
if network != C.NetworkTCP {
|
if network != N.NetworkTCP {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
conn, err := d.dialer.DialContext(ctx, network, destination)
|
conn, err := d.dialer.DialContext(ctx, network, destination)
|
||||||
|
|
476
common/mux/client.go
Normal file
476
common/mux/client.go
Normal file
|
@ -0,0 +1,476 @@
|
||||||
|
package mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"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"
|
||||||
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
|
||||||
|
"github.com/hashicorp/yamux"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ N.Dialer = (*Client)(nil)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
access sync.Mutex
|
||||||
|
connections list.List[*yamux.Session]
|
||||||
|
ctx context.Context
|
||||||
|
dialer N.Dialer
|
||||||
|
maxConnections int
|
||||||
|
minStreams int
|
||||||
|
maxStreams int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(ctx context.Context, dialer N.Dialer, maxConnections int, minStreams int, maxStreams int) *Client {
|
||||||
|
return &Client{
|
||||||
|
ctx: ctx,
|
||||||
|
dialer: dialer,
|
||||||
|
maxConnections: maxConnections,
|
||||||
|
minStreams: minStreams,
|
||||||
|
maxStreams: maxStreams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.MultiplexOptions) N.Dialer {
|
||||||
|
if !options.Enabled {
|
||||||
|
return dialer
|
||||||
|
}
|
||||||
|
if options.MaxConnections == 0 && options.MaxStreams == 0 {
|
||||||
|
options.MinStreams = 8
|
||||||
|
}
|
||||||
|
return NewClient(ctx, dialer, options.MaxConnections, options.MinStreams, options.MaxStreams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
switch N.NetworkName(network) {
|
||||||
|
case N.NetworkTCP:
|
||||||
|
stream, err := c.openStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ClientConn{Conn: stream, destination: destination}, nil
|
||||||
|
case N.NetworkUDP:
|
||||||
|
stream, err := c.openStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||||
|
default:
|
||||||
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
stream, err := c.openStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||||
|
return &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) openStream() (net.Conn, error) {
|
||||||
|
session, err := c.offer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn, err := session.Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) offer() (*yamux.Session, error) {
|
||||||
|
c.access.Lock()
|
||||||
|
defer c.access.Unlock()
|
||||||
|
|
||||||
|
sessions := make([]*yamux.Session, 0, c.maxConnections)
|
||||||
|
for element := c.connections.Front(); element != nil; {
|
||||||
|
if element.Value.IsClosed() {
|
||||||
|
nextElement := element.Next()
|
||||||
|
c.connections.Remove(element)
|
||||||
|
element = nextElement
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sessions = append(sessions, element.Value)
|
||||||
|
element = element.Next()
|
||||||
|
}
|
||||||
|
sLen := len(sessions)
|
||||||
|
if sLen == 0 {
|
||||||
|
return c.offerNew()
|
||||||
|
}
|
||||||
|
// session := common.MinBy(sessions, yamux.Session.NumStreams)
|
||||||
|
session := common.MinBy(sessions, func(it *yamux.Session) int {
|
||||||
|
return it.NumStreams()
|
||||||
|
})
|
||||||
|
numStreams := session.NumStreams()
|
||||||
|
if numStreams == 0 {
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
if c.maxConnections > 0 {
|
||||||
|
if sLen >= c.maxConnections || numStreams < c.minStreams {
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if c.maxStreams > 0 && numStreams < c.maxStreams {
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.offerNew()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) offerNew() (*yamux.Session, error) {
|
||||||
|
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, Destination)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
session, err := yamux.Client(conn, newMuxConfig())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.connections.PushBack(session)
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
c.access.Lock()
|
||||||
|
defer c.access.Unlock()
|
||||||
|
for _, session := range c.connections.Array() {
|
||||||
|
session.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConn struct {
|
||||||
|
net.Conn
|
||||||
|
destination M.Socksaddr
|
||||||
|
requestWrite bool
|
||||||
|
responseRead bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) readResponse() error {
|
||||||
|
response, err := ReadResponse(c.Conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if response.Status == statusError {
|
||||||
|
return E.New("remote error: ", response.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Read(b []byte) (n int, err error) {
|
||||||
|
if !c.responseRead {
|
||||||
|
err = c.readResponse()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.responseRead = true
|
||||||
|
}
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Write(b []byte) (n int, err error) {
|
||||||
|
if c.requestWrite {
|
||||||
|
return c.Conn.Write(b)
|
||||||
|
}
|
||||||
|
request := Request{
|
||||||
|
Network: N.NetworkTCP,
|
||||||
|
Destination: c.destination,
|
||||||
|
}
|
||||||
|
_buffer := buf.StackNewSize(requestLen(request) + len(b))
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
EncodeRequest(request, buffer)
|
||||||
|
buffer.Write(b)
|
||||||
|
_, err = c.Conn.Write(buffer.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.requestWrite = true
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
if !c.requestWrite {
|
||||||
|
return bufio.ReadFrom0(c, r)
|
||||||
|
}
|
||||||
|
return bufio.Copy(c.Conn, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
if !c.responseRead {
|
||||||
|
return bufio.WriteTo0(c, w)
|
||||||
|
}
|
||||||
|
return bufio.Copy(w, c.Conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) LocalAddr() net.Addr {
|
||||||
|
return c.Conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) RemoteAddr() net.Addr {
|
||||||
|
return c.destination.TCPAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) ReaderReplaceable() bool {
|
||||||
|
return c.responseRead
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) WriterReplaceable() bool {
|
||||||
|
return c.requestWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Upstream() any {
|
||||||
|
return c.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientPacketConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
destination M.Socksaddr
|
||||||
|
requestWrite bool
|
||||||
|
responseRead bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) readResponse() error {
|
||||||
|
response, err := ReadResponse(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if response.Status == statusError {
|
||||||
|
return E.New("remote error: ", response.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) Read(b []byte) (n int, err error) {
|
||||||
|
if !c.responseRead {
|
||||||
|
err = c.readResponse()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.responseRead = true
|
||||||
|
}
|
||||||
|
var length uint16
|
||||||
|
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cap(b) < int(length) {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
return io.ReadFull(c.ExtendedConn, b[:length])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) writeRequest(payload []byte) (n int, err error) {
|
||||||
|
request := Request{
|
||||||
|
Network: N.NetworkUDP,
|
||||||
|
Destination: c.destination,
|
||||||
|
}
|
||||||
|
rLen := requestLen(request)
|
||||||
|
if len(payload) > 0 {
|
||||||
|
rLen += 2 + len(payload)
|
||||||
|
}
|
||||||
|
_buffer := buf.StackNewSize(rLen)
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
EncodeRequest(request, buffer)
|
||||||
|
if len(payload) > 0 {
|
||||||
|
common.Must(
|
||||||
|
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
|
||||||
|
common.Error(buffer.Write(payload)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_, err = c.ExtendedConn.Write(buffer.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.requestWrite = true
|
||||||
|
return len(payload), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) Write(b []byte) (n int, err error) {
|
||||||
|
if !c.requestWrite {
|
||||||
|
return c.writeRequest(b)
|
||||||
|
}
|
||||||
|
err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
if !c.requestWrite {
|
||||||
|
defer buffer.Release()
|
||||||
|
return common.Error(c.writeRequest(buffer.Bytes()))
|
||||||
|
}
|
||||||
|
bLen := buffer.Len()
|
||||||
|
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(bLen))
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
return c.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) LocalAddr() net.Addr {
|
||||||
|
return c.ExtendedConn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) RemoteAddr() net.Addr {
|
||||||
|
return c.destination.UDPAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) Upstream() any {
|
||||||
|
return c.ExtendedConn
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ N.NetPacketConn = (*ClientPacketAddrConn)(nil)
|
||||||
|
|
||||||
|
type ClientPacketAddrConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
destination M.Socksaddr
|
||||||
|
requestWrite bool
|
||||||
|
responseRead bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) readResponse() error {
|
||||||
|
response, err := ReadResponse(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if response.Status == statusError {
|
||||||
|
return E.New("remote error: ", response.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if !c.responseRead {
|
||||||
|
err = c.readResponse()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.responseRead = true
|
||||||
|
}
|
||||||
|
destination, err := M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addr = destination.UDPAddr()
|
||||||
|
var length uint16
|
||||||
|
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cap(p) < int(length) {
|
||||||
|
return 0, nil, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
n, err = io.ReadFull(c.ExtendedConn, p[:length])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) writeRequest(payload []byte, destination M.Socksaddr) (n int, err error) {
|
||||||
|
request := Request{
|
||||||
|
Network: N.NetworkUDP,
|
||||||
|
Destination: c.destination,
|
||||||
|
PacketAddr: true,
|
||||||
|
}
|
||||||
|
rLen := requestLen(request)
|
||||||
|
if len(payload) > 0 {
|
||||||
|
rLen += M.SocksaddrSerializer.AddrPortLen(destination) + 2 + len(payload)
|
||||||
|
}
|
||||||
|
_buffer := buf.StackNewSize(rLen)
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
EncodeRequest(request, buffer)
|
||||||
|
if len(payload) > 0 {
|
||||||
|
common.Must(
|
||||||
|
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
|
||||||
|
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
|
||||||
|
common.Error(buffer.Write(payload)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_, err = c.ExtendedConn.Write(buffer.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.requestWrite = true
|
||||||
|
return len(payload), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if !c.requestWrite {
|
||||||
|
return c.writeRequest(p, M.SocksaddrFromNet(addr))
|
||||||
|
}
|
||||||
|
err = M.SocksaddrSerializer.WriteAddrPort(c.ExtendedConn, M.SocksaddrFromNet(addr))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(p)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||||
|
if !c.responseRead {
|
||||||
|
err = c.readResponse()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.responseRead = true
|
||||||
|
}
|
||||||
|
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var length uint16
|
||||||
|
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if buffer.FreeLen() < int(length) {
|
||||||
|
return destination, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
_, err = io.ReadFull(c.ExtendedConn, buffer.Extend(int(length)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
if !c.requestWrite {
|
||||||
|
defer buffer.Release()
|
||||||
|
return common.Error(c.writeRequest(buffer.Bytes(), destination))
|
||||||
|
}
|
||||||
|
bLen := buffer.Len()
|
||||||
|
header := buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination) + 2))
|
||||||
|
common.Must(
|
||||||
|
M.SocksaddrSerializer.WriteAddrPort(header, destination),
|
||||||
|
binary.Write(header, binary.BigEndian, uint16(bLen)),
|
||||||
|
)
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) LocalAddr() net.Addr {
|
||||||
|
return c.ExtendedConn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketAddrConn) Upstream() any {
|
||||||
|
return c.ExtendedConn
|
||||||
|
}
|
119
common/mux/protocol.go
Normal file
119
common/mux/protocol.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
|
||||||
|
"github.com/hashicorp/yamux"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Destination = M.Socksaddr{
|
||||||
|
Fqdn: "sp.mux.sing-box.arpa",
|
||||||
|
Port: 444,
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMuxConfig() *yamux.Config {
|
||||||
|
config := yamux.DefaultConfig()
|
||||||
|
config.LogOutput = io.Discard
|
||||||
|
config.StreamCloseTimeout = C.TCPTimeout
|
||||||
|
config.StreamOpenTimeout = C.TCPTimeout
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
version0 = 0
|
||||||
|
flagUDP = 1
|
||||||
|
flagAddr = 2
|
||||||
|
statusSuccess = 0
|
||||||
|
statusError = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Network string
|
||||||
|
Destination M.Socksaddr
|
||||||
|
PacketAddr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadRequest(reader io.Reader) (*Request, error) {
|
||||||
|
version, err := rw.ReadByte(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if version != version0 {
|
||||||
|
return nil, E.New("unsupported version: ", version)
|
||||||
|
}
|
||||||
|
var flags uint16
|
||||||
|
err = binary.Read(reader, binary.BigEndian, &flags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
destination, err := M.SocksaddrSerializer.ReadAddrPort(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var network string
|
||||||
|
var udpAddr bool
|
||||||
|
if flags&flagUDP == 0 {
|
||||||
|
network = N.NetworkTCP
|
||||||
|
} else {
|
||||||
|
network = N.NetworkUDP
|
||||||
|
udpAddr = flags&flagAddr != 0
|
||||||
|
}
|
||||||
|
return &Request{network, destination, udpAddr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestLen(request Request) int {
|
||||||
|
var rLen int
|
||||||
|
rLen += 1 // version
|
||||||
|
rLen += 2 // flags
|
||||||
|
rLen += M.SocksaddrSerializer.AddrPortLen(request.Destination)
|
||||||
|
return rLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeRequest(request Request, buffer *buf.Buffer) {
|
||||||
|
destination := request.Destination
|
||||||
|
var flags uint16
|
||||||
|
if request.Network == N.NetworkUDP {
|
||||||
|
flags |= flagUDP
|
||||||
|
}
|
||||||
|
if request.PacketAddr {
|
||||||
|
flags |= flagAddr
|
||||||
|
if !destination.IsValid() {
|
||||||
|
destination = Destination
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(version0),
|
||||||
|
binary.Write(buffer, binary.BigEndian, flags),
|
||||||
|
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Status uint8
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadResponse(reader io.Reader) (*Response, error) {
|
||||||
|
var response Response
|
||||||
|
status, err := rw.ReadByte(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.Status = status
|
||||||
|
if status == statusError {
|
||||||
|
response.Message, err = rw.ReadVString(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &response, nil
|
||||||
|
}
|
210
common/mux/service.go
Normal file
210
common/mux/service.go
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
package mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"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"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
|
||||||
|
"github.com/hashicorp/yamux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
session, err := yamux.Server(conn, newMuxConfig())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
stream, err := session.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
request, err := ReadRequest(stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.Destination = request.Destination
|
||||||
|
if request.Network == N.NetworkTCP {
|
||||||
|
go func() {
|
||||||
|
logger.InfoContext(ctx, "inbound multiplex connection to ", metadata.Destination)
|
||||||
|
hErr := router.RouteConnection(ctx, &ServerConn{ExtendedConn: bufio.NewExtendedConn(stream)}, metadata)
|
||||||
|
// hErr := router.RouteConnection(ctx, &ServerConn{ExtendedConn: bufio.NewExtendedConn(stream)}, metadata)
|
||||||
|
if hErr != nil {
|
||||||
|
errorHandler.NewError(ctx, hErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
var packetConn N.PacketConn
|
||||||
|
if !request.PacketAddr {
|
||||||
|
logger.InfoContext(ctx, "inbound multiplex packet connection to ", metadata.Destination)
|
||||||
|
packetConn = &ServerPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: request.Destination}
|
||||||
|
} else {
|
||||||
|
logger.InfoContext(ctx, "inbound multiplex packet connection")
|
||||||
|
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
|
||||||
|
}
|
||||||
|
hErr := router.RoutePacketConnection(ctx, packetConn, metadata)
|
||||||
|
if hErr != nil {
|
||||||
|
errorHandler.NewError(ctx, hErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ N.HandshakeConn = (*ServerConn)(nil)
|
||||||
|
|
||||||
|
type ServerConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
responseWrite bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) HandshakeFailure(err error) error {
|
||||||
|
errMessage := err.Error()
|
||||||
|
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(statusError),
|
||||||
|
rw.WriteVString(_buffer, errMessage),
|
||||||
|
)
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Write(b []byte) (n int, err error) {
|
||||||
|
if c.responseWrite {
|
||||||
|
return c.ExtendedConn.Write(b)
|
||||||
|
}
|
||||||
|
_buffer := buf.StackNewSize(1 + len(b))
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(statusSuccess),
|
||||||
|
common.Error(buffer.Write(b)),
|
||||||
|
)
|
||||||
|
_, err = c.ExtendedConn.Write(buffer.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.responseWrite = true
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
if c.responseWrite {
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
buffer.ExtendHeader(1)[0] = statusSuccess
|
||||||
|
c.responseWrite = true
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ N.HandshakeConn = (*ServerPacketConn)(nil)
|
||||||
|
_ N.PacketConn = (*ServerPacketConn)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerPacketConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
destination M.Socksaddr
|
||||||
|
responseWrite bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerPacketConn) HandshakeFailure(err error) error {
|
||||||
|
errMessage := err.Error()
|
||||||
|
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(statusError),
|
||||||
|
rw.WriteVString(_buffer, errMessage),
|
||||||
|
)
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||||
|
var length uint16
|
||||||
|
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
destination = c.destination
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
pLen := buffer.Len()
|
||||||
|
common.Must(binary.Write(buf.With(buffer.ExtendHeader(2)), binary.BigEndian, uint16(pLen)))
|
||||||
|
if !c.responseWrite {
|
||||||
|
buffer.ExtendHeader(1)[0] = statusSuccess
|
||||||
|
c.responseWrite = true
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ N.HandshakeConn = (*ServerPacketAddrConn)(nil)
|
||||||
|
_ N.PacketConn = (*ServerPacketAddrConn)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerPacketAddrConn struct {
|
||||||
|
N.ExtendedConn
|
||||||
|
responseWrite bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerPacketAddrConn) HandshakeFailure(err error) error {
|
||||||
|
errMessage := err.Error()
|
||||||
|
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(statusError),
|
||||||
|
rw.WriteVString(_buffer, errMessage),
|
||||||
|
)
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||||
|
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var length uint16
|
||||||
|
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
pLen := buffer.Len()
|
||||||
|
common.Must(binary.Write(buf.With(buffer.ExtendHeader(2)), binary.BigEndian, uint16(pLen)))
|
||||||
|
common.Must(M.SocksaddrSerializer.WriteAddrPort(buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination))), destination))
|
||||||
|
if !c.responseWrite {
|
||||||
|
buffer.ExtendHeader(1)[0] = statusSuccess
|
||||||
|
c.responseWrite = true
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
@ -33,9 +33,9 @@ func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, sr
|
||||||
func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
||||||
var spath string
|
var spath string
|
||||||
switch network {
|
switch network {
|
||||||
case C.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
spath = "net.inet.tcp.pcblist_n"
|
spath = "net.inet.tcp.pcblist_n"
|
||||||
case C.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
spath = "net.inet.udp.pcblist_n"
|
spath = "net.inet.udp.pcblist_n"
|
||||||
default:
|
default:
|
||||||
return "", os.ErrInvalid
|
return "", os.ErrInvalid
|
||||||
|
@ -55,7 +55,7 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
||||||
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
|
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
|
||||||
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
|
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
|
||||||
itemSize := 384
|
itemSize := 384
|
||||||
if network == C.NetworkTCP {
|
if network == N.NetworkTCP {
|
||||||
// rup8(sizeof(xtcpcb_n))
|
// rup8(sizeof(xtcpcb_n))
|
||||||
itemSize += 208
|
itemSize += 208
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62
|
// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62
|
||||||
|
@ -52,9 +52,9 @@ func resolveSocketByNetlink0(network string, ip netip.Addr, srcPort int) (inode
|
||||||
var protocol byte
|
var protocol byte
|
||||||
|
|
||||||
switch network {
|
switch network {
|
||||||
case C.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
protocol = syscall.IPPROTO_TCP
|
protocol = syscall.IPPROTO_TCP
|
||||||
case C.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
protocol = syscall.IPPROTO_UDP
|
protocol = syscall.IPPROTO_UDP
|
||||||
default:
|
default:
|
||||||
return 0, 0, os.ErrInvalid
|
return 0, 0, os.ErrInvalid
|
||||||
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
@ -86,10 +86,10 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
|
||||||
var class int
|
var class int
|
||||||
var fn uintptr
|
var fn uintptr
|
||||||
switch network {
|
switch network {
|
||||||
case C.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
fn = procGetExtendedTcpTable.Addr()
|
fn = procGetExtendedTcpTable.Addr()
|
||||||
class = tcpTablePidConn
|
class = tcpTablePidConn
|
||||||
case C.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
fn = procGetExtendedUdpTable.Addr()
|
fn = procGetExtendedUdpTable.Addr()
|
||||||
class = udpTablePid
|
class = udpTablePid
|
||||||
default:
|
default:
|
||||||
|
@ -101,7 +101,7 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
s := newSearcher(family == windows.AF_INET, network == C.NetworkTCP)
|
s := newSearcher(family == windows.AF_INET, network == N.NetworkTCP)
|
||||||
|
|
||||||
pid, err := s.Search(buf, ip, uint16(srcPort))
|
pid, err := s.Search(buf, ip, uint16(srcPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package constant
|
|
||||||
|
|
||||||
const (
|
|
||||||
NetworkTCP = "tcp"
|
|
||||||
NetworkUDP = "udp"
|
|
||||||
)
|
|
|
@ -6,8 +6,6 @@ const (
|
||||||
TCPTimeout = 5 * time.Second
|
TCPTimeout = 5 * time.Second
|
||||||
TCPKeepAlivePeriod = 30 * time.Second
|
TCPKeepAlivePeriod = 30 * time.Second
|
||||||
ReadPayloadTimeout = 300 * time.Millisecond
|
ReadPayloadTimeout = 300 * time.Millisecond
|
||||||
URLTestTimeout = TCPTimeout
|
|
||||||
DefaultURLTestInterval = 1 * time.Minute
|
|
||||||
DNSTimeout = 10 * time.Second
|
DNSTimeout = 10 * time.Second
|
||||||
QUICTimeout = 30 * time.Second
|
QUICTimeout = 30 * time.Second
|
||||||
STUNTimeout = 15 * time.Second
|
STUNTimeout = 15 * time.Second
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
|
@ -82,7 +83,7 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||||
}
|
}
|
||||||
info.Put("type", clashType)
|
info.Put("type", clashType)
|
||||||
info.Put("name", detour.Tag())
|
info.Put("name", detour.Tag())
|
||||||
info.Put("udp", common.Contains(detour.Network(), C.NetworkUDP))
|
info.Put("udp", common.Contains(detour.Network(), N.NetworkUDP))
|
||||||
delayHistory := server.urlTestHistory.LoadURLTestHistory(adapter.OutboundTag(detour))
|
delayHistory := server.urlTestHistory.LoadURLTestHistory(adapter.OutboundTag(detour))
|
||||||
if delayHistory != nil {
|
if delayHistory != nil {
|
||||||
info.Put("history", []*urltest.History{delayHistory})
|
info.Put("history", []*urltest.History{delayHistory})
|
||||||
|
@ -114,7 +115,7 @@ func getProxies(server *Server, router adapter.Router) func(w http.ResponseWrite
|
||||||
allProxies = append(allProxies, detour.Tag())
|
allProxies = append(allProxies, detour.Tag())
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultTag := router.DefaultOutbound(C.NetworkTCP).Tag()
|
defaultTag := router.DefaultOutbound(N.NetworkTCP).Tag()
|
||||||
if defaultTag == "" {
|
if defaultTag == "" {
|
||||||
defaultTag = allProxies[0]
|
defaultTag = allProxies[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
@ -86,7 +85,7 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
|
||||||
var chain []string
|
var chain []string
|
||||||
var next string
|
var next string
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
next = router.DefaultOutbound(C.NetworkTCP).Tag()
|
next = router.DefaultOutbound(N.NetworkTCP).Tag()
|
||||||
} else {
|
} else {
|
||||||
next = rule.Outbound()
|
next = rule.Outbound()
|
||||||
}
|
}
|
||||||
|
@ -173,7 +172,7 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, route
|
||||||
var chain []string
|
var chain []string
|
||||||
var next string
|
var next string
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
next = router.DefaultOutbound(C.NetworkUDP).Tag()
|
next = router.DefaultOutbound(N.NetworkUDP).Tag()
|
||||||
} else {
|
} else {
|
||||||
next = rule.Outbound()
|
next = rule.Outbound()
|
||||||
}
|
}
|
||||||
|
|
14
go.mod
14
go.mod
|
@ -7,25 +7,27 @@ require (
|
||||||
github.com/fsnotify/fsnotify v1.5.4
|
github.com/fsnotify/fsnotify v1.5.4
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.1
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible
|
github.com/gofrs/uuid v4.2.0+incompatible
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
github.com/hashicorp/yamux v0.1.1
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/oschwald/maxminddb-golang v1.9.0
|
github.com/oschwald/maxminddb-golang v1.9.0
|
||||||
github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e
|
github.com/sagernet/sing v0.0.0-20220729120910-4376f188c512
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220726044716-2b8c696b09f5
|
github.com/sagernet/sing-dns v0.0.0-20220729120941-109c0a7aabb1
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220729155919-91d2780bfc80
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01
|
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5
|
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
go.uber.org/atomic v1.9.0
|
go.uber.org/atomic v1.9.0
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||||
golang.org/x/net v0.0.0-20220725212005-46097bf591d3
|
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/cheekybits/genny v1.0.0 // indirect
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
|
|
28
go.sum
28
go.sum
|
@ -8,6 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
|
||||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
|
@ -35,8 +37,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
@ -80,6 +82,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
@ -143,12 +147,12 @@ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7q
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e h1:5lfrAc+vSv0iW6eHGNLyHC+a/k6BDGJvYxYxwB/68Kk=
|
github.com/sagernet/sing v0.0.0-20220729120910-4376f188c512 h1:dCWDE55LpZu//W02FccNbGObZFlv1N2NS0yUdf2i4Mc=
|
||||||
github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
|
github.com/sagernet/sing v0.0.0-20220729120910-4376f188c512/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220726044716-2b8c696b09f5 h1:l6ztUAFVhWhY0XOq7ISbwVBE4YLWMxfIN6HptgaOl4I=
|
github.com/sagernet/sing-dns v0.0.0-20220729120941-109c0a7aabb1 h1:Gv9ow1IF98Qdxs+X8unPHJG4iwuEWoq0PE/jvlIqgqY=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220726044716-2b8c696b09f5/go.mod h1:KL+8wZG3gqHLm+nvNI3ZNaPzCMA4T7KIwsGp7ix9a34=
|
github.com/sagernet/sing-dns v0.0.0-20220729120941-109c0a7aabb1/go.mod h1:LQJDT4IpqyWI6NugkSSqxTcFfxxNBp94n+fXtHFMboQ=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b h1:6wJoJaroW3WCGjHGu7XPOSLEKP9Loi3Ox4+7A1kRTsQ=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220729155919-91d2780bfc80 h1:gpCPZyZJQVn6ZTBCJ/XaYbPi6j43TdyTty/MI5bXhbE=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b/go.mod h1:mH6wE4b5FZp1Q/meATe4tjiPjvQO9E7Lr0FBBwFYp4I=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220729155919-91d2780bfc80/go.mod h1:mH6wE4b5FZp1Q/meATe4tjiPjvQO9E7Lr0FBBwFYp4I=
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01 h1:tNJn7T87sgQyA8gpEvC6LbusV4lkhZU8oi4mRujOhM8=
|
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01 h1:tNJn7T87sgQyA8gpEvC6LbusV4lkhZU8oi4mRujOhM8=
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01/go.mod h1:bYHamPB16GFGt34ayYt56Pb7aN64RPY0+uuFPBSbj0U=
|
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01/go.mod h1:bYHamPB16GFGt34ayYt56Pb7aN64RPY0+uuFPBSbj0U=
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5 h1:TNguWTPF6gxX/gR02hY3LGviUn6LGlDPofE6lpSJWeo=
|
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5 h1:TNguWTPF6gxX/gR02hY3LGviUn6LGlDPofE6lpSJWeo=
|
||||||
|
@ -238,8 +242,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220725212005-46097bf591d3 h1:2yWTtPWWRcISTw3/o+s/Y4UOMnQL71DWyToOANFusCg=
|
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I=
|
||||||
golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
|
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -275,8 +279,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|
|
@ -62,13 +62,13 @@ func (a *myInboundAdapter) Tag() string {
|
||||||
|
|
||||||
func (a *myInboundAdapter) Start() error {
|
func (a *myInboundAdapter) Start() error {
|
||||||
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
|
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
|
||||||
if common.Contains(a.network, C.NetworkTCP) {
|
if common.Contains(a.network, N.NetworkTCP) {
|
||||||
var tcpListener *net.TCPListener
|
var tcpListener *net.TCPListener
|
||||||
var err error
|
var err error
|
||||||
if !a.listenOptions.TCPFastOpen {
|
if !a.listenOptions.TCPFastOpen {
|
||||||
tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(C.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
|
tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
|
||||||
} else {
|
} else {
|
||||||
tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(C.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
|
tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -77,8 +77,8 @@ func (a *myInboundAdapter) Start() error {
|
||||||
go a.loopTCPIn()
|
go a.loopTCPIn()
|
||||||
a.logger.Info("tcp server started at ", tcpListener.Addr())
|
a.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||||
}
|
}
|
||||||
if common.Contains(a.network, C.NetworkUDP) {
|
if common.Contains(a.network, N.NetworkUDP) {
|
||||||
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(C.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
|
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ func (a *myInboundAdapter) loopTCPIn() {
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||||
metadata.Network = C.NetworkTCP
|
metadata.Network = N.NetworkTCP
|
||||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
||||||
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
hErr := a.connHandler.NewConnection(ctx, conn, metadata)
|
hErr := a.connHandler.NewConnection(ctx, conn, metadata)
|
||||||
|
@ -196,7 +196,7 @@ func (a *myInboundAdapter) loopUDPIn() {
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||||
metadata.Network = C.NetworkUDP
|
metadata.Network = N.NetworkUDP
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -228,7 +228,7 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||||
metadata.Network = C.NetworkUDP
|
metadata.Network = N.NetworkUDP
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -254,7 +254,7 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||||
metadata.Network = C.NetworkUDP
|
metadata.Network = N.NetworkUDP
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -282,7 +282,7 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||||
metadata.Network = C.NetworkUDP
|
metadata.Network = N.NetworkUDP
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -334,7 +334,7 @@ func NewError(logger log.ContextLogger, ctx context.Context, err error) {
|
||||||
func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
if destination.IsFqdn() {
|
if destination.IsFqdn() {
|
||||||
udpAddr, err := net.ResolveUDPAddr(C.NetworkUDP, destination.String())
|
udpAddr, err := net.ResolveUDPAddr(N.NetworkUDP, destination.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||||
inbound := &HTTP{
|
inbound := &HTTP{
|
||||||
myInboundAdapter: myInboundAdapter{
|
myInboundAdapter: myInboundAdapter{
|
||||||
protocol: C.TypeHTTP,
|
protocol: C.TypeHTTP,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{N.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
"github.com/sagernet/sing/protocol/http"
|
"github.com/sagernet/sing/protocol/http"
|
||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
@ -31,7 +32,7 @@ func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||||
inbound := &Mixed{
|
inbound := &Mixed{
|
||||||
myInboundAdapter{
|
myInboundAdapter{
|
||||||
protocol: C.TypeMixed,
|
protocol: C.TypeMixed,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{N.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Redirect struct {
|
type Redirect struct {
|
||||||
|
@ -21,7 +22,7 @@ func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||||
redirect := &Redirect{
|
redirect := &Redirect{
|
||||||
myInboundAdapter{
|
myInboundAdapter{
|
||||||
protocol: C.TypeRedirect,
|
protocol: C.TypeRedirect,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{N.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||||
inbound := &Socks{
|
inbound := &Socks{
|
||||||
myInboundAdapter{
|
myInboundAdapter{
|
||||||
protocol: C.TypeSocks,
|
protocol: C.TypeSocks,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{N.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
|
||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = t.tag
|
metadata.Inbound = t.tag
|
||||||
metadata.InboundType = C.TypeTun
|
metadata.InboundType = C.TypeTun
|
||||||
metadata.Network = C.NetworkTCP
|
metadata.Network = N.NetworkTCP
|
||||||
metadata.Source = upstreamMetadata.Source
|
metadata.Source = upstreamMetadata.Source
|
||||||
metadata.Destination = upstreamMetadata.Destination
|
metadata.Destination = upstreamMetadata.Destination
|
||||||
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
|
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
|
||||||
|
@ -134,7 +134,7 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre
|
||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = t.tag
|
metadata.Inbound = t.tag
|
||||||
metadata.InboundType = C.TypeTun
|
metadata.InboundType = C.TypeTun
|
||||||
metadata.Network = C.NetworkUDP
|
metadata.Network = N.NetworkUDP
|
||||||
metadata.Source = upstreamMetadata.Source
|
metadata.Source = upstreamMetadata.Source
|
||||||
metadata.Destination = upstreamMetadata.Destination
|
metadata.Destination = upstreamMetadata.Destination
|
||||||
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
|
metadata.SniffEnabled = t.inboundOptions.SniffEnabled
|
||||||
|
|
|
@ -30,7 +30,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||||
inbound := &VMess{
|
inbound := &VMess{
|
||||||
myInboundAdapter: myInboundAdapter{
|
myInboundAdapter: myInboundAdapter{
|
||||||
protocol: C.TypeVMess,
|
protocol: C.TypeVMess,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{N.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -12,6 +12,10 @@ func init() {
|
||||||
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr).Logger()
|
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr).Logger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StdLogger() ContextLogger {
|
||||||
|
return std
|
||||||
|
}
|
||||||
|
|
||||||
func Trace(args ...any) {
|
func Trace(args ...any) {
|
||||||
std.Trace(args...)
|
std.Trace(args...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,3 +98,10 @@ type ServerOptions struct {
|
||||||
func (o ServerOptions) Build() M.Socksaddr {
|
func (o ServerOptions) Build() M.Socksaddr {
|
||||||
return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
|
return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MultiplexOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
MaxConnections int `json:"max_connections,omitempty"`
|
||||||
|
MinStreams int `json:"min_streams,omitempty"`
|
||||||
|
MaxStreams int `json:"max_streams,omitempty"`
|
||||||
|
}
|
||||||
|
|
|
@ -27,4 +27,5 @@ type ShadowsocksOutboundOptions struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
|
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/json"
|
"github.com/sagernet/sing-box/common/json"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ListenAddress netip.Addr
|
type ListenAddress netip.Addr
|
||||||
|
@ -50,7 +50,7 @@ func (v *NetworkList) UnmarshalJSON(content []byte) error {
|
||||||
}
|
}
|
||||||
for _, networkName := range networkList {
|
for _, networkName := range networkList {
|
||||||
switch networkName {
|
switch networkName {
|
||||||
case C.NetworkTCP, C.NetworkUDP:
|
case N.NetworkTCP, N.NetworkUDP:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return E.New("unknown network: " + networkName)
|
return E.New("unknown network: " + networkName)
|
||||||
|
@ -62,7 +62,7 @@ func (v *NetworkList) UnmarshalJSON(content []byte) error {
|
||||||
|
|
||||||
func (v NetworkList) Build() []string {
|
func (v NetworkList) Build() []string {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
return []string{C.NetworkTCP, C.NetworkUDP}
|
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||||
}
|
}
|
||||||
return strings.Split(string(v), "\n")
|
return strings.Split(string(v), "\n")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func NewBlock(logger log.ContextLogger, tag string) *Block {
|
||||||
return &Block{
|
return &Block{
|
||||||
myOutboundAdapter{
|
myOutboundAdapter{
|
||||||
protocol: C.TypeBlock,
|
protocol: C.TypeBlock,
|
||||||
network: []string{C.NetworkTCP, C.NetworkUDP},
|
network: []string{N.NetworkTCP, N.NetworkUDP},
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package outbound
|
package outbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -8,7 +10,7 @@ import (
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(router adapter.Router, logger log.ContextLogger, options option.Outbound) (adapter.Outbound, error) {
|
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Outbound) (adapter.Outbound, error) {
|
||||||
if options.Type == "" {
|
if options.Type == "" {
|
||||||
return nil, E.New("missing outbound type")
|
return nil, E.New("missing outbound type")
|
||||||
}
|
}
|
||||||
|
@ -24,7 +26,7 @@ func New(router adapter.Router, logger log.ContextLogger, options option.Outboun
|
||||||
case C.TypeHTTP:
|
case C.TypeHTTP:
|
||||||
return NewHTTP(router, logger, options.Tag, options.HTTPOptions)
|
return NewHTTP(router, logger, options.Tag, options.HTTPOptions)
|
||||||
case C.TypeShadowsocks:
|
case C.TypeShadowsocks:
|
||||||
return NewShadowsocks(router, logger, options.Tag, options.ShadowsocksOptions)
|
return NewShadowsocks(ctx, router, logger, options.Tag, options.ShadowsocksOptions)
|
||||||
case C.TypeVMess:
|
case C.TypeVMess:
|
||||||
return NewVMess(router, logger, options.Tag, options.VMessOptions)
|
return NewVMess(router, logger, options.Tag, options.VMessOptions)
|
||||||
case C.TypeSelector:
|
case C.TypeSelector:
|
||||||
|
|
|
@ -42,12 +42,12 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
|
||||||
var outConn net.Conn
|
var outConn net.Conn
|
||||||
var err error
|
var err error
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
if len(metadata.DestinationAddresses) > 0 {
|
||||||
outConn, err = N.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
||||||
} else {
|
} else {
|
||||||
outConn, err = this.DialContext(ctx, C.NetworkTCP, metadata.Destination)
|
outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return N.HandshakeFailure(conn, err)
|
||||||
}
|
}
|
||||||
return bufio.CopyConn(ctx, conn, outConn)
|
return bufio.CopyConn(ctx, conn, outConn)
|
||||||
}
|
}
|
||||||
|
@ -57,12 +57,12 @@ func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metad
|
||||||
var outConn net.Conn
|
var outConn net.Conn
|
||||||
var err error
|
var err error
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
if len(metadata.DestinationAddresses) > 0 {
|
||||||
outConn, err = N.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
||||||
} else {
|
} else {
|
||||||
outConn, err = this.DialContext(ctx, C.NetworkTCP, metadata.Destination)
|
outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return N.HandshakeFailure(conn, err)
|
||||||
}
|
}
|
||||||
return CopyEarlyConn(ctx, conn, outConn)
|
return CopyEarlyConn(ctx, conn, outConn)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
|
||||||
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return N.HandshakeFailure(conn, err)
|
||||||
}
|
}
|
||||||
if metadata.Protocol != "" {
|
if metadata.Protocol != "" {
|
||||||
switch metadata.Protocol {
|
switch metadata.Protocol {
|
||||||
|
@ -120,7 +120,7 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||||
}
|
}
|
||||||
_, err = serverConn.Write(payload.Bytes())
|
_, err = serverConn.Write(payload.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "client handshake")
|
return N.HandshakeFailure(conn, err)
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(_payload)
|
runtime.KeepAlive(_payload)
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
return bufio.CopyConn(ctx, conn, serverConn)
|
||||||
|
|
|
@ -26,7 +26,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
||||||
outbound := &Direct{
|
outbound := &Direct{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
myOutboundAdapter: myOutboundAdapter{
|
||||||
protocol: C.TypeDirect,
|
protocol: C.TypeDirect,
|
||||||
network: []string{C.NetworkTCP, C.NetworkUDP},
|
network: []string{N.NetworkTCP, N.NetworkUDP},
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
|
@ -61,9 +61,9 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
|
||||||
destination.Port = h.overrideDestination.Port
|
destination.Port = h.overrideDestination.Port
|
||||||
}
|
}
|
||||||
switch network {
|
switch network {
|
||||||
case C.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
case C.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
}
|
}
|
||||||
return h.dialer.DialContext(ctx, network, destination)
|
return h.dialer.DialContext(ctx, network, destination)
|
||||||
|
|
|
@ -30,7 +30,7 @@ func NewDNS(router adapter.Router, logger log.ContextLogger, tag string) *DNS {
|
||||||
return &DNS{
|
return &DNS{
|
||||||
myOutboundAdapter{
|
myOutboundAdapter{
|
||||||
protocol: C.TypeDNS,
|
protocol: C.TypeDNS,
|
||||||
network: []string{C.NetworkTCP, C.NetworkUDP},
|
network: []string{N.NetworkTCP, N.NetworkUDP},
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
|
|
|
@ -31,7 +31,7 @@ func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, option
|
||||||
return &HTTP{
|
return &HTTP{
|
||||||
myOutboundAdapter{
|
myOutboundAdapter{
|
||||||
protocol: C.TypeHTTP,
|
protocol: C.TypeHTTP,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{N.NetworkTCP},
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
|
|
|
@ -46,7 +46,7 @@ func NewSelector(router adapter.Router, logger log.ContextLogger, tag string, op
|
||||||
|
|
||||||
func (s *Selector) Network() []string {
|
func (s *Selector) Network() []string {
|
||||||
if s.selected == nil {
|
if s.selected == nil {
|
||||||
return []string{C.NetworkTCP, C.NetworkUDP}
|
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||||
}
|
}
|
||||||
return s.selected.Network()
|
return s.selected.Network()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,15 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
|
"github.com/sagernet/sing-box/common/mux"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-shadowsocks"
|
"github.com/sagernet/sing-shadowsocks"
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowimpl"
|
"github.com/sagernet/sing-shadowsocks/shadowimpl"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
@ -23,61 +26,36 @@ type Shadowsocks struct {
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
method shadowsocks.Method
|
method shadowsocks.Method
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
|
multiplexDialer N.Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowsocks(router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
|
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
|
||||||
method, err := shadowimpl.FetchMethod(options.Method, options.Password)
|
method, err := shadowimpl.FetchMethod(options.Method, options.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Shadowsocks{
|
outbound := &Shadowsocks{
|
||||||
myOutboundAdapter{
|
myOutboundAdapter: myOutboundAdapter{
|
||||||
protocol: C.TypeShadowsocks,
|
protocol: C.TypeShadowsocks,
|
||||||
network: options.Network.Build(),
|
network: options.Network.Build(),
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
},
|
},
|
||||||
dialer.NewOutbound(router, options.OutboundDialerOptions),
|
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
|
||||||
method,
|
method: method,
|
||||||
options.ServerOptions.Build(),
|
serverAddr: options.ServerOptions.Build(),
|
||||||
}, nil
|
}
|
||||||
|
outbound.multiplexDialer = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||||
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
return h.multiplexDialer.DialContext(ctx, network, destination)
|
||||||
metadata.Outbound = h.tag
|
|
||||||
metadata.Destination = destination
|
|
||||||
switch network {
|
|
||||||
case C.NetworkTCP:
|
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
|
||||||
outConn, err := h.dialer.DialContext(ctx, C.NetworkTCP, h.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return h.method.DialEarlyConn(outConn, destination), nil
|
|
||||||
case C.NetworkUDP:
|
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
|
||||||
outConn, err := h.dialer.DialContext(ctx, C.NetworkUDP, h.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &bufio.BindPacketConn{PacketConn: h.method.DialPacketConn(outConn), Addr: destination}, nil
|
|
||||||
default:
|
|
||||||
panic("unknown network " + network)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
return h.multiplexDialer.ListenPacket(ctx, destination)
|
||||||
metadata.Outbound = h.tag
|
|
||||||
metadata.Destination = destination
|
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
|
||||||
outConn, err := h.dialer.DialContext(ctx, "udp", h.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return h.method.DialPacketConn(outConn), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
@ -87,3 +65,43 @@ func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata
|
||||||
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
return NewPacketConnection(ctx, h, conn, metadata)
|
return NewPacketConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ N.Dialer = (*shadowsocksDialer)(nil)
|
||||||
|
|
||||||
|
type shadowsocksDialer Shadowsocks
|
||||||
|
|
||||||
|
func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
|
metadata.Outbound = h.tag
|
||||||
|
metadata.Destination = destination
|
||||||
|
switch N.NetworkName(network) {
|
||||||
|
case N.NetworkTCP:
|
||||||
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
|
outConn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return h.method.DialEarlyConn(outConn, destination), nil
|
||||||
|
case N.NetworkUDP:
|
||||||
|
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 &bufio.BindPacketConn{PacketConn: h.method.DialPacketConn(outConn), Addr: destination}, nil
|
||||||
|
default:
|
||||||
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
|
metadata.Outbound = h.tag
|
||||||
|
metadata.Destination = destination
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
@ -49,13 +50,13 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.tag
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch network {
|
switch N.NetworkName(network) {
|
||||||
case C.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
case C.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
default:
|
default:
|
||||||
panic("unknown network " + network)
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||||
}
|
}
|
||||||
return h.client.DialContext(ctx, network, destination)
|
return h.client.DialContext(ctx, network, destination)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-vmess"
|
"github.com/sagernet/sing-vmess"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
@ -58,28 +59,28 @@ func (h *VMess) DialContext(ctx context.Context, network string, destination M.S
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.tag
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch network {
|
switch N.NetworkName(network) {
|
||||||
case C.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
outConn, err := h.dialer.DialContext(ctx, C.NetworkTCP, h.serverAddr)
|
outConn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return h.client.DialEarlyConn(outConn, destination), nil
|
return h.client.DialEarlyConn(outConn, destination), nil
|
||||||
case C.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
outConn, err := h.dialer.DialContext(ctx, C.NetworkTCP, h.serverAddr)
|
outConn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return h.client.DialEarlyPacketConn(outConn, destination), nil
|
return h.client.DialEarlyPacketConn(outConn, destination), nil
|
||||||
default:
|
default:
|
||||||
panic("unknown network " + network)
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
conn, err := h.DialContext(ctx, C.NetworkUDP, destination)
|
conn, err := h.DialContext(ctx, N.NetworkUDP, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/geoip"
|
"github.com/sagernet/sing-box/common/geoip"
|
||||||
"github.com/sagernet/sing-box/common/geosite"
|
"github.com/sagernet/sing-box/common/geosite"
|
||||||
|
"github.com/sagernet/sing-box/common/mux"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
"github.com/sagernet/sing-box/common/sniff"
|
"github.com/sagernet/sing-box/common/sniff"
|
||||||
"github.com/sagernet/sing-box/common/warning"
|
"github.com/sagernet/sing-box/common/warning"
|
||||||
|
@ -278,17 +279,17 @@ func (r *Router) Initialize(outbounds []adapter.Outbound, defaultOutbound func()
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("default detour not found: ", r.defaultDetour)
|
return E.New("default detour not found: ", r.defaultDetour)
|
||||||
}
|
}
|
||||||
if common.Contains(detour.Network(), C.NetworkTCP) {
|
if common.Contains(detour.Network(), N.NetworkTCP) {
|
||||||
defaultOutboundForConnection = detour
|
defaultOutboundForConnection = detour
|
||||||
}
|
}
|
||||||
if common.Contains(detour.Network(), C.NetworkUDP) {
|
if common.Contains(detour.Network(), N.NetworkUDP) {
|
||||||
defaultOutboundForPacketConnection = detour
|
defaultOutboundForPacketConnection = detour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var index, packetIndex int
|
var index, packetIndex int
|
||||||
if defaultOutboundForConnection == nil {
|
if defaultOutboundForConnection == nil {
|
||||||
for i, detour := range outbounds {
|
for i, detour := range outbounds {
|
||||||
if common.Contains(detour.Network(), C.NetworkTCP) {
|
if common.Contains(detour.Network(), N.NetworkTCP) {
|
||||||
index = i
|
index = i
|
||||||
defaultOutboundForConnection = detour
|
defaultOutboundForConnection = detour
|
||||||
break
|
break
|
||||||
|
@ -297,7 +298,7 @@ func (r *Router) Initialize(outbounds []adapter.Outbound, defaultOutbound func()
|
||||||
}
|
}
|
||||||
if defaultOutboundForPacketConnection == nil {
|
if defaultOutboundForPacketConnection == nil {
|
||||||
for i, detour := range outbounds {
|
for i, detour := range outbounds {
|
||||||
if common.Contains(detour.Network(), C.NetworkUDP) {
|
if common.Contains(detour.Network(), N.NetworkUDP) {
|
||||||
packetIndex = i
|
packetIndex = i
|
||||||
defaultOutboundForPacketConnection = detour
|
defaultOutboundForPacketConnection = detour
|
||||||
break
|
break
|
||||||
|
@ -478,7 +479,7 @@ func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
||||||
if network == C.NetworkTCP {
|
if network == N.NetworkTCP {
|
||||||
return r.defaultOutboundForConnection
|
return r.defaultOutboundForConnection
|
||||||
} else {
|
} else {
|
||||||
return r.defaultOutboundForPacketConnection
|
return r.defaultOutboundForPacketConnection
|
||||||
|
@ -486,6 +487,10 @@ func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
if metadata.Destination.Fqdn == mux.Destination.Fqdn {
|
||||||
|
r.logger.InfoContext(ctx, "inbound multiplex connection")
|
||||||
|
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
|
||||||
|
}
|
||||||
if metadata.SniffEnabled {
|
if metadata.SniffEnabled {
|
||||||
_buffer := buf.StackNew()
|
_buffer := buf.StackNew()
|
||||||
defer common.KeepAlive(_buffer)
|
defer common.KeepAlive(_buffer)
|
||||||
|
@ -517,7 +522,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||||
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
||||||
}
|
}
|
||||||
matchedRule, detour := r.match(ctx, &metadata, r.defaultOutboundForConnection)
|
matchedRule, detour := r.match(ctx, &metadata, r.defaultOutboundForConnection)
|
||||||
if !common.Contains(detour.Network(), C.NetworkTCP) {
|
if !common.Contains(detour.Network(), N.NetworkTCP) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return E.New("missing supported outbound, closing connection")
|
return E.New("missing supported outbound, closing connection")
|
||||||
}
|
}
|
||||||
|
@ -564,7 +569,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||||
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
||||||
}
|
}
|
||||||
matchedRule, detour := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection)
|
matchedRule, detour := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection)
|
||||||
if !common.Contains(detour.Network(), C.NetworkUDP) {
|
if !common.Contains(detour.Network(), N.NetworkUDP) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return E.New("missing supported outbound, closing packet connection")
|
return E.New("missing supported outbound, closing packet connection")
|
||||||
}
|
}
|
||||||
|
@ -927,5 +932,10 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) NewError(ctx context.Context, err error) {
|
func (r *Router) NewError(ctx context.Context, err error) {
|
||||||
|
common.Close(err)
|
||||||
|
if E.IsClosedOrCanceled(err) {
|
||||||
|
r.logger.TraceContext(ctx, "connection closed: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
r.logger.ErrorContext(ctx, err)
|
r.logger.ErrorContext(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRule(router adapter.Router, logger log.ContextLogger, options option.Rule) (adapter.Rule, error) {
|
func NewRule(router adapter.Router, logger log.ContextLogger, options option.Rule) (adapter.Rule, error) {
|
||||||
|
@ -73,7 +74,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||||
}
|
}
|
||||||
if options.Network != "" {
|
if options.Network != "" {
|
||||||
switch options.Network {
|
switch options.Network {
|
||||||
case C.NetworkTCP, C.NetworkUDP:
|
case N.NetworkTCP, N.NetworkUDP:
|
||||||
item := NewNetworkItem(options.Network)
|
item := NewNetworkItem(options.Network)
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.DNSRule) (adapter.DNSRule, error) {
|
func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.DNSRule) (adapter.DNSRule, error) {
|
||||||
|
@ -59,7 +60,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||||
}
|
}
|
||||||
if options.Network != "" {
|
if options.Network != "" {
|
||||||
switch options.Network {
|
switch options.Network {
|
||||||
case C.NetworkTCP, C.NetworkUDP:
|
case N.NetworkTCP, N.NetworkUDP:
|
||||||
item := NewNetworkItem(options.Network)
|
item := NewNetworkItem(options.Network)
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
|
|
14
test/go.mod
14
test/go.mod
|
@ -10,14 +10,16 @@ require (
|
||||||
github.com/docker/docker v20.10.17+incompatible
|
github.com/docker/docker v20.10.17+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible
|
github.com/gofrs/uuid v4.2.0+incompatible
|
||||||
github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e
|
github.com/sagernet/sing v0.0.0-20220729120910-4376f188c512
|
||||||
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220729155919-91d2780bfc80
|
||||||
github.com/spyzhov/ajson v0.7.1
|
github.com/spyzhov/ajson v0.7.1
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
golang.org/x/net v0.0.0-20220725212005-46097bf591d3
|
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||||
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/cheekybits/genny v1.0.0 // indirect
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
github.com/database64128/tfo-go v1.1.0 // indirect
|
github.com/database64128/tfo-go v1.1.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
@ -26,11 +28,12 @@ require (
|
||||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.0.7 // indirect
|
github.com/go-chi/chi/v5 v5.0.7 // indirect
|
||||||
github.com/go-chi/cors v1.2.1 // indirect
|
github.com/go-chi/cors v1.2.1 // indirect
|
||||||
github.com/go-chi/render v1.0.1 // indirect
|
github.com/go-chi/render v1.0.2 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||||
|
@ -49,8 +52,7 @@ require (
|
||||||
github.com/oschwald/maxminddb-golang v1.9.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.9.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220726044716-2b8c696b09f5 // indirect
|
github.com/sagernet/sing-dns v0.0.0-20220729120941-109c0a7aabb1 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b // indirect
|
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01 // indirect
|
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5 // indirect
|
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
|
@ -59,7 +61,7 @@ require (
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||||
golang.org/x/mod v0.5.1 // indirect
|
golang.org/x/mod v0.5.1 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||||
golang.org/x/tools v0.1.9 // indirect
|
golang.org/x/tools v0.1.9 // indirect
|
||||||
|
|
28
test/go.sum
28
test/go.sum
|
@ -12,6 +12,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg6
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
|
@ -48,8 +50,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
@ -96,6 +98,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
@ -168,12 +172,12 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e h1:5lfrAc+vSv0iW6eHGNLyHC+a/k6BDGJvYxYxwB/68Kk=
|
github.com/sagernet/sing v0.0.0-20220729120910-4376f188c512 h1:dCWDE55LpZu//W02FccNbGObZFlv1N2NS0yUdf2i4Mc=
|
||||||
github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
|
github.com/sagernet/sing v0.0.0-20220729120910-4376f188c512/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220726044716-2b8c696b09f5 h1:l6ztUAFVhWhY0XOq7ISbwVBE4YLWMxfIN6HptgaOl4I=
|
github.com/sagernet/sing-dns v0.0.0-20220729120941-109c0a7aabb1 h1:Gv9ow1IF98Qdxs+X8unPHJG4iwuEWoq0PE/jvlIqgqY=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220726044716-2b8c696b09f5/go.mod h1:KL+8wZG3gqHLm+nvNI3ZNaPzCMA4T7KIwsGp7ix9a34=
|
github.com/sagernet/sing-dns v0.0.0-20220729120941-109c0a7aabb1/go.mod h1:LQJDT4IpqyWI6NugkSSqxTcFfxxNBp94n+fXtHFMboQ=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b h1:6wJoJaroW3WCGjHGu7XPOSLEKP9Loi3Ox4+7A1kRTsQ=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220729155919-91d2780bfc80 h1:gpCPZyZJQVn6ZTBCJ/XaYbPi6j43TdyTty/MI5bXhbE=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b/go.mod h1:mH6wE4b5FZp1Q/meATe4tjiPjvQO9E7Lr0FBBwFYp4I=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220729155919-91d2780bfc80/go.mod h1:mH6wE4b5FZp1Q/meATe4tjiPjvQO9E7Lr0FBBwFYp4I=
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01 h1:tNJn7T87sgQyA8gpEvC6LbusV4lkhZU8oi4mRujOhM8=
|
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01 h1:tNJn7T87sgQyA8gpEvC6LbusV4lkhZU8oi4mRujOhM8=
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01/go.mod h1:bYHamPB16GFGt34ayYt56Pb7aN64RPY0+uuFPBSbj0U=
|
github.com/sagernet/sing-tun v0.0.0-20220726111504-b4bded886e01/go.mod h1:bYHamPB16GFGt34ayYt56Pb7aN64RPY0+uuFPBSbj0U=
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5 h1:TNguWTPF6gxX/gR02hY3LGviUn6LGlDPofE6lpSJWeo=
|
github.com/sagernet/sing-vmess v0.0.0-20220726034841-4dae776653e5 h1:TNguWTPF6gxX/gR02hY3LGviUn6LGlDPofE6lpSJWeo=
|
||||||
|
@ -268,8 +272,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220725212005-46097bf591d3 h1:2yWTtPWWRcISTw3/o+s/Y4UOMnQL71DWyToOANFusCg=
|
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I=
|
||||||
golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
|
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -310,8 +314,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|
74
test/mux_test.go
Normal file
74
test/mux_test.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShadowsocksMux(t *testing.T) {
|
||||||
|
method := shadowaead_2022.List[0]
|
||||||
|
password := mkBase64(t, 16)
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Log: &option.LogOptions{
|
||||||
|
Level: "debug",
|
||||||
|
},
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeShadowsocks,
|
||||||
|
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeShadowsocks,
|
||||||
|
Tag: "ss-out",
|
||||||
|
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
|
Multiplex: &option.MultiplexOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "ss-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
Loading…
Reference in a new issue