nameserver.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package dns
  2. import (
  3. "context"
  4. "net/url"
  5. "strings"
  6. "time"
  7. "github.com/xtls/xray-core/app/router"
  8. "github.com/xtls/xray-core/common/errors"
  9. "github.com/xtls/xray-core/common/net"
  10. "github.com/xtls/xray-core/common/session"
  11. "github.com/xtls/xray-core/common/strmatcher"
  12. "github.com/xtls/xray-core/core"
  13. "github.com/xtls/xray-core/features/dns"
  14. "github.com/xtls/xray-core/features/routing"
  15. )
  16. // Server is the interface for Name Server.
  17. type Server interface {
  18. // Name of the Client.
  19. Name() string
  20. // QueryIP sends IP queries to its configured server.
  21. QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error)
  22. }
  23. // Client is the interface for DNS client.
  24. type Client struct {
  25. server Server
  26. skipFallback bool
  27. domains []string
  28. expectedIPs []*router.GeoIPMatcher
  29. unexpectedIPs []*router.GeoIPMatcher
  30. actPrior bool
  31. actUnprior bool
  32. tag string
  33. timeoutMs time.Duration
  34. finalQuery bool
  35. ipOption *dns.IPOption
  36. checkSystem bool
  37. }
  38. // NewServer creates a name server object according to the network destination url.
  39. func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (Server, error) {
  40. if address := dest.Address; address.Family().IsDomain() {
  41. u, err := url.Parse(address.Domain())
  42. if err != nil {
  43. return nil, err
  44. }
  45. switch {
  46. case strings.EqualFold(u.String(), "localhost"):
  47. return NewLocalNameServer(), nil
  48. case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
  49. return NewDoHNameServer(u, dispatcher, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil
  50. case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
  51. return NewDoHNameServer(u, dispatcher, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil
  52. case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
  53. return NewDoHNameServer(u, nil, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil
  54. case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
  55. return NewDoHNameServer(u, nil, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil
  56. case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
  57. return NewQUICNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP)
  58. case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
  59. return NewTCPNameServer(u, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP)
  60. case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
  61. return NewTCPLocalNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP)
  62. case strings.EqualFold(u.String(), "fakedns"):
  63. var fd dns.FakeDNSEngine
  64. err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
  65. fd = fdns
  66. })
  67. if err != nil {
  68. return nil, err
  69. }
  70. return NewFakeDNSServer(fd), nil
  71. }
  72. }
  73. if dest.Network == net.Network_Unknown {
  74. dest.Network = net.Network_UDP
  75. }
  76. if dest.Network == net.Network_UDP { // UDP classic DNS mode
  77. return NewClassicNameServer(dest, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP), nil
  78. }
  79. return nil, errors.New("No available name server could be created from ", dest).AtWarning()
  80. }
  81. // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
  82. func NewClient(
  83. ctx context.Context,
  84. ns *NameServer,
  85. clientIP net.IP,
  86. disableCache bool, serveStale bool, serveExpiredTTL uint32,
  87. tag string,
  88. ipOption dns.IPOption,
  89. matcherInfos *[]*DomainMatcherInfo,
  90. updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error,
  91. ) (*Client, error) {
  92. client := &Client{}
  93. err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
  94. // Create a new server for each client for now
  95. server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP)
  96. if err != nil {
  97. return errors.New("failed to create nameserver").Base(err).AtWarning()
  98. }
  99. // Prioritize local domains with specific TLDs or those without any dot for the local DNS
  100. if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
  101. ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
  102. ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
  103. // The following lines is a solution to avoid core panics(rule index out of range) when setting `localhost` DNS client in config.
  104. // Because the `localhost` DNS client will append len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule.
  105. // But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range).
  106. // To avoid this, the length of `matcherInfos` must be equal to the expected, so manually append it with Golang default zero value first for later modification.
  107. // Related issues:
  108. // https://github.com/v2fly/v2ray-core/issues/529
  109. // https://github.com/v2fly/v2ray-core/issues/719
  110. for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
  111. *matcherInfos = append(*matcherInfos, &DomainMatcherInfo{
  112. clientIdx: uint16(0),
  113. domainRuleIdx: uint16(0),
  114. })
  115. }
  116. }
  117. // Establish domain rules
  118. var rules []string
  119. ruleCurr := 0
  120. ruleIter := 0
  121. for _, domain := range ns.PrioritizedDomain {
  122. domainRule, err := toStrMatcher(domain.Type, domain.Domain)
  123. if err != nil {
  124. return errors.New("failed to create prioritized domain").Base(err).AtWarning()
  125. }
  126. originalRuleIdx := ruleCurr
  127. if ruleCurr < len(ns.OriginalRules) {
  128. rule := ns.OriginalRules[ruleCurr]
  129. if ruleCurr >= len(rules) {
  130. rules = append(rules, rule.Rule)
  131. }
  132. ruleIter++
  133. if ruleIter >= int(rule.Size) {
  134. ruleIter = 0
  135. ruleCurr++
  136. }
  137. } else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
  138. rules = append(rules, domainRule.String())
  139. ruleCurr++
  140. }
  141. err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
  142. if err != nil {
  143. return errors.New("failed to create prioritized domain").Base(err).AtWarning()
  144. }
  145. }
  146. // Establish expected IPs
  147. var expectedMatchers []*router.GeoIPMatcher
  148. for _, geoip := range ns.ExpectedGeoip {
  149. matcher, err := router.GlobalGeoIPContainer.Add(geoip)
  150. if err != nil {
  151. return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
  152. }
  153. expectedMatchers = append(expectedMatchers, matcher)
  154. }
  155. // Establish unexpected IPs
  156. var unexpectedMatchers []*router.GeoIPMatcher
  157. for _, geoip := range ns.UnexpectedGeoip {
  158. matcher, err := router.GlobalGeoIPContainer.Add(geoip)
  159. if err != nil {
  160. return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
  161. }
  162. unexpectedMatchers = append(unexpectedMatchers, matcher)
  163. }
  164. if len(clientIP) > 0 {
  165. switch ns.Address.Address.GetAddress().(type) {
  166. case *net.IPOrDomain_Domain:
  167. errors.LogInfo(ctx, "DNS: client ", ns.Address.Address.GetDomain(), " uses clientIP ", clientIP.String())
  168. case *net.IPOrDomain_Ip:
  169. errors.LogInfo(ctx, "DNS: client ", net.IP(ns.Address.Address.GetIp()), " uses clientIP ", clientIP.String())
  170. }
  171. }
  172. var timeoutMs = 4000 * time.Millisecond
  173. if ns.TimeoutMs > 0 {
  174. timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
  175. }
  176. checkSystem := ns.QueryStrategy == QueryStrategy_USE_SYS
  177. client.server = server
  178. client.skipFallback = ns.SkipFallback
  179. client.domains = rules
  180. client.expectedIPs = expectedMatchers
  181. client.unexpectedIPs = unexpectedMatchers
  182. client.actPrior = ns.ActPrior
  183. client.actUnprior = ns.ActUnprior
  184. client.tag = tag
  185. client.timeoutMs = timeoutMs
  186. client.finalQuery = ns.FinalQuery
  187. client.ipOption = &ipOption
  188. client.checkSystem = checkSystem
  189. return nil
  190. })
  191. return client, err
  192. }
  193. // Name returns the server name the client manages.
  194. func (c *Client) Name() string {
  195. return c.server.Name()
  196. }
  197. func (c *Client) IsFinalQuery() bool {
  198. return c.finalQuery
  199. }
  200. // QueryIP sends DNS query to the name server with the client's IP.
  201. func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
  202. if c.checkSystem {
  203. supportIPv4, supportIPv6 := checkSystemNetwork()
  204. option.IPv4Enable = option.IPv4Enable && supportIPv4
  205. option.IPv6Enable = option.IPv6Enable && supportIPv6
  206. } else {
  207. option.IPv4Enable = option.IPv4Enable && c.ipOption.IPv4Enable
  208. option.IPv6Enable = option.IPv6Enable && c.ipOption.IPv6Enable
  209. }
  210. if !option.IPv4Enable && !option.IPv6Enable {
  211. return nil, 0, dns.ErrEmptyResponse
  212. }
  213. ctx, cancel := context.WithTimeout(ctx, c.timeoutMs)
  214. ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag})
  215. ips, ttl, err := c.server.QueryIP(ctx, domain, option)
  216. cancel()
  217. if err != nil {
  218. return nil, 0, err
  219. }
  220. if len(ips) == 0 {
  221. return nil, 0, dns.ErrEmptyResponse
  222. }
  223. if len(c.expectedIPs) > 0 && !c.actPrior {
  224. ips = router.MatchIPs(c.expectedIPs, ips, false)
  225. errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
  226. if len(ips) == 0 {
  227. return nil, 0, dns.ErrEmptyResponse
  228. }
  229. }
  230. if len(c.unexpectedIPs) > 0 && !c.actUnprior {
  231. ips = router.MatchIPs(c.unexpectedIPs, ips, true)
  232. errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
  233. if len(ips) == 0 {
  234. return nil, 0, dns.ErrEmptyResponse
  235. }
  236. }
  237. if len(c.expectedIPs) > 0 && c.actPrior {
  238. ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
  239. if len(ipsNew) > 0 {
  240. ips = ipsNew
  241. errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name())
  242. }
  243. }
  244. if len(c.unexpectedIPs) > 0 && c.actUnprior {
  245. ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true)
  246. if len(ipsNew) > 0 {
  247. ips = ipsNew
  248. errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
  249. }
  250. }
  251. return ips, ttl, nil
  252. }
  253. func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) dns.IPOption {
  254. switch queryStrategy {
  255. case QueryStrategy_USE_IP:
  256. return ipOption
  257. case QueryStrategy_USE_SYS:
  258. return ipOption
  259. case QueryStrategy_USE_IP4:
  260. return dns.IPOption{
  261. IPv4Enable: ipOption.IPv4Enable,
  262. IPv6Enable: false,
  263. FakeEnable: false,
  264. }
  265. case QueryStrategy_USE_IP6:
  266. return dns.IPOption{
  267. IPv4Enable: false,
  268. IPv6Enable: ipOption.IPv6Enable,
  269. FakeEnable: false,
  270. }
  271. default:
  272. return ipOption
  273. }
  274. }