232 lines
7.4 KiB
Go
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"`
|
|
}
|