github.com/sagernet/sing-box@v1.9.0-rc.20/outbound/hysteria.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/humanize" 13 "github.com/sagernet/sing-box/common/tls" 14 C "github.com/sagernet/sing-box/constant" 15 "github.com/sagernet/sing-box/log" 16 "github.com/sagernet/sing-box/option" 17 "github.com/sagernet/sing-quic/hysteria" 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 Hysteria struct { 31 myOutboundAdapter 32 client *hysteria.Client 33 } 34 35 func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (*Hysteria, 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 outboundDialer, err := dialer.New(router, options.DialerOptions) 45 if err != nil { 46 return nil, err 47 } 48 networkList := options.Network.Build() 49 var password string 50 if options.AuthString != "" { 51 password = options.AuthString 52 } else { 53 password = string(options.Auth) 54 } 55 var sendBps, receiveBps uint64 56 if len(options.Up) > 0 { 57 sendBps, err = humanize.ParseBytes(options.Up) 58 if err != nil { 59 return nil, E.Cause(err, "invalid up speed format: ", options.Up) 60 } 61 } else { 62 sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps 63 } 64 if len(options.Down) > 0 { 65 receiveBps, err = humanize.ParseBytes(options.Down) 66 if receiveBps == 0 { 67 return nil, E.New("invalid down speed format: ", options.Down) 68 } 69 } else { 70 receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps 71 } 72 client, err := hysteria.NewClient(hysteria.ClientOptions{ 73 Context: ctx, 74 Dialer: outboundDialer, 75 Logger: logger, 76 ServerAddress: options.ServerOptions.Build(), 77 SendBPS: sendBps, 78 ReceiveBPS: receiveBps, 79 XPlusPassword: options.Obfs, 80 Password: password, 81 TLSConfig: tlsConfig, 82 UDPDisabled: !common.Contains(networkList, N.NetworkUDP), 83 84 ConnReceiveWindow: options.ReceiveWindowConn, 85 StreamReceiveWindow: options.ReceiveWindow, 86 DisableMTUDiscovery: options.DisableMTUDiscovery, 87 }) 88 if err != nil { 89 return nil, err 90 } 91 return &Hysteria{ 92 myOutboundAdapter: myOutboundAdapter{ 93 protocol: C.TypeHysteria, 94 network: networkList, 95 router: router, 96 logger: logger, 97 tag: tag, 98 dependencies: withDialerDependency(options.DialerOptions), 99 }, 100 client: client, 101 }, nil 102 } 103 104 func (h *Hysteria) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { 105 switch N.NetworkName(network) { 106 case N.NetworkTCP: 107 h.logger.InfoContext(ctx, "outbound connection to ", destination) 108 return h.client.DialConn(ctx, destination) 109 case N.NetworkUDP: 110 conn, err := h.ListenPacket(ctx, destination) 111 if err != nil { 112 return nil, err 113 } 114 return bufio.NewBindPacketConn(conn, destination), nil 115 default: 116 return nil, E.New("unsupported network: ", network) 117 } 118 } 119 120 func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { 121 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 122 return h.client.ListenPacket(ctx, destination) 123 } 124 125 func (h *Hysteria) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { 126 return NewConnection(ctx, h, conn, metadata) 127 } 128 129 func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { 130 return NewPacketConnection(ctx, h, conn, metadata) 131 } 132 133 func (h *Hysteria) InterfaceUpdated() error { 134 return h.client.CloseWithError(E.New("network changed")) 135 } 136 137 func (h *Hysteria) Close() error { 138 return h.client.CloseWithError(os.ErrClosed) 139 }