Files
2026-07-25 22:58:38 +08:00

232 lines
7.4 KiB
Go

package option
import (
"net/url"
"reflect"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/schema"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
)
type Hysteria2InboundOptions struct {
ListenOptions
UpMbps int `json:"up_mbps,omitempty"`
DownMbps int `json:"down_mbps,omitempty"`
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
Users []Hysteria2User `json:"users,omitempty"`
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
InboundTLSOptionsContainer
QUICOptions
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
BBRProfile string `json:"bbr_profile,omitempty" enum:"standard,conservative,aggressive"`
BrutalDebug bool `json:"brutal_debug,omitempty"`
Realm *Hysteria2InboundRealm `json:"realm,omitempty"`
}
type Hysteria2Realm struct {
ServerURL string `json:"server_url"`
Token string `json:"token,omitempty"`
RealmID string `json:"realm_id"`
STUNServers badoption.Listable[string] `json:"stun_servers"`
IPVersion int `json:"ip_version,omitempty" enum:"0,4,6"`
PortMapping *Hysteria2RealmPortMapping `json:"port_mapping,omitempty"`
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
}
type Hysteria2RealmPortMapping struct {
Enabled bool `json:"enabled,omitempty"`
Timeout badoption.Duration `json:"timeout,omitempty"`
Lifetime badoption.Duration `json:"lifetime,omitempty"`
}
type Hysteria2InboundRealm struct {
Hysteria2Realm
STUNDomainResolver *DomainResolveOptions `json:"stun_domain_resolver,omitempty"`
}
type Hysteria2ObfsGecko struct {
MinPacketSize int `json:"min_packet_size,omitempty"`
MaxPacketSize int `json:"max_packet_size,omitempty"`
}
type _Hysteria2Obfs struct {
Type string `json:"type,omitempty" enum:"salamander,gecko"`
Password string `json:"password,omitempty"`
GeckoOptions Hysteria2ObfsGecko `json:"-"`
}
type Hysteria2Obfs _Hysteria2Obfs
func (o Hysteria2Obfs) MarshalJSON() ([]byte, error) {
var v any
switch o.Type {
case C.Hysteria2ObfsTypeSalamander:
case C.Hysteria2ObfsTypeGecko:
v = o.GeckoOptions
default:
return nil, E.New("unknown obfs type: ", o.Type)
}
if v == nil {
return json.Marshal((_Hysteria2Obfs)(o))
}
return badjson.MarshallObjects((_Hysteria2Obfs)(o), v)
}
func (o *Hysteria2Obfs) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_Hysteria2Obfs)(o))
if err != nil {
return err
}
var v any
switch o.Type {
case C.Hysteria2ObfsTypeSalamander:
case C.Hysteria2ObfsTypeGecko:
v = &o.GeckoOptions
default:
return E.New("unknown obfs type: ", o.Type)
}
if v == nil {
return nil
}
return badjson.UnmarshallExcluded(bytes, (*_Hysteria2Obfs)(o), v)
}
func (o Hysteria2Obfs) DescribeSchema(builder schema.Builder) (*schema.Node, error) {
return schema.DiscriminatedUnion(builder, "type", true, []schema.UnionVariant{
{Value: C.Hysteria2ObfsTypeSalamander},
{Value: C.Hysteria2ObfsTypeGecko, StructType: reflect.TypeFor[Hysteria2ObfsGecko]()},
}, func(variant *schema.Node) error {
variant.Properties.Put("password", schema.StringNode())
return nil
})
}
type Hysteria2User struct {
Name string `json:"name,omitempty"`
Password string `json:"password,omitempty"`
}
type _Hysteria2Masquerade struct {
Type string `json:"type,omitempty" enum:"file,proxy,string"`
FileOptions Hysteria2MasqueradeFile `json:"-"`
ProxyOptions Hysteria2MasqueradeProxy `json:"-"`
StringOptions Hysteria2MasqueradeString `json:"-"`
}
type Hysteria2Masquerade _Hysteria2Masquerade
func (m Hysteria2Masquerade) MarshalJSON() ([]byte, error) {
var v any
switch m.Type {
case C.Hysterai2MasqueradeTypeFile:
v = m.FileOptions
case C.Hysterai2MasqueradeTypeProxy:
v = m.ProxyOptions
case C.Hysterai2MasqueradeTypeString:
v = m.StringOptions
default:
return nil, E.New("unknown masquerade type: ", m.Type)
}
return badjson.MarshallObjects((_Hysteria2Masquerade)(m), v)
}
func (m *Hysteria2Masquerade) UnmarshalJSON(bytes []byte) error {
var urlString string
err := json.Unmarshal(bytes, &urlString)
if err == nil {
masqueradeURL, err := url.Parse(urlString)
if err != nil {
return E.Cause(err, "invalid masquerade URL")
}
switch masqueradeURL.Scheme {
case "file":
m.Type = C.Hysterai2MasqueradeTypeFile
m.FileOptions.Directory = masqueradeURL.Path
case "http", "https":
m.Type = C.Hysterai2MasqueradeTypeProxy
m.ProxyOptions.URL = urlString
default:
return E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
}
return nil
}
err = json.Unmarshal(bytes, (*_Hysteria2Masquerade)(m))
if err != nil {
return err
}
var v any
switch m.Type {
case C.Hysterai2MasqueradeTypeFile:
v = &m.FileOptions
case C.Hysterai2MasqueradeTypeProxy:
v = &m.ProxyOptions
case C.Hysterai2MasqueradeTypeString:
v = &m.StringOptions
default:
return E.New("unknown masquerade type: ", m.Type)
}
return badjson.UnmarshallExcluded(bytes, (*_Hysteria2Masquerade)(m), v)
}
func (m Hysteria2Masquerade) DescribeSchema(builder schema.Builder) (*schema.Node, error) {
union, err := schema.DiscriminatedUnion(builder, "type", true, []schema.UnionVariant{
{Value: C.Hysterai2MasqueradeTypeFile, StructType: reflect.TypeFor[Hysteria2MasqueradeFile]()},
{Value: C.Hysterai2MasqueradeTypeProxy, StructType: reflect.TypeFor[Hysteria2MasqueradeProxy]()},
{Value: C.Hysterai2MasqueradeTypeString, StructType: reflect.TypeFor[Hysteria2MasqueradeString]()},
}, nil)
if err != nil {
return nil, err
}
return schema.AnyOf(schema.StringNode(), union), nil
}
type Hysteria2MasqueradeFile struct {
Directory string `json:"directory"`
}
type Hysteria2MasqueradeProxy struct {
URL string `json:"url"`
RewriteHost bool `json:"rewrite_host,omitempty"`
}
type Hysteria2MasqueradeString struct {
StatusCode int `json:"status_code,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"`
Content string `json:"content"`
}
type Hysteria2OutboundOptions struct {
DialerOptions
ServerOptions
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
HopIntervalMax badoption.Duration `json:"hop_interval_max,omitempty"`
UpMbps int `json:"up_mbps,omitempty"`
DownMbps int `json:"down_mbps,omitempty"`
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
Password string `json:"password,omitempty"`
Network NetworkList `json:"network,omitempty"`
OutboundTLSOptionsContainer
QUICOptions
BBRProfile string `json:"bbr_profile,omitempty" enum:"standard,conservative,aggressive"`
BrutalDebug bool `json:"brutal_debug,omitempty"`
Realm *Hysteria2Realm `json:"realm,omitempty"`
}
type HysteriaRealmUser struct {
Name string `json:"name"`
Token string `json:"token"`
MaxRealms int `json:"max_realms,omitempty"`
}
type HysteriaRealmServiceOptions struct {
ListenOptions
InboundTLSOptionsContainer
HTTP2Options
Users []HysteriaRealmUser `json:"users"`
}