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

     1  //go:build with_quic
     2  
     3  package outbound
     4  
     5  import (
     6  	"context"
     7  	"net"
     8  	"os"
     9  
    10  	"github.com/sagernet/sing-box/adapter"
    11  	"github.com/sagernet/sing-box/common/dialer"
    12  	"github.com/sagernet/sing-box/common/tls"
    13  	C "github.com/sagernet/sing-box/constant"
    14  	"github.com/sagernet/sing-box/log"
    15  	"github.com/sagernet/sing-box/option"
    16  	"github.com/sagernet/sing-quic/hysteria"
    17  	"github.com/sagernet/sing-quic/hysteria2"
    18  	"github.com/sagernet/sing/common"
    19  	"github.com/sagernet/sing/common/bufio"
    20  	E "github.com/sagernet/sing/common/exceptions"
    21  	M "github.com/sagernet/sing/common/metadata"
    22  	N "github.com/sagernet/sing/common/network"
    23  )
    24  
    25  var (
    26  	_ adapter.Outbound                = (*TUIC)(nil)
    27  	_ adapter.InterfaceUpdateListener = (*TUIC)(nil)
    28  )
    29  
    30  type Hysteria2 struct {
    31  	myOutboundAdapter
    32  	client *hysteria2.Client
    33  }
    34  
    35  func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (*Hysteria2, error) {
    36  	options.UDPFragmentDefault = true
    37  	if options.TLS == nil || !options.TLS.Enabled {
    38  		return nil, C.ErrTLSRequired
    39  	}
    40  	tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	var salamanderPassword string
    45  	if options.Obfs != nil {
    46  		if options.Obfs.Password == "" {
    47  			return nil, E.New("missing obfs password")
    48  		}
    49  		switch options.Obfs.Type {
    50  		case hysteria2.ObfsTypeSalamander:
    51  			salamanderPassword = options.Obfs.Password
    52  		default:
    53  			return nil, E.New("unknown obfs type: ", options.Obfs.Type)
    54  		}
    55  	}
    56  	outboundDialer, err := dialer.New(router, options.DialerOptions)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	networkList := options.Network.Build()
    61  	client, err := hysteria2.NewClient(hysteria2.ClientOptions{
    62  		Context:            ctx,
    63  		Dialer:             outboundDialer,
    64  		Logger:             logger,
    65  		BrutalDebug:        options.BrutalDebug,
    66  		ServerAddress:      options.ServerOptions.Build(),
    67  		SendBPS:            uint64(options.UpMbps * hysteria.MbpsToBps),
    68  		ReceiveBPS:         uint64(options.DownMbps * hysteria.MbpsToBps),
    69  		SalamanderPassword: salamanderPassword,
    70  		Password:           options.Password,
    71  		TLSConfig:          tlsConfig,
    72  		UDPDisabled:        !common.Contains(networkList, N.NetworkUDP),
    73  	})
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return &Hysteria2{
    78  		myOutboundAdapter: myOutboundAdapter{
    79  			protocol:     C.TypeHysteria2,
    80  			network:      networkList,
    81  			router:       router,
    82  			logger:       logger,
    83  			tag:          tag,
    84  			dependencies: withDialerDependency(options.DialerOptions),
    85  		},
    86  		client: client,
    87  	}, nil
    88  }
    89  
    90  func (h *Hysteria2) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
    91  	switch N.NetworkName(network) {
    92  	case N.NetworkTCP:
    93  		h.logger.InfoContext(ctx, "outbound connection to ", destination)
    94  		return h.client.DialConn(ctx, destination)
    95  	case N.NetworkUDP:
    96  		conn, err := h.ListenPacket(ctx, destination)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  		return bufio.NewBindPacketConn(conn, destination), nil
   101  	default:
   102  		return nil, E.New("unsupported network: ", network)
   103  	}
   104  }
   105  
   106  func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   107  	h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   108  	return h.client.ListenPacket(ctx)
   109  }
   110  
   111  func (h *Hysteria2) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   112  	return NewConnection(ctx, h, conn, metadata)
   113  }
   114  
   115  func (h *Hysteria2) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   116  	return NewPacketConnection(ctx, h, conn, metadata)
   117  }
   118  
   119  func (h *Hysteria2) InterfaceUpdated() error {
   120  	return h.client.CloseWithError(E.New("network changed"))
   121  }
   122  
   123  func (h *Hysteria2) Close() error {
   124  	return h.client.CloseWithError(os.ErrClosed)
   125  }