github.com/imannamdari/v2ray-core/v5@v5.0.5/infra/conf/synthetic/dns/dns.go (about) 1 package dns 2 3 //go:generate go run github.com/imannamdari/v2ray-core/v5/common/errors/errorgen 4 5 import ( 6 "context" 7 "encoding/json" 8 "sort" 9 "strings" 10 11 "github.com/imannamdari/v2ray-core/v5/app/dns" 12 "github.com/imannamdari/v2ray-core/v5/app/dns/fakedns" 13 "github.com/imannamdari/v2ray-core/v5/app/router/routercommon" 14 "github.com/imannamdari/v2ray-core/v5/common/net" 15 "github.com/imannamdari/v2ray-core/v5/common/platform" 16 "github.com/imannamdari/v2ray-core/v5/infra/conf/cfgcommon" 17 "github.com/imannamdari/v2ray-core/v5/infra/conf/geodata" 18 rule2 "github.com/imannamdari/v2ray-core/v5/infra/conf/rule" 19 ) 20 21 type NameServerConfig struct { 22 Address *cfgcommon.Address 23 ClientIP *cfgcommon.Address 24 Port uint16 25 Tag string 26 QueryStrategy string 27 CacheStrategy string 28 FallbackStrategy string 29 SkipFallback bool 30 Domains []string 31 ExpectIPs cfgcommon.StringList 32 FakeDNS FakeDNSConfigExtend 33 34 cfgctx context.Context 35 } 36 37 func (c *NameServerConfig) UnmarshalJSON(data []byte) error { 38 var address cfgcommon.Address 39 if err := json.Unmarshal(data, &address); err == nil { 40 c.Address = &address 41 return nil 42 } 43 44 var advanced struct { 45 Address *cfgcommon.Address `json:"address"` 46 ClientIP *cfgcommon.Address `json:"clientIp"` 47 Port uint16 `json:"port"` 48 Tag string `json:"tag"` 49 QueryStrategy string `json:"queryStrategy"` 50 CacheStrategy string `json:"cacheStrategy"` 51 FallbackStrategy string `json:"fallbackStrategy"` 52 SkipFallback bool `json:"skipFallback"` 53 Domains []string `json:"domains"` 54 ExpectIPs cfgcommon.StringList `json:"expectIps"` 55 FakeDNS FakeDNSConfigExtend `json:"fakedns"` 56 } 57 if err := json.Unmarshal(data, &advanced); err == nil { 58 c.Address = advanced.Address 59 c.ClientIP = advanced.ClientIP 60 c.Port = advanced.Port 61 c.Tag = advanced.Tag 62 c.QueryStrategy = advanced.QueryStrategy 63 c.CacheStrategy = advanced.CacheStrategy 64 c.FallbackStrategy = advanced.FallbackStrategy 65 c.SkipFallback = advanced.SkipFallback 66 c.Domains = advanced.Domains 67 c.ExpectIPs = advanced.ExpectIPs 68 c.FakeDNS = advanced.FakeDNS 69 return nil 70 } 71 72 return newError("failed to parse name server: ", string(data)) 73 } 74 75 func toDomainMatchingType(t routercommon.Domain_Type) dns.DomainMatchingType { 76 switch t { 77 case routercommon.Domain_RootDomain: 78 return dns.DomainMatchingType_Subdomain 79 case routercommon.Domain_Full: 80 return dns.DomainMatchingType_Full 81 case routercommon.Domain_Plain: 82 return dns.DomainMatchingType_Keyword 83 case routercommon.Domain_Regex: 84 return dns.DomainMatchingType_Regex 85 default: 86 panic("unknown domain type") 87 } 88 } 89 90 func (c *NameServerConfig) BuildV5(ctx context.Context) (*dns.NameServer, error) { 91 c.cfgctx = ctx 92 return c.Build() 93 } 94 95 func (c *NameServerConfig) Build() (*dns.NameServer, error) { 96 cfgctx := c.cfgctx 97 98 if c.Address == nil { 99 return nil, newError("NameServer address is not specified.") 100 } 101 102 var domains []*dns.NameServer_PriorityDomain 103 var originalRules []*dns.NameServer_OriginalRule 104 105 for _, rule := range c.Domains { 106 parsedDomain, err := rule2.ParseDomainRule(cfgctx, rule) 107 if err != nil { 108 return nil, newError("invalid domain rule: ", rule).Base(err) 109 } 110 111 for _, pd := range parsedDomain { 112 domains = append(domains, &dns.NameServer_PriorityDomain{ 113 Type: toDomainMatchingType(pd.Type), 114 Domain: pd.Value, 115 }) 116 } 117 originalRules = append(originalRules, &dns.NameServer_OriginalRule{ 118 Rule: rule, 119 Size: uint32(len(parsedDomain)), 120 }) 121 } 122 123 geoipList, err := rule2.ToCidrList(cfgctx, c.ExpectIPs) 124 if err != nil { 125 return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err) 126 } 127 128 var fakeDNS *fakedns.FakeDnsPoolMulti 129 if c.FakeDNS.FakeDNSConfig != nil { 130 fake, err := c.FakeDNS.FakeDNSConfig.Build() 131 if err != nil { 132 return nil, newError("failed to build fakedns").Base(err) 133 } 134 fakeDNS = fake 135 } 136 137 var myClientIP []byte 138 if c.ClientIP != nil { 139 if !c.ClientIP.Family().IsIP() { 140 return nil, newError("not an IP address:", c.ClientIP.String()) 141 } 142 myClientIP = []byte(c.ClientIP.IP()) 143 } 144 145 queryStrategy := new(dns.QueryStrategy) 146 switch strings.ToLower(c.QueryStrategy) { 147 case "useip", "use_ip", "use-ip": 148 *queryStrategy = dns.QueryStrategy_USE_IP 149 case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": 150 *queryStrategy = dns.QueryStrategy_USE_IP4 151 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": 152 *queryStrategy = dns.QueryStrategy_USE_IP6 153 default: 154 queryStrategy = nil 155 } 156 157 cacheStrategy := new(dns.CacheStrategy) 158 switch strings.ToLower(c.CacheStrategy) { 159 case "enabled": 160 *cacheStrategy = dns.CacheStrategy_CacheEnabled 161 case "disabled": 162 *cacheStrategy = dns.CacheStrategy_CacheDisabled 163 default: 164 cacheStrategy = nil 165 } 166 167 fallbackStrategy := new(dns.FallbackStrategy) 168 switch strings.ToLower(c.FallbackStrategy) { 169 case "enabled": 170 *fallbackStrategy = dns.FallbackStrategy_Enabled 171 case "disabled": 172 *fallbackStrategy = dns.FallbackStrategy_Disabled 173 case "disabledifanymatch", "disabled_if_any_match", "disabled-if-any-match": 174 *fallbackStrategy = dns.FallbackStrategy_DisabledIfAnyMatch 175 default: 176 fallbackStrategy = nil 177 } 178 179 return &dns.NameServer{ 180 Address: &net.Endpoint{ 181 Network: net.Network_UDP, 182 Address: c.Address.Build(), 183 Port: uint32(c.Port), 184 }, 185 ClientIp: myClientIP, 186 Tag: c.Tag, 187 SkipFallback: c.SkipFallback, 188 QueryStrategy: queryStrategy, 189 CacheStrategy: cacheStrategy, 190 FallbackStrategy: fallbackStrategy, 191 PrioritizedDomain: domains, 192 Geoip: geoipList, 193 OriginalRules: originalRules, 194 FakeDns: fakeDNS, 195 }, nil 196 } 197 198 var typeMap = map[routercommon.Domain_Type]dns.DomainMatchingType{ 199 routercommon.Domain_Full: dns.DomainMatchingType_Full, 200 routercommon.Domain_RootDomain: dns.DomainMatchingType_Subdomain, 201 routercommon.Domain_Plain: dns.DomainMatchingType_Keyword, 202 routercommon.Domain_Regex: dns.DomainMatchingType_Regex, 203 } 204 205 // DNSConfig is a JSON serializable object for dns.Config. 206 type DNSConfig struct { // nolint: revive 207 Servers []*NameServerConfig `json:"servers"` 208 Hosts map[string]*HostAddress `json:"hosts"` 209 FakeDNS *FakeDNSConfig `json:"fakedns"` 210 DomainMatcher string `json:"domainMatcher"` 211 ClientIP *cfgcommon.Address `json:"clientIp"` 212 Tag string `json:"tag"` 213 QueryStrategy string `json:"queryStrategy"` 214 CacheStrategy string `json:"cacheStrategy"` 215 FallbackStrategy string `json:"fallbackStrategy"` 216 DisableCache bool `json:"disableCache"` 217 DisableFallback bool `json:"disableFallback"` 218 DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` 219 cfgctx context.Context 220 } 221 222 type HostAddress struct { 223 addr *cfgcommon.Address 224 addrs []*cfgcommon.Address 225 } 226 227 // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON 228 func (h *HostAddress) UnmarshalJSON(data []byte) error { 229 addr := new(cfgcommon.Address) 230 var addrs []*cfgcommon.Address 231 switch { 232 case json.Unmarshal(data, &addr) == nil: 233 h.addr = addr 234 case json.Unmarshal(data, &addrs) == nil: 235 h.addrs = addrs 236 default: 237 return newError("invalid address") 238 } 239 return nil 240 } 241 242 func getHostMapping(ha *HostAddress) *dns.HostMapping { 243 if ha.addr != nil { 244 if ha.addr.Family().IsDomain() { 245 return &dns.HostMapping{ 246 ProxiedDomain: ha.addr.Domain(), 247 } 248 } 249 return &dns.HostMapping{ 250 Ip: [][]byte{ha.addr.IP()}, 251 } 252 } 253 254 ips := make([][]byte, 0, len(ha.addrs)) 255 for _, addr := range ha.addrs { 256 if addr.Family().IsDomain() { 257 return &dns.HostMapping{ 258 ProxiedDomain: addr.Domain(), 259 } 260 } 261 ips = append(ips, []byte(addr.IP())) 262 } 263 return &dns.HostMapping{ 264 Ip: ips, 265 } 266 } 267 268 func (c *DNSConfig) BuildV5(ctx context.Context) (*dns.Config, error) { 269 c.cfgctx = ctx 270 return c.Build() 271 } 272 273 // Build implements Buildable 274 func (c *DNSConfig) Build() (*dns.Config, error) { 275 if c.cfgctx == nil { 276 c.cfgctx = cfgcommon.NewConfigureLoadingContext(context.Background()) 277 278 geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { 279 return "standard" 280 }) 281 282 if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { 283 cfgcommon.SetGeoDataLoader(c.cfgctx, loader) 284 } else { 285 return nil, newError("unable to create geo data loader ").Base(err) 286 } 287 } 288 289 cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(c.cfgctx) 290 geoLoader := cfgEnv.GetGeoLoader() 291 292 config := &dns.Config{ 293 Tag: c.Tag, 294 DisableCache: c.DisableCache, 295 DisableFallback: c.DisableFallback, 296 DisableFallbackIfMatch: c.DisableFallbackIfMatch, 297 DomainMatcher: c.DomainMatcher, 298 } 299 300 if c.ClientIP != nil { 301 if !c.ClientIP.Family().IsIP() { 302 return nil, newError("not an IP address:", c.ClientIP.String()) 303 } 304 config.ClientIp = []byte(c.ClientIP.IP()) 305 } 306 307 config.QueryStrategy = dns.QueryStrategy_USE_IP 308 switch strings.ToLower(c.QueryStrategy) { 309 case "useip", "use_ip", "use-ip": 310 config.QueryStrategy = dns.QueryStrategy_USE_IP 311 case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": 312 config.QueryStrategy = dns.QueryStrategy_USE_IP4 313 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": 314 config.QueryStrategy = dns.QueryStrategy_USE_IP6 315 } 316 317 config.CacheStrategy = dns.CacheStrategy_CacheEnabled 318 switch strings.ToLower(c.CacheStrategy) { 319 case "enabled": 320 config.CacheStrategy = dns.CacheStrategy_CacheEnabled 321 case "disabled": 322 config.CacheStrategy = dns.CacheStrategy_CacheDisabled 323 } 324 325 config.FallbackStrategy = dns.FallbackStrategy_Enabled 326 switch strings.ToLower(c.FallbackStrategy) { 327 case "enabled": 328 config.FallbackStrategy = dns.FallbackStrategy_Enabled 329 case "disabled": 330 config.FallbackStrategy = dns.FallbackStrategy_Disabled 331 case "disabledifanymatch", "disabled_if_any_match", "disabled-if-any-match": 332 config.FallbackStrategy = dns.FallbackStrategy_DisabledIfAnyMatch 333 } 334 335 for _, server := range c.Servers { 336 server.cfgctx = c.cfgctx 337 ns, err := server.Build() 338 if err != nil { 339 return nil, newError("failed to build nameserver").Base(err) 340 } 341 config.NameServer = append(config.NameServer, ns) 342 } 343 344 if c.Hosts != nil { 345 mappings := make([]*dns.HostMapping, 0, 20) 346 347 domains := make([]string, 0, len(c.Hosts)) 348 for domain := range c.Hosts { 349 domains = append(domains, domain) 350 } 351 sort.Strings(domains) 352 353 for _, domain := range domains { 354 switch { 355 case strings.HasPrefix(domain, "domain:"): 356 domainName := domain[7:] 357 if len(domainName) == 0 { 358 return nil, newError("empty domain type of rule: ", domain) 359 } 360 mapping := getHostMapping(c.Hosts[domain]) 361 mapping.Type = dns.DomainMatchingType_Subdomain 362 mapping.Domain = domainName 363 mappings = append(mappings, mapping) 364 365 case strings.HasPrefix(domain, "geosite:"): 366 listName := domain[8:] 367 if len(listName) == 0 { 368 return nil, newError("empty geosite rule: ", domain) 369 } 370 geositeList, err := geoLoader.LoadGeoSite(listName) 371 if err != nil { 372 return nil, newError("failed to load geosite: ", listName).Base(err) 373 } 374 for _, d := range geositeList { 375 mapping := getHostMapping(c.Hosts[domain]) 376 mapping.Type = typeMap[d.Type] 377 mapping.Domain = d.Value 378 mappings = append(mappings, mapping) 379 } 380 381 case strings.HasPrefix(domain, "regexp:"): 382 regexpVal := domain[7:] 383 if len(regexpVal) == 0 { 384 return nil, newError("empty regexp type of rule: ", domain) 385 } 386 mapping := getHostMapping(c.Hosts[domain]) 387 mapping.Type = dns.DomainMatchingType_Regex 388 mapping.Domain = regexpVal 389 mappings = append(mappings, mapping) 390 391 case strings.HasPrefix(domain, "keyword:"): 392 keywordVal := domain[8:] 393 if len(keywordVal) == 0 { 394 return nil, newError("empty keyword type of rule: ", domain) 395 } 396 mapping := getHostMapping(c.Hosts[domain]) 397 mapping.Type = dns.DomainMatchingType_Keyword 398 mapping.Domain = keywordVal 399 mappings = append(mappings, mapping) 400 401 case strings.HasPrefix(domain, "full:"): 402 fullVal := domain[5:] 403 if len(fullVal) == 0 { 404 return nil, newError("empty full domain type of rule: ", domain) 405 } 406 mapping := getHostMapping(c.Hosts[domain]) 407 mapping.Type = dns.DomainMatchingType_Full 408 mapping.Domain = fullVal 409 mappings = append(mappings, mapping) 410 411 case strings.HasPrefix(domain, "dotless:"): 412 mapping := getHostMapping(c.Hosts[domain]) 413 mapping.Type = dns.DomainMatchingType_Regex 414 switch substr := domain[8:]; { 415 case substr == "": 416 mapping.Domain = "^[^.]*$" 417 case !strings.Contains(substr, "."): 418 mapping.Domain = "^[^.]*" + substr + "[^.]*$" 419 default: 420 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 421 } 422 mappings = append(mappings, mapping) 423 424 case strings.HasPrefix(domain, "ext:"): 425 kv := strings.Split(domain[4:], ":") 426 if len(kv) != 2 { 427 return nil, newError("invalid external resource: ", domain) 428 } 429 filename := kv[0] 430 list := kv[1] 431 geositeList, err := geoLoader.LoadGeoSiteWithAttr(filename, list) 432 if err != nil { 433 return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err) 434 } 435 for _, d := range geositeList { 436 mapping := getHostMapping(c.Hosts[domain]) 437 mapping.Type = typeMap[d.Type] 438 mapping.Domain = d.Value 439 mappings = append(mappings, mapping) 440 } 441 442 default: 443 mapping := getHostMapping(c.Hosts[domain]) 444 mapping.Type = dns.DomainMatchingType_Full 445 mapping.Domain = domain 446 mappings = append(mappings, mapping) 447 } 448 } 449 450 config.StaticHosts = append(config.StaticHosts, mappings...) 451 } 452 453 if c.FakeDNS != nil { 454 fakeDNS, err := c.FakeDNS.Build() 455 if err != nil { 456 return nil, newError("failed to build fakedns").Base(err) 457 } 458 config.FakeDns = fakeDNS 459 } 460 461 return config, nil 462 }