github.com/eagleql/xray-core@v1.4.4/infra/conf/dns.go (about) 1 package conf 2 3 import ( 4 "encoding/json" 5 "sort" 6 "strings" 7 8 "github.com/eagleql/xray-core/app/dns" 9 "github.com/eagleql/xray-core/app/router" 10 "github.com/eagleql/xray-core/common/net" 11 ) 12 13 type NameServerConfig struct { 14 Address *Address 15 Port uint16 16 Domains []string 17 ExpectIPs StringList 18 } 19 20 func (c *NameServerConfig) UnmarshalJSON(data []byte) error { 21 var address Address 22 if err := json.Unmarshal(data, &address); err == nil { 23 c.Address = &address 24 return nil 25 } 26 27 var advanced struct { 28 Address *Address `json:"address"` 29 Port uint16 `json:"port"` 30 Domains []string `json:"domains"` 31 ExpectIPs StringList `json:"expectIps"` 32 } 33 if err := json.Unmarshal(data, &advanced); err == nil { 34 c.Address = advanced.Address 35 c.Port = advanced.Port 36 c.Domains = advanced.Domains 37 c.ExpectIPs = advanced.ExpectIPs 38 return nil 39 } 40 41 return newError("failed to parse name server: ", string(data)) 42 } 43 44 func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType { 45 switch t { 46 case router.Domain_Domain: 47 return dns.DomainMatchingType_Subdomain 48 case router.Domain_Full: 49 return dns.DomainMatchingType_Full 50 case router.Domain_Plain: 51 return dns.DomainMatchingType_Keyword 52 case router.Domain_Regex: 53 return dns.DomainMatchingType_Regex 54 default: 55 panic("unknown domain type") 56 } 57 } 58 59 func (c *NameServerConfig) Build() (*dns.NameServer, error) { 60 if c.Address == nil { 61 return nil, newError("NameServer address is not specified.") 62 } 63 64 var domains []*dns.NameServer_PriorityDomain 65 var originalRules []*dns.NameServer_OriginalRule 66 67 for _, rule := range c.Domains { 68 parsedDomain, err := parseDomainRule(rule) 69 if err != nil { 70 return nil, newError("invalid domain rule: ", rule).Base(err) 71 } 72 73 for _, pd := range parsedDomain { 74 domains = append(domains, &dns.NameServer_PriorityDomain{ 75 Type: toDomainMatchingType(pd.Type), 76 Domain: pd.Value, 77 }) 78 } 79 originalRules = append(originalRules, &dns.NameServer_OriginalRule{ 80 Rule: rule, 81 Size: uint32(len(parsedDomain)), 82 }) 83 } 84 85 geoipList, err := toCidrList(c.ExpectIPs) 86 if err != nil { 87 return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err) 88 } 89 90 return &dns.NameServer{ 91 Address: &net.Endpoint{ 92 Network: net.Network_UDP, 93 Address: c.Address.Build(), 94 Port: uint32(c.Port), 95 }, 96 PrioritizedDomain: domains, 97 Geoip: geoipList, 98 OriginalRules: originalRules, 99 }, nil 100 } 101 102 var typeMap = map[router.Domain_Type]dns.DomainMatchingType{ 103 router.Domain_Full: dns.DomainMatchingType_Full, 104 router.Domain_Domain: dns.DomainMatchingType_Subdomain, 105 router.Domain_Plain: dns.DomainMatchingType_Keyword, 106 router.Domain_Regex: dns.DomainMatchingType_Regex, 107 } 108 109 // DNSConfig is a JSON serializable object for dns.Config. 110 type DNSConfig struct { 111 Servers []*NameServerConfig `json:"servers"` 112 Hosts map[string]*Address `json:"hosts"` 113 ClientIP *Address `json:"clientIp"` 114 Tag string `json:"tag"` 115 } 116 117 func getHostMapping(addr *Address) *dns.Config_HostMapping { 118 if addr.Family().IsIP() { 119 return &dns.Config_HostMapping{ 120 Ip: [][]byte{[]byte(addr.IP())}, 121 } 122 } else { 123 return &dns.Config_HostMapping{ 124 ProxiedDomain: addr.Domain(), 125 } 126 } 127 } 128 129 // Build implements Buildable 130 func (c *DNSConfig) Build() (*dns.Config, error) { 131 config := &dns.Config{ 132 Tag: c.Tag, 133 } 134 135 if c.ClientIP != nil { 136 if !c.ClientIP.Family().IsIP() { 137 return nil, newError("not an IP address:", c.ClientIP.String()) 138 } 139 config.ClientIp = []byte(c.ClientIP.IP()) 140 } 141 142 for _, server := range c.Servers { 143 ns, err := server.Build() 144 if err != nil { 145 return nil, newError("failed to build nameserver").Base(err) 146 } 147 config.NameServer = append(config.NameServer, ns) 148 } 149 150 if c.Hosts != nil && len(c.Hosts) > 0 { 151 domains := make([]string, 0, len(c.Hosts)) 152 for domain := range c.Hosts { 153 domains = append(domains, domain) 154 } 155 sort.Strings(domains) 156 157 for _, domain := range domains { 158 addr := c.Hosts[domain] 159 var mappings []*dns.Config_HostMapping 160 switch { 161 case strings.HasPrefix(domain, "domain:"): 162 domainName := domain[7:] 163 if len(domainName) == 0 { 164 return nil, newError("empty domain type of rule: ", domain) 165 } 166 mapping := getHostMapping(addr) 167 mapping.Type = dns.DomainMatchingType_Subdomain 168 mapping.Domain = domainName 169 mappings = append(mappings, mapping) 170 171 case strings.HasPrefix(domain, "geosite:"): 172 listName := domain[8:] 173 if len(listName) == 0 { 174 return nil, newError("empty geosite rule: ", domain) 175 } 176 domains, err := loadGeositeWithAttr("geosite.dat", listName) 177 if err != nil { 178 return nil, newError("failed to load geosite: ", listName).Base(err) 179 } 180 for _, d := range domains { 181 mapping := getHostMapping(addr) 182 mapping.Type = typeMap[d.Type] 183 mapping.Domain = d.Value 184 mappings = append(mappings, mapping) 185 } 186 187 case strings.HasPrefix(domain, "regexp:"): 188 regexpVal := domain[7:] 189 if len(regexpVal) == 0 { 190 return nil, newError("empty regexp type of rule: ", domain) 191 } 192 mapping := getHostMapping(addr) 193 mapping.Type = dns.DomainMatchingType_Regex 194 mapping.Domain = regexpVal 195 mappings = append(mappings, mapping) 196 197 case strings.HasPrefix(domain, "keyword:"): 198 keywordVal := domain[8:] 199 if len(keywordVal) == 0 { 200 return nil, newError("empty keyword type of rule: ", domain) 201 } 202 mapping := getHostMapping(addr) 203 mapping.Type = dns.DomainMatchingType_Keyword 204 mapping.Domain = keywordVal 205 mappings = append(mappings, mapping) 206 207 case strings.HasPrefix(domain, "full:"): 208 fullVal := domain[5:] 209 if len(fullVal) == 0 { 210 return nil, newError("empty full domain type of rule: ", domain) 211 } 212 mapping := getHostMapping(addr) 213 mapping.Type = dns.DomainMatchingType_Full 214 mapping.Domain = fullVal 215 mappings = append(mappings, mapping) 216 217 case strings.HasPrefix(domain, "dotless:"): 218 mapping := getHostMapping(addr) 219 mapping.Type = dns.DomainMatchingType_Regex 220 switch substr := domain[8:]; { 221 case substr == "": 222 mapping.Domain = "^[^.]*$" 223 case !strings.Contains(substr, "."): 224 mapping.Domain = "^[^.]*" + substr + "[^.]*$" 225 default: 226 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 227 } 228 mappings = append(mappings, mapping) 229 230 case strings.HasPrefix(domain, "ext:"): 231 kv := strings.Split(domain[4:], ":") 232 if len(kv) != 2 { 233 return nil, newError("invalid external resource: ", domain) 234 } 235 filename := kv[0] 236 list := kv[1] 237 domains, err := loadGeositeWithAttr(filename, list) 238 if err != nil { 239 return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err) 240 } 241 for _, d := range domains { 242 mapping := getHostMapping(addr) 243 mapping.Type = typeMap[d.Type] 244 mapping.Domain = d.Value 245 mappings = append(mappings, mapping) 246 } 247 248 default: 249 mapping := getHostMapping(addr) 250 mapping.Type = dns.DomainMatchingType_Full 251 mapping.Domain = domain 252 mappings = append(mappings, mapping) 253 } 254 255 config.StaticHosts = append(config.StaticHosts, mappings...) 256 } 257 } 258 259 return config, nil 260 }