Hunter0x7c7
2022-08-11 b8230139fb40edea387617b6accd8371e37eda58
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
package dns
 
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
 
import (
    "github.com/v2fly/v2ray-core/v5/common/net"
    "github.com/v2fly/v2ray-core/v5/features/dns"
    "github.com/v2fly/v2ray-core/v5/features/routing"
)
 
// ResolvableContext is an implementation of routing.Context, with domain resolving capability.
type ResolvableContext struct {
    routing.Context
    dnsClient   dns.Client
    resolvedIPs []net.IP
}
 
// GetTargetIPs overrides original routing.Context's implementation.
func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
    if ips := ctx.Context.GetTargetIPs(); len(ips) != 0 {
        return ips
    }
 
    if len(ctx.resolvedIPs) > 0 {
        return ctx.resolvedIPs
    }
 
    if domain := ctx.GetTargetDomain(); len(domain) != 0 {
        lookupFunc := ctx.dnsClient.LookupIP
        ipOption := &dns.IPOption{
            IPv4Enable: true,
            IPv6Enable: true,
        }
 
        if c, ok := ctx.dnsClient.(dns.ClientWithIPOption); ok {
            ipOption = c.GetIPOption()
            c.SetFakeDNSOption(false) // Skip FakeDNS.
        } else {
            newError("ctx.dnsClient doesn't implement ClientWithIPOption").AtDebug().WriteToLog()
        }
 
        switch {
        case ipOption.IPv4Enable && !ipOption.IPv6Enable:
            if lookupIPv4, ok := ctx.dnsClient.(dns.IPv4Lookup); ok {
                lookupFunc = lookupIPv4.LookupIPv4
            } else {
                newError("ctx.dnsClient doesn't implement IPv4Lookup. Use LookupIP instead.").AtDebug().WriteToLog()
            }
        case !ipOption.IPv4Enable && ipOption.IPv6Enable:
            if lookupIPv6, ok := ctx.dnsClient.(dns.IPv6Lookup); ok {
                lookupFunc = lookupIPv6.LookupIPv6
            } else {
                newError("ctx.dnsClient doesn't implement IPv6Lookup. Use LookupIP instead.").AtDebug().WriteToLog()
            }
        }
 
        ips, err := lookupFunc(domain)
        if err == nil {
            ctx.resolvedIPs = ips
            return ips
        }
        newError("resolve ip for ", domain).Base(err).WriteToLog()
    }
 
    return nil
}
 
// ContextWithDNSClient creates a new routing context with domain resolving capability.
// Resolved domain IPs can be retrieved by GetTargetIPs().
func ContextWithDNSClient(ctx routing.Context, client dns.Client) routing.Context {
    return &ResolvableContext{Context: ctx, dnsClient: client}
}