github.com/sagernet/sing-box@v1.9.0-rc.20/outbound/vmess.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  
     7  	"github.com/sagernet/sing-box/adapter"
     8  	"github.com/sagernet/sing-box/common/dialer"
     9  	"github.com/sagernet/sing-box/common/mux"
    10  	"github.com/sagernet/sing-box/common/tls"
    11  	C "github.com/sagernet/sing-box/constant"
    12  	"github.com/sagernet/sing-box/log"
    13  	"github.com/sagernet/sing-box/option"
    14  	"github.com/sagernet/sing-box/transport/v2ray"
    15  	"github.com/sagernet/sing-vmess"
    16  	"github.com/sagernet/sing-vmess/packetaddr"
    17  	"github.com/sagernet/sing/common"
    18  	E "github.com/sagernet/sing/common/exceptions"
    19  	M "github.com/sagernet/sing/common/metadata"
    20  	N "github.com/sagernet/sing/common/network"
    21  	"github.com/sagernet/sing/common/ntp"
    22  )
    23  
    24  var _ adapter.Outbound = (*VMess)(nil)
    25  
    26  type VMess struct {
    27  	myOutboundAdapter
    28  	dialer          N.Dialer
    29  	client          *vmess.Client
    30  	serverAddr      M.Socksaddr
    31  	multiplexDialer *mux.Client
    32  	tlsConfig       tls.Config
    33  	transport       adapter.V2RayClientTransport
    34  	packetAddr      bool
    35  	xudp            bool
    36  }
    37  
    38  func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
    39  	outboundDialer, err := dialer.New(router, options.DialerOptions)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	outbound := &VMess{
    44  		myOutboundAdapter: myOutboundAdapter{
    45  			protocol:     C.TypeVMess,
    46  			network:      options.Network.Build(),
    47  			router:       router,
    48  			logger:       logger,
    49  			tag:          tag,
    50  			dependencies: withDialerDependency(options.DialerOptions),
    51  		},
    52  		dialer:     outboundDialer,
    53  		serverAddr: options.ServerOptions.Build(),
    54  	}
    55  	if options.TLS != nil {
    56  		outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  	}
    61  	if options.Transport != nil {
    62  		outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
    63  		if err != nil {
    64  			return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
    65  		}
    66  	}
    67  	outbound.multiplexDialer, err = mux.NewClientWithOptions((*vmessDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex))
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	switch options.PacketEncoding {
    72  	case "":
    73  	case "packetaddr":
    74  		outbound.packetAddr = true
    75  	case "xudp":
    76  		outbound.xudp = true
    77  	default:
    78  		return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
    79  	}
    80  	var clientOptions []vmess.ClientOption
    81  	if timeFunc := ntp.TimeFuncFromContext(ctx); timeFunc != nil {
    82  		clientOptions = append(clientOptions, vmess.ClientWithTimeFunc(timeFunc))
    83  	}
    84  	if options.GlobalPadding {
    85  		clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
    86  	}
    87  	if options.AuthenticatedLength {
    88  		clientOptions = append(clientOptions, vmess.ClientWithAuthenticatedLength())
    89  	}
    90  	security := options.Security
    91  	if security == "" {
    92  		security = "auto"
    93  	}
    94  	if security == "auto" && outbound.tlsConfig != nil {
    95  		security = "zero"
    96  	}
    97  	client, err := vmess.NewClient(options.UUID, security, options.AlterId, clientOptions...)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	outbound.client = client
   102  	return outbound, nil
   103  }
   104  
   105  func (h *VMess) InterfaceUpdated() {
   106  	if h.multiplexDialer != nil {
   107  		h.multiplexDialer.Reset()
   108  	}
   109  	return
   110  }
   111  
   112  func (h *VMess) Close() error {
   113  	return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
   114  }
   115  
   116  func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
   117  	if h.multiplexDialer == nil {
   118  		switch N.NetworkName(network) {
   119  		case N.NetworkTCP:
   120  			h.logger.InfoContext(ctx, "outbound connection to ", destination)
   121  		case N.NetworkUDP:
   122  			h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   123  		}
   124  		return (*vmessDialer)(h).DialContext(ctx, network, destination)
   125  	} else {
   126  		switch N.NetworkName(network) {
   127  		case N.NetworkTCP:
   128  			h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
   129  		case N.NetworkUDP:
   130  			h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
   131  		}
   132  		return h.multiplexDialer.DialContext(ctx, network, destination)
   133  	}
   134  }
   135  
   136  func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   137  	if h.multiplexDialer == nil {
   138  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   139  		return (*vmessDialer)(h).ListenPacket(ctx, destination)
   140  	} else {
   141  		h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
   142  		return h.multiplexDialer.ListenPacket(ctx, destination)
   143  	}
   144  }
   145  
   146  func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   147  	return NewConnection(ctx, h, conn, metadata)
   148  }
   149  
   150  func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   151  	return NewPacketConnection(ctx, h, conn, metadata)
   152  }
   153  
   154  type vmessDialer VMess
   155  
   156  func (h *vmessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
   157  	ctx, metadata := adapter.AppendContext(ctx)
   158  	metadata.Outbound = h.tag
   159  	metadata.Destination = destination
   160  	var conn net.Conn
   161  	var err error
   162  	if h.transport != nil {
   163  		conn, err = h.transport.DialContext(ctx)
   164  	} else {
   165  		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
   166  		if err == nil && h.tlsConfig != nil {
   167  			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
   168  		}
   169  	}
   170  	if err != nil {
   171  		common.Close(conn)
   172  		return nil, err
   173  	}
   174  	switch N.NetworkName(network) {
   175  	case N.NetworkTCP:
   176  		return h.client.DialEarlyConn(conn, destination), nil
   177  	case N.NetworkUDP:
   178  		return h.client.DialEarlyPacketConn(conn, destination), nil
   179  	default:
   180  		return nil, E.Extend(N.ErrUnknownNetwork, network)
   181  	}
   182  }
   183  
   184  func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   185  	ctx, metadata := adapter.AppendContext(ctx)
   186  	metadata.Outbound = h.tag
   187  	metadata.Destination = destination
   188  	var conn net.Conn
   189  	var err error
   190  	if h.transport != nil {
   191  		conn, err = h.transport.DialContext(ctx)
   192  	} else {
   193  		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
   194  		if err == nil && h.tlsConfig != nil {
   195  			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
   196  		}
   197  	}
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	if h.packetAddr {
   202  		if destination.IsFqdn() {
   203  			return nil, E.New("packetaddr: domain destination is not supported")
   204  		}
   205  		return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
   206  	} else if h.xudp {
   207  		return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
   208  	} else {
   209  		return h.client.DialEarlyPacketConn(conn, destination), nil
   210  	}
   211  }