Skip to content

Commit 25fce9e

Browse files
authored
fix!: Only resolve the first DNS-like component (#61)
* fix!: Only resolve a single DNS-like component in multiaddrs BREAKING CHANGE: Previously Resolve would resolve all DNS components in a multiaddr. Now it will only resolve the first one. Users may iteratively call Resolve to get the old behavior. See the tests for an example. * Make test fn resolveAllDNS better * Handle nil input
1 parent f8a0713 commit 25fce9e

File tree

3 files changed

+213
-190
lines changed

3 files changed

+213
-190
lines changed

resolve.go

Lines changed: 137 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ var (
1616
dnsProtocol = ma.ProtocolWithCode(ma.P_DNS)
1717
)
1818

19-
var ResolvableProtocols = []ma.Protocol{dnsaddrProtocol, dns4Protocol, dns6Protocol, dnsProtocol}
20-
var DefaultResolver = &Resolver{def: net.DefaultResolver}
19+
var (
20+
ResolvableProtocols = []ma.Protocol{dnsaddrProtocol, dns4Protocol, dns6Protocol, dnsProtocol}
21+
DefaultResolver = &Resolver{def: net.DefaultResolver}
22+
)
2123

2224
const dnsaddrTXTPrefix = "dnsaddr="
2325

@@ -104,179 +106,166 @@ func (r *Resolver) getResolver(domain string) BasicResolver {
104106
return r.def
105107
}
106108

107-
// Resolve resolves a DNS multiaddr.
109+
// Resolve resolves a DNS multiaddr. It will only resolve the first DNS component in the multiaddr.
110+
// If you need to resolve multiple DNS components, you may call this function again with each returned address.
108111
func (r *Resolver) Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
109-
var results []ma.Multiaddr
110-
for i := 0; maddr != nil; i++ {
111-
var keep ma.Multiaddr
112-
113-
// Find the next dns component.
114-
keep, maddr = ma.SplitFunc(maddr, func(c ma.Component) bool {
115-
switch c.Protocol().Code {
116-
case dnsProtocol.Code, dns4Protocol.Code, dns6Protocol.Code, dnsaddrProtocol.Code:
117-
return true
118-
default:
119-
return false
120-
}
121-
})
112+
if maddr == nil {
113+
return nil, nil
114+
}
122115

123-
// Keep everything before the dns component.
124-
if keep != nil {
125-
if len(results) == 0 {
126-
results = []ma.Multiaddr{keep}
127-
} else {
128-
for i, r := range results {
129-
results[i] = r.Encapsulate(keep)
130-
}
131-
}
116+
// Find the next dns component.
117+
preDNS, maddr := ma.SplitFunc(maddr, func(c ma.Component) bool {
118+
switch c.Protocol().Code {
119+
case dnsProtocol.Code, dns4Protocol.Code, dns6Protocol.Code, dnsaddrProtocol.Code:
120+
return true
121+
default:
122+
return false
132123
}
124+
})
133125

134-
// If the rest is empty, we've hit the end (there _was_ no dns component).
135-
if maddr == nil {
136-
break
137-
}
126+
// If the rest is empty, we've hit the end (there _was_ no dns component).
127+
if maddr == nil {
128+
return []ma.Multiaddr{preDNS}, nil
129+
}
138130

139-
// split off the dns component.
140-
var resolve *ma.Component
141-
resolve, maddr = ma.SplitFirst(maddr)
142-
143-
proto := resolve.Protocol()
144-
value := resolve.Value()
145-
rslv := r.getResolver(value)
146-
147-
// resolve the dns component
148-
var resolved []ma.Multiaddr
149-
switch proto.Code {
150-
case dns4Protocol.Code, dns6Protocol.Code, dnsProtocol.Code:
151-
// The dns, dns4, and dns6 resolver simply resolves each
152-
// dns* component into an ipv4/ipv6 address.
153-
154-
v4only := proto.Code == dns4Protocol.Code
155-
v6only := proto.Code == dns6Protocol.Code
156-
157-
// XXX: Unfortunately, go does a pretty terrible job of
158-
// differentiating between IPv6 and IPv4. A v4-in-v6
159-
// AAAA record will _look_ like an A record to us and
160-
// there's nothing we can do about that.
161-
records, err := rslv.LookupIPAddr(ctx, value)
162-
if err != nil {
163-
return nil, err
164-
}
131+
// split off the dns component.
132+
resolve, postDNS := ma.SplitFirst(maddr)
133+
134+
proto := resolve.Protocol()
135+
value := resolve.Value()
136+
rslv := r.getResolver(value)
137+
138+
// resolve the dns component
139+
var resolved []ma.Multiaddr
140+
switch proto.Code {
141+
case dns4Protocol.Code, dns6Protocol.Code, dnsProtocol.Code:
142+
// The dns, dns4, and dns6 resolver simply resolves each
143+
// dns* component into an ipv4/ipv6 address.
144+
145+
v4only := proto.Code == dns4Protocol.Code
146+
v6only := proto.Code == dns6Protocol.Code
165147

166-
// Convert each DNS record into a multiaddr. If the
167-
// protocol is dns4, throw away any IPv6 addresses. If
168-
// the protocol is dns6, throw away any IPv4 addresses.
169-
170-
for _, r := range records {
171-
var (
172-
rmaddr ma.Multiaddr
173-
err error
174-
)
175-
ip4 := r.IP.To4()
176-
if ip4 == nil {
177-
if v4only {
178-
continue
179-
}
180-
rmaddr, err = ma.NewMultiaddr("/ip6/" + r.IP.String())
181-
} else {
182-
if v6only {
183-
continue
184-
}
185-
rmaddr, err = ma.NewMultiaddr("/ip4/" + ip4.String())
148+
// XXX: Unfortunately, go does a pretty terrible job of
149+
// differentiating between IPv6 and IPv4. A v4-in-v6
150+
// AAAA record will _look_ like an A record to us and
151+
// there's nothing we can do about that.
152+
records, err := rslv.LookupIPAddr(ctx, value)
153+
if err != nil {
154+
return nil, err
155+
}
156+
157+
// Convert each DNS record into a multiaddr. If the
158+
// protocol is dns4, throw away any IPv6 addresses. If
159+
// the protocol is dns6, throw away any IPv4 addresses.
160+
161+
for _, r := range records {
162+
var (
163+
rmaddr ma.Multiaddr
164+
err error
165+
)
166+
ip4 := r.IP.To4()
167+
if ip4 == nil {
168+
if v4only {
169+
continue
186170
}
187-
if err != nil {
188-
return nil, err
171+
rmaddr, err = ma.NewMultiaddr("/ip6/" + r.IP.String())
172+
} else {
173+
if v6only {
174+
continue
189175
}
190-
resolved = append(resolved, rmaddr)
176+
rmaddr, err = ma.NewMultiaddr("/ip4/" + ip4.String())
191177
}
192-
case dnsaddrProtocol.Code:
193-
// The dnsaddr resolver is a bit more complicated. We:
194-
//
195-
// 1. Lookup the dnsaddr txt record on _dnsaddr.DOMAIN.TLD
196-
// 2. Take everything _after_ the `/dnsaddr/DOMAIN.TLD`
197-
// part of the multiaddr.
198-
// 3. Find the dnsaddr records (if any) with suffixes
199-
// matching the result of step 2.
200-
201-
// First, lookup the TXT record
202-
records, err := rslv.LookupTXT(ctx, "_dnsaddr."+value)
203178
if err != nil {
204179
return nil, err
205180
}
181+
resolved = append(resolved, rmaddr)
182+
}
183+
case dnsaddrProtocol.Code:
184+
// The dnsaddr resolver is a bit more complicated. We:
185+
//
186+
// 1. Lookup the dnsaddr txt record on _dnsaddr.DOMAIN.TLD
187+
// 2. Take everything _after_ the `/dnsaddr/DOMAIN.TLD`
188+
// part of the multiaddr.
189+
// 3. Find the dnsaddr records (if any) with suffixes
190+
// matching the result of step 2.
191+
192+
// First, lookup the TXT record
193+
records, err := rslv.LookupTXT(ctx, "_dnsaddr."+value)
194+
if err != nil {
195+
return nil, err
196+
}
206197

207-
// Then, calculate the length of the suffix we're
208-
// looking for.
209-
length := 0
210-
if maddr != nil {
211-
length = addrLen(maddr)
198+
// Then, calculate the length of the suffix we're
199+
// looking for.
200+
length := 0
201+
if postDNS != nil {
202+
length = addrLen(postDNS)
203+
}
204+
205+
for _, r := range records {
206+
// Ignore non dnsaddr TXT records.
207+
if !strings.HasPrefix(r, dnsaddrTXTPrefix) {
208+
continue
212209
}
213210

214-
for _, r := range records {
215-
// Ignore non dnsaddr TXT records.
216-
if !strings.HasPrefix(r, dnsaddrTXTPrefix) {
217-
continue
218-
}
211+
// Extract and decode the multiaddr.
212+
rmaddr, err := ma.NewMultiaddr(r[len(dnsaddrTXTPrefix):])
213+
if err != nil {
214+
// discard multiaddrs we don't understand.
215+
// XXX: Is this right? It's the best we
216+
// can do for now, really.
217+
continue
218+
}
219219

220-
// Extract and decode the multiaddr.
221-
rmaddr, err := ma.NewMultiaddr(r[len(dnsaddrTXTPrefix):])
222-
if err != nil {
223-
// discard multiaddrs we don't understand.
224-
// XXX: Is this right? It's the best we
225-
// can do for now, really.
220+
// If we have a suffix to match on.
221+
if postDNS != nil {
222+
// Make sure the new address is at least
223+
// as long as the suffix we're looking
224+
// for.
225+
rmlen := addrLen(rmaddr)
226+
if rmlen < length {
227+
// not long enough.
226228
continue
227229
}
228230

229-
// If we have a suffix to match on.
230-
if maddr != nil {
231-
// Make sure the new address is at least
232-
// as long as the suffix we're looking
233-
// for.
234-
rmlen := addrLen(rmaddr)
235-
if rmlen < length {
236-
// not long enough.
237-
continue
238-
}
239-
240-
// Matches everything after the /dnsaddr/... with the end of the
241-
// dnsaddr record:
242-
//
243-
// v----------rmlen-----------------v
244-
// /ip4/1.2.3.4/tcp/1234/p2p/QmFoobar
245-
// /p2p/QmFoobar
246-
// ^--(rmlen - length)--^---length--^
247-
if !maddr.Equal(offset(rmaddr, rmlen-length)) {
248-
continue
249-
}
231+
// Matches everything after the /dnsaddr/... with the end of the
232+
// dnsaddr record:
233+
//
234+
// v----------rmlen-----------------v
235+
// /ip4/1.2.3.4/tcp/1234/p2p/QmFoobar
236+
// /p2p/QmFoobar
237+
// ^--(rmlen - length)--^---length--^
238+
if !postDNS.Equal(offset(rmaddr, rmlen-length)) {
239+
continue
250240
}
251-
252-
resolved = append(resolved, rmaddr)
253241
}
254242

255-
// consumes the rest of the multiaddr as part of the "match" process.
256-
maddr = nil
257-
default:
258-
panic("unreachable")
243+
// remove the suffix from the multiaddr, we'll add it back at the end.
244+
if postDNS != nil {
245+
rmaddr = rmaddr.Decapsulate(postDNS)
246+
}
247+
resolved = append(resolved, rmaddr)
259248
}
249+
default:
250+
panic("unreachable")
251+
}
252+
253+
if len(resolved) == 0 {
254+
return nil, nil
255+
}
260256

261-
if len(resolved) == 0 {
262-
return nil, nil
263-
} else if len(results) == 0 {
264-
results = resolved
265-
} else {
266-
// We take the cross product here as we don't have any
267-
// better way to represent "ORs" in multiaddrs. For
268-
// example, `/dns/foo.com/p2p-circuit/dns/bar.com` could
269-
// resolve to:
270-
//
271-
// * /ip4/1.1.1.1/p2p-circuit/ip4/2.1.1.1
272-
// * /ip4/1.1.1.1/p2p-circuit/ip4/2.1.1.2
273-
// * /ip4/1.1.1.2/p2p-circuit/ip4/2.1.1.1
274-
// * /ip4/1.1.1.2/p2p-circuit/ip4/2.1.1.2
275-
results = cross(results, resolved)
257+
if preDNS != nil {
258+
for i, m := range resolved {
259+
resolved[i] = preDNS.Encapsulate(m)
260+
}
261+
}
262+
if postDNS != nil {
263+
for i, m := range resolved {
264+
resolved[i] = m.Encapsulate(postDNS)
276265
}
277266
}
278267

279-
return results, nil
268+
return resolved, nil
280269
}
281270

282271
func (r *Resolver) LookupIPAddr(ctx context.Context, domain string) ([]net.IPAddr, error) {

0 commit comments

Comments
 (0)