Files

130 lines
3.5 KiB
Go
Raw Permalink Normal View History

2020-11-25 19:01:53 +08:00
package dns
import (
2024-06-29 14:32:57 -04:00
"context"
2025-07-23 18:11:43 +08:00
"strconv"
2026-04-14 00:42:29 +08:00
"strings"
2025-07-23 18:11:43 +08:00
2024-06-29 14:32:57 -04:00
"github.com/xtls/xray-core/common/errors"
2026-04-14 00:42:29 +08:00
"github.com/xtls/xray-core/common/geodata"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/net"
2021-03-06 23:39:50 -05:00
"github.com/xtls/xray-core/features/dns"
2020-11-25 19:01:53 +08:00
)
// StaticHosts represents static domain-ip mapping in DNS server.
type StaticHosts struct {
2026-04-26 00:15:37 +08:00
responses [][]net.Address
matcher geodata.DomainMatcher
2020-11-25 19:01:53 +08:00
}
// NewStaticHosts creates a new StaticHosts instance.
2024-09-19 09:05:59 +08:00
func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
2026-04-14 00:42:29 +08:00
reps := make([][]net.Address, 0, len(hosts))
rules := make([]*geodata.DomainRule, 0, len(hosts))
2020-11-25 19:01:53 +08:00
2026-04-14 00:42:29 +08:00
for _, mapping := range hosts {
rep := make([]net.Address, 0, len(mapping.Ip))
2020-11-25 19:01:53 +08:00
switch {
2021-10-16 21:02:51 +08:00
case len(mapping.ProxiedDomain) > 0:
2025-07-23 18:11:43 +08:00
if mapping.ProxiedDomain[0] == '#' {
rcode, err := strconv.Atoi(mapping.ProxiedDomain[1:])
if err != nil {
return nil, err
}
2026-04-14 00:42:29 +08:00
rep = append(rep, dns.RCodeError(rcode))
2025-07-23 18:11:43 +08:00
} else {
2026-04-14 00:42:29 +08:00
rep = append(rep, net.DomainAddress(mapping.ProxiedDomain))
2025-07-23 18:11:43 +08:00
}
2020-11-25 19:01:53 +08:00
case len(mapping.Ip) > 0:
for _, ip := range mapping.Ip {
addr := net.IPAddress(ip)
if addr == nil {
2026-04-14 00:42:29 +08:00
errors.LogError(context.Background(), "invalid IP address in static hosts: ", ip, ", ignore this ip for rule: ", mapping.Domain)
continue
2020-11-25 19:01:53 +08:00
}
2026-04-14 00:42:29 +08:00
rep = append(rep, addr)
}
2020-11-25 19:01:53 +08:00
}
2026-04-14 00:42:29 +08:00
reps = append(reps, rep)
rules = append(rules, mapping.Domain)
2020-11-25 19:01:53 +08:00
}
2026-04-26 00:15:37 +08:00
if len(rules) == 0 {
return &StaticHosts{}, nil
}
2026-04-14 00:42:29 +08:00
matcher, err := geodata.DomainReg.BuildDomainMatcher(rules)
if err != nil {
return nil, err
}
return &StaticHosts{
2026-04-26 00:15:37 +08:00
responses: reps,
matcher: matcher,
2026-04-14 00:42:29 +08:00
}, nil
2020-11-25 19:01:53 +08:00
}
2021-03-06 23:39:50 -05:00
func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
2020-11-25 19:01:53 +08:00
filtered := make([]net.Address, 0, len(ips))
for _, ip := range ips {
if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
filtered = append(filtered, ip)
}
}
return filtered
}
2025-07-23 18:11:43 +08:00
func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
ips := make([]net.Address, 0)
found := false
2026-04-26 00:15:37 +08:00
for _, idx := range h.matcher.Match(domain) {
for _, rep := range h.responses[idx] {
2026-04-14 00:42:29 +08:00
if err, ok := rep.(dns.RCodeError); ok {
2025-07-23 18:11:43 +08:00
if uint16(err) == 0 {
return nil, dns.ErrEmptyResponse
}
return nil, err
}
}
2026-04-26 00:15:37 +08:00
ips = append(ips, h.responses[idx]...)
found = true
}
if !found {
2025-07-23 18:11:43 +08:00
return nil, nil
2020-11-25 19:01:53 +08:00
}
2025-07-23 18:11:43 +08:00
return ips, nil
2021-10-16 21:02:51 +08:00
}
2025-07-23 18:11:43 +08:00
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) {
2026-04-14 00:42:29 +08:00
domain = strings.ToLower(domain)
2025-07-23 18:11:43 +08:00
switch addrs, err := h.lookupInternal(domain); {
case err != nil:
return nil, err
2026-04-14 00:42:29 +08:00
case addrs == nil: // Not recorded in static hosts, return nil
return nil, nil
case len(addrs) == 0: // Domain recorded, but no valid IP returned
2025-07-23 18:11:43 +08:00
return addrs, nil
2021-10-16 21:02:51 +08:00
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
2024-06-29 14:32:57 -04:00
errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it")
2021-10-16 21:02:51 +08:00
if maxDepth > 0 {
2025-07-23 18:11:43 +08:00
unwrapped, err := h.lookup(addrs[0].Domain(), option, maxDepth-1)
if err != nil {
return nil, err
}
2021-10-16 21:02:51 +08:00
if unwrapped != nil {
2025-07-23 18:11:43 +08:00
return unwrapped, nil
2021-10-16 21:02:51 +08:00
}
}
2025-07-23 18:11:43 +08:00
return addrs, nil
2021-10-16 21:02:51 +08:00
default: // IP record found, return a non-nil IP array
2025-07-23 18:11:43 +08:00
return filterIP(addrs, option), nil
2020-11-25 19:01:53 +08:00
}
2021-10-16 21:02:51 +08:00
}
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
2025-07-23 18:11:43 +08:00
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) ([]net.Address, error) {
2026-04-26 00:15:37 +08:00
if h.matcher == nil {
return nil, nil
}
2021-10-16 21:02:51 +08:00
return h.lookup(domain, option, 5)
2020-11-25 19:01:53 +08:00
}