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