package conf import ( "strings" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/geodata" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/proxy/dns" "google.golang.org/protobuf/proto" ) type DNSOutboundRuleConfig struct { Action string `json:"action"` QType *PortList `json:"qType"` Domain *StringList `json:"domain"` RCode uint32 `json:"rCode"` } func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) { rule := &dns.DNSRuleConfig{} switch strings.ToLower(c.Action) { case "direct": rule.Action = dns.RuleAction_Direct case "drop": rule.Action = dns.RuleAction_Drop case "return": rule.Action = dns.RuleAction_Return case "hijack": rule.Action = dns.RuleAction_Hijack default: return nil, errors.New("unknown action: ", c.Action) } if c.QType != nil { for _, r := range c.QType.Range { for qType := r.From; qType <= r.To; qType++ { rule.QType = append(rule.QType, int32(qType)) } } } if c.Domain != nil { rules, err := geodata.ParseDomainRules(*c.Domain, geodata.Domain_Substr) if err != nil { return nil, err } rule.Domain = rules } if c.RCode > 65535 { return nil, errors.New("rCode out of range: ", c.RCode) } rule.RCode = c.RCode return rule, nil } type DNSOutboundConfig struct { RewriteNetwork Network `json:"rewriteNetwork"` RewriteAddress *Address `json:"rewriteAddress"` RewritePort uint16 `json:"rewritePort"` Network Network `json:"network"` Address *Address `json:"address"` Port uint16 `json:"port"` UserLevel uint32 `json:"userLevel"` Rules []*DNSOutboundRuleConfig `json:"rules"` NonIPQuery *string `json:"nonIPQuery"` // todo: remove legacy BlockTypes *[]int32 `json:"blockTypes"` // todo: remove legacy } func (c *DNSOutboundConfig) Build() (proto.Message, error) { if len(c.Network) > 0 { c.RewriteNetwork = c.Network } if c.Address != nil { c.RewriteAddress = c.Address } if c.Port != 0 { c.RewritePort = c.Port } config := &dns.Config{ RewriteServer: &net.Endpoint{ Network: c.RewriteNetwork.Build(), Port: uint32(c.RewritePort), }, UserLevel: c.UserLevel, } if c.RewriteAddress != nil { config.RewriteServer.Address = c.RewriteAddress.Build() } // todo: remove legacy if c.NonIPQuery != nil || c.BlockTypes != nil { if c.Rules != nil { return nil, errors.New("legacy nonIPQuery and blockTypes cannot be mixed with rules") } errors.PrintDeprecatedFeatureWarning(`"nonIPQuery" and "blockTypes"`, `"rules"`) rules, err := c.buildLegacyDNSPolicy() if err != nil { return nil, err } config.Rule = rules return config, nil } for _, r := range c.Rules { rule, err := r.Build() if err != nil { return nil, err } config.Rule = append(config.Rule, rule) } return config, nil } // todo: remove legacy func (c *DNSOutboundConfig) buildLegacyDNSPolicy() ([]*dns.DNSRuleConfig, error) { rules := make([]*dns.DNSRuleConfig, 0, 3) mode := "reject" if c.NonIPQuery != nil && *c.NonIPQuery != "" { mode = *c.NonIPQuery } switch mode { case "", "reject", "drop", "skip": default: return nil, errors.New("unknown nonIPQuery: ", mode) } if c.BlockTypes != nil && len(*c.BlockTypes) > 0 { rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Drop} if mode == "reject" { rule.Action = dns.RuleAction_Return rule.RCode = 5 } for _, qType := range *c.BlockTypes { if qType < 0 || qType > 65535 { return nil, errors.New("legacy blockTypes qType out of range: ", qType) } rule.QType = append(rule.QType, qType) } rules = append(rules, rule) } { rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Hijack} rule.QType = append(rule.QType, 1) rule.QType = append(rule.QType, 28) rules = append(rules, rule) } { rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Return} if mode == "reject" { rule.Action = dns.RuleAction_Return rule.RCode = 5 } else if mode == "drop" { rule.Action = dns.RuleAction_Drop } else if mode == "skip" { rule.Action = dns.RuleAction_Direct } rules = append(rules, rule) } return rules, nil }