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 }