github.com/eagleql/xray-core@v1.4.4/app/dns/hosts.go (about) 1 package dns 2 3 import ( 4 "github.com/eagleql/xray-core/common" 5 "github.com/eagleql/xray-core/common/net" 6 "github.com/eagleql/xray-core/common/strmatcher" 7 "github.com/eagleql/xray-core/features" 8 "github.com/eagleql/xray-core/features/dns" 9 ) 10 11 // StaticHosts represents static domain-ip mapping in DNS server. 12 type StaticHosts struct { 13 ips [][]net.Address 14 matchers *strmatcher.MatcherGroup 15 } 16 17 var typeMap = map[DomainMatchingType]strmatcher.Type{ 18 DomainMatchingType_Full: strmatcher.Full, 19 DomainMatchingType_Subdomain: strmatcher.Domain, 20 DomainMatchingType_Keyword: strmatcher.Substr, 21 DomainMatchingType_Regex: strmatcher.Regex, 22 } 23 24 func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) { 25 strMType, f := typeMap[t] 26 if !f { 27 return nil, newError("unknown mapping type", t).AtWarning() 28 } 29 matcher, err := strMType.New(domain) 30 if err != nil { 31 return nil, newError("failed to create str matcher").Base(err) 32 } 33 return matcher, nil 34 } 35 36 // NewStaticHosts creates a new StaticHosts instance. 37 func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) { 38 g := new(strmatcher.MatcherGroup) 39 sh := &StaticHosts{ 40 ips: make([][]net.Address, len(hosts)+len(legacy)+16), 41 matchers: g, 42 } 43 44 if legacy != nil { 45 features.PrintDeprecatedFeatureWarning("simple host mapping") 46 47 for domain, ip := range legacy { 48 matcher, err := strmatcher.Full.New(domain) 49 common.Must(err) 50 id := g.Add(matcher) 51 52 address := ip.AsAddress() 53 if address.Family().IsDomain() { 54 return nil, newError("invalid domain address in static hosts: ", address.Domain()).AtWarning() 55 } 56 57 sh.ips[id] = []net.Address{address} 58 } 59 } 60 61 for _, mapping := range hosts { 62 matcher, err := toStrMatcher(mapping.Type, mapping.Domain) 63 if err != nil { 64 return nil, newError("failed to create domain matcher").Base(err) 65 } 66 id := g.Add(matcher) 67 ips := make([]net.Address, 0, len(mapping.Ip)+1) 68 switch { 69 case len(mapping.Ip) > 0: 70 for _, ip := range mapping.Ip { 71 addr := net.IPAddress(ip) 72 if addr == nil { 73 return nil, newError("invalid IP address in static hosts: ", ip).AtWarning() 74 } 75 ips = append(ips, addr) 76 } 77 78 case len(mapping.ProxiedDomain) > 0: 79 ips = append(ips, net.DomainAddress(mapping.ProxiedDomain)) 80 81 default: 82 return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning() 83 } 84 85 // Special handling for localhost IPv6. This is a dirty workaround as JSON config supports only single IP mapping. 86 if len(ips) == 1 && ips[0] == net.LocalHostIP { 87 ips = append(ips, net.LocalHostIPv6) 88 } 89 90 sh.ips[id] = ips 91 } 92 93 return sh, nil 94 } 95 96 func filterIP(ips []net.Address, option dns.IPOption) []net.Address { 97 filtered := make([]net.Address, 0, len(ips)) 98 for _, ip := range ips { 99 if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) { 100 filtered = append(filtered, ip) 101 } 102 } 103 if len(filtered) == 0 { 104 return nil 105 } 106 return filtered 107 } 108 109 // LookupIP returns IP address for the given domain, if exists in this StaticHosts. 110 func (h *StaticHosts) LookupIP(domain string, option dns.IPOption) []net.Address { 111 indices := h.matchers.Match(domain) 112 if len(indices) == 0 { 113 return nil 114 } 115 ips := []net.Address{} 116 for _, id := range indices { 117 ips = append(ips, h.ips[id]...) 118 } 119 if len(ips) == 1 && ips[0].Family().IsDomain() { 120 return ips 121 } 122 return filterIP(ips, option) 123 }