dns.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. // Package dns is an implementation of core.DNS feature.
  2. package dns
  3. import (
  4. "context"
  5. go_errors "errors"
  6. "fmt"
  7. "os"
  8. "runtime"
  9. "sort"
  10. "strings"
  11. "sync"
  12. "time"
  13. router "github.com/xtls/xray-core/app/router"
  14. "github.com/xtls/xray-core/common"
  15. "github.com/xtls/xray-core/common/errors"
  16. "github.com/xtls/xray-core/common/net"
  17. "github.com/xtls/xray-core/common/platform/filesystem"
  18. "github.com/xtls/xray-core/common/session"
  19. "github.com/xtls/xray-core/common/strmatcher"
  20. "github.com/xtls/xray-core/features/dns"
  21. "google.golang.org/protobuf/proto"
  22. )
  23. // DNS is a DNS rely server.
  24. type DNS struct {
  25. sync.Mutex
  26. disableFallback bool
  27. disableFallbackIfMatch bool
  28. enableParallelQuery bool
  29. ipOption *dns.IPOption
  30. hosts *StaticHosts
  31. clients []*Client
  32. ctx context.Context
  33. domainMatcher strmatcher.IndexMatcher
  34. matcherInfos []*DomainMatcherInfo
  35. checkSystem bool
  36. }
  37. // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
  38. type DomainMatcherInfo struct {
  39. clientIdx uint16
  40. domainRuleIdx uint16
  41. }
  42. // New creates a new DNS server with given configuration.
  43. func New(ctx context.Context, config *Config) (*DNS, error) {
  44. var clientIP net.IP
  45. switch len(config.ClientIp) {
  46. case 0, net.IPv4len, net.IPv6len:
  47. clientIP = net.IP(config.ClientIp)
  48. default:
  49. return nil, errors.New("unexpected client IP length ", len(config.ClientIp))
  50. }
  51. var ipOption dns.IPOption
  52. checkSystem := false
  53. switch config.QueryStrategy {
  54. case QueryStrategy_USE_IP:
  55. ipOption = dns.IPOption{
  56. IPv4Enable: true,
  57. IPv6Enable: true,
  58. FakeEnable: false,
  59. }
  60. case QueryStrategy_USE_SYS:
  61. ipOption = dns.IPOption{
  62. IPv4Enable: true,
  63. IPv6Enable: true,
  64. FakeEnable: false,
  65. }
  66. checkSystem = true
  67. case QueryStrategy_USE_IP4:
  68. ipOption = dns.IPOption{
  69. IPv4Enable: true,
  70. IPv6Enable: false,
  71. FakeEnable: false,
  72. }
  73. case QueryStrategy_USE_IP6:
  74. ipOption = dns.IPOption{
  75. IPv4Enable: false,
  76. IPv6Enable: true,
  77. FakeEnable: false,
  78. }
  79. default:
  80. return nil, errors.New("unexpected query strategy ", config.QueryStrategy)
  81. }
  82. hosts, err := NewStaticHosts(config.StaticHosts)
  83. if err != nil {
  84. return nil, errors.New("failed to create hosts").Base(err)
  85. }
  86. var clients []*Client
  87. domainRuleCount := 0
  88. var defaultTag = config.Tag
  89. if len(config.Tag) == 0 {
  90. defaultTag = generateRandomTag()
  91. }
  92. for _, ns := range config.NameServer {
  93. if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
  94. err := parseDomains(ns)
  95. if err != nil {
  96. return nil, errors.New("failed to parse dns domain rules: ").Base(err)
  97. }
  98. expectedGeoip, err := router.GetGeoIPList(ns.ExpectedGeoip)
  99. if err != nil {
  100. return nil, errors.New("failed to parse dns expectIPs rules: ").Base(err)
  101. }
  102. ns.ExpectedGeoip = expectedGeoip
  103. unexpectedGeoip, err := router.GetGeoIPList(ns.UnexpectedGeoip)
  104. if err != nil {
  105. return nil, errors.New("failed to parse dns unexpectedGeoip rules: ").Base(err)
  106. }
  107. ns.UnexpectedGeoip = unexpectedGeoip
  108. }
  109. domainRuleCount += len(ns.PrioritizedDomain)
  110. }
  111. // MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
  112. matcherInfos := make([]*DomainMatcherInfo, domainRuleCount+1)
  113. domainMatcher := &strmatcher.MatcherGroup{}
  114. for _, ns := range config.NameServer {
  115. clientIdx := len(clients)
  116. updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []*DomainMatcherInfo) {
  117. midx := domainMatcher.Add(domainRule)
  118. matcherInfos[midx] = &DomainMatcherInfo{
  119. clientIdx: uint16(clientIdx),
  120. domainRuleIdx: uint16(originalRuleIdx),
  121. }
  122. }
  123. myClientIP := clientIP
  124. switch len(ns.ClientIp) {
  125. case net.IPv4len, net.IPv6len:
  126. myClientIP = net.IP(ns.ClientIp)
  127. }
  128. disableCache := config.DisableCache
  129. if ns.DisableCache != nil {
  130. disableCache = *ns.DisableCache
  131. }
  132. serveStale := config.ServeStale
  133. if ns.ServeStale != nil {
  134. serveStale = *ns.ServeStale
  135. }
  136. serveExpiredTTL := config.ServeExpiredTTL
  137. if ns.ServeExpiredTTL != nil {
  138. serveExpiredTTL = *ns.ServeExpiredTTL
  139. }
  140. var tag = defaultTag
  141. if len(ns.Tag) > 0 {
  142. tag = ns.Tag
  143. }
  144. clientIPOption := ResolveIpOptionOverride(ns.QueryStrategy, ipOption)
  145. if !clientIPOption.IPv4Enable && !clientIPOption.IPv6Enable {
  146. return nil, errors.New("no QueryStrategy available for ", ns.Address)
  147. }
  148. client, err := NewClient(ctx, ns, myClientIP, disableCache, serveStale, serveExpiredTTL, tag, clientIPOption, &matcherInfos, updateDomain)
  149. if err != nil {
  150. return nil, errors.New("failed to create client").Base(err)
  151. }
  152. clients = append(clients, client)
  153. }
  154. // If there is no DNS client in config, add a `localhost` DNS client
  155. if len(clients) == 0 {
  156. clients = append(clients, NewLocalDNSClient(ipOption))
  157. }
  158. return &DNS{
  159. hosts: hosts,
  160. ipOption: &ipOption,
  161. clients: clients,
  162. ctx: ctx,
  163. domainMatcher: domainMatcher,
  164. matcherInfos: matcherInfos,
  165. disableFallback: config.DisableFallback,
  166. disableFallbackIfMatch: config.DisableFallbackIfMatch,
  167. enableParallelQuery: config.EnableParallelQuery,
  168. checkSystem: checkSystem,
  169. }, nil
  170. }
  171. // Type implements common.HasType.
  172. func (*DNS) Type() interface{} {
  173. return dns.ClientType()
  174. }
  175. // Start implements common.Runnable.
  176. func (s *DNS) Start() error {
  177. return nil
  178. }
  179. // Close implements common.Closable.
  180. func (s *DNS) Close() error {
  181. return nil
  182. }
  183. // IsOwnLink implements proxy.dns.ownLinkVerifier
  184. func (s *DNS) IsOwnLink(ctx context.Context) bool {
  185. inbound := session.InboundFromContext(ctx)
  186. if inbound == nil {
  187. return false
  188. }
  189. for _, client := range s.clients {
  190. if client.tag == inbound.Tag {
  191. return true
  192. }
  193. }
  194. return false
  195. }
  196. // LookupIP implements dns.Client.
  197. func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
  198. // Normalize the FQDN form query
  199. domain = strings.TrimSuffix(domain, ".")
  200. if domain == "" {
  201. return nil, 0, errors.New("empty domain name")
  202. }
  203. if s.checkSystem {
  204. supportIPv4, supportIPv6 := checkRoutes()
  205. option.IPv4Enable = option.IPv4Enable && supportIPv4
  206. option.IPv6Enable = option.IPv6Enable && supportIPv6
  207. } else {
  208. option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
  209. option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
  210. }
  211. if !option.IPv4Enable && !option.IPv6Enable {
  212. return nil, 0, dns.ErrEmptyResponse
  213. }
  214. // Static host lookup
  215. switch addrs, err := s.hosts.Lookup(domain, option); {
  216. case err != nil:
  217. if go_errors.Is(err, dns.ErrEmptyResponse) {
  218. return nil, 0, dns.ErrEmptyResponse
  219. }
  220. return nil, 0, errors.New("returning nil for domain ", domain).Base(err)
  221. case addrs == nil: // Domain not recorded in static host
  222. break
  223. case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
  224. return nil, 0, dns.ErrEmptyResponse
  225. case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement
  226. errors.LogInfo(s.ctx, "domain replaced: ", domain, " -> ", addrs[0].Domain())
  227. domain = addrs[0].Domain()
  228. default: // Successfully found ip records in static host
  229. errors.LogInfo(s.ctx, "returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs)
  230. ips, err := toNetIP(addrs)
  231. if err != nil {
  232. return nil, 0, err
  233. }
  234. return ips, 10, nil // Hosts ttl is 10
  235. }
  236. // Name servers lookup
  237. if s.enableParallelQuery {
  238. return s.parallelQuery(domain, option)
  239. } else {
  240. return s.serialQuery(domain, option)
  241. }
  242. }
  243. func (s *DNS) sortClients(domain string) []*Client {
  244. clients := make([]*Client, 0, len(s.clients))
  245. clientUsed := make([]bool, len(s.clients))
  246. clientNames := make([]string, 0, len(s.clients))
  247. domainRules := []string{}
  248. // Priority domain matching
  249. hasMatch := false
  250. MatchSlice := s.domainMatcher.Match(domain)
  251. sort.Slice(MatchSlice, func(i, j int) bool {
  252. return MatchSlice[i] < MatchSlice[j]
  253. })
  254. for _, match := range MatchSlice {
  255. info := s.matcherInfos[match]
  256. client := s.clients[info.clientIdx]
  257. domainRule := client.domains[info.domainRuleIdx]
  258. domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
  259. if clientUsed[info.clientIdx] {
  260. continue
  261. }
  262. clientUsed[info.clientIdx] = true
  263. clients = append(clients, client)
  264. clientNames = append(clientNames, client.Name())
  265. hasMatch = true
  266. if client.finalQuery {
  267. return clients
  268. }
  269. }
  270. if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
  271. // Default round-robin query
  272. for idx, client := range s.clients {
  273. if clientUsed[idx] || client.skipFallback {
  274. continue
  275. }
  276. clientUsed[idx] = true
  277. clients = append(clients, client)
  278. clientNames = append(clientNames, client.Name())
  279. if client.finalQuery {
  280. return clients
  281. }
  282. }
  283. }
  284. if len(domainRules) > 0 {
  285. errors.LogDebug(s.ctx, "domain ", domain, " matches following rules: ", domainRules)
  286. }
  287. if len(clientNames) > 0 {
  288. errors.LogDebug(s.ctx, "domain ", domain, " will use DNS in order: ", clientNames)
  289. }
  290. if len(clients) == 0 {
  291. if len(s.clients) > 0 {
  292. clients = append(clients, s.clients[0])
  293. clientNames = append(clientNames, s.clients[0].Name())
  294. errors.LogWarning(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
  295. } else {
  296. errors.LogError(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured")
  297. }
  298. }
  299. return clients
  300. }
  301. func mergeQueryErrors(domain string, errs []error) error {
  302. if len(errs) == 0 {
  303. return dns.ErrEmptyResponse
  304. }
  305. var noRNF error
  306. for _, err := range errs {
  307. if go_errors.Is(err, errRecordNotFound) {
  308. continue // server no response, ignore
  309. } else if noRNF == nil {
  310. noRNF = err
  311. } else if !go_errors.Is(err, noRNF) {
  312. return errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
  313. }
  314. }
  315. if go_errors.Is(noRNF, dns.ErrEmptyResponse) {
  316. return dns.ErrEmptyResponse
  317. }
  318. if noRNF == nil {
  319. noRNF = errRecordNotFound
  320. }
  321. return errors.New("returning nil for domain ", domain).Base(noRNF)
  322. }
  323. func (s *DNS) serialQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
  324. var errs []error
  325. for _, client := range s.sortClients(domain) {
  326. if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
  327. errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
  328. continue
  329. }
  330. ips, ttl, err := client.QueryIP(s.ctx, domain, option)
  331. if len(ips) > 0 {
  332. return ips, ttl, nil
  333. }
  334. errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode")
  335. if err == nil {
  336. err = dns.ErrEmptyResponse
  337. }
  338. errs = append(errs, err)
  339. }
  340. return nil, 0, mergeQueryErrors(domain, errs)
  341. }
  342. func (s *DNS) parallelQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
  343. var errs []error
  344. clients := s.sortClients(domain)
  345. resultsChan := asyncQueryAll(domain, option, clients, s.ctx)
  346. groups, groupOf := makeGroups( /*s.ctx,*/ clients)
  347. results := make([]*queryResult, len(clients))
  348. pending := make([]int, len(groups))
  349. for gi, g := range groups {
  350. pending[gi] = g.end - g.start + 1
  351. }
  352. nextGroup := 0
  353. for range clients {
  354. result := <-resultsChan
  355. results[result.index] = &result
  356. gi := groupOf[result.index]
  357. pending[gi]--
  358. for nextGroup < len(groups) {
  359. g := groups[nextGroup]
  360. // group race, minimum rtt -> return
  361. for j := g.start; j <= g.end; j++ {
  362. r := results[j]
  363. if r != nil && r.err == nil && len(r.ips) > 0 {
  364. return r.ips, r.ttl, nil
  365. }
  366. }
  367. // current group is incomplete and no one success -> continue pending
  368. if pending[nextGroup] > 0 {
  369. break
  370. }
  371. // all failed -> log and continue next group
  372. for j := g.start; j <= g.end; j++ {
  373. r := results[j]
  374. e := r.err
  375. if e == nil {
  376. e = dns.ErrEmptyResponse
  377. }
  378. errors.LogInfoInner(s.ctx, e, "failed to lookup ip for domain ", domain, " at server ", clients[j].Name(), " in parallel query mode")
  379. errs = append(errs, e)
  380. }
  381. nextGroup++
  382. }
  383. }
  384. return nil, 0, mergeQueryErrors(domain, errs)
  385. }
  386. type queryResult struct {
  387. ips []net.IP
  388. ttl uint32
  389. err error
  390. index int
  391. }
  392. func asyncQueryAll(domain string, option dns.IPOption, clients []*Client, ctx context.Context) chan queryResult {
  393. if len(clients) == 0 {
  394. ch := make(chan queryResult)
  395. close(ch)
  396. return ch
  397. }
  398. ch := make(chan queryResult, len(clients))
  399. for i, client := range clients {
  400. if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
  401. errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
  402. ch <- queryResult{err: dns.ErrEmptyResponse, index: i}
  403. continue
  404. }
  405. go func(i int, c *Client) {
  406. qctx := ctx
  407. if !c.server.IsDisableCache() {
  408. nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), c.timeoutMs*2)
  409. qctx = nctx
  410. defer cancel()
  411. }
  412. ips, ttl, err := c.QueryIP(qctx, domain, option)
  413. ch <- queryResult{ips: ips, ttl: ttl, err: err, index: i}
  414. }(i, client)
  415. }
  416. return ch
  417. }
  418. type group struct{ start, end int }
  419. // merge only adjacent and rule-equivalent Client into a single group
  420. func makeGroups( /*ctx context.Context,*/ clients []*Client) ([]group, []int) {
  421. n := len(clients)
  422. if n == 0 {
  423. return nil, nil
  424. }
  425. groups := make([]group, 0, n)
  426. groupOf := make([]int, n)
  427. s, e := 0, 0
  428. for i := 1; i < n; i++ {
  429. if clients[i-1].policyID == clients[i].policyID {
  430. e = i
  431. } else {
  432. for k := s; k <= e; k++ {
  433. groupOf[k] = len(groups)
  434. }
  435. groups = append(groups, group{start: s, end: e})
  436. s, e = i, i
  437. }
  438. }
  439. for k := s; k <= e; k++ {
  440. groupOf[k] = len(groups)
  441. }
  442. groups = append(groups, group{start: s, end: e})
  443. // var b strings.Builder
  444. // b.WriteString("dns grouping: total clients=")
  445. // b.WriteString(strconv.Itoa(n))
  446. // b.WriteString(", groups=")
  447. // b.WriteString(strconv.Itoa(len(groups)))
  448. // for gi, g := range groups {
  449. // b.WriteString("\n [")
  450. // b.WriteString(strconv.Itoa(g.start))
  451. // b.WriteString("..")
  452. // b.WriteString(strconv.Itoa(g.end))
  453. // b.WriteString("] gid=")
  454. // b.WriteString(strconv.Itoa(gi))
  455. // b.WriteString(" pid=")
  456. // b.WriteString(strconv.FormatUint(uint64(clients[g.start].policyID), 10))
  457. // b.WriteString(" members: ")
  458. // for i := g.start; i <= g.end; i++ {
  459. // if i > g.start {
  460. // b.WriteString(", ")
  461. // }
  462. // b.WriteString(strconv.Itoa(i))
  463. // b.WriteByte(':')
  464. // b.WriteString(clients[i].Name())
  465. // }
  466. // }
  467. // errors.LogDebug(ctx, b.String())
  468. return groups, groupOf
  469. }
  470. func init() {
  471. common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
  472. return New(ctx, config.(*Config))
  473. }))
  474. }
  475. func probeRoutes() (ipv4 bool, ipv6 bool) {
  476. if conn, err := net.Dial("udp4", "192.33.4.12:53"); err == nil {
  477. ipv4 = true
  478. conn.Close()
  479. }
  480. if conn, err := net.Dial("udp6", "[2001:500:2::c]:53"); err == nil {
  481. ipv6 = true
  482. conn.Close()
  483. }
  484. return
  485. }
  486. var routeCache struct {
  487. sync.Once
  488. sync.RWMutex
  489. expire time.Time
  490. ipv4, ipv6 bool
  491. }
  492. func checkRoutes() (bool, bool) {
  493. if !isGUIPlatform {
  494. routeCache.Once.Do(func() {
  495. routeCache.ipv4, routeCache.ipv6 = probeRoutes()
  496. })
  497. return routeCache.ipv4, routeCache.ipv6
  498. }
  499. routeCache.RWMutex.RLock()
  500. now := time.Now()
  501. if routeCache.expire.After(now) {
  502. routeCache.RWMutex.RUnlock()
  503. return routeCache.ipv4, routeCache.ipv6
  504. }
  505. routeCache.RWMutex.RUnlock()
  506. routeCache.RWMutex.Lock()
  507. defer routeCache.RWMutex.Unlock()
  508. now = time.Now()
  509. if routeCache.expire.After(now) { // double-check
  510. return routeCache.ipv4, routeCache.ipv6
  511. }
  512. routeCache.ipv4, routeCache.ipv6 = probeRoutes() // ~2ms
  513. routeCache.expire = now.Add(100 * time.Millisecond) // ttl
  514. return routeCache.ipv4, routeCache.ipv6
  515. }
  516. var isGUIPlatform = detectGUIPlatform()
  517. func detectGUIPlatform() bool {
  518. switch runtime.GOOS {
  519. case "android", "ios", "windows", "darwin":
  520. return true
  521. case "linux", "freebsd", "openbsd":
  522. if t := os.Getenv("XDG_SESSION_TYPE"); t == "wayland" || t == "x11" {
  523. return true
  524. }
  525. if os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" {
  526. return true
  527. }
  528. }
  529. return false
  530. }
  531. func parseDomains(ns *NameServer) error {
  532. pureDomains := []*router.Domain{}
  533. // convert to pure domain
  534. for _, pd := range ns.PrioritizedDomain {
  535. pureDomains = append(pureDomains, &router.Domain{
  536. Type: router.Domain_Type(pd.Type),
  537. Value: pd.Domain,
  538. })
  539. }
  540. domainList := []*router.Domain{}
  541. for _, domain := range pureDomains {
  542. val := strings.Split(domain.Value, "_")
  543. if len(val) >= 2 {
  544. fileName := val[0]
  545. code := val[1]
  546. bs, err := filesystem.ReadAsset(fileName)
  547. if err != nil {
  548. return errors.New("failed to load file: ", fileName).Base(err)
  549. }
  550. bs = filesystem.Find(bs, []byte(code))
  551. var geosite router.GeoSite
  552. if err := proto.Unmarshal(bs, &geosite); err != nil {
  553. return errors.New("failed Unmarshal :").Base(err)
  554. }
  555. // parse attr
  556. if len(val) == 3 {
  557. siteWithAttr := strings.Split(val[2], ",")
  558. attrs := router.ParseAttrs(siteWithAttr)
  559. if !attrs.IsEmpty() {
  560. filteredDomains := make([]*router.Domain, 0, len(pureDomains))
  561. for _, domain := range geosite.Domain {
  562. if attrs.Match(domain) {
  563. filteredDomains = append(filteredDomains, domain)
  564. }
  565. }
  566. geosite.Domain = filteredDomains
  567. }
  568. }
  569. domainList = append(domainList, geosite.Domain...)
  570. // update ns.OriginalRules Size
  571. ruleTag := strings.Join(val, ":")
  572. for i, oRule := range ns.OriginalRules {
  573. if oRule.Rule == strings.ToLower(ruleTag) {
  574. ns.OriginalRules[i].Size = uint32(len(geosite.Domain))
  575. }
  576. }
  577. } else {
  578. domainList = append(domainList, domain)
  579. }
  580. }
  581. // convert back to NameServer_PriorityDomain
  582. ns.PrioritizedDomain = []*NameServer_PriorityDomain{}
  583. for _, pd := range domainList {
  584. ns.PrioritizedDomain = append(ns.PrioritizedDomain, &NameServer_PriorityDomain{
  585. Type: ToDomainMatchingType(pd.Type),
  586. Domain: pd.Value,
  587. })
  588. }
  589. return nil
  590. }