summaryrefslogtreecommitdiff
path: root/tools/network/resolver.go
blob: ae9d123f2e81656a3b9463ee338e7ae23e886619 (plain)
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
110
111
112
113
114
115
116
117
118
119
// Copyright (C) 2019-2024 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand.  If not, see <https://www.gnu.org/licenses/>.

package network

import (
	"context"
	"net"
)

const (
	dnsPortSuffix     = ":53"
	defaultDNSAddress = "8.8.8.8"
)

// Resolver provides equivalent functionality to the net.Resolver with one exception - it allows to use a provided DNS server instead of relying on the existing default resolver.
type Resolver struct {
	// DNSAddress is the the DNS server that we'll be trying to connect to.
	dnsAddress net.IPAddr
	resolver   ResolverIf
}

// LookupSRV tries to resolve an SRV query of the given service, protocol, and domain name. The proto is "tcp" or "udp". The returned records are sorted by priority and randomized by weight within a priority.
// LookupSRV constructs the DNS name to look up following RFC 2782. That is, it looks up _service._proto.name. To accommodate services publishing SRV records under non-standard names, if both service and proto are empty strings, LookupSRV looks up name directly.
func (p *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupSRV(ctx, service, proto, name)
}

// LookupAddr performs a reverse lookup for the given address, returning a list of names mapping to that address.
func (p *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupAddr(ctx, addr)
}

// LookupCNAME returns the canonical name for the given host. Callers that do not care about the canonical name can call LookupHost or LookupIP directly; both take care of resolving the canonical name as part of the lookup.
// A canonical name is the final name after following zero or more CNAME records. LookupCNAME does not return an error if host does not contain DNS "CNAME" records, as long as host resolves to address records.
func (p *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, err error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupCNAME(ctx, host)

}

// LookupHost looks up the given host using the local resolver. It returns a slice of that host's addresses.
func (p *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupHost(ctx, host)

}

// LookupIPAddr looks up host using the local resolver. It returns a slice of that host's IPv4 and IPv6 addresses.
func (p *Resolver) LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupIPAddr(ctx, host)

}

// LookupMX returns the DNS MX records for the given domain name sorted by preference.
func (p *Resolver) LookupMX(ctx context.Context, name string) ([]*net.MX, error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupMX(ctx, name)

}

// LookupNS returns the DNS NS records for the given domain name.
func (p *Resolver) LookupNS(ctx context.Context, name string) ([]*net.NS, error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupNS(ctx, name)
}

// LookupPort looks up the port for the given network and service.
func (p *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupPort(ctx, network, service)
}

// LookupTXT returns the DNS TXT records for the given domain name.
func (p *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
	p.resolver = p.effectiveResolver()
	return p.resolver.LookupTXT(ctx, name)
}

// EffectiveResolverDNS returns effective DNS server address that would be used for the resolve operation.
func (p *Resolver) EffectiveResolverDNS() string {
	address := p.dnsAddress.String()
	if address == "" {
		return defaultDNSAddress
	}
	return address
}

func (p *Resolver) effectiveResolver() ResolverIf {
	return &net.Resolver{PreferGo: true, Dial: p.resolverDial}
}

// SetFallbackResolverAddress sets preferred DNS server address
func (p *Resolver) SetFallbackResolverAddress(fallbackDNSResolverAddress net.IPAddr) {
	p.dnsAddress = fallbackDNSResolverAddress
	return
}

func (p *Resolver) resolverDial(ctx context.Context, network, address string) (net.Conn, error) {
	// override the default address with our own.
	address = p.EffectiveResolverDNS() + dnsPortSuffix
	return (&net.Dialer{}).DialContext(ctx, network, address)
}