github.com/sagernet/sing-box@v1.9.0-rc.20/outbound/vless.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-box/transport/vless"
    16  	"github.com/sagernet/sing-vmess/packetaddr"
    17  	"github.com/sagernet/sing/common"
    18  	"github.com/sagernet/sing/common/bufio"
    19  	E "github.com/sagernet/sing/common/exceptions"
    20  	M "github.com/sagernet/sing/common/metadata"
    21  	N "github.com/sagernet/sing/common/network"
    22  )
    23  
    24  var _ adapter.Outbound = (*VLESS)(nil)
    25  
    26  type VLESS struct {
    27  	myOutboundAdapter
    28  	dialer          N.Dialer
    29  	client          *vless.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 NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) {
    39  	outboundDialer, err := dialer.New(router, options.DialerOptions)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	outbound := &VLESS{
    44  		myOutboundAdapter: myOutboundAdapter{
    45  			protocol:     C.TypeVLESS,
    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  	if options.PacketEncoding == nil {
    68  		outbound.xudp = true
    69  	} else {
    70  		switch *options.PacketEncoding {
    71  		case "":
    72  		case "packetaddr":
    73  			outbound.packetAddr = true
    74  		case "xudp":
    75  			outbound.xudp = true
    76  		default:
    77  			return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
    78  		}
    79  	}
    80  	outbound.client, err = vless.NewClient(options.UUID, options.Flow, logger)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex))
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	return outbound, nil
    89  }
    90  
    91  func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
    92  	if h.multiplexDialer == nil {
    93  		switch N.NetworkName(network) {
    94  		case N.NetworkTCP:
    95  			h.logger.InfoContext(ctx, "outbound connection to ", destination)
    96  		case N.NetworkUDP:
    97  			h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
    98  		}
    99  		return (*vlessDialer)(h).DialContext(ctx, network, destination)
   100  	} else {
   101  		switch N.NetworkName(network) {
   102  		case N.NetworkTCP:
   103  			h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
   104  		case N.NetworkUDP:
   105  			h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
   106  		}
   107  		return h.multiplexDialer.DialContext(ctx, network, destination)
   108  	}
   109  }
   110  
   111  func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   112  	if h.multiplexDialer == nil {
   113  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   114  		return (*vlessDialer)(h).ListenPacket(ctx, destination)
   115  	} else {
   116  		h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
   117  		return h.multiplexDialer.ListenPacket(ctx, destination)
   118  	}
   119  }
   120  
   121  func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   122  	return NewConnection(ctx, h, conn, metadata)
   123  }
   124  
   125  func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   126  	return NewPacketConnection(ctx, h, conn, metadata)
   127  }
   128  
   129  func (h *VLESS) InterfaceUpdated() {
   130  	if h.multiplexDialer != nil {
   131  		h.multiplexDialer.Reset()
   132  	}
   133  	return
   134  }
   135  
   136  func (h *VLESS) Close() error {
   137  	return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
   138  }
   139  
   140  type vlessDialer VLESS
   141  
   142  func (h *vlessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
   143  	ctx, metadata := adapter.AppendContext(ctx)
   144  	metadata.Outbound = h.tag
   145  	metadata.Destination = destination
   146  	var conn net.Conn
   147  	var err error
   148  	if h.transport != nil {
   149  		conn, err = h.transport.DialContext(ctx)
   150  	} else {
   151  		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
   152  		if err == nil && h.tlsConfig != nil {
   153  			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
   154  		}
   155  	}
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	switch N.NetworkName(network) {
   160  	case N.NetworkTCP:
   161  		h.logger.InfoContext(ctx, "outbound connection to ", destination)
   162  		return h.client.DialEarlyConn(conn, destination)
   163  	case N.NetworkUDP:
   164  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   165  		if h.xudp {
   166  			return h.client.DialEarlyXUDPPacketConn(conn, destination)
   167  		} else if h.packetAddr {
   168  			if destination.IsFqdn() {
   169  				return nil, E.New("packetaddr: domain destination is not supported")
   170  			}
   171  			packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
   172  			if err != nil {
   173  				return nil, err
   174  			}
   175  			return bufio.NewBindPacketConn(packetaddr.NewConn(packetConn, destination), destination), nil
   176  		} else {
   177  			return h.client.DialEarlyPacketConn(conn, destination)
   178  		}
   179  	default:
   180  		return nil, E.Extend(N.ErrUnknownNetwork, network)
   181  	}
   182  }
   183  
   184  func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   185  	h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   186  	ctx, metadata := adapter.AppendContext(ctx)
   187  	metadata.Outbound = h.tag
   188  	metadata.Destination = destination
   189  	var conn net.Conn
   190  	var err error
   191  	if h.transport != nil {
   192  		conn, err = h.transport.DialContext(ctx)
   193  	} else {
   194  		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
   195  		if err == nil && h.tlsConfig != nil {
   196  			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
   197  		}
   198  	}
   199  	if err != nil {
   200  		common.Close(conn)
   201  		return nil, err
   202  	}
   203  	if h.xudp {
   204  		return h.client.DialEarlyXUDPPacketConn(conn, destination)
   205  	} else if h.packetAddr {
   206  		if destination.IsFqdn() {
   207  			return nil, E.New("packetaddr: domain destination is not supported")
   208  		}
   209  		conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  		return packetaddr.NewConn(conn, destination), nil
   214  	} else {
   215  		return h.client.DialEarlyPacketConn(conn, destination)
   216  	}
   217  }