github.com/xraypb/xray-core@v1.6.6/infra/conf/dns.go (about) 1 package conf 2 3 import ( 4 "encoding/json" 5 "sort" 6 "strings" 7 8 "github.com/xraypb/xray-core/app/dns" 9 "github.com/xraypb/xray-core/app/router" 10 "github.com/xraypb/xray-core/common/net" 11 ) 12 13 type NameServerConfig struct { 14 Address *Address 15 ClientIP *Address 16 Port uint16 17 SkipFallback bool 18 Domains []string 19 ExpectIPs StringList 20 } 21 22 func (c *NameServerConfig) UnmarshalJSON(data []byte) error { 23 var address Address 24 if err := json.Unmarshal(data, &address); err == nil { 25 c.Address = &address 26 return nil 27 } 28 29 var advanced struct { 30 Address *Address `json:"address"` 31 ClientIP *Address `json:"clientIp"` 32 Port uint16 `json:"port"` 33 SkipFallback bool `json:"skipFallback"` 34 Domains []string `json:"domains"` 35 ExpectIPs StringList `json:"expectIps"` 36 } 37 if err := json.Unmarshal(data, &advanced); err == nil { 38 c.Address = advanced.Address 39 c.ClientIP = advanced.ClientIP 40 c.Port = advanced.Port 41 c.SkipFallback = advanced.SkipFallback 42 c.Domains = advanced.Domains 43 c.ExpectIPs = advanced.ExpectIPs 44 return nil 45 } 46 47 return newError("failed to parse name server: ", string(data)) 48 } 49 50 func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType { 51 switch t { 52 case router.Domain_Domain: 53 return dns.DomainMatchingType_Subdomain 54 case router.Domain_Full: 55 return dns.DomainMatchingType_Full 56 case router.Domain_Plain: 57 return dns.DomainMatchingType_Keyword 58 case router.Domain_Regex: 59 return dns.DomainMatchingType_Regex 60 default: 61 panic("unknown domain type") 62 } 63 } 64 65 func (c *NameServerConfig) Build() (*dns.NameServer, error) { 66 if c.Address == nil { 67 return nil, newError("NameServer address is not specified.") 68 } 69 70 var domains []*dns.NameServer_PriorityDomain 71 var originalRules []*dns.NameServer_OriginalRule 72 73 for _, rule := range c.Domains { 74 parsedDomain, err := parseDomainRule(rule) 75 if err != nil { 76 return nil, newError("invalid domain rule: ", rule).Base(err) 77 } 78 79 for _, pd := range parsedDomain { 80 domains = append(domains, &dns.NameServer_PriorityDomain{ 81 Type: toDomainMatchingType(pd.Type), 82 Domain: pd.Value, 83 }) 84 } 85 originalRules = append(originalRules, &dns.NameServer_OriginalRule{ 86 Rule: rule, 87 Size: uint32(len(parsedDomain)), 88 }) 89 } 90 91 geoipList, err := ToCidrList(c.ExpectIPs) 92 if err != nil { 93 return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err) 94 } 95 96 var myClientIP []byte 97 if c.ClientIP != nil { 98 if !c.ClientIP.Family().IsIP() { 99 return nil, newError("not an IP address:", c.ClientIP.String()) 100 } 101 myClientIP = []byte(c.ClientIP.IP()) 102 } 103 104 return &dns.NameServer{ 105 Address: &net.Endpoint{ 106 Network: net.Network_UDP, 107 Address: c.Address.Build(), 108 Port: uint32(c.Port), 109 }, 110 ClientIp: myClientIP, 111 SkipFallback: c.SkipFallback, 112 PrioritizedDomain: domains, 113 Geoip: geoipList, 114 OriginalRules: originalRules, 115 }, nil 116 } 117 118 var typeMap = map[router.Domain_Type]dns.DomainMatchingType{ 119 router.Domain_Full: dns.DomainMatchingType_Full, 120 router.Domain_Domain: dns.DomainMatchingType_Subdomain, 121 router.Domain_Plain: dns.DomainMatchingType_Keyword, 122 router.Domain_Regex: dns.DomainMatchingType_Regex, 123 } 124 125 // DNSConfig is a JSON serializable object for dns.Config. 126 type DNSConfig struct { 127 Servers []*NameServerConfig `json:"servers"` 128 Hosts *HostsWrapper `json:"hosts"` 129 ClientIP *Address `json:"clientIp"` 130 Tag string `json:"tag"` 131 QueryStrategy string `json:"queryStrategy"` 132 DisableCache bool `json:"disableCache"` 133 DisableFallback bool `json:"disableFallback"` 134 DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` 135 } 136 137 type HostAddress struct { 138 addr *Address 139 addrs []*Address 140 } 141 142 // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON 143 func (h *HostAddress) UnmarshalJSON(data []byte) error { 144 addr := new(Address) 145 var addrs []*Address 146 switch { 147 case json.Unmarshal(data, &addr) == nil: 148 h.addr = addr 149 case json.Unmarshal(data, &addrs) == nil: 150 h.addrs = addrs 151 default: 152 return newError("invalid address") 153 } 154 return nil 155 } 156 157 type HostsWrapper struct { 158 Hosts map[string]*HostAddress 159 } 160 161 func getHostMapping(ha *HostAddress) *dns.Config_HostMapping { 162 if ha.addr != nil { 163 if ha.addr.Family().IsDomain() { 164 return &dns.Config_HostMapping{ 165 ProxiedDomain: ha.addr.Domain(), 166 } 167 } 168 return &dns.Config_HostMapping{ 169 Ip: [][]byte{ha.addr.IP()}, 170 } 171 } 172 173 ips := make([][]byte, 0, len(ha.addrs)) 174 for _, addr := range ha.addrs { 175 if addr.Family().IsDomain() { 176 return &dns.Config_HostMapping{ 177 ProxiedDomain: addr.Domain(), 178 } 179 } 180 ips = append(ips, []byte(addr.IP())) 181 } 182 return &dns.Config_HostMapping{ 183 Ip: ips, 184 } 185 } 186 187 // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON 188 func (m *HostsWrapper) UnmarshalJSON(data []byte) error { 189 hosts := make(map[string]*HostAddress) 190 err := json.Unmarshal(data, &hosts) 191 if err == nil { 192 m.Hosts = hosts 193 return nil 194 } 195 return newError("invalid DNS hosts").Base(err) 196 } 197 198 // Build implements Buildable 199 func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { 200 mappings := make([]*dns.Config_HostMapping, 0, 20) 201 202 domains := make([]string, 0, len(m.Hosts)) 203 for domain := range m.Hosts { 204 domains = append(domains, domain) 205 } 206 sort.Strings(domains) 207 208 for _, domain := range domains { 209 switch { 210 case strings.HasPrefix(domain, "domain:"): 211 domainName := domain[7:] 212 if len(domainName) == 0 { 213 return nil, newError("empty domain type of rule: ", domain) 214 } 215 mapping := getHostMapping(m.Hosts[domain]) 216 mapping.Type = dns.DomainMatchingType_Subdomain 217 mapping.Domain = domainName 218 mappings = append(mappings, mapping) 219 220 case strings.HasPrefix(domain, "geosite:"): 221 listName := domain[8:] 222 if len(listName) == 0 { 223 return nil, newError("empty geosite rule: ", domain) 224 } 225 geositeList, err := loadGeositeWithAttr("geosite.dat", listName) 226 if err != nil { 227 return nil, newError("failed to load geosite: ", listName).Base(err) 228 } 229 for _, d := range geositeList { 230 mapping := getHostMapping(m.Hosts[domain]) 231 mapping.Type = typeMap[d.Type] 232 mapping.Domain = d.Value 233 mappings = append(mappings, mapping) 234 } 235 236 case strings.HasPrefix(domain, "regexp:"): 237 regexpVal := domain[7:] 238 if len(regexpVal) == 0 { 239 return nil, newError("empty regexp type of rule: ", domain) 240 } 241 mapping := getHostMapping(m.Hosts[domain]) 242 mapping.Type = dns.DomainMatchingType_Regex 243 mapping.Domain = regexpVal 244 mappings = append(mappings, mapping) 245 246 case strings.HasPrefix(domain, "keyword:"): 247 keywordVal := domain[8:] 248 if len(keywordVal) == 0 { 249 return nil, newError("empty keyword type of rule: ", domain) 250 } 251 mapping := getHostMapping(m.Hosts[domain]) 252 mapping.Type = dns.DomainMatchingType_Keyword 253 mapping.Domain = keywordVal 254 mappings = append(mappings, mapping) 255 256 case strings.HasPrefix(domain, "full:"): 257 fullVal := domain[5:] 258 if len(fullVal) == 0 { 259 return nil, newError("empty full domain type of rule: ", domain) 260 } 261 mapping := getHostMapping(m.Hosts[domain]) 262 mapping.Type = dns.DomainMatchingType_Full 263 mapping.Domain = fullVal 264 mappings = append(mappings, mapping) 265 266 case strings.HasPrefix(domain, "dotless:"): 267 mapping := getHostMapping(m.Hosts[domain]) 268 mapping.Type = dns.DomainMatchingType_Regex 269 switch substr := domain[8:]; { 270 case substr == "": 271 mapping.Domain = "^[^.]*$" 272 case !strings.Contains(substr, "."): 273 mapping.Domain = "^[^.]*" + substr + "[^.]*$" 274 default: 275 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 276 } 277 mappings = append(mappings, mapping) 278 279 case strings.HasPrefix(domain, "ext:"): 280 kv := strings.Split(domain[4:], ":") 281 if len(kv) != 2 { 282 return nil, newError("invalid external resource: ", domain) 283 } 284 filename := kv[0] 285 list := kv[1] 286 geositeList, err := loadGeositeWithAttr(filename, list) 287 if err != nil { 288 return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err) 289 } 290 for _, d := range geositeList { 291 mapping := getHostMapping(m.Hosts[domain]) 292 mapping.Type = typeMap[d.Type] 293 mapping.Domain = d.Value 294 mappings = append(mappings, mapping) 295 } 296 297 default: 298 mapping := getHostMapping(m.Hosts[domain]) 299 mapping.Type = dns.DomainMatchingType_Full 300 mapping.Domain = domain 301 mappings = append(mappings, mapping) 302 } 303 } 304 return mappings, nil 305 } 306 307 // Build implements Buildable 308 func (c *DNSConfig) Build() (*dns.Config, error) { 309 config := &dns.Config{ 310 Tag: c.Tag, 311 DisableCache: c.DisableCache, 312 DisableFallback: c.DisableFallback, 313 DisableFallbackIfMatch: c.DisableFallbackIfMatch, 314 } 315 316 if c.ClientIP != nil { 317 if !c.ClientIP.Family().IsIP() { 318 return nil, newError("not an IP address:", c.ClientIP.String()) 319 } 320 config.ClientIp = []byte(c.ClientIP.IP()) 321 } 322 323 config.QueryStrategy = dns.QueryStrategy_USE_IP 324 switch strings.ToLower(c.QueryStrategy) { 325 case "useip", "use_ip", "use-ip": 326 config.QueryStrategy = dns.QueryStrategy_USE_IP 327 case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": 328 config.QueryStrategy = dns.QueryStrategy_USE_IP4 329 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": 330 config.QueryStrategy = dns.QueryStrategy_USE_IP6 331 } 332 333 for _, server := range c.Servers { 334 ns, err := server.Build() 335 if err != nil { 336 return nil, newError("failed to build nameserver").Base(err) 337 } 338 config.NameServer = append(config.NameServer, ns) 339 } 340 341 if c.Hosts != nil { 342 staticHosts, err := c.Hosts.Build() 343 if err != nil { 344 return nil, newError("failed to build hosts").Base(err) 345 } 346 config.StaticHosts = append(config.StaticHosts, staticHosts...) 347 } 348 349 return config, nil 350 }