github.com/v2fly/v2ray-core/v4@v4.45.2/infra/conf/dns.go (about) 1 package conf 2 3 import ( 4 "context" 5 "encoding/json" 6 "sort" 7 "strings" 8 9 "github.com/v2fly/v2ray-core/v4/app/dns" 10 "github.com/v2fly/v2ray-core/v4/app/router" 11 "github.com/v2fly/v2ray-core/v4/common/net" 12 "github.com/v2fly/v2ray-core/v4/common/platform" 13 "github.com/v2fly/v2ray-core/v4/infra/conf/cfgcommon" 14 "github.com/v2fly/v2ray-core/v4/infra/conf/geodata" 15 rule2 "github.com/v2fly/v2ray-core/v4/infra/conf/rule" 16 ) 17 18 type NameServerConfig struct { 19 Address *cfgcommon.Address 20 ClientIP *cfgcommon.Address 21 Port uint16 22 SkipFallback bool 23 Domains []string 24 ExpectIPs cfgcommon.StringList 25 26 cfgctx context.Context 27 } 28 29 func (c *NameServerConfig) UnmarshalJSON(data []byte) error { 30 var address cfgcommon.Address 31 if err := json.Unmarshal(data, &address); err == nil { 32 c.Address = &address 33 return nil 34 } 35 36 var advanced struct { 37 Address *cfgcommon.Address `json:"address"` 38 ClientIP *cfgcommon.Address `json:"clientIp"` 39 Port uint16 `json:"port"` 40 SkipFallback bool `json:"skipFallback"` 41 Domains []string `json:"domains"` 42 ExpectIPs cfgcommon.StringList `json:"expectIps"` 43 } 44 if err := json.Unmarshal(data, &advanced); err == nil { 45 c.Address = advanced.Address 46 c.ClientIP = advanced.ClientIP 47 c.Port = advanced.Port 48 c.SkipFallback = advanced.SkipFallback 49 c.Domains = advanced.Domains 50 c.ExpectIPs = advanced.ExpectIPs 51 return nil 52 } 53 54 return newError("failed to parse name server: ", string(data)) 55 } 56 57 func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType { 58 switch t { 59 case router.Domain_Domain: 60 return dns.DomainMatchingType_Subdomain 61 case router.Domain_Full: 62 return dns.DomainMatchingType_Full 63 case router.Domain_Plain: 64 return dns.DomainMatchingType_Keyword 65 case router.Domain_Regex: 66 return dns.DomainMatchingType_Regex 67 default: 68 panic("unknown domain type") 69 } 70 } 71 72 func (c *NameServerConfig) Build() (*dns.NameServer, error) { 73 cfgctx := c.cfgctx 74 75 if c.Address == nil { 76 return nil, newError("NameServer address is not specified.") 77 } 78 79 var domains []*dns.NameServer_PriorityDomain 80 var originalRules []*dns.NameServer_OriginalRule 81 82 for _, rule := range c.Domains { 83 parsedDomain, err := rule2.ParseDomainRule(cfgctx, rule) 84 if err != nil { 85 return nil, newError("invalid domain rule: ", rule).Base(err) 86 } 87 88 for _, pd := range parsedDomain { 89 domains = append(domains, &dns.NameServer_PriorityDomain{ 90 Type: toDomainMatchingType(pd.Type), 91 Domain: pd.Value, 92 }) 93 } 94 originalRules = append(originalRules, &dns.NameServer_OriginalRule{ 95 Rule: rule, 96 Size: uint32(len(parsedDomain)), 97 }) 98 } 99 100 geoipList, err := rule2.ToCidrList(cfgctx, c.ExpectIPs) 101 if err != nil { 102 return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err) 103 } 104 105 var myClientIP []byte 106 if c.ClientIP != nil { 107 if !c.ClientIP.Family().IsIP() { 108 return nil, newError("not an IP address:", c.ClientIP.String()) 109 } 110 myClientIP = []byte(c.ClientIP.IP()) 111 } 112 113 return &dns.NameServer{ 114 Address: &net.Endpoint{ 115 Network: net.Network_UDP, 116 Address: c.Address.Build(), 117 Port: uint32(c.Port), 118 }, 119 ClientIp: myClientIP, 120 SkipFallback: c.SkipFallback, 121 PrioritizedDomain: domains, 122 Geoip: geoipList, 123 OriginalRules: originalRules, 124 }, nil 125 } 126 127 var typeMap = map[router.Domain_Type]dns.DomainMatchingType{ 128 router.Domain_Full: dns.DomainMatchingType_Full, 129 router.Domain_Domain: dns.DomainMatchingType_Subdomain, 130 router.Domain_Plain: dns.DomainMatchingType_Keyword, 131 router.Domain_Regex: dns.DomainMatchingType_Regex, 132 } 133 134 // DNSConfig is a JSON serializable object for dns.Config. 135 type DNSConfig struct { 136 Servers []*NameServerConfig `json:"servers"` 137 Hosts map[string]*HostAddress `json:"hosts"` 138 ClientIP *cfgcommon.Address `json:"clientIp"` 139 Tag string `json:"tag"` 140 QueryStrategy string `json:"queryStrategy"` 141 DisableCache bool `json:"disableCache"` 142 DisableFallback bool `json:"disableFallback"` 143 DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` 144 } 145 146 type HostAddress struct { 147 addr *cfgcommon.Address 148 addrs []*cfgcommon.Address 149 } 150 151 // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON 152 func (h *HostAddress) UnmarshalJSON(data []byte) error { 153 addr := new(cfgcommon.Address) 154 var addrs []*cfgcommon.Address 155 switch { 156 case json.Unmarshal(data, &addr) == nil: 157 h.addr = addr 158 case json.Unmarshal(data, &addrs) == nil: 159 h.addrs = addrs 160 default: 161 return newError("invalid address") 162 } 163 return nil 164 } 165 166 func getHostMapping(ha *HostAddress) *dns.Config_HostMapping { 167 if ha.addr != nil { 168 if ha.addr.Family().IsDomain() { 169 return &dns.Config_HostMapping{ 170 ProxiedDomain: ha.addr.Domain(), 171 } 172 } 173 return &dns.Config_HostMapping{ 174 Ip: [][]byte{ha.addr.IP()}, 175 } 176 } 177 178 ips := make([][]byte, 0, len(ha.addrs)) 179 for _, addr := range ha.addrs { 180 if addr.Family().IsDomain() { 181 return &dns.Config_HostMapping{ 182 ProxiedDomain: addr.Domain(), 183 } 184 } 185 ips = append(ips, []byte(addr.IP())) 186 } 187 return &dns.Config_HostMapping{ 188 Ip: ips, 189 } 190 } 191 192 // Build implements Buildable 193 func (c *DNSConfig) Build() (*dns.Config, error) { 194 cfgctx := cfgcommon.NewConfigureLoadingContext(context.Background()) 195 196 geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { 197 return "standard" 198 }) 199 200 if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { 201 cfgcommon.SetGeoDataLoader(cfgctx, loader) 202 } else { 203 return nil, newError("unable to create geo data loader ").Base(err) 204 } 205 206 cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(cfgctx) 207 geoLoader := cfgEnv.GetGeoLoader() 208 209 config := &dns.Config{ 210 Tag: c.Tag, 211 DisableCache: c.DisableCache, 212 DisableFallback: c.DisableFallback, 213 DisableFallbackIfMatch: c.DisableFallbackIfMatch, 214 } 215 216 if c.ClientIP != nil { 217 if !c.ClientIP.Family().IsIP() { 218 return nil, newError("not an IP address:", c.ClientIP.String()) 219 } 220 config.ClientIp = []byte(c.ClientIP.IP()) 221 } 222 223 config.QueryStrategy = dns.QueryStrategy_USE_IP 224 switch strings.ToLower(c.QueryStrategy) { 225 case "useip", "use_ip", "use-ip": 226 config.QueryStrategy = dns.QueryStrategy_USE_IP 227 case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": 228 config.QueryStrategy = dns.QueryStrategy_USE_IP4 229 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": 230 config.QueryStrategy = dns.QueryStrategy_USE_IP6 231 } 232 233 for _, server := range c.Servers { 234 server.cfgctx = cfgctx 235 ns, err := server.Build() 236 if err != nil { 237 return nil, newError("failed to build nameserver").Base(err) 238 } 239 config.NameServer = append(config.NameServer, ns) 240 } 241 242 if c.Hosts != nil { 243 mappings := make([]*dns.Config_HostMapping, 0, 20) 244 245 domains := make([]string, 0, len(c.Hosts)) 246 for domain := range c.Hosts { 247 domains = append(domains, domain) 248 } 249 sort.Strings(domains) 250 251 for _, domain := range domains { 252 switch { 253 case strings.HasPrefix(domain, "domain:"): 254 domainName := domain[7:] 255 if len(domainName) == 0 { 256 return nil, newError("empty domain type of rule: ", domain) 257 } 258 mapping := getHostMapping(c.Hosts[domain]) 259 mapping.Type = dns.DomainMatchingType_Subdomain 260 mapping.Domain = domainName 261 mappings = append(mappings, mapping) 262 263 case strings.HasPrefix(domain, "geosite:"): 264 listName := domain[8:] 265 if len(listName) == 0 { 266 return nil, newError("empty geosite rule: ", domain) 267 } 268 geositeList, err := geoLoader.LoadGeoSite(listName) 269 if err != nil { 270 return nil, newError("failed to load geosite: ", listName).Base(err) 271 } 272 for _, d := range geositeList { 273 mapping := getHostMapping(c.Hosts[domain]) 274 mapping.Type = typeMap[d.Type] 275 mapping.Domain = d.Value 276 mappings = append(mappings, mapping) 277 } 278 279 case strings.HasPrefix(domain, "regexp:"): 280 regexpVal := domain[7:] 281 if len(regexpVal) == 0 { 282 return nil, newError("empty regexp type of rule: ", domain) 283 } 284 mapping := getHostMapping(c.Hosts[domain]) 285 mapping.Type = dns.DomainMatchingType_Regex 286 mapping.Domain = regexpVal 287 mappings = append(mappings, mapping) 288 289 case strings.HasPrefix(domain, "keyword:"): 290 keywordVal := domain[8:] 291 if len(keywordVal) == 0 { 292 return nil, newError("empty keyword type of rule: ", domain) 293 } 294 mapping := getHostMapping(c.Hosts[domain]) 295 mapping.Type = dns.DomainMatchingType_Keyword 296 mapping.Domain = keywordVal 297 mappings = append(mappings, mapping) 298 299 case strings.HasPrefix(domain, "full:"): 300 fullVal := domain[5:] 301 if len(fullVal) == 0 { 302 return nil, newError("empty full domain type of rule: ", domain) 303 } 304 mapping := getHostMapping(c.Hosts[domain]) 305 mapping.Type = dns.DomainMatchingType_Full 306 mapping.Domain = fullVal 307 mappings = append(mappings, mapping) 308 309 case strings.HasPrefix(domain, "dotless:"): 310 mapping := getHostMapping(c.Hosts[domain]) 311 mapping.Type = dns.DomainMatchingType_Regex 312 switch substr := domain[8:]; { 313 case substr == "": 314 mapping.Domain = "^[^.]*$" 315 case !strings.Contains(substr, "."): 316 mapping.Domain = "^[^.]*" + substr + "[^.]*$" 317 default: 318 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 319 } 320 mappings = append(mappings, mapping) 321 322 case strings.HasPrefix(domain, "ext:"): 323 kv := strings.Split(domain[4:], ":") 324 if len(kv) != 2 { 325 return nil, newError("invalid external resource: ", domain) 326 } 327 filename := kv[0] 328 list := kv[1] 329 geositeList, err := geoLoader.LoadGeoSiteWithAttr(filename, list) 330 if err != nil { 331 return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err) 332 } 333 for _, d := range geositeList { 334 mapping := getHostMapping(c.Hosts[domain]) 335 mapping.Type = typeMap[d.Type] 336 mapping.Domain = d.Value 337 mappings = append(mappings, mapping) 338 } 339 340 default: 341 mapping := getHostMapping(c.Hosts[domain]) 342 mapping.Type = dns.DomainMatchingType_Full 343 mapping.Domain = domain 344 mappings = append(mappings, mapping) 345 } 346 } 347 348 config.StaticHosts = append(config.StaticHosts, mappings...) 349 } 350 351 return config, nil 352 }