github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/dns/dns.go (about) 1 //go:build !confonly 2 // +build !confonly 3 4 // Package dns is an implementation of core.DNS feature. 5 package dns 6 7 //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen 8 9 import ( 10 "context" 11 "fmt" 12 "strings" 13 "sync" 14 15 core "github.com/v2fly/v2ray-core/v5" 16 "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" 17 "github.com/v2fly/v2ray-core/v5/app/router" 18 "github.com/v2fly/v2ray-core/v5/app/router/routercommon" 19 "github.com/v2fly/v2ray-core/v5/common" 20 "github.com/v2fly/v2ray-core/v5/common/errors" 21 "github.com/v2fly/v2ray-core/v5/common/net" 22 "github.com/v2fly/v2ray-core/v5/common/platform" 23 "github.com/v2fly/v2ray-core/v5/common/session" 24 "github.com/v2fly/v2ray-core/v5/common/strmatcher" 25 "github.com/v2fly/v2ray-core/v5/features" 26 "github.com/v2fly/v2ray-core/v5/features/dns" 27 "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" 28 "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" 29 ) 30 31 // DNS is a DNS rely server. 32 type DNS struct { 33 sync.Mutex 34 hosts *StaticHosts 35 clients []*Client 36 ctx context.Context 37 clientTags map[string]bool 38 fakeDNSEngine *FakeDNSEngine 39 domainMatcher strmatcher.IndexMatcher 40 matcherInfos []DomainMatcherInfo 41 } 42 43 // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher 44 type DomainMatcherInfo struct { 45 clientIdx uint16 46 domainRuleIdx uint16 47 } 48 49 // New creates a new DNS server with given configuration. 50 func New(ctx context.Context, config *Config) (*DNS, error) { 51 // Create static hosts 52 hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts) 53 if err != nil { 54 return nil, newError("failed to create hosts").Base(err) 55 } 56 57 // Create name servers from legacy configs 58 clients := []*Client{} 59 for _, endpoint := range config.NameServers { 60 features.PrintDeprecatedFeatureWarning("simple DNS server") 61 client, err := NewClient(ctx, &NameServer{Address: endpoint}, config) 62 if err != nil { 63 return nil, newError("failed to create client").Base(err) 64 } 65 clients = append(clients, client) 66 } 67 68 // Create name servers 69 nsClientMap := map[int]int{} 70 for nsIdx, ns := range config.NameServer { 71 client, err := NewClient(ctx, ns, config) 72 if err != nil { 73 return nil, newError("failed to create client").Base(err) 74 } 75 nsClientMap[nsIdx] = len(clients) 76 clients = append(clients, client) 77 } 78 79 // If there is no DNS client in config, add a `localhost` DNS client 80 if len(clients) == 0 { 81 clients = append(clients, NewLocalDNSClient()) 82 } 83 84 s := &DNS{ 85 hosts: hosts, 86 clients: clients, 87 ctx: ctx, 88 } 89 90 // Establish members related to global DNS state 91 s.clientTags = make(map[string]bool) 92 for _, client := range clients { 93 s.clientTags[client.tag] = true 94 } 95 if err := establishDomainRules(s, config, nsClientMap); err != nil { 96 return nil, err 97 } 98 if err := establishExpectedIPs(s, config, nsClientMap); err != nil { 99 return nil, err 100 } 101 if err := establishFakeDNS(s, config, nsClientMap); err != nil { 102 return nil, err 103 } 104 105 return s, nil 106 } 107 108 func establishDomainRules(s *DNS, config *Config, nsClientMap map[int]int) error { 109 domainRuleCount := 0 110 for _, ns := range config.NameServer { 111 domainRuleCount += len(ns.PrioritizedDomain) 112 } 113 // MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1 114 matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1) 115 var domainMatcher strmatcher.IndexMatcher 116 switch config.DomainMatcher { 117 case "mph", "hybrid": 118 newError("using mph domain matcher").AtDebug().WriteToLog() 119 domainMatcher = strmatcher.NewMphIndexMatcher() 120 case "linear": 121 fallthrough 122 default: 123 newError("using default domain matcher").AtDebug().WriteToLog() 124 domainMatcher = strmatcher.NewLinearIndexMatcher() 125 } 126 for nsIdx, ns := range config.NameServer { 127 clientIdx := nsClientMap[nsIdx] 128 var rules []string 129 ruleCurr := 0 130 ruleIter := 0 131 for _, domain := range ns.PrioritizedDomain { 132 domainRule, err := toStrMatcher(domain.Type, domain.Domain) 133 if err != nil { 134 return newError("failed to create prioritized domain").Base(err).AtWarning() 135 } 136 originalRuleIdx := ruleCurr 137 if ruleCurr < len(ns.OriginalRules) { 138 rule := ns.OriginalRules[ruleCurr] 139 if ruleCurr >= len(rules) { 140 rules = append(rules, rule.Rule) 141 } 142 ruleIter++ 143 if ruleIter >= int(rule.Size) { 144 ruleIter = 0 145 ruleCurr++ 146 } 147 } else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests) 148 rules = append(rules, domainRule.String()) 149 ruleCurr++ 150 } 151 midx := domainMatcher.Add(domainRule) 152 matcherInfos[midx] = DomainMatcherInfo{ 153 clientIdx: uint16(clientIdx), 154 domainRuleIdx: uint16(originalRuleIdx), 155 } 156 if err != nil { 157 return newError("failed to create prioritized domain").Base(err).AtWarning() 158 } 159 } 160 s.clients[clientIdx].domains = rules 161 } 162 if err := domainMatcher.Build(); err != nil { 163 return err 164 } 165 s.domainMatcher = domainMatcher 166 s.matcherInfos = matcherInfos 167 return nil 168 } 169 170 func establishExpectedIPs(s *DNS, config *Config, nsClientMap map[int]int) error { 171 geoipContainer := router.GeoIPMatcherContainer{} 172 for nsIdx, ns := range config.NameServer { 173 clientIdx := nsClientMap[nsIdx] 174 var matchers []*router.GeoIPMatcher 175 for _, geoip := range ns.Geoip { 176 matcher, err := geoipContainer.Add(geoip) 177 if err != nil { 178 return newError("failed to create ip matcher").Base(err).AtWarning() 179 } 180 matchers = append(matchers, matcher) 181 } 182 s.clients[clientIdx].expectIPs = matchers 183 } 184 return nil 185 } 186 187 func establishFakeDNS(s *DNS, config *Config, nsClientMap map[int]int) error { 188 fakeHolders := &fakedns.HolderMulti{} 189 fakeDefault := (*fakedns.HolderMulti)(nil) 190 if config.FakeDns != nil { 191 defaultEngine, err := fakeHolders.AddPoolMulti(config.FakeDns) 192 if err != nil { 193 return newError("fail to create fake dns").Base(err).AtWarning() 194 } 195 fakeDefault = defaultEngine 196 } 197 for nsIdx, ns := range config.NameServer { 198 clientIdx := nsClientMap[nsIdx] 199 if ns.FakeDns == nil { 200 continue 201 } 202 engine, err := fakeHolders.AddPoolMulti(ns.FakeDns) 203 if err != nil { 204 return newError("fail to create fake dns").Base(err).AtWarning() 205 } 206 s.clients[clientIdx].fakeDNS = NewFakeDNSServer(engine) 207 s.clients[clientIdx].queryStrategy.FakeEnable = true 208 } 209 // Do not create FakeDNSEngine feature if no FakeDNS server is configured 210 if fakeHolders.IsEmpty() { 211 return nil 212 } 213 // Add FakeDNSEngine feature when DNS feature is added for the first time 214 s.fakeDNSEngine = &FakeDNSEngine{dns: s, fakeHolders: fakeHolders, fakeDefault: fakeDefault} 215 return core.RequireFeatures(s.ctx, func(client dns.Client) error { 216 v := core.MustFromContext(s.ctx) 217 if v.GetFeature(dns.FakeDNSEngineType()) != nil { 218 return nil 219 } 220 if client, ok := client.(dns.ClientWithFakeDNS); ok { 221 return v.AddFeature(client.AsFakeDNSEngine()) 222 } 223 return nil 224 }) 225 } 226 227 // Type implements common.HasType. 228 func (*DNS) Type() interface{} { 229 return dns.ClientType() 230 } 231 232 // Start implements common.Runnable. 233 func (s *DNS) Start() error { 234 return nil 235 } 236 237 // Close implements common.Closable. 238 func (s *DNS) Close() error { 239 return nil 240 } 241 242 // IsOwnLink implements proxy.dns.ownLinkVerifier 243 func (s *DNS) IsOwnLink(ctx context.Context) bool { 244 inbound := session.InboundFromContext(ctx) 245 return inbound != nil && s.clientTags[inbound.Tag] 246 } 247 248 // AsFakeDNSClient implements dns.ClientWithFakeDNS. 249 func (s *DNS) AsFakeDNSClient() dns.Client { 250 return &FakeDNSClient{DNS: s} 251 } 252 253 // AsFakeDNSEngine implements dns.ClientWithFakeDNS. 254 func (s *DNS) AsFakeDNSEngine() dns.FakeDNSEngine { 255 return s.fakeDNSEngine 256 } 257 258 // LookupIP implements dns.Client. 259 func (s *DNS) LookupIP(domain string) ([]net.IP, error) { 260 return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true, FakeEnable: false}) 261 } 262 263 // LookupIPv4 implements dns.IPv4Lookup. 264 func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) { 265 return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, FakeEnable: false}) 266 } 267 268 // LookupIPv6 implements dns.IPv6Lookup. 269 func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) { 270 return s.lookupIPInternal(domain, dns.IPOption{IPv6Enable: true, FakeEnable: false}) 271 } 272 273 func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, error) { 274 if domain == "" { 275 return nil, newError("empty domain name") 276 } 277 278 // Normalize the FQDN form query 279 domain = strings.TrimSuffix(domain, ".") 280 281 // Static host lookup 282 switch addrs := s.hosts.Lookup(domain, option); { 283 case addrs == nil: // Domain not recorded in static host 284 break 285 case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled) 286 return nil, dns.ErrEmptyResponse 287 case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement 288 newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog() 289 domain = addrs[0].Domain() 290 default: // Successfully found ip records in static host 291 newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog() 292 return toNetIP(addrs) 293 } 294 295 // Name servers lookup 296 errs := []error{} 297 for _, client := range s.sortClients(domain, option) { 298 ips, err := client.QueryIP(s.ctx, domain, option) 299 if len(ips) > 0 { 300 return ips, nil 301 } 302 if err != nil { 303 errs = append(errs, err) 304 } 305 if err != dns.ErrEmptyResponse { // ErrEmptyResponse is not seen as failure, so no failed log 306 newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog() 307 } 308 if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch { 309 return nil, err // Only continue lookup for certain errors 310 } 311 } 312 313 if len(errs) == 0 { 314 return nil, dns.ErrEmptyResponse 315 } 316 return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...)) 317 } 318 319 func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { 320 clients := make([]*Client, 0, len(s.clients)) 321 clientUsed := make([]bool, len(s.clients)) 322 clientIdxs := make([]int, 0, len(s.clients)) 323 domainRules := []string{} 324 325 // Priority domain matching 326 for _, match := range s.domainMatcher.Match(domain) { 327 info := s.matcherInfos[match] 328 client := s.clients[info.clientIdx] 329 domainRule := client.domains[info.domainRuleIdx] 330 domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx)) 331 switch { 332 case clientUsed[info.clientIdx]: 333 continue 334 case !option.FakeEnable && isFakeDNS(client.server): 335 continue 336 } 337 clientUsed[info.clientIdx] = true 338 clients = append(clients, client) 339 clientIdxs = append(clientIdxs, int(info.clientIdx)) 340 } 341 342 // Default round-robin query 343 hasDomainMatch := len(clients) > 0 344 for idx, client := range s.clients { 345 switch { 346 case clientUsed[idx]: 347 continue 348 case !option.FakeEnable && isFakeDNS(client.server): 349 continue 350 case client.fallbackStrategy == FallbackStrategy_Disabled: 351 continue 352 case client.fallbackStrategy == FallbackStrategy_DisabledIfAnyMatch && hasDomainMatch: 353 continue 354 } 355 clientUsed[idx] = true 356 clients = append(clients, client) 357 clientIdxs = append(clientIdxs, idx) 358 } 359 360 if len(domainRules) > 0 { 361 newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog() 362 } 363 if len(clientIdxs) > 0 { 364 newError("domain ", domain, " will use DNS in order: ", s.formatClientNames(clientIdxs, option), " ", toReqTypes(option)).AtDebug().WriteToLog() 365 } 366 367 return clients 368 } 369 370 func (s *DNS) formatClientNames(clientIdxs []int, option dns.IPOption) []string { 371 clientNames := make([]string, 0, len(clientIdxs)) 372 counter := make(map[string]uint, len(clientIdxs)) 373 for _, clientIdx := range clientIdxs { 374 client := s.clients[clientIdx] 375 var name string 376 if option.With(client.queryStrategy).FakeEnable { 377 name = fmt.Sprintf("%s(DNS idx:%d)", client.fakeDNS.Name(), clientIdx) 378 } else { 379 name = client.Name() 380 } 381 counter[name]++ 382 clientNames = append(clientNames, name) 383 } 384 for idx, clientIdx := range clientIdxs { 385 name := clientNames[idx] 386 if counter[name] > 1 { 387 clientNames[idx] = fmt.Sprintf("%s(DNS idx:%d)", name, clientIdx) 388 } 389 } 390 return clientNames 391 } 392 393 var matcherTypeMap = map[routercommon.Domain_Type]DomainMatchingType{ 394 routercommon.Domain_Plain: DomainMatchingType_Keyword, 395 routercommon.Domain_Regex: DomainMatchingType_Regex, 396 routercommon.Domain_RootDomain: DomainMatchingType_Subdomain, 397 routercommon.Domain_Full: DomainMatchingType_Full, 398 } 399 400 func init() { 401 common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { 402 return New(ctx, config.(*Config)) 403 })) 404 405 common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { 406 ctx = cfgcommon.NewConfigureLoadingContext(ctx) 407 408 geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { 409 return "standard" 410 }) 411 412 if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { 413 cfgcommon.SetGeoDataLoader(ctx, loader) 414 } else { 415 return nil, newError("unable to create geo data loader ").Base(err) 416 } 417 418 cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) 419 geoLoader := cfgEnv.GetGeoLoader() 420 421 simplifiedConfig := config.(*SimplifiedConfig) 422 for _, v := range simplifiedConfig.NameServer { 423 for _, geo := range v.Geoip { 424 if geo.Code != "" { 425 filepath := "geoip.dat" 426 if geo.FilePath != "" { 427 filepath = geo.FilePath 428 } else { 429 geo.CountryCode = geo.Code 430 } 431 var err error 432 geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code) 433 if err != nil { 434 return nil, newError("unable to load geoip").Base(err) 435 } 436 } 437 } 438 for _, geo := range v.GeoDomain { 439 if geo.Code != "" { 440 filepath := "geosite.dat" 441 if geo.FilePath != "" { 442 filepath = geo.FilePath 443 } 444 var err error 445 geo.Domain, err = geoLoader.LoadGeoSiteWithAttr(filepath, geo.Code) 446 if err != nil { 447 return nil, newError("unable to load geodomain").Base(err) 448 } 449 } 450 for _, domain := range geo.Domain { 451 v.PrioritizedDomain = append(v.PrioritizedDomain, &SimplifiedNameServer_PriorityDomain{ 452 Type: matcherTypeMap[domain.Type], 453 Domain: domain.Value, 454 }) 455 } 456 } 457 } 458 459 var nameservers []*NameServer 460 461 for _, v := range simplifiedConfig.NameServer { 462 nameserver := &NameServer{ 463 Address: v.Address, 464 ClientIp: net.ParseIP(v.ClientIp), 465 Tag: v.Tag, 466 QueryStrategy: v.QueryStrategy, 467 CacheStrategy: v.CacheStrategy, 468 FallbackStrategy: v.FallbackStrategy, 469 SkipFallback: v.SkipFallback, 470 Geoip: v.Geoip, 471 } 472 for _, prioritizedDomain := range v.PrioritizedDomain { 473 nameserver.PrioritizedDomain = append(nameserver.PrioritizedDomain, &NameServer_PriorityDomain{ 474 Type: prioritizedDomain.Type, 475 Domain: prioritizedDomain.Domain, 476 }) 477 } 478 nameservers = append(nameservers, nameserver) 479 } 480 481 var statichosts []*HostMapping 482 483 for _, v := range simplifiedConfig.StaticHosts { 484 statichost := &HostMapping{ 485 Type: v.Type, 486 Domain: v.Domain, 487 ProxiedDomain: v.ProxiedDomain, 488 } 489 for _, ip := range v.Ip { 490 statichost.Ip = append(statichost.Ip, net.ParseIP(ip)) 491 } 492 statichosts = append(statichosts, statichost) 493 } 494 495 fullConfig := &Config{ 496 StaticHosts: statichosts, 497 NameServer: nameservers, 498 ClientIp: net.ParseIP(simplifiedConfig.ClientIp), 499 Tag: simplifiedConfig.Tag, 500 DomainMatcher: simplifiedConfig.DomainMatcher, 501 QueryStrategy: simplifiedConfig.QueryStrategy, 502 CacheStrategy: simplifiedConfig.CacheStrategy, 503 FallbackStrategy: simplifiedConfig.FallbackStrategy, 504 // Deprecated flags 505 DisableCache: simplifiedConfig.DisableCache, 506 DisableFallback: simplifiedConfig.DisableFallback, 507 DisableFallbackIfMatch: simplifiedConfig.DisableFallbackIfMatch, 508 } 509 return common.CreateObject(ctx, fullConfig) 510 })) 511 }