github.com/sagernet/sing-box@v1.9.0-rc.20/inbound/hysteria2.go (about) 1 //go:build with_quic 2 3 package inbound 4 5 import ( 6 "context" 7 "net" 8 "net/http" 9 "net/http/httputil" 10 "net/url" 11 "time" 12 13 "github.com/sagernet/sing-box/adapter" 14 "github.com/sagernet/sing-box/common/tls" 15 C "github.com/sagernet/sing-box/constant" 16 "github.com/sagernet/sing-box/log" 17 "github.com/sagernet/sing-box/option" 18 "github.com/sagernet/sing-quic/hysteria" 19 "github.com/sagernet/sing-quic/hysteria2" 20 "github.com/sagernet/sing/common" 21 "github.com/sagernet/sing/common/auth" 22 E "github.com/sagernet/sing/common/exceptions" 23 N "github.com/sagernet/sing/common/network" 24 ) 25 26 var _ adapter.Inbound = (*Hysteria2)(nil) 27 28 type Hysteria2 struct { 29 myInboundAdapter 30 tlsConfig tls.ServerConfig 31 service *hysteria2.Service[int] 32 userNameList []string 33 } 34 35 func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, error) { 36 options.UDPFragmentDefault = true 37 if options.TLS == nil || !options.TLS.Enabled { 38 return nil, C.ErrTLSRequired 39 } 40 tlsConfig, err := tls.NewServer(ctx, logger, 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 var masqueradeHandler http.Handler 57 if options.Masquerade != "" { 58 masqueradeURL, err := url.Parse(options.Masquerade) 59 if err != nil { 60 return nil, E.Cause(err, "parse masquerade URL") 61 } 62 switch masqueradeURL.Scheme { 63 case "file": 64 masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path)) 65 case "http", "https": 66 masqueradeHandler = &httputil.ReverseProxy{ 67 Rewrite: func(r *httputil.ProxyRequest) { 68 r.SetURL(masqueradeURL) 69 r.Out.Host = r.In.Host 70 }, 71 ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { 72 w.WriteHeader(http.StatusBadGateway) 73 }, 74 } 75 default: 76 return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme) 77 } 78 } 79 inbound := &Hysteria2{ 80 myInboundAdapter: myInboundAdapter{ 81 protocol: C.TypeHysteria2, 82 network: []string{N.NetworkUDP}, 83 ctx: ctx, 84 router: router, 85 logger: logger, 86 tag: tag, 87 listenOptions: options.ListenOptions, 88 }, 89 tlsConfig: tlsConfig, 90 } 91 var udpTimeout time.Duration 92 if options.UDPTimeout != 0 { 93 udpTimeout = time.Duration(options.UDPTimeout) 94 } else { 95 udpTimeout = C.UDPTimeout 96 } 97 service, err := hysteria2.NewService[int](hysteria2.ServiceOptions{ 98 Context: ctx, 99 Logger: logger, 100 BrutalDebug: options.BrutalDebug, 101 SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps), 102 ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps), 103 SalamanderPassword: salamanderPassword, 104 TLSConfig: tlsConfig, 105 IgnoreClientBandwidth: options.IgnoreClientBandwidth, 106 UDPTimeout: udpTimeout, 107 Handler: adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil), 108 MasqueradeHandler: masqueradeHandler, 109 }) 110 if err != nil { 111 return nil, err 112 } 113 userList := make([]int, 0, len(options.Users)) 114 userNameList := make([]string, 0, len(options.Users)) 115 userPasswordList := make([]string, 0, len(options.Users)) 116 for index, user := range options.Users { 117 userList = append(userList, index) 118 userNameList = append(userNameList, user.Name) 119 userPasswordList = append(userPasswordList, user.Password) 120 } 121 service.UpdateUsers(userList, userPasswordList) 122 inbound.service = service 123 inbound.userNameList = userNameList 124 return inbound, nil 125 } 126 127 func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { 128 ctx = log.ContextWithNewID(ctx) 129 metadata = h.createMetadata(conn, metadata) 130 h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) 131 userID, _ := auth.UserFromContext[int](ctx) 132 if userName := h.userNameList[userID]; userName != "" { 133 metadata.User = userName 134 h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination) 135 } else { 136 h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) 137 } 138 return h.router.RouteConnection(ctx, conn, metadata) 139 } 140 141 func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { 142 ctx = log.ContextWithNewID(ctx) 143 metadata = h.createPacketMetadata(conn, metadata) 144 h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) 145 userID, _ := auth.UserFromContext[int](ctx) 146 if userName := h.userNameList[userID]; userName != "" { 147 metadata.User = userName 148 h.logger.InfoContext(ctx, "[", userName, "] inbound packet connection to ", metadata.Destination) 149 } else { 150 h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) 151 } 152 return h.router.RoutePacketConnection(ctx, conn, metadata) 153 } 154 155 func (h *Hysteria2) Start() error { 156 if h.tlsConfig != nil { 157 err := h.tlsConfig.Start() 158 if err != nil { 159 return err 160 } 161 } 162 packetConn, err := h.myInboundAdapter.ListenUDP() 163 if err != nil { 164 return err 165 } 166 return h.service.Start(packetConn) 167 } 168 169 func (h *Hysteria2) Close() error { 170 return common.Close( 171 &h.myInboundAdapter, 172 h.tlsConfig, 173 common.PtrOrNil(h.service), 174 ) 175 }