github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/dns/nameserver.go (about) 1 package dns 2 3 import ( 4 "context" 5 "net/url" 6 "strings" 7 "time" 8 9 core "github.com/v2fly/v2ray-core/v5" 10 "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" 11 "github.com/v2fly/v2ray-core/v5/app/router" 12 "github.com/v2fly/v2ray-core/v5/common/errors" 13 "github.com/v2fly/v2ray-core/v5/common/net" 14 "github.com/v2fly/v2ray-core/v5/common/session" 15 "github.com/v2fly/v2ray-core/v5/features" 16 "github.com/v2fly/v2ray-core/v5/features/dns" 17 "github.com/v2fly/v2ray-core/v5/features/routing" 18 ) 19 20 // Server is the interface for Name Server. 21 type Server interface { 22 // Name of the Client. 23 Name() string 24 // QueryIP sends IP queries to its configured server. 25 QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error) 26 } 27 28 // Client is the interface for DNS client. 29 type Client struct { 30 server Server 31 clientIP net.IP 32 tag string 33 34 queryStrategy dns.IPOption 35 cacheStrategy CacheStrategy 36 fallbackStrategy FallbackStrategy 37 38 domains []string 39 expectIPs []*router.GeoIPMatcher 40 fakeDNS Server 41 } 42 43 var errExpectedIPNonMatch = errors.New("expectIPs not match") 44 45 // NewServer creates a name server object according to the network destination url. 46 func NewServer(ctx context.Context, dest net.Destination, onCreated func(Server) error) error { 47 onCreatedWithError := func(server Server, err error) error { 48 if err != nil { 49 return err 50 } 51 return onCreated(server) 52 } 53 if address := dest.Address; address.Family().IsDomain() { 54 u, err := url.Parse(address.Domain()) 55 if err != nil { 56 return err 57 } 58 switch { 59 case strings.EqualFold(u.String(), "localhost"): 60 return onCreated(NewLocalNameServer()) 61 case strings.EqualFold(u.String(), "fakedns"): 62 return core.RequireFeatures(ctx, func(fakedns dns.FakeDNSEngine) error { return onCreated(NewFakeDNSServer(fakedns)) }) 63 case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode 64 return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreatedWithError(NewDoHNameServer(u, dispatcher)) }) 65 case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode 66 return onCreated(NewDoHLocalNameServer(u)) 67 case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode 68 return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreatedWithError(NewTCPNameServer(u, dispatcher)) }) 69 case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode 70 return onCreatedWithError(NewTCPLocalNameServer(u)) 71 case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode 72 return onCreatedWithError(NewQUICNameServer(u)) 73 } 74 } 75 if dest.Network == net.Network_Unknown { 76 dest.Network = net.Network_UDP 77 } 78 if dest.Network == net.Network_UDP { // UDP classic DNS mode 79 return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreated(NewClassicNameServer(dest, dispatcher)) }) 80 } 81 return newError("No available name server could be created from ", dest).AtWarning() 82 } 83 84 // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs. 85 func NewClient(ctx context.Context, ns *NameServer, dns *Config) (*Client, error) { 86 client := &Client{} 87 88 // Create DNS server instance 89 err := NewServer(ctx, ns.Address.AsDestination(), func(server Server) error { 90 client.server = server 91 return nil 92 }) 93 if err != nil { 94 return nil, err 95 } 96 97 // Initialize fields with default values 98 if len(ns.Tag) == 0 { 99 ns.Tag = dns.Tag 100 if len(ns.Tag) == 0 { 101 ns.Tag = generateRandomTag() 102 } 103 } 104 if len(ns.ClientIp) == 0 { 105 ns.ClientIp = dns.ClientIp 106 } 107 if ns.QueryStrategy == nil { 108 ns.QueryStrategy = &dns.QueryStrategy 109 } 110 if ns.CacheStrategy == nil { 111 ns.CacheStrategy = new(CacheStrategy) 112 switch { 113 case dns.CacheStrategy != CacheStrategy_CacheEnabled: 114 *ns.CacheStrategy = dns.CacheStrategy 115 case dns.DisableCache: 116 features.PrintDeprecatedFeatureWarning("DNS disableCache settings") 117 *ns.CacheStrategy = CacheStrategy_CacheDisabled 118 } 119 } 120 if ns.FallbackStrategy == nil { 121 ns.FallbackStrategy = new(FallbackStrategy) 122 switch { 123 case ns.SkipFallback: 124 features.PrintDeprecatedFeatureWarning("DNS server skipFallback settings") 125 *ns.FallbackStrategy = FallbackStrategy_Disabled 126 case dns.FallbackStrategy != FallbackStrategy_Enabled: 127 *ns.FallbackStrategy = dns.FallbackStrategy 128 case dns.DisableFallback: 129 features.PrintDeprecatedFeatureWarning("DNS disableFallback settings") 130 *ns.FallbackStrategy = FallbackStrategy_Disabled 131 case dns.DisableFallbackIfMatch: 132 features.PrintDeprecatedFeatureWarning("DNS disableFallbackIfMatch settings") 133 *ns.FallbackStrategy = FallbackStrategy_DisabledIfAnyMatch 134 } 135 } 136 if (ns.FakeDns != nil && len(ns.FakeDns.Pools) == 0) || // Use globally configured fake ip pool if: 1. `fakedns` config is set, but empty(represents { "fakedns": true } in JSON settings); 137 ns.FakeDns == nil && strings.EqualFold(ns.Address.Address.GetDomain(), "fakedns") { // 2. `fakedns` config not set, but server address is `fakedns`(represents { "address": "fakedns" } in JSON settings). 138 if dns.FakeDns != nil { 139 ns.FakeDns = dns.FakeDns 140 } else { 141 ns.FakeDns = &fakedns.FakeDnsPoolMulti{} 142 queryStrategy := toIPOption(*ns.QueryStrategy) 143 if queryStrategy.IPv4Enable { 144 ns.FakeDns.Pools = append(ns.FakeDns.Pools, &fakedns.FakeDnsPool{ 145 IpPool: "198.18.0.0/15", 146 LruSize: 65535, 147 }) 148 } 149 if queryStrategy.IPv6Enable { 150 ns.FakeDns.Pools = append(ns.FakeDns.Pools, &fakedns.FakeDnsPool{ 151 IpPool: "fc00::/18", 152 LruSize: 65535, 153 }) 154 } 155 } 156 } 157 158 // Priotize local domains with specific TLDs or without any dot to local DNS 159 if strings.EqualFold(ns.Address.Address.GetDomain(), "localhost") { 160 ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...) 161 ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule) 162 } 163 164 if len(ns.ClientIp) > 0 { 165 newError("DNS: client ", ns.Address.Address.AsAddress(), " uses clientIP ", net.IP(ns.ClientIp).String()).AtInfo().WriteToLog() 166 } 167 168 client.clientIP = ns.ClientIp 169 client.tag = ns.Tag 170 client.queryStrategy = toIPOption(*ns.QueryStrategy) 171 client.cacheStrategy = *ns.CacheStrategy 172 client.fallbackStrategy = *ns.FallbackStrategy 173 return client, nil 174 } 175 176 // Name returns the server name the client manages. 177 func (c *Client) Name() string { 178 return c.server.Name() 179 } 180 181 // QueryIP send DNS query to the name server with the client's IP and IP options. 182 func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error) { 183 queryOption := option.With(c.queryStrategy) 184 if !queryOption.IsValid() { 185 newError(c.server.Name(), " returns empty answer: ", domain, ". ", toReqTypes(option)).AtInfo().WriteToLog() 186 return nil, dns.ErrEmptyResponse 187 } 188 server := c.server 189 if queryOption.FakeEnable && c.fakeDNS != nil { 190 server = c.fakeDNS 191 } 192 disableCache := c.cacheStrategy == CacheStrategy_CacheDisabled 193 194 ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag}) 195 ctx, cancel := context.WithTimeout(ctx, 4*time.Second) 196 ips, err := server.QueryIP(ctx, domain, c.clientIP, queryOption, disableCache) 197 cancel() 198 199 if err != nil || queryOption.FakeEnable { 200 return ips, err 201 } 202 return c.MatchExpectedIPs(domain, ips) 203 } 204 205 // MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones. 206 func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) { 207 if len(c.expectIPs) == 0 { 208 return ips, nil 209 } 210 newIps := []net.IP{} 211 for _, ip := range ips { 212 for _, matcher := range c.expectIPs { 213 if matcher.Match(ip) { 214 newIps = append(newIps, ip) 215 break 216 } 217 } 218 } 219 if len(newIps) == 0 { 220 return nil, errExpectedIPNonMatch 221 } 222 newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog() 223 return newIps, nil 224 }