Improve multiplex

This commit is contained in:
世界 2023-04-20 11:16:22 +08:00
parent daee0db7bb
commit bc32c78d03
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
18 changed files with 155 additions and 1200 deletions

View File

@ -1,535 +1,21 @@
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"
"github.com/sagernet/sing-mux"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/x/list"
)
var _ N.Dialer = (*Client)(nil)
type Client struct {
access sync.Mutex
connections list.List[abstractSession]
ctx context.Context
dialer N.Dialer
protocol Protocol
maxConnections int
minStreams int
maxStreams int
}
func NewClient(ctx context.Context, dialer N.Dialer, protocol Protocol, maxConnections int, minStreams int, maxStreams int) *Client {
return &Client{
ctx: ctx,
dialer: dialer,
protocol: protocol,
maxConnections: maxConnections,
minStreams: minStreams,
maxStreams: maxStreams,
}
}
func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.MultiplexOptions) (N.Dialer, error) {
func NewClientWithOptions(dialer N.Dialer, options option.MultiplexOptions) (*Client, error) {
if !options.Enabled {
return nil, nil
}
if options.MaxConnections == 0 && options.MaxStreams == 0 {
options.MinStreams = 8
}
protocol, err := ParseProtocol(options.Protocol)
if err != nil {
return nil, err
}
return NewClient(ctx, dialer, protocol, options.MaxConnections, options.MinStreams, options.MaxStreams), nil
}
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 &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil
}
func (c *Client) openStream() (net.Conn, error) {
var (
session abstractSession
stream net.Conn
err error
)
for attempts := 0; attempts < 2; attempts++ {
session, err = c.offer()
if err != nil {
continue
}
stream, err = session.Open()
if err != nil {
continue
}
break
}
if err != nil {
return nil, err
}
return &wrapStream{stream}, nil
}
func (c *Client) offer() (abstractSession, error) {
c.access.Lock()
defer c.access.Unlock()
sessions := make([]abstractSession, 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, abstractSession.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() (abstractSession, error) {
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, Destination)
if err != nil {
return nil, err
}
if vectorisedWriter, isVectorised := bufio.CreateVectorisedWriter(conn); isVectorised {
conn = &vectorisedProtocolConn{protocolConn{Conn: conn, protocol: c.protocol}, vectorisedWriter}
} else {
conn = &protocolConn{Conn: conn, protocol: c.protocol}
}
session, err := c.protocol.newClient(conn)
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 := ReadStreamResponse(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 := StreamRequest{
Network: N.NetworkTCP,
Destination: c.destination,
}
_buffer := buf.StackNewSize(requestLen(request) + len(b))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeStreamRequest(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) NeedAdditionalReadDeadline() bool {
return true
}
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 := ReadStreamResponse(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 := StreamRequest{
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()
EncodeStreamRequest(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) ReadBuffer(buffer *buf.Buffer) (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
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
return
}
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) FrontHeadroom() int {
return 2
}
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
err = c.ReadBuffer(buffer)
return
}
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) NeedAdditionalReadDeadline() bool {
return true
}
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 := ReadStreamResponse(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
}
if destination.IsFqdn() {
addr = destination
} else {
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 := StreamRequest{
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()
EncodeStreamRequest(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
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, 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) FrontHeadroom() int {
return 2 + M.MaxSocksaddrLength
}
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketAddrConn) Upstream() any {
return c.ExtendedConn
return mux.NewClient(mux.Options{
Dialer: dialer,
Protocol: options.Protocol,
MaxConnections: options.MaxConnections,
MinStreams: options.MinStreams,
MaxStreams: options.MaxStreams,
Padding: options.Padding,
})
}

View File

@ -1,240 +1,14 @@
package mux
import (
"encoding/binary"
"io"
"net"
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/sagernet/smux"
"github.com/hashicorp/yamux"
"github.com/sagernet/sing-mux"
)
var Destination = M.Socksaddr{
Fqdn: "sp.mux.sing-box.arpa",
Port: 444,
}
const (
ProtocolSMux Protocol = iota
ProtocolYAMux
type (
Client = mux.Client
)
type Protocol byte
func ParseProtocol(name string) (Protocol, error) {
switch name {
case "", "smux":
return ProtocolSMux, nil
case "yamux":
return ProtocolYAMux, nil
default:
return ProtocolYAMux, E.New("unknown multiplex protocol: ", name)
}
}
func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
switch p {
case ProtocolSMux:
session, err := smux.Server(conn, smuxConfig())
if err != nil {
return nil, err
}
return &smuxSession{session}, nil
case ProtocolYAMux:
return yamux.Server(conn, yaMuxConfig())
default:
panic("unknown protocol")
}
}
func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
switch p {
case ProtocolSMux:
session, err := smux.Client(conn, smuxConfig())
if err != nil {
return nil, err
}
return &smuxSession{session}, nil
case ProtocolYAMux:
return yamux.Client(conn, yaMuxConfig())
default:
panic("unknown protocol")
}
}
func smuxConfig() *smux.Config {
config := smux.DefaultConfig()
config.KeepAliveDisabled = true
return config
}
func yaMuxConfig() *yamux.Config {
config := yamux.DefaultConfig()
config.LogOutput = io.Discard
config.StreamCloseTimeout = C.TCPTimeout
config.StreamOpenTimeout = C.TCPTimeout
return config
}
func (p Protocol) String() string {
switch p {
case ProtocolSMux:
return "smux"
case ProtocolYAMux:
return "yamux"
default:
return "unknown"
}
}
const (
version0 = 0
var (
Destination = mux.Destination
HandleConnection = mux.HandleConnection
)
type Request struct {
Protocol Protocol
}
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)
}
protocol, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
if protocol > byte(ProtocolYAMux) {
return nil, E.New("unsupported protocol: ", protocol)
}
return &Request{Protocol: Protocol(protocol)}, nil
}
func EncodeRequest(buffer *buf.Buffer, request Request) {
buffer.WriteByte(version0)
buffer.WriteByte(byte(request.Protocol))
}
const (
flagUDP = 1
flagAddr = 2
statusSuccess = 0
statusError = 1
)
type StreamRequest struct {
Network string
Destination M.Socksaddr
PacketAddr bool
}
func ReadStreamRequest(reader io.Reader) (*StreamRequest, error) {
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 &StreamRequest{network, destination, udpAddr}, nil
}
func requestLen(request StreamRequest) int {
var rLen int
rLen += 1 // version
rLen += 2 // flags
rLen += M.SocksaddrSerializer.AddrPortLen(request.Destination)
return rLen
}
func EncodeStreamRequest(request StreamRequest, 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(
binary.Write(buffer, binary.BigEndian, flags),
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
)
}
type StreamResponse struct {
Status uint8
Message string
}
func ReadStreamResponse(reader io.Reader) (*StreamResponse, error) {
var response StreamResponse
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
}
type wrapStream struct {
net.Conn
}
func (w *wrapStream) Read(p []byte) (n int, err error) {
n, err = w.Conn.Read(p)
err = wrapError(err)
return
}
func (w *wrapStream) Write(p []byte) (n int, err error) {
n, err = w.Conn.Write(p)
err = wrapError(err)
return
}
func (w *wrapStream) WriteIsThreadUnsafe() {
}
func (w *wrapStream) Upstream() any {
return w.Conn
}
func wrapError(err error) error {
switch err {
case yamux.ErrStreamClosed:
return io.EOF
default:
return err
}
}

View File

@ -1,269 +0,0 @@
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/sagernet/sing/common/task"
)
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
request, err := ReadRequest(conn)
if err != nil {
return err
}
session, err := request.Protocol.newServer(conn)
if err != nil {
return err
}
var group task.Group
group.Append0(func(ctx context.Context) error {
var stream net.Conn
for {
stream, err = session.Accept()
if err != nil {
return err
}
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
}
})
group.Cleanup(func() {
session.Close()
})
return group.Run(ctx)
}
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
stream = &wrapStream{stream}
request, err := ReadStreamRequest(stream)
if err != nil {
logger.ErrorContext(ctx, err)
return
}
metadata.Destination = request.Destination
if request.Network == N.NetworkTCP {
logger.InfoContext(ctx, "inbound multiplex connection to ", metadata.Destination)
hErr := router.RouteConnection(ctx, &ServerConn{ExtendedConn: bufio.NewExtendedConn(stream)}, metadata)
stream.Close()
if hErr != nil {
errorHandler.NewError(ctx, hErr)
}
} else {
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)
stream.Close()
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)
}
func (c *ServerConn) FrontHeadroom() int {
if !c.responseWrite {
return 1
}
return 0
}
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerConn) Upstream() any {
return c.ExtendedConn
}
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)
}
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketConn) Upstream() any {
return c.ExtendedConn
}
func (c *ServerPacketConn) FrontHeadroom() int {
if !c.responseWrite {
return 3
}
return 2
}
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)
}
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketAddrConn) Upstream() any {
return c.ExtendedConn
}
func (c *ServerPacketAddrConn) FrontHeadroom() int {
if !c.responseWrite {
return 3 + M.MaxSocksaddrLength
}
return 2 + M.MaxSocksaddrLength
}

View File

@ -1,91 +0,0 @@
package mux
import (
"io"
"net"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/smux"
)
type abstractSession interface {
Open() (net.Conn, error)
Accept() (net.Conn, error)
NumStreams() int
Close() error
IsClosed() bool
}
var _ abstractSession = (*smuxSession)(nil)
type smuxSession struct {
*smux.Session
}
func (s *smuxSession) Open() (net.Conn, error) {
return s.OpenStream()
}
func (s *smuxSession) Accept() (net.Conn, error) {
return s.AcceptStream()
}
type protocolConn struct {
net.Conn
protocol Protocol
protocolWritten bool
}
func (c *protocolConn) Write(p []byte) (n int, err error) {
if c.protocolWritten {
return c.Conn.Write(p)
}
_buffer := buf.StackNewSize(2 + len(p))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeRequest(buffer, Request{
Protocol: c.protocol,
})
common.Must(common.Error(buffer.Write(p)))
n, err = c.Conn.Write(buffer.Bytes())
if err == nil {
n--
}
c.protocolWritten = true
return n, err
}
func (c *protocolConn) ReadFrom(r io.Reader) (n int64, err error) {
if !c.protocolWritten {
return bufio.ReadFrom0(c, r)
}
return bufio.Copy(c.Conn, r)
}
func (c *protocolConn) Upstream() any {
return c.Conn
}
type vectorisedProtocolConn struct {
protocolConn
N.VectorisedWriter
}
func (c *vectorisedProtocolConn) WriteVectorised(buffers []*buf.Buffer) error {
if c.protocolWritten {
return c.VectorisedWriter.WriteVectorised(buffers)
}
c.protocolWritten = true
_buffer := buf.StackNewSize(2)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeRequest(buffer, Request{
Protocol: c.protocol,
})
return c.VectorisedWriter.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...))
}

View File

@ -10,7 +10,8 @@
"protocol": "smux",
"max_connections": 4,
"min_streams": 4,
"max_streams": 0
"max_streams": 0,
"padding": false
}
```
@ -28,8 +29,9 @@ Multiplex protocol.
|----------|------------------------------------|
| smux | https://github.com/xtaci/smux |
| yamux | https://github.com/hashicorp/yamux |
| h2mux | https://golang.org/x/net/http2 |
SMux is used by default.
h2mux is used by default.
#### max_connections
@ -48,3 +50,12 @@ Conflict with `max_streams`.
Maximum multiplexed streams in a connection before opening a new connection.
Conflict with `max_connections` and `min_streams`.
#### padding
!!! info
Requires sing-box server version 1.3-beta9 or later.
Enable padding.

View File

@ -28,8 +28,9 @@
|-------|------------------------------------|
| smux | https://github.com/xtaci/smux |
| yamux | https://github.com/hashicorp/yamux |
| h2mux | https://golang.org/x/net/http2 |
默认使用 SMux。
默认使用 h2mux。
#### max_connections
@ -47,4 +48,13 @@
在打开新连接之前,连接中的最大多路复用流数量。
`max_connections``min_streams` 冲突。
`max_connections``min_streams` 冲突。
#### padding
!!! info
需要 sing-box 服务器版本 1.3-beta9 或更高。
启用填充。

5
go.mod
View File

@ -13,7 +13,6 @@ require (
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid/v5 v5.0.0
github.com/hashicorp/yamux v0.1.1
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.1.1
@ -25,8 +24,9 @@ require (
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.4
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b
@ -63,6 +63,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect

6
go.sum
View File

@ -111,10 +111,12 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo=
github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss=
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc=
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=

View File

@ -150,4 +150,5 @@ type MultiplexOptions struct {
MaxConnections int `json:"max_connections,omitempty"`
MinStreams int `json:"min_streams,omitempty"`
MaxStreams int `json:"max_streams,omitempty"`
Padding bool `json:"padding,omitempty"`
}

View File

@ -30,7 +30,7 @@ type Shadowsocks struct {
serverAddr M.Socksaddr
plugin sip003.Plugin
uotClient *uot.Client
multiplexDialer N.Dialer
multiplexDialer *mux.Client
}
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
@ -58,7 +58,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
}
uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions)
if !uotOptions.Enabled {
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
outbound.multiplexDialer, err = mux.NewClientWithOptions((*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
if err != nil {
return nil, err
}
@ -127,8 +127,15 @@ func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Shadowsocks) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *Shadowsocks) Close() error {
return common.Close(h.multiplexDialer)
return common.Close(common.PtrOrNil(h.multiplexDialer))
}
var _ N.Dialer = (*shadowsocksDialer)(nil)

View File

@ -27,7 +27,7 @@ type Trojan struct {
dialer N.Dialer
serverAddr M.Socksaddr
key [56]byte
multiplexDialer N.Dialer
multiplexDialer *mux.Client
tlsConfig tls.Config
transport adapter.V2RayClientTransport
}
@ -58,7 +58,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
}
}
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
outbound.multiplexDialer, err = mux.NewClientWithOptions((*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
if err != nil {
return nil, err
}
@ -103,8 +103,15 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Trojan) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *Trojan) Close() error {
return common.Close(h.multiplexDialer, h.transport)
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
}
type trojanDialer Trojan

View File

@ -27,7 +27,7 @@ type VMess struct {
dialer N.Dialer
client *vmess.Client
serverAddr M.Socksaddr
multiplexDialer N.Dialer
multiplexDialer *mux.Client
tlsConfig tls.Config
transport adapter.V2RayClientTransport
packetAddr bool
@ -59,7 +59,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
}
}
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
outbound.multiplexDialer, err = mux.NewClientWithOptions((*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
if err != nil {
return nil, err
}
@ -97,8 +97,15 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return outbound, nil
}
func (h *VMess) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *VMess) Close() error {
return common.Close(h.multiplexDialer, h.transport)
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
}
func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

View File

@ -589,7 +589,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
r.logger.InfoContext(ctx, "inbound multiplex connection")
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
handler := adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r)
return mux.HandleConnection(ctx, handler, r.logger, conn, adapter.UpstreamMetadata(metadata))
case vmess.MuxDestination.Fqdn:
r.logger.InfoContext(ctx, "inbound legacy multiplex connection")
return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r))

View File

@ -9,9 +9,9 @@ replace github.com/sagernet/sing-box => ../
require (
github.com/docker/docker v20.10.18+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
github.com/gofrs/uuid/v5 v5.0.0
github.com/sagernet/sing v0.2.4
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507
github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.2
go.uber.org/goleak v1.2.0
@ -21,7 +21,7 @@ require (
require (
berty.tech/go-libtor v1.0.385 // indirect
github.com/Dreamacro/clash v1.15.0 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/caddyserver/certmagic v0.17.2 // indirect
@ -36,7 +36,6 @@ require (
github.com/go-chi/cors v1.2.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/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
@ -51,7 +50,7 @@ require (
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/mholt/acmez v1.1.0 // indirect
github.com/miekg/dns v1.1.53 // indirect
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/ooni/go-libtor v1.1.7 // indirect
@ -71,15 +70,15 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 // indirect
github.com/sagernet/sing-shadowtls v0.1.0 // indirect
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 // indirect
github.com/sagernet/sing-vmess v0.1.3 // indirect
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc // indirect
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b // indirect
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b // indirect
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
@ -93,15 +92,12 @@ require (
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)
//replace github.com/sagernet/sing => ../../sing

View File

@ -3,8 +3,8 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
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.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
@ -43,8 +43,6 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
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/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -84,8 +82,8 @@ github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
@ -128,18 +126,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31 h1:qgq8jeY/rbnY9NwYXByO//AP0ByIxnsKUxQx1tOB3W0=
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo=
github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b h1:9NsciSJGwzdkXwVvT2c2g+RvkTVkANeBLr2l+soJ7LM=
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b/go.mod h1:DD7Ce2Gt0GFc6I/1+Uw4D/aUlBsGqrQsC52CMK/V818=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
@ -148,9 +146,8 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
@ -158,7 +155,6 @@ github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzy
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -218,13 +214,10 @@ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -243,13 +236,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
@ -272,8 +264,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@ -18,18 +18,40 @@ var muxProtocols = []mux.Protocol{
}
func TestVMessSMux(t *testing.T) {
testVMessMux(t, mux.ProtocolSMux.String())
testVMessMux(t, option.MultiplexOptions{
Enabled: true,
Protocol: mux.ProtocolSMux.String(),
})
}
func TestShadowsocksMux(t *testing.T) {
for _, protocol := range muxProtocols {
t.Run(protocol.String(), func(t *testing.T) {
testShadowsocksMux(t, protocol.String())
testShadowsocksMux(t, option.MultiplexOptions{
Enabled: true,
Protocol: protocol.String(),
})
})
}
}
func testShadowsocksMux(t *testing.T, protocol string) {
func TestShadowsockH2Mux(t *testing.T) {
testShadowsocksMux(t, option.MultiplexOptions{
Enabled: true,
Protocol: mux.ProtocolH2Mux.String(),
Padding: true,
})
}
func TestShadowsockSMuxPadding(t *testing.T) {
testShadowsocksMux(t, option.MultiplexOptions{
Enabled: true,
Protocol: mux.ProtocolSMux.String(),
Padding: true,
})
}
func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) {
method := shadowaead_2022.List[0]
password := mkBase64(t, 16)
startInstance(t, option.Options{
@ -68,12 +90,9 @@ func testShadowsocksMux(t *testing.T, protocol string) {
Server: "127.0.0.1",
ServerPort: serverPort,
},
Method: method,
Password: password,
MultiplexOptions: &option.MultiplexOptions{
Enabled: true,
Protocol: protocol,
},
Method: method,
Password: password,
MultiplexOptions: &options,
},
},
},
@ -91,7 +110,7 @@ func testShadowsocksMux(t *testing.T, protocol string) {
testSuit(t, clientPort, testPort)
}
func testVMessMux(t *testing.T, protocol string) {
func testVMessMux(t *testing.T, options option.MultiplexOptions) {
user, _ := uuid.NewV4()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
@ -132,12 +151,9 @@ func testVMessMux(t *testing.T, protocol string) {
Server: "127.0.0.1",
ServerPort: serverPort,
},
Security: "auto",
UUID: user.String(),
Multiplex: &option.MultiplexOptions{
Enabled: true,
Protocol: protocol,
},
Security: "auto",
UUID: user.String(),
Multiplex: &options,
},
},
},

View File

@ -19,7 +19,7 @@ func TestUDPNatClose(t *testing.T) {
defer cancel()
connCtx, connCancel := common.ContextWithCancelCause(context.Background())
defer connCancel(net.ErrClosed)
service := udpnat.New[int](ctx, 1, &testUDPNatCloseHandler{connCancel})
service := udpnat.New[int](1, &testUDPNatCloseHandler{connCancel})
service.NewPacket(ctx, 0, buf.As([]byte("Hello")), M.Metadata{}, func(natConn N.PacketConn) N.PacketWriter {
return &testPacketWriter{}
})

View File

@ -199,13 +199,13 @@ func (c *HTTP2Conn) NeedAdditionalReadDeadline() bool {
type ServerHTTPConn struct {
HTTP2Conn
flusher http.Flusher
Flusher http.Flusher
}
func (c *ServerHTTPConn) Write(b []byte) (n int, err error) {
n, err = c.writer.Write(b)
if err == nil {
c.flusher.Flush()
c.Flusher.Flush()
}
return
}
@ -246,6 +246,11 @@ func (w *HTTP2ConnWrapper) CloseWrapper() {
w.closed = true
}
func (w *HTTP2ConnWrapper) Close() error {
w.CloseWrapper()
return w.ExtendedConn.Close()
}
func (w *HTTP2ConnWrapper) Upstream() any {
return w.ExtendedConn
}