Update hysteria2 realm

This commit is contained in:
世界 2026-05-11 16:24:34 +08:00
parent 34c90e6f2e
commit 2e1a7a53ae
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
11 changed files with 109 additions and 43 deletions

View file

@ -42,16 +42,16 @@ type DNSQueryOptions struct {
ClientSubnet netip.Prefix
}
func DNSQueryOptionsFrom(ctx context.Context, options *option.DomainResolveOptions) (*DNSQueryOptions, error) {
if options == nil {
return &DNSQueryOptions{}, nil
func DNSQueryOptionsFrom(ctx context.Context, options *option.DomainResolveOptions) (DNSQueryOptions, error) {
if options == nil || options.Server == "" {
return DNSQueryOptions{}, nil
}
transportManager := service.FromContext[DNSTransportManager](ctx)
transport, loaded := transportManager.Transport(options.Server)
if !loaded {
return nil, E.New("domain resolver not found: " + options.Server)
return DNSQueryOptions{}, E.New("domain resolver not found: " + options.Server)
}
return &DNSQueryOptions{
return DNSQueryOptions{
Transport: transport,
Strategy: C.DomainStrategy(options.Strategy),
DisableCache: options.DisableCache,

View file

@ -46,6 +46,7 @@ icon: material/alert-decagram
"token": "",
"realm_id": "",
"stun_servers": [],
"stun_domain_resolver": "", // or {}
"http_client": {}
}
}
@ -209,7 +210,15 @@ Outbounds must use the same `realm_id` to find this server.
List of STUN servers (`host` or `host:port`) used to discover public addresses.
Port defaults to `3478`.
#### realm.stun_domain_resolver
Set domain resolver to use for resolving STUN server domain names.
This option uses the same format as the [route DNS rule action](/configuration/dns/rule_action/#route) without the `action` field.
Setting this option directly to a string is equivalent to setting `server` of this options.
If empty, the default domain resolver is used.
#### realm.http_client

View file

@ -46,6 +46,7 @@ icon: material/alert-decagram
"token": "",
"realm_id": "",
"stun_servers": [],
"stun_domain_resolver": "", // 或 {}
"http_client": {}
}
}
@ -206,7 +207,15 @@ Realm 上的槽位标识符。
用于发现公网地址的 STUN 服务器列表(`host``host:port`)。
端口默认为 `3478`
#### realm.stun_domain_resolver
用于解析 STUN 服务器域名的域名解析器。
此选项的格式与 [路由 DNS 规则动作](/zh/configuration/dns/rule_action/#route) 相同,但不包含 `action` 字段。
若直接将此选项设置为字符串,则等同于设置该选项的 `server` 字段。
如果为空,则使用默认域名解析器。
#### realm.http_client

View file

@ -191,7 +191,7 @@ The same slot identifier the target Hysteria2 server registered.
List of STUN servers (`host` or `host:port`) used to discover this client's public addresses.
Port defaults to `3478`.
Domain names are resolved using [`domain_resolver`](/configuration/shared/dial/#domain_resolver) from Dial Fields.
#### realm.http_client

View file

@ -189,7 +189,7 @@ Realm 的 Bearer 令牌,需与 realm 上配置的 `users[].token` 之一匹配
用于发现本客户端公网地址的 STUN 服务器列表(`host``host:port`)。
端口默认为 `3478`
域名通过 [拨号字段](/zh/configuration/shared/dial/) 中的 [`domain_resolver`](/zh/configuration/shared/dial/#domain_resolver) 解析
#### realm.http_client

2
go.mod
View file

@ -40,7 +40,7 @@ require (
github.com/sagernet/sing v0.8.10-0.20260428084616-2bc976d03e39
github.com/sagernet/sing-cloudflared v0.1.0
github.com/sagernet/sing-mux v0.3.4
github.com/sagernet/sing-quic v0.6.2-0.20260510042401-4055a2bb2381
github.com/sagernet/sing-quic v0.6.2-0.20260511084842-9deddf006024
github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11

4
go.sum
View file

@ -248,8 +248,8 @@ github.com/sagernet/sing-cloudflared v0.1.0 h1:to+2fcCx8zu4X/DirRw9Ihc+FrEZ7oEyI
github.com/sagernet/sing-cloudflared v0.1.0/go.mod h1:bH2NKX+NpDTY1Zkxfboxw6MXB/ZywaNLmrDJYgKMJ2Y=
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
github.com/sagernet/sing-quic v0.6.2-0.20260510042401-4055a2bb2381 h1:bwja5I7Sgr4Z1nyCLlN6T5eBhhSE/ilYZmkEFIoPAhQ=
github.com/sagernet/sing-quic v0.6.2-0.20260510042401-4055a2bb2381/go.mod h1:+oqD54aHel4ALKkp1hVXWCgLU/EjLojvm6AUzDfvj0I=
github.com/sagernet/sing-quic v0.6.2-0.20260511084842-9deddf006024 h1:P1iab6udg2I2igIrn+mNKpPZNcuejSqno3jwJ/94upw=
github.com/sagernet/sing-quic v0.6.2-0.20260511084842-9deddf006024/go.mod h1:+oqD54aHel4ALKkp1hVXWCgLU/EjLojvm6AUzDfvj0I=
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=

View file

@ -19,10 +19,10 @@ type Hysteria2InboundOptions struct {
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
InboundTLSOptionsContainer
QUICOptions
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
BBRProfile string `json:"bbr_profile,omitempty"`
BrutalDebug bool `json:"brutal_debug,omitempty"`
Realm *Hysteria2Realm `json:"realm,omitempty"`
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
BBRProfile string `json:"bbr_profile,omitempty"`
BrutalDebug bool `json:"brutal_debug,omitempty"`
Realm *Hysteria2InboundRealm `json:"realm,omitempty"`
}
type Hysteria2Realm struct {
@ -33,6 +33,11 @@ type Hysteria2Realm struct {
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
}
type Hysteria2InboundRealm struct {
Hysteria2Realm
STUNDomainResolver *DomainResolveOptions `json:"stun_domain_resolver,omitempty"`
}
type Hysteria2Obfs struct {
Type string `json:"type,omitempty"`
Password string `json:"password,omitempty"`

View file

@ -5,6 +5,7 @@ import (
"net"
"net/http"
"net/http/httputil"
"net/netip"
"net/url"
"time"
@ -18,11 +19,13 @@ import (
qtls "github.com/sagernet/sing-quic"
"github.com/sagernet/sing-quic/hysteria"
"github.com/sagernet/sing-quic/hysteria2"
"github.com/sagernet/sing-quic/hysteria2/realm"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
)
func RegisterInbound(registry *inbound.Registry) {
@ -114,9 +117,35 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
} else {
udpTimeout = C.UDPTimeout
}
realmOptions, err := buildRealmOptions(ctx, logger, options.Realm)
if err != nil {
return nil, err
var realmOptions *realm.Options
if options.Realm != nil {
queryOptions, err := adapter.DNSQueryOptionsFrom(ctx, options.Realm.STUNDomainResolver)
if err != nil {
return nil, err
}
httpClientTransport, err := service.FromContext[adapter.HTTPClientManager](ctx).ResolveTransport(ctx, logger, common.PtrValueOrDefault(options.Realm.HTTPClient))
if err != nil {
return nil, E.Cause(err, "create realm http client")
}
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
realmOptions = &realm.Options{
ServerURL: options.Realm.ServerURL,
Token: options.Realm.Token,
RealmID: options.Realm.RealmID,
STUNServers: options.Realm.STUNServers,
HTTPClient: &http.Client{Transport: httpClientTransport},
Resolver: func(ctx context.Context, host string, ipv4, ipv6 bool) ([]netip.Addr, error) {
dnsOptions := queryOptions
switch {
case ipv4 && !ipv6:
dnsOptions.Strategy = C.DomainStrategyIPv4Only
case !ipv4 && ipv6:
dnsOptions.Strategy = C.DomainStrategyIPv6Only
}
return dnsRouter.Lookup(ctx, host, dnsOptions)
},
Logger: logger,
}
}
hysteriaService, err := hysteria2.NewService[int](hysteria2.ServiceOptions{
Context: ctx,

View file

@ -3,6 +3,8 @@ package hysteria2
import (
"context"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"time"
@ -18,12 +20,14 @@ import (
qtls "github.com/sagernet/sing-quic"
"github.com/sagernet/sing-quic/hysteria"
"github.com/sagernet/sing-quic/hysteria2"
"github.com/sagernet/sing-quic/hysteria2/realm"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
)
func RegisterOutbound(registry *outbound.Registry) {
@ -66,13 +70,43 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
return nil, E.New("unknown obfs type: ", options.Obfs.Type)
}
}
outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain())
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
Context: ctx,
Options: options.DialerOptions,
RemoteIsDomain: options.ServerIsDomain(),
})
if err != nil {
return nil, err
}
realmOptions, err := buildRealmOptions(ctx, logger, options.Realm)
if err != nil {
return nil, err
var realmOptions *realm.Options
if options.Realm != nil {
queryOptions, err := adapter.DNSQueryOptionsFrom(ctx, options.DialerOptions.DomainResolver)
if err != nil {
return nil, err
}
httpClientTransport, err := service.FromContext[adapter.HTTPClientManager](ctx).ResolveTransport(ctx, logger, common.PtrValueOrDefault(options.Realm.HTTPClient))
if err != nil {
return nil, E.Cause(err, "create realm http client")
}
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
realmOptions = &realm.Options{
ServerURL: options.Realm.ServerURL,
Token: options.Realm.Token,
RealmID: options.Realm.RealmID,
STUNServers: options.Realm.STUNServers,
HTTPClient: &http.Client{Transport: httpClientTransport},
Resolver: func(ctx context.Context, host string, ipv4, ipv6 bool) ([]netip.Addr, error) {
dnsOptions := queryOptions
switch {
case ipv4 && !ipv6:
dnsOptions.Strategy = C.DomainStrategyIPv4Only
case !ipv4 && ipv6:
dnsOptions.Strategy = C.DomainStrategyIPv6Only
}
return dnsRouter.Lookup(ctx, host, dnsOptions)
},
Logger: logger,
}
}
networkList := options.Network.Build()
client, err := hysteria2.NewClient(hysteria2.ClientOptions{

View file

@ -13,13 +13,11 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-quic/hysteria2/realm"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
aTLS "github.com/sagernet/sing/common/tls"
sHTTP "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/sing/service"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
@ -31,24 +29,6 @@ func RegisterRealmService(registry *boxService.Registry) {
boxService.Register[option.HysteriaRealmServiceOptions](registry, C.TypeHysteriaRealm, NewRealmService)
}
func buildRealmOptions(ctx context.Context, logger log.ContextLogger, options *option.Hysteria2Realm) (*realm.Options, error) {
if options == nil {
return nil, nil
}
transport, err := service.FromContext[adapter.HTTPClientManager](ctx).ResolveTransport(ctx, logger, common.PtrValueOrDefault(options.HTTPClient))
if err != nil {
return nil, E.Cause(err, "create realm http client")
}
return &realm.Options{
ServerURL: options.ServerURL,
Token: options.Token,
RealmID: options.RealmID,
STUNServers: options.STUNServers,
HTTPClient: &http.Client{Transport: transport},
Logger: logger,
}, nil
}
type RealmService struct {
boxService.Adapter
ctx context.Context