Resolve conflicts

This commit is contained in:
Sergei Maklagin
2025-08-15 12:56:52 +03:00
365 changed files with 22978 additions and 4724 deletions

25
option/anytls.go Normal file
View File

@@ -0,0 +1,25 @@
package option
import "github.com/sagernet/sing/common/json/badoption"
type AnyTLSInboundOptions struct {
ListenOptions
InboundTLSOptionsContainer
Users []AnyTLSUser `json:"users,omitempty"`
PaddingScheme badoption.Listable[string] `json:"padding_scheme,omitempty"`
}
type AnyTLSUser struct {
Name string `json:"name,omitempty"`
Password string `json:"password,omitempty"`
}
type AnyTLSOutboundOptions struct {
DialerOptions
ServerOptions
OutboundTLSOptionsContainer
Password string `json:"password,omitempty"`
IdleSessionCheckInterval badoption.Duration `json:"idle_session_check_interval,omitempty"`
IdleSessionTimeout badoption.Duration `json:"idle_session_timeout,omitempty"`
MinIdleSession int `json:"min_idle_session,omitempty"`
}

36
option/certificate.go Normal file
View File

@@ -0,0 +1,36 @@
package option
import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badoption"
)
type _CertificateOptions struct {
Store string `json:"store,omitempty"`
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
CertificatePath badoption.Listable[string] `json:"certificate_path,omitempty"`
CertificateDirectoryPath badoption.Listable[string] `json:"certificate_directory_path,omitempty"`
}
type CertificateOptions _CertificateOptions
func (o CertificateOptions) MarshalJSON() ([]byte, error) {
switch o.Store {
case C.CertificateStoreSystem:
o.Store = ""
}
return json.Marshal((*_CertificateOptions)(&o))
}
func (o *CertificateOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_CertificateOptions)(o))
if err != nil {
return err
}
switch o.Store {
case C.CertificateStoreSystem, "":
o.Store = C.CertificateStoreSystem
}
return nil
}

View File

@@ -1,29 +1,106 @@
package option
import (
"context"
"net/netip"
"net/url"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/deprecated"
"github.com/sagernet/sing/common"
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"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/service"
"github.com/miekg/dns"
)
type DNSOptions struct {
type RawDNSOptions struct {
Servers []DNSServerOptions `json:"servers,omitempty"`
Rules []DNSRule `json:"rules,omitempty"`
Final string `json:"final,omitempty"`
ReverseMapping bool `json:"reverse_mapping,omitempty"`
FakeIP *DNSFakeIPOptions `json:"fakeip,omitempty"`
DNSClientOptions
}
type DNSServerOptions struct {
Tag string `json:"tag,omitempty"`
Address string `json:"address"`
AddressResolver string `json:"address_resolver,omitempty"`
AddressStrategy DomainStrategy `json:"address_strategy,omitempty"`
AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"`
Detour string `json:"detour,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
type LegacyDNSOptions struct {
FakeIP *LegacyDNSFakeIPOptions `json:"fakeip,omitempty"`
}
type DNSOptions struct {
RawDNSOptions
LegacyDNSOptions
}
type contextKeyDontUpgrade struct{}
func ContextWithDontUpgrade(ctx context.Context) context.Context {
return context.WithValue(ctx, (*contextKeyDontUpgrade)(nil), true)
}
func dontUpgradeFromContext(ctx context.Context) bool {
return ctx.Value((*contextKeyDontUpgrade)(nil)) == true
}
func (o *DNSOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error {
err := json.UnmarshalContext(ctx, content, &o.LegacyDNSOptions)
if err != nil {
return err
}
dontUpgrade := dontUpgradeFromContext(ctx)
legacyOptions := o.LegacyDNSOptions
if !dontUpgrade {
if o.FakeIP != nil && o.FakeIP.Enabled {
deprecated.Report(ctx, deprecated.OptionLegacyDNSFakeIPOptions)
ctx = context.WithValue(ctx, (*LegacyDNSFakeIPOptions)(nil), o.FakeIP)
}
o.LegacyDNSOptions = LegacyDNSOptions{}
}
err = badjson.UnmarshallExcludedContext(ctx, content, legacyOptions, &o.RawDNSOptions)
if err != nil {
return err
}
if !dontUpgrade {
rcodeMap := make(map[string]int)
o.Servers = common.Filter(o.Servers, func(it DNSServerOptions) bool {
if it.Type == C.DNSTypeLegacyRcode {
rcodeMap[it.Tag] = it.Options.(int)
return false
}
return true
})
if len(rcodeMap) > 0 {
for i := 0; i < len(o.Rules); i++ {
rewriteRcode(rcodeMap, &o.Rules[i])
}
}
}
return nil
}
func rewriteRcode(rcodeMap map[string]int, rule *DNSRule) {
switch rule.Type {
case C.RuleTypeDefault:
rewriteRcodeAction(rcodeMap, &rule.DefaultOptions.DNSRuleAction)
case C.RuleTypeLogical:
rewriteRcodeAction(rcodeMap, &rule.LogicalOptions.DNSRuleAction)
}
}
func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) {
if ruleAction.Action != C.RuleActionTypeRoute {
return
}
rcode, loaded := rcodeMap[ruleAction.RouteOptions.Server]
if !loaded {
return
}
ruleAction.Action = C.RuleActionTypePredefined
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
return
}
type DNSClientOptions struct {
@@ -35,8 +112,310 @@ type DNSClientOptions struct {
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type DNSFakeIPOptions struct {
Enabled bool `json:"enabled,omitempty"`
Inet4Range *netip.Prefix `json:"inet4_range,omitempty"`
Inet6Range *netip.Prefix `json:"inet6_range,omitempty"`
type LegacyDNSFakeIPOptions struct {
Enabled bool `json:"enabled,omitempty"`
Inet4Range *badoption.Prefix `json:"inet4_range,omitempty"`
Inet6Range *badoption.Prefix `json:"inet6_range,omitempty"`
}
type DNSTransportOptionsRegistry interface {
CreateOptions(transportType string) (any, bool)
}
type _DNSServerOptions struct {
Type string `json:"type,omitempty"`
Tag string `json:"tag,omitempty"`
Options any `json:"-"`
}
type DNSServerOptions _DNSServerOptions
func (o *DNSServerOptions) MarshalJSONContext(ctx context.Context) ([]byte, error) {
switch o.Type {
case C.DNSTypeLegacy:
o.Type = ""
}
return badjson.MarshallObjectsContext(ctx, (*_DNSServerOptions)(o), o.Options)
}
func (o *DNSServerOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error {
err := json.UnmarshalContext(ctx, content, (*_DNSServerOptions)(o))
if err != nil {
return err
}
registry := service.FromContext[DNSTransportOptionsRegistry](ctx)
if registry == nil {
return E.New("missing DNS transport options registry in context")
}
var options any
switch o.Type {
case "", C.DNSTypeLegacy:
o.Type = C.DNSTypeLegacy
options = new(LegacyDNSServerOptions)
deprecated.Report(ctx, deprecated.OptionLegacyDNSTransport)
default:
var loaded bool
options, loaded = registry.CreateOptions(o.Type)
if !loaded {
return E.New("unknown transport type: ", o.Type)
}
}
err = badjson.UnmarshallExcludedContext(ctx, content, (*_DNSServerOptions)(o), options)
if err != nil {
return err
}
o.Options = options
if o.Type == C.DNSTypeLegacy && !dontUpgradeFromContext(ctx) {
err = o.Upgrade(ctx)
if err != nil {
return err
}
}
return nil
}
func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
if o.Type != C.DNSTypeLegacy {
return nil
}
options := o.Options.(*LegacyDNSServerOptions)
serverURL, _ := url.Parse(options.Address)
var serverType string
if serverURL != nil && serverURL.Scheme != "" {
serverType = serverURL.Scheme
} else {
switch options.Address {
case "local", "fakeip":
serverType = options.Address
default:
serverType = C.DNSTypeUDP
}
}
remoteOptions := RemoteDNSServerOptions{
LocalDNSServerOptions: LocalDNSServerOptions{
DialerOptions: DialerOptions{
Detour: options.Detour,
DomainResolver: &DomainResolveOptions{
Server: options.AddressResolver,
Strategy: options.AddressStrategy,
},
FallbackDelay: options.AddressFallbackDelay,
},
Legacy: true,
LegacyStrategy: options.Strategy,
LegacyDefaultDialer: options.Detour == "",
LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}),
},
LegacyAddressResolver: options.AddressResolver,
LegacyAddressStrategy: options.AddressStrategy,
LegacyAddressFallbackDelay: options.AddressFallbackDelay,
}
switch serverType {
case C.DNSTypeLocal:
o.Type = C.DNSTypeLocal
o.Options = &remoteOptions.LocalDNSServerOptions
case C.DNSTypeUDP:
o.Type = C.DNSTypeUDP
o.Options = &remoteOptions
var serverAddr M.Socksaddr
if serverURL == nil || serverURL.Scheme == "" {
serverAddr = M.ParseSocksaddr(options.Address)
} else {
serverAddr = M.ParseSocksaddr(serverURL.Host)
}
if !serverAddr.IsValid() {
return E.New("invalid server address")
}
remoteOptions.Server = serverAddr.AddrString()
if serverAddr.Port != 0 && serverAddr.Port != 53 {
remoteOptions.ServerPort = serverAddr.Port
}
case C.DNSTypeTCP:
o.Type = C.DNSTypeTCP
o.Options = &remoteOptions
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() {
return E.New("invalid server address")
}
remoteOptions.Server = serverAddr.AddrString()
if serverAddr.Port != 0 && serverAddr.Port != 53 {
remoteOptions.ServerPort = serverAddr.Port
}
case C.DNSTypeTLS, C.DNSTypeQUIC:
o.Type = serverType
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() {
return E.New("invalid server address")
}
remoteOptions.Server = serverAddr.AddrString()
if serverAddr.Port != 0 && serverAddr.Port != 853 {
remoteOptions.ServerPort = serverAddr.Port
}
o.Options = &RemoteTLSDNSServerOptions{
RemoteDNSServerOptions: remoteOptions,
}
case C.DNSTypeHTTPS, C.DNSTypeHTTP3:
o.Type = serverType
httpsOptions := RemoteHTTPSDNSServerOptions{
RemoteTLSDNSServerOptions: RemoteTLSDNSServerOptions{
RemoteDNSServerOptions: remoteOptions,
},
}
o.Options = &httpsOptions
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() {
return E.New("invalid server address")
}
httpsOptions.Server = serverAddr.AddrString()
if serverAddr.Port != 0 && serverAddr.Port != 443 {
httpsOptions.ServerPort = serverAddr.Port
}
if serverURL.Path != "/dns-query" {
httpsOptions.Path = serverURL.Path
}
case "rcode":
var rcode int
if serverURL == nil {
return E.New("invalid server address")
}
switch serverURL.Host {
case "success":
rcode = dns.RcodeSuccess
case "format_error":
rcode = dns.RcodeFormatError
case "server_failure":
rcode = dns.RcodeServerFailure
case "name_error":
rcode = dns.RcodeNameError
case "not_implemented":
rcode = dns.RcodeNotImplemented
case "refused":
rcode = dns.RcodeRefused
default:
return E.New("unknown rcode: ", serverURL.Host)
}
o.Type = C.DNSTypeLegacyRcode
o.Options = rcode
case C.DNSTypeDHCP:
o.Type = C.DNSTypeDHCP
dhcpOptions := DHCPDNSServerOptions{}
if serverURL == nil {
return E.New("invalid server address")
}
if serverURL.Host != "" && serverURL.Host != "auto" {
dhcpOptions.Interface = serverURL.Host
}
o.Options = &dhcpOptions
case C.DNSTypeFakeIP:
o.Type = C.DNSTypeFakeIP
fakeipOptions := FakeIPDNSServerOptions{}
if legacyOptions, loaded := ctx.Value((*LegacyDNSFakeIPOptions)(nil)).(*LegacyDNSFakeIPOptions); loaded {
fakeipOptions.Inet4Range = legacyOptions.Inet4Range
fakeipOptions.Inet6Range = legacyOptions.Inet6Range
}
o.Options = &fakeipOptions
case C.DNSTypeSDNS:
o.Type = C.DNSTypeSDNS
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() {
return E.New("invalid server address")
}
o.Options = &SDNSDNSServerOptions{
RemoteDNSServerOptions: remoteOptions,
Stamp: serverAddr.AddrString(),
}
default:
return E.New("unsupported DNS server scheme: ", serverType)
}
return nil
}
type DNSServerAddressOptions struct {
Server string `json:"server"`
ServerPort uint16 `json:"server_port,omitempty"`
}
func (o DNSServerAddressOptions) Build() M.Socksaddr {
return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
}
func (o DNSServerAddressOptions) ServerIsDomain() bool {
return M.IsDomainName(o.Server)
}
func (o *DNSServerAddressOptions) TakeServerOptions() ServerOptions {
return ServerOptions(*o)
}
func (o *DNSServerAddressOptions) ReplaceServerOptions(options ServerOptions) {
*o = DNSServerAddressOptions(options)
}
type LegacyDNSServerOptions struct {
Address string `json:"address"`
AddressResolver string `json:"address_resolver,omitempty"`
AddressStrategy DomainStrategy `json:"address_strategy,omitempty"`
AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"`
Detour string `json:"detour,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type HostsDNSServerOptions struct {
Path badoption.Listable[string] `json:"path,omitempty"`
Predefined *badjson.TypedMap[string, badoption.Listable[netip.Addr]] `json:"predefined,omitempty"`
}
type LocalDNSServerOptions struct {
DialerOptions
Legacy bool `json:"-"`
LegacyStrategy DomainStrategy `json:"-"`
LegacyDefaultDialer bool `json:"-"`
LegacyClientSubnet netip.Prefix `json:"-"`
}
type RemoteDNSServerOptions struct {
LocalDNSServerOptions
DNSServerAddressOptions
LegacyAddressResolver string `json:"-"`
LegacyAddressStrategy DomainStrategy `json:"-"`
LegacyAddressFallbackDelay badoption.Duration `json:"-"`
}
type RemoteTLSDNSServerOptions struct {
RemoteDNSServerOptions
OutboundTLSOptionsContainer
}
type RemoteHTTPSDNSServerOptions struct {
RemoteTLSDNSServerOptions
Path string `json:"path,omitempty"`
Method string `json:"method,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"`
}
type FakeIPDNSServerOptions struct {
Inet4Range *badoption.Prefix `json:"inet4_range,omitempty"`
Inet6Range *badoption.Prefix `json:"inet6_range,omitempty"`
}
type DHCPDNSServerOptions struct {
LocalDNSServerOptions
Interface string `json:"interface,omitempty"`
}
type SDNSDNSServerOptions struct {
RemoteDNSServerOptions
Stamp string `json:"stamp"`
}

102
option/dns_record.go Normal file
View File

@@ -0,0 +1,102 @@
package option
import (
"encoding/base64"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
M "github.com/sagernet/sing/common/metadata"
"github.com/miekg/dns"
)
type DNSRCode int
func (r DNSRCode) MarshalJSON() ([]byte, error) {
rCodeValue, loaded := dns.RcodeToString[int(r)]
if loaded {
return json.Marshal(rCodeValue)
}
return json.Marshal(int(r))
}
func (r *DNSRCode) UnmarshalJSON(bytes []byte) error {
var intValue int
err := json.Unmarshal(bytes, &intValue)
if err == nil {
*r = DNSRCode(intValue)
return nil
}
var stringValue string
err = json.Unmarshal(bytes, &stringValue)
if err != nil {
return err
}
rCodeValue, loaded := dns.StringToRcode[stringValue]
if !loaded {
return E.New("unknown rcode: " + stringValue)
}
*r = DNSRCode(rCodeValue)
return nil
}
func (r *DNSRCode) Build() int {
if r == nil {
return dns.RcodeSuccess
}
return int(*r)
}
type DNSRecordOptions struct {
dns.RR
fromBase64 bool
}
func (o DNSRecordOptions) MarshalJSON() ([]byte, error) {
if o.fromBase64 {
buffer := buf.Get(dns.Len(o.RR))
defer buf.Put(buffer)
offset, err := dns.PackRR(o.RR, buffer, 0, nil, false)
if err != nil {
return nil, err
}
return json.Marshal(base64.StdEncoding.EncodeToString(buffer[:offset]))
}
return json.Marshal(o.RR.String())
}
func (o *DNSRecordOptions) UnmarshalJSON(data []byte) error {
var stringValue string
err := json.Unmarshal(data, &stringValue)
if err != nil {
return err
}
binary, err := base64.StdEncoding.DecodeString(stringValue)
if err == nil {
return o.unmarshalBase64(binary)
}
record, err := dns.NewRR(stringValue)
if err != nil {
return err
}
if a, isA := record.(*dns.A); isA {
a.A = M.AddrFromIP(a.A).Unmap().AsSlice()
}
o.RR = record
return nil
}
func (o *DNSRecordOptions) unmarshalBase64(binary []byte) error {
record, _, err := dns.UnpackRR(binary, 0)
if err != nil {
return E.New("parse binary DNS record")
}
o.RR = record
o.fromBase64 = true
return nil
}
func (o DNSRecordOptions) Build() dns.RR {
return o.RR
}

View File

@@ -32,11 +32,11 @@ func (h *Endpoint) UnmarshalJSONContext(ctx context.Context, content []byte) err
}
registry := service.FromContext[EndpointOptionsRegistry](ctx)
if registry == nil {
return E.New("missing Endpoint fields registry in context")
return E.New("missing endpoint fields registry in context")
}
options, loaded := registry.CreateOptions(h.Type)
if !loaded {
return E.New("unknown inbound type: ", h.Type)
return E.New("unknown endpoint type: ", h.Type)
}
err = badjson.UnmarshallExcludedContext(ctx, content, (*_Endpoint)(h), options)
if err != nil {

View File

@@ -1,6 +1,9 @@
package option
import "github.com/sagernet/sing/common/byteformats"
import (
"github.com/sagernet/sing/common/byteformats"
"github.com/sagernet/sing/common/json/badoption"
)
type HysteriaInboundOptions struct {
ListenOptions
@@ -26,6 +29,8 @@ type HysteriaUser struct {
type HysteriaOutboundOptions struct {
DialerOptions
ServerOptions
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
UpMbps int `json:"up_mbps,omitempty"`
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`

View File

@@ -34,7 +34,7 @@ func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) erro
}
registry := service.FromContext[InboundOptionsRegistry](ctx)
if registry == nil {
return E.New("missing Inbound fields registry in context")
return E.New("missing inbound fields registry in context")
}
options, loaded := registry.CreateOptions(h.Type)
if !loaded {
@@ -61,6 +61,10 @@ type InboundOptions struct {
type ListenOptions struct {
Listen *badoption.Addr `json:"listen,omitempty"`
ListenPort uint16 `json:"listen_port,omitempty"`
BindInterface string `json:"bind_interface,omitempty"`
RoutingMark FwMark `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`
NetNs string `json:"netns,omitempty"`
TCPKeepAlive badoption.Duration `json:"tcp_keep_alive,omitempty"`
TCPKeepAliveInterval badoption.Duration `json:"tcp_keep_alive_interval,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`

View File

@@ -14,10 +14,12 @@ type _Options struct {
Log *LogOptions `json:"log,omitempty"`
DNS *DNSOptions `json:"dns,omitempty"`
NTP *NTPOptions `json:"ntp,omitempty"`
Certificate *CertificateOptions `json:"certificate,omitempty"`
Endpoints []Endpoint `json:"endpoints,omitempty"`
Inbounds []Inbound `json:"inbounds,omitempty"`
Outbounds []Outbound `json:"outbounds,omitempty"`
Route *RouteOptions `json:"route,omitempty"`
Services []Service `json:"services,omitempty"`
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
}

View File

@@ -72,17 +72,60 @@ type DialerOptions struct {
ProtectPath string `json:"protect_path,omitempty"`
RoutingMark FwMark `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`
NetNs string `json:"netns,omitempty"`
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"`
UDPFragmentDefault bool `json:"-"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"`
NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"`
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"`
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
IsWireGuardListener bool `json:"-"`
// Deprecated: migrated to domain resolver
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
}
type _DomainResolveOptions struct {
Server string `json:"server"`
Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type DomainResolveOptions _DomainResolveOptions
func (o DomainResolveOptions) MarshalJSON() ([]byte, error) {
if o.Server == "" {
return []byte("{}"), nil
} else if o.Strategy == DomainStrategy(C.DomainStrategyAsIS) &&
!o.DisableCache &&
o.RewriteTTL == nil &&
o.ClientSubnet == nil {
return json.Marshal(o.Server)
} else {
return json.Marshal((_DomainResolveOptions)(o))
}
}
func (o *DomainResolveOptions) UnmarshalJSON(bytes []byte) error {
var stringValue string
err := json.Unmarshal(bytes, &stringValue)
if err == nil {
o.Server = stringValue
return nil
}
err = json.Unmarshal(bytes, (*_DomainResolveOptions)(o))
if err != nil {
return err
}
if o.Server == "" {
return E.New("empty domain_resolver.server")
}
return nil
}
func (o *DialerOptions) TakeDialerOptions() DialerOptions {
@@ -107,6 +150,10 @@ func (o ServerOptions) Build() M.Socksaddr {
return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
}
func (o ServerOptions) ServerIsDomain() bool {
return M.IsDomainName(o.Server)
}
func (o *ServerOptions) TakeServerOptions() ServerOptions {
return *o
}

49
option/resolved.go Normal file
View File

@@ -0,0 +1,49 @@
package option
import (
"context"
"net/netip"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badoption"
)
type _ResolvedServiceOptions struct {
ListenOptions
}
type ResolvedServiceOptions _ResolvedServiceOptions
func (r ResolvedServiceOptions) MarshalJSONContext(ctx context.Context) ([]byte, error) {
if r.Listen != nil && netip.Addr(*r.Listen) == (netip.AddrFrom4([4]byte{127, 0, 0, 53})) {
r.Listen = nil
}
if r.ListenPort == 53 {
r.ListenPort = 0
}
return json.MarshalContext(ctx, (*_ResolvedServiceOptions)(&r))
}
func (r *ResolvedServiceOptions) UnmarshalJSONContext(ctx context.Context, bytes []byte) error {
err := json.UnmarshalContextDisallowUnknownFields(ctx, bytes, (*_ResolvedServiceOptions)(r))
if err != nil {
return err
}
if r.Listen == nil {
r.Listen = (*badoption.Addr)(common.Ptr(netip.AddrFrom4([4]byte{127, 0, 0, 53})))
}
if r.ListenPort == 0 {
r.ListenPort = 53
}
return nil
}
type ResolvedDNSServerOptions struct {
Service string `json:"service"`
AcceptDefaultResolvers bool `json:"accept_default_resolvers,omitempty"`
// NDots int `json:"ndots,omitempty"`
// Timeout badoption.Duration `json:"timeout,omitempty"`
// Attempts int `json:"attempts,omitempty"`
// Rotate bool `json:"rotate,omitempty"`
}

View File

@@ -13,6 +13,7 @@ type RouteOptions struct {
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
DefaultInterface string `json:"default_interface,omitempty"`
DefaultMark FwMark `json:"default_mark,omitempty"`
DefaultDomainResolver *DomainResolveOptions `json:"default_domain_resolver,omitempty"`
DefaultNetworkStrategy *NetworkStrategy `json:"default_network_strategy,omitempty"`
DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"`
DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"`

View File

@@ -127,10 +127,9 @@ func (r *DefaultRule) UnmarshalJSON(data []byte) error {
return badjson.UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction)
}
func (r *DefaultRule) IsValid() bool {
func (r DefaultRule) IsValid() bool {
var defaultValue DefaultRule
defaultValue.Invert = r.Invert
defaultValue.Action = r.Action
return !reflect.DeepEqual(r, defaultValue)
}

View File

@@ -7,7 +7,6 @@ import (
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson"
@@ -93,6 +92,7 @@ type _DNSRuleAction struct {
RouteOptions DNSRouteActionOptions `json:"-"`
RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"`
PredefinedOptions DNSRouteActionPredefined `json:"-"`
}
type DNSRuleAction _DNSRuleAction
@@ -110,6 +110,8 @@ func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
v = r.RouteOptionsOptions
case C.RuleActionTypeReject:
v = r.RejectOptions
case C.RuleActionTypePredefined:
v = r.PredefinedOptions
default:
return nil, E.New("unknown DNS rule action: " + r.Action)
}
@@ -130,6 +132,8 @@ func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) e
v = &r.RouteOptionsOptions
case C.RuleActionTypeReject:
v = &r.RejectOptions
case C.RuleActionTypePredefined:
v = &r.PredefinedOptions
default:
return E.New("unknown DNS rule action: " + r.Action)
}
@@ -152,6 +156,10 @@ type RawRouteOptionsActionOptions struct {
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
UDPConnect bool `json:"udp_connect,omitempty"`
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
TLSFragment bool `json:"tls_fragment,omitempty"`
TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
TLSRecordFragment bool `json:"tls_record_fragment,omitempty"`
}
type RouteOptionsActionOptions RawRouteOptionsActionOptions
@@ -164,17 +172,22 @@ func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
if *r == (RouteOptionsActionOptions{}) {
return E.New("empty route option action")
}
if r.TLSFragment && r.TLSRecordFragment {
return E.New("`tls_fragment` and `tls_record_fragment` are mutually exclusive")
}
return nil
}
type DNSRouteActionOptions struct {
Server string `json:"server,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type _DNSRouteOptionsActionOptions struct {
Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
@@ -226,7 +239,7 @@ func (d DirectActionOptions) Descriptions() []string {
if d.UDPFragment != nil {
descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
}
if d.DomainStrategy != DomainStrategy(dns.DomainStrategyAsIS) {
if d.DomainStrategy != DomainStrategy(C.DomainStrategyAsIS) {
descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
}
if d.FallbackDelay != 0 {
@@ -285,6 +298,16 @@ type RouteActionSniff struct {
}
type RouteActionResolve struct {
Strategy DomainStrategy `json:"strategy,omitempty"`
Server string `json:"server,omitempty"`
Server string `json:"server,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
}
type DNSRouteActionPredefined struct {
Rcode *DNSRCode `json:"rcode,omitempty"`
Answer badoption.Listable[DNSRecordOptions] `json:"answer,omitempty"`
Ns badoption.Listable[DNSRecordOptions] `json:"ns,omitempty"`
Extra badoption.Listable[DNSRecordOptions] `json:"extra,omitempty"`
}

View File

@@ -83,6 +83,7 @@ type RawDefaultDNSRule struct {
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
IPIsPrivate bool `json:"ip_is_private,omitempty"`
IPAcceptAny bool `json:"ip_accept_any,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
@@ -133,7 +134,6 @@ func (r *DefaultDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte)
func (r DefaultDNSRule) IsValid() bool {
var defaultValue DefaultDNSRule
defaultValue.Invert = r.Invert
defaultValue.DNSRuleAction = r.DNSRuleAction
return !reflect.DeepEqual(r, defaultValue)
}

47
option/service.go Normal file
View File

@@ -0,0 +1,47 @@
package option
import (
"context"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/service"
)
type ServiceOptionsRegistry interface {
CreateOptions(serviceType string) (any, bool)
}
type _Service struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Options any `json:"-"`
}
type Service _Service
func (h *Service) MarshalJSONContext(ctx context.Context) ([]byte, error) {
return badjson.MarshallObjectsContext(ctx, (*_Service)(h), h.Options)
}
func (h *Service) UnmarshalJSONContext(ctx context.Context, content []byte) error {
err := json.UnmarshalContext(ctx, content, (*_Service)(h))
if err != nil {
return err
}
registry := service.FromContext[ServiceOptionsRegistry](ctx)
if registry == nil {
return E.New("missing service fields registry in context")
}
options, loaded := registry.CreateOptions(h.Type)
if !loaded {
return E.New("unknown inbound type: ", h.Type)
}
err = badjson.UnmarshallExcludedContext(ctx, content, (*_Service)(h), options)
if err != nil {
return err
}
h.Options = options
return nil
}

View File

@@ -8,6 +8,7 @@ type ShadowsocksInboundOptions struct {
Users []ShadowsocksUser `json:"users,omitempty"`
Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
Multiplex *InboundMultiplexOptions `json:"multiplex,omitempty"`
Managed bool `json:"managed,omitempty"`
}
type ShadowsocksUser struct {

View File

@@ -1,13 +1,65 @@
package option
import (
"encoding/json"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json/badjson"
)
type ShadowTLSInboundOptions struct {
ListenOptions
Version int `json:"version,omitempty"`
Password string `json:"password,omitempty"`
Users []ShadowTLSUser `json:"users,omitempty"`
Handshake ShadowTLSHandshakeOptions `json:"handshake,omitempty"`
HandshakeForServerName map[string]ShadowTLSHandshakeOptions `json:"handshake_for_server_name,omitempty"`
StrictMode bool `json:"strict_mode,omitempty"`
Version int `json:"version,omitempty"`
Password string `json:"password,omitempty"`
Users []ShadowTLSUser `json:"users,omitempty"`
Handshake ShadowTLSHandshakeOptions `json:"handshake,omitempty"`
HandshakeForServerName *badjson.TypedMap[string, ShadowTLSHandshakeOptions] `json:"handshake_for_server_name,omitempty"`
StrictMode bool `json:"strict_mode,omitempty"`
WildcardSNI WildcardSNI `json:"wildcard_sni,omitempty"`
}
type WildcardSNI int
const (
ShadowTLSWildcardSNIOff WildcardSNI = iota
ShadowTLSWildcardSNIAuthed
ShadowTLSWildcardSNIAll
)
func (w WildcardSNI) MarshalJSON() ([]byte, error) {
return json.Marshal(w.String())
}
func (w WildcardSNI) String() string {
switch w {
case ShadowTLSWildcardSNIOff:
return "off"
case ShadowTLSWildcardSNIAuthed:
return "authed"
case ShadowTLSWildcardSNIAll:
return "all"
default:
panic("unknown wildcard SNI value")
}
}
func (w *WildcardSNI) UnmarshalJSON(bytes []byte) error {
var valueString string
err := json.Unmarshal(bytes, &valueString)
if err != nil {
return err
}
switch valueString {
case "off", "":
*w = ShadowTLSWildcardSNIOff
case "authed":
*w = ShadowTLSWildcardSNIAuthed
case "all":
*w = ShadowTLSWildcardSNIAll
default:
return E.New("unknown wildcard SNI value: ", valueString)
}
return nil
}
type ShadowTLSUser struct {

View File

@@ -7,13 +7,15 @@ import (
type SocksInboundOptions struct {
ListenOptions
Users []auth.User `json:"users,omitempty"`
Users []auth.User `json:"users,omitempty"`
DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"`
}
type HTTPMixedInboundOptions struct {
ListenOptions
Users []auth.User `json:"users,omitempty"`
SetSystemProxy bool `json:"set_system_proxy,omitempty"`
Users []auth.User `json:"users,omitempty"`
DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"`
SetSystemProxy bool `json:"set_system_proxy,omitempty"`
InboundTLSOptionsContainer
}

12
option/ssmapi.go Normal file
View File

@@ -0,0 +1,12 @@
package option
import (
"github.com/sagernet/sing/common/json/badjson"
)
type SSMAPIServiceOptions struct {
ListenOptions
Servers *badjson.TypedMap[string, string] `json:"servers"`
CachePath string `json:"cache_path,omitempty"`
InboundTLSOptionsContainer
}

116
option/tailscale.go Normal file
View File

@@ -0,0 +1,116 @@
package option
import (
"net/netip"
"net/url"
"reflect"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badoption"
M "github.com/sagernet/sing/common/metadata"
)
type TailscaleEndpointOptions struct {
DialerOptions
StateDirectory string `json:"state_directory,omitempty"`
AuthKey string `json:"auth_key,omitempty"`
ControlURL string `json:"control_url,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
Hostname string `json:"hostname,omitempty"`
AcceptRoutes bool `json:"accept_routes,omitempty"`
ExitNode string `json:"exit_node,omitempty"`
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
}
type TailscaleDNSServerOptions struct {
Endpoint string `json:"endpoint,omitempty"`
AcceptDefaultResolvers bool `json:"accept_default_resolvers,omitempty"`
}
type DERPServiceOptions struct {
ListenOptions
InboundTLSOptionsContainer
ConfigPath string `json:"config_path,omitempty"`
VerifyClientEndpoint badoption.Listable[string] `json:"verify_client_endpoint,omitempty"`
VerifyClientURL badoption.Listable[*DERPVerifyClientURLOptions] `json:"verify_client_url,omitempty"`
Home string `json:"home,omitempty"`
MeshWith badoption.Listable[*DERPMeshOptions] `json:"mesh_with,omitempty"`
MeshPSK string `json:"mesh_psk,omitempty"`
MeshPSKFile string `json:"mesh_psk_file,omitempty"`
STUN *DERPSTUNListenOptions `json:"stun,omitempty"`
}
type _DERPVerifyClientURLOptions struct {
URL string `json:"url,omitempty"`
DialerOptions
}
type DERPVerifyClientURLOptions _DERPVerifyClientURLOptions
func (d DERPVerifyClientURLOptions) ServerIsDomain() bool {
verifyURL, err := url.Parse(d.URL)
if err != nil {
return false
}
return M.IsDomainName(verifyURL.Host)
}
func (d DERPVerifyClientURLOptions) MarshalJSON() ([]byte, error) {
if reflect.DeepEqual(d, _DERPVerifyClientURLOptions{}) {
return json.Marshal(d.URL)
} else {
return json.Marshal(_DERPVerifyClientURLOptions(d))
}
}
func (d *DERPVerifyClientURLOptions) UnmarshalJSON(bytes []byte) error {
var stringValue string
err := json.Unmarshal(bytes, &stringValue)
if err == nil {
d.URL = stringValue
return nil
}
return json.Unmarshal(bytes, (*_DERPVerifyClientURLOptions)(d))
}
type DERPMeshOptions struct {
ServerOptions
Host string `json:"host,omitempty"`
OutboundTLSOptionsContainer
DialerOptions
}
type _DERPSTUNListenOptions struct {
Enabled bool
ListenOptions
}
type DERPSTUNListenOptions _DERPSTUNListenOptions
func (d DERPSTUNListenOptions) MarshalJSON() ([]byte, error) {
portOptions := _DERPSTUNListenOptions{
Enabled: d.Enabled,
ListenOptions: ListenOptions{
ListenPort: d.ListenPort,
},
}
if _DERPSTUNListenOptions(d) == portOptions {
return json.Marshal(d.Enabled)
} else {
return json.Marshal(_DERPSTUNListenOptions(d))
}
}
func (d *DERPSTUNListenOptions) UnmarshalJSON(bytes []byte) error {
var portValue uint16
err := json.Unmarshal(bytes, &portValue)
if err == nil {
d.Enabled = true
d.ListenPort = portValue
return nil
}
return json.Unmarshal(bytes, (*_DERPSTUNListenOptions)(d))
}

View File

@@ -37,19 +37,22 @@ func (o *InboundTLSOptionsContainer) ReplaceInboundTLSOptions(options *InboundTL
}
type OutboundTLSOptions struct {
Enabled bool `json:"enabled,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"`
ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"`
ALPN badoption.Listable[string] `json:"alpn,omitempty"`
MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"`
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"`
ECH *OutboundECHOptions `json:"ech,omitempty"`
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
Reality *OutboundRealityOptions `json:"reality,omitempty"`
Enabled bool `json:"enabled,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"`
ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"`
ALPN badoption.Listable[string] `json:"alpn,omitempty"`
MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"`
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"`
Fragment bool `json:"fragment,omitempty"`
FragmentFallbackDelay badoption.Duration `json:"fragment_fallback_delay,omitempty"`
RecordFragment bool `json:"record_fragment,omitempty"`
ECH *OutboundECHOptions `json:"ech,omitempty"`
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
Reality *OutboundRealityOptions `json:"reality,omitempty"`
}
type OutboundTLSOptionsContainer struct {
@@ -83,19 +86,25 @@ type InboundRealityHandshakeOptions struct {
}
type InboundECHOptions struct {
Enabled bool `json:"enabled,omitempty"`
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
Key badoption.Listable[string] `json:"key,omitempty"`
KeyPath string `json:"key_path,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Key badoption.Listable[string] `json:"key,omitempty"`
KeyPath string `json:"key_path,omitempty"`
// Deprecated: not supported by stdlib
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
// Deprecated: added by fault
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
}
type OutboundECHOptions struct {
Enabled bool `json:"enabled,omitempty"`
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
Config badoption.Listable[string] `json:"config,omitempty"`
ConfigPath string `json:"config_path,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Config badoption.Listable[string] `json:"config,omitempty"`
ConfigPath string `json:"config_path,omitempty"`
// Deprecated: not supported by stdlib
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
// Deprecated: added by fault
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
}
type OutboundUTLSOptions struct {

View File

@@ -20,6 +20,7 @@ type TunInboundOptions struct {
AutoRedirect bool `json:"auto_redirect,omitempty"`
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`

View File

@@ -4,7 +4,6 @@ import (
"strings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
@@ -45,19 +44,19 @@ func (v NetworkList) Build() []string {
return strings.Split(string(v), "\n")
}
type DomainStrategy dns.DomainStrategy
type DomainStrategy C.DomainStrategy
func (s DomainStrategy) String() string {
switch dns.DomainStrategy(s) {
case dns.DomainStrategyAsIS:
switch C.DomainStrategy(s) {
case C.DomainStrategyAsIS:
return ""
case dns.DomainStrategyPreferIPv4:
case C.DomainStrategyPreferIPv4:
return "prefer_ipv4"
case dns.DomainStrategyPreferIPv6:
case C.DomainStrategyPreferIPv6:
return "prefer_ipv6"
case dns.DomainStrategyUseIPv4:
case C.DomainStrategyIPv4Only:
return "ipv4_only"
case dns.DomainStrategyUseIPv6:
case C.DomainStrategyIPv6Only:
return "ipv6_only"
default:
panic(E.New("unknown domain strategy: ", s))
@@ -66,17 +65,17 @@ func (s DomainStrategy) String() string {
func (s DomainStrategy) MarshalJSON() ([]byte, error) {
var value string
switch dns.DomainStrategy(s) {
case dns.DomainStrategyAsIS:
switch C.DomainStrategy(s) {
case C.DomainStrategyAsIS:
value = ""
// value = "as_is"
case dns.DomainStrategyPreferIPv4:
case C.DomainStrategyPreferIPv4:
value = "prefer_ipv4"
case dns.DomainStrategyPreferIPv6:
case C.DomainStrategyPreferIPv6:
value = "prefer_ipv6"
case dns.DomainStrategyUseIPv4:
case C.DomainStrategyIPv4Only:
value = "ipv4_only"
case dns.DomainStrategyUseIPv6:
case C.DomainStrategyIPv6Only:
value = "ipv6_only"
default:
return nil, E.New("unknown domain strategy: ", s)
@@ -92,15 +91,15 @@ func (s *DomainStrategy) UnmarshalJSON(bytes []byte) error {
}
switch value {
case "", "as_is":
*s = DomainStrategy(dns.DomainStrategyAsIS)
*s = DomainStrategy(C.DomainStrategyAsIS)
case "prefer_ipv4":
*s = DomainStrategy(dns.DomainStrategyPreferIPv4)
*s = DomainStrategy(C.DomainStrategyPreferIPv4)
case "prefer_ipv6":
*s = DomainStrategy(dns.DomainStrategyPreferIPv6)
*s = DomainStrategy(C.DomainStrategyPreferIPv6)
case "ipv4_only":
*s = DomainStrategy(dns.DomainStrategyUseIPv4)
*s = DomainStrategy(C.DomainStrategyIPv4Only)
case "ipv6_only":
*s = DomainStrategy(dns.DomainStrategyUseIPv6)
*s = DomainStrategy(C.DomainStrategyIPv6Only)
default:
return E.New("unknown domain strategy: ", value)
}