-
Notifications
You must be signed in to change notification settings - Fork 308
/
fake_resolver.go
165 lines (146 loc) · 3.67 KB
/
fake_resolver.go
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package zgrab2
import (
"context"
"errors"
"fmt"
"golang.org/x/net/dns/dnsmessage"
"net"
"time"
)
// Fake DNS Resolver, to force a DNS lookup to return a pinned address
// Inspired by the golang net/dnsclient_unix_test.go code
//
// For a given IP, create a new Resolver that wraps a fake
// DNS server. This resolver will always return an IP that
// is represented by "ipstr", for DNS queries of the same
// IP type. Otherwise, it will return a DNS lookup error.
func NewFakeResolver(ipstr string) (*net.Resolver, error) {
ip := net.ParseIP(ipstr)
if len(ip) < 4 {
return nil, fmt.Errorf("Fake resolver can't use non-IP '%s'", ipstr)
}
fDNS := FakeDNSServer{
IP: ip,
}
return &net.Resolver{
PreferGo: true, // Needed to force the use of the Go internal resolver
Dial: fDNS.DialContext,
}, nil
}
type FakeDNSServer struct {
// Any domain name will resolve to this IP. It can be either ipv4 or ipv6
IP net.IP
}
// For a given DNS query, return the hard-coded IP that is part of
// FakeDNSServer.
//
// It will work with either ipv4 or ipv6 addresses; if a TypeA question
// is received, we will only return the IP if what we have to return is
// ipv4. The same for TypeAAAA and ipv6.
func (f *FakeDNSServer) fakeDNS(s string, dmsg dnsmessage.Message) (r dnsmessage.Message, err error) {
r = dnsmessage.Message{
Header: dnsmessage.Header{
ID: dmsg.ID,
Response: true,
},
Questions: dmsg.Questions,
}
ipv6 := f.IP.To16()
ipv4 := f.IP.To4()
switch t := dmsg.Questions[0].Type; {
case t == dnsmessage.TypeA && ipv4 != nil:
var ip [4]byte
copy(ip[:], []byte(ipv4))
r.Answers = []dnsmessage.Resource{
{
Header: dnsmessage.ResourceHeader{
Name: dmsg.Questions[0].Name,
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
Length: 4,
},
Body: &dnsmessage.AResource{
A: ip,
},
},
}
case t == dnsmessage.TypeAAAA && ipv4 == nil:
var ip [16]byte
copy(ip[:], []byte(ipv6))
r.Answers = []dnsmessage.Resource{
{
Header: dnsmessage.ResourceHeader{
Name: dmsg.Questions[0].Name,
Type: dnsmessage.TypeAAAA,
Class: dnsmessage.ClassINET,
Length: 16,
},
Body: &dnsmessage.AAAAResource{
AAAA: ip,
},
},
}
default:
r.Header.RCode = dnsmessage.RCodeNameError
}
return r, nil
}
// This merely wraps a custom net.Conn, that is only good for DNS
// messages
func (f *FakeDNSServer) DialContext(ctx context.Context, network,
address string) (net.Conn, error) {
conn := &fakeDNSPacketConn{
fakeDNSConn: fakeDNSConn{
server: f,
network: network,
address: address,
},
}
return conn, nil
}
type fakeDNSConn struct {
net.Conn
server *FakeDNSServer
network string
address string
dmsg dnsmessage.Message
}
func (fc *fakeDNSConn) Read(b []byte) (int, error) {
resp, err := fc.server.fakeDNS(fc.address, fc.dmsg)
if err != nil {
return 0, err
}
bb := make([]byte, 2, 514)
bb, err = resp.AppendPack(bb)
if err != nil {
return 0, fmt.Errorf("cannot marshal DNS message: %v", err)
}
bb = bb[2:]
if len(b) < len(bb) {
return 0, errors.New("read would fragment DNS message")
}
copy(b, bb)
return len(bb), nil
}
func (fc *fakeDNSConn) Write(b []byte) (int, error) {
if fc.dmsg.Unpack(b) != nil {
return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", fc.network, len(b))
}
return len(b), nil
}
func (fc *fakeDNSConn) SetDeadline(deadline time.Time) error {
return nil
}
func (fc *fakeDNSConn) Close() error {
return nil
}
type fakeDNSPacketConn struct {
net.PacketConn
fakeDNSConn
}
func (f *fakeDNSPacketConn) SetDeadline(deadline time.Time) error {
return nil
}
func (f *fakeDNSPacketConn) Close() error {
return f.fakeDNSConn.Close()
}