Hunter0x7c7
2022-08-11 a82f9cb69f63aaeba40c024960deda7d75b9fece
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package dns
 
import (
    "github.com/v2fly/v2ray-core/v5/common"
    "github.com/v2fly/v2ray-core/v5/common/net"
    "github.com/v2fly/v2ray-core/v5/common/strmatcher"
    "github.com/v2fly/v2ray-core/v5/features"
    "github.com/v2fly/v2ray-core/v5/features/dns"
)
 
// StaticHosts represents static domain-ip mapping in DNS server.
type StaticHosts struct {
    ips      [][]net.Address
    matchers *strmatcher.LinearIndexMatcher
}
 
// NewStaticHosts creates a new StaticHosts instance.
func NewStaticHosts(hosts []*HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
    g := new(strmatcher.LinearIndexMatcher)
    sh := &StaticHosts{
        ips:      make([][]net.Address, len(hosts)+len(legacy)+16),
        matchers: g,
    }
 
    if legacy != nil {
        features.PrintDeprecatedFeatureWarning("simple host mapping")
 
        for domain, ip := range legacy {
            matcher, err := strmatcher.Full.New(domain)
            common.Must(err)
            id := g.Add(matcher)
 
            address := ip.AsAddress()
            if address.Family().IsDomain() {
                return nil, newError("invalid domain address in static hosts: ", address.Domain()).AtWarning()
            }
 
            sh.ips[id] = []net.Address{address}
        }
    }
 
    for _, mapping := range hosts {
        matcher, err := toStrMatcher(mapping.Type, mapping.Domain)
        if err != nil {
            return nil, newError("failed to create domain matcher").Base(err)
        }
        id := g.Add(matcher)
        ips := make([]net.Address, 0, len(mapping.Ip)+1)
        switch {
        case len(mapping.ProxiedDomain) > 0:
            ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
        case len(mapping.Ip) > 0:
            for _, ip := range mapping.Ip {
                addr := net.IPAddress(ip)
                if addr == nil {
                    return nil, newError("invalid IP address in static hosts: ", ip).AtWarning()
                }
                ips = append(ips, addr)
            }
        default:
            return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
        }
 
        sh.ips[id] = ips
    }
 
    return sh, nil
}
 
func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
    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
}
 
func (h *StaticHosts) lookupInternal(domain string) []net.Address {
    var ips []net.Address
    for _, id := range h.matchers.Match(domain) {
        ips = append(ips, h.ips[id]...)
    }
    return ips
}
 
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address {
    switch addrs := h.lookupInternal(domain); {
    case len(addrs) == 0: // Not recorded in static hosts, return nil
        return nil
    case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
        newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog()
        if maxDepth > 0 {
            unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
            if unwrapped != nil {
                return unwrapped
            }
        }
        return addrs
    default: // IP record found, return a non-nil IP array
        return filterIP(addrs, option)
    }
}
 
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address {
    return h.lookup(domain, option, 5)
}