nim-libp2p/tests/testnameresolve.nim

252 lines
11 KiB
Nim

{.used.}
# Nim-Libp2p
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import std/[strutils, sequtils, tables]
import chronos
import ../libp2p/[stream/connection,
transports/tcptransport,
upgrademngrs/upgrade,
multiaddress,
nameresolving/nameresolver,
nameresolving/dnsresolver,
nameresolving/mockresolver]
import ./helpers
#
#Cloudflare
const fallbackDnsServers = @[
initTAddress("1.1.1.1:53"),
initTAddress("1.0.0.1:53"),
initTAddress("[2606:4700:4700::1111]:53")
]
const unixPlatform = defined(linux) or defined(solaris) or
defined(macosx) or defined(freebsd) or
defined(netbsd) or defined(openbsd) or
defined(dragonfly)
proc guessOsNameServers(): seq[TransportAddress] =
when unixPlatform:
var resultSeq = newSeqOfCap[TransportAddress](3)
try:
for l in lines("/etc/resolv.conf"):
let lineParsed = l.strip().split(seps = Whitespace + {'%'}, maxsplit = 2)
if lineParsed.len < 2: continue
if lineParsed[0].startsWith('#'): continue
if lineParsed[0] == "nameserver":
resultSeq.add(initTAddress(lineParsed[1], Port(53)))
if resultSeq.len > 2: break #3 nameserver max on linux
except CatchableError as err:
echo "Failed to get unix nameservers ", err.msg
finally:
if resultSeq.len > 0:
return resultSeq
return fallbackDnsServers
elif defined(windows):
#TODO
return fallbackDnsServers
else:
return fallbackDnsServers
suite "Name resolving":
suite "Generic Resolving":
var resolver {.threadvar.}: MockResolver
proc testOne(input: string, output: seq[MultiAddress]): bool =
let resolved = waitFor resolver.resolveMAddress(MultiAddress.init(input).tryGet())
if resolved != output:
echo "Expected ", output
echo "Got ", resolved
return false
return true
proc testOne(input: string, output: seq[string]): bool =
testOne(input, output.mapIt(MultiAddress.init(it).tryGet()))
proc testOne(input, output: string): bool =
testOne(input, @[MultiAddress.init(output).tryGet()])
asyncSetup:
resolver = MockResolver.new()
asyncTest "test multi address dns resolve":
resolver.ipResponses[("localhost", false)] = @["127.0.0.1"]
resolver.ipResponses[("localhost", true)] = @["::1"]
check testOne("/dns/localhost/udp/0", @["/ip4/127.0.0.1/udp/0", "/ip6/::1/udp/0"])
check testOne("/dns4/localhost/tcp/0", "/ip4/127.0.0.1/tcp/0")
check testOne("/dns6/localhost/tcp/0", "/ip6/::1/tcp/0")
check testOne("/dns6/localhost/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "/ip6/::1/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")
asyncTest "test non dns resolve":
resolver.ipResponses[("localhost", false)] = @["127.0.0.1"]
resolver.ipResponses[("localhost", true)] = @["::1"]
check testOne("/ip6/::1/tcp/0", "/ip6/::1/tcp/0")
asyncTest "dnsaddr recursive test":
resolver.txtResponses["_dnsaddr.bootstrap.libp2p.io"] = @[
"dnsaddr=/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"dnsaddr=/dnsaddr/ams-2.bootstrap.libp2p.io/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb"
]
resolver.txtResponses["_dnsaddr.sjc-1.bootstrap.libp2p.io"] = @[
"dnsaddr=/ip6/2604:1380:1000:6000::1/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"dnsaddr=/ip4/147.75.69.143/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"
]
resolver.txtResponses["_dnsaddr.ams-2.bootstrap.libp2p.io"] = @[
"dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
"dnsaddr=/ip6/2604:1380:2000:7a00::1/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb"
]
check testOne("/dnsaddr/bootstrap.libp2p.io/", @[
"/ip6/2604:1380:1000:6000::1/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/ip4/147.75.69.143/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
"/ip6/2604:1380:2000:7a00::1/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
])
asyncTest "dnsaddr suffix matching test":
resolver.txtResponses["_dnsaddr.bootstrap.libp2p.io"] = @[
"dnsaddr=/dnsaddr/ams-2.bootstrap.libp2p.io/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
"dnsaddr=/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"dnsaddr=/dnsaddr/nrt-1.bootstrap.libp2p.io/tcp/4001/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
"dnsaddr=/dnsaddr/ewr-1.bootstrap.libp2p.io/tcp/4001/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
]
resolver.txtResponses["_dnsaddr.sjc-1.bootstrap.libp2p.io"] = @[
"dnsaddr=/ip4/147.75.69.143/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"dnsaddr=/ip6/2604:1380:1000:6000::1/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
]
resolver.txtResponses["_dnsaddr.ams-1.bootstrap.libp2p.io"] = @[
"dnsaddr=/ip4/147.75.69.143/tcp/4001/p2p/shouldbefiltered",
"dnsaddr=/ip6/2604:1380:1000:6000::1/tcp/4001/p2p/shouldbefiltered",
]
check testOne("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", @[
"/ip4/147.75.69.143/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/ip6/2604:1380:1000:6000::1/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
])
asyncTest "dnsaddr infinite recursion":
resolver.txtResponses["_dnsaddr.bootstrap.libp2p.io"] = @["dnsaddr=/dnsaddr/bootstrap.libp2p.io"]
check testOne("/dnsaddr/bootstrap.libp2p.io/", newSeq[string]())
test "getHostname":
check:
MultiAddress.init("/dnsaddr/bootstrap.libp2p.io/").tryGet().getHostname == "bootstrap.libp2p.io"
MultiAddress.init("/ip4/147.75.69.143/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN").tryGet().getHostname == "147.75.69.143"
MultiAddress.init("/ip6/2604:1380:1000:6000::1/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN").tryGet().getHostname == "2604:1380:1000:6000::1"
MultiAddress.init("/dns/localhost/udp/0").tryGet().getHostname == "localhost"
MultiAddress.init("/dns4/hello.com/udp/0").tryGet().getHostname == "hello.com"
MultiAddress.init("/dns6/hello.com/udp/0").tryGet().getHostname == "hello.com"
MultiAddress.init("/wss/").tryGet().getHostname == ""
suite "DNS Resolving":
teardown:
checkTrackers()
asyncTest "test manual dns ip resolve":
## DNS mock server
proc clientMark1(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var msg = transp.getMessage()
let
resp = if msg[24] == 1: #AAAA or A
"\xae\xbf\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x06\x73\x74\x61" &
"\x74\x75\x73\x02\x69\x6d\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00" &
"\x01\x00\x00\x00\x4f\x00\x04\x68\x16\x18\xb5\xc0\x0c\x00\x01\x00" &
"\x01\x00\x00\x00\x4f\x00\x04\xac\x43\x0a\xa1\xc0\x0c\x00\x01\x00" &
"\x01\x00\x00\x00\x4f\x00\x04\x68\x16\x19\xb5"
else:
"\xe8\xc5\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x06\x73\x74\x61" &
"\x74\x75\x73\x02\x69\x6d\x00\x00\x1c\x00\x01\xc0\x0c\x00\x1c\x00" &
"\x01\x00\x00\x00\x4f\x00\x10\x26\x06\x47\x00\x00\x10\x00\x00\x00" &
"\x00\x00\x00\x68\x16\x19\xb5\xc0\x0c\x00\x1c\x00\x01\x00\x00\x00" &
"\x4f\x00\x10\x26\x06\x47\x00\x00\x10\x00\x00\x00\x00\x00\x00\x68" &
"\x16\x18\xb5\xc0\x0c\x00\x1c\x00\x01\x00\x00\x00\x4f\x00\x10\x26" &
"\x06\x47\x00\x00\x10\x00\x00\x00\x00\x00\x00\xac\x43\x0a\xa1"
await transp.sendTo(raddr, resp)
let server = newDatagramTransport(clientMark1)
# The test
var dnsresolver = DnsResolver.new(@[server.localAddress])
check await(dnsresolver.resolveIp("status.im", 0.Port, Domain.AF_UNSPEC)) ==
mapIt(
@["104.22.24.181:0", "172.67.10.161:0", "104.22.25.181:0",
"[2606:4700:10::6816:19b5]:0", "[2606:4700:10::6816:18b5]:0", "[2606:4700:10::ac43:aa1]:0"
], initTAddress(it))
check await(dnsresolver.resolveIp("status.im", 0.Port, Domain.AF_INET)) ==
mapIt(@["104.22.24.181:0", "172.67.10.161:0", "104.22.25.181:0"], initTAddress(it))
check await(dnsresolver.resolveIp("status.im", 0.Port, Domain.AF_INET6)) ==
mapIt(@["[2606:4700:10::6816:19b5]:0", "[2606:4700:10::6816:18b5]:0", "[2606:4700:10::ac43:aa1]:0"], initTAddress(it))
await server.closeWait()
asyncTest "test unresponsive dns server":
var unresponsiveTentatives = 0
## DNS mock server
proc clientMark1(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
unresponsiveTentatives.inc()
check unresponsiveTentatives == 1
proc clientMark2(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var msg = transp.getMessage()
let resp =
"\xae\xbf\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x06\x73\x74\x61" &
"\x74\x75\x73\x02\x69\x6d\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00" &
"\x01\x00\x00\x00\x4f\x00\x04\x68\x16\x18\xb5\xc0\x0c\x00\x01\x00" &
"\x01\x00\x00\x00\x4f\x00\x04\xac\x43\x0a\xa1\xc0\x0c\x00\x01\x00" &
"\x01\x00\x00\x00\x4f\x00\x04\x68\x16\x19\xb5"
await transp.sendTo(raddr, resp)
let
unresponsiveServer = newDatagramTransport(clientMark1)
server = newDatagramTransport(clientMark2)
# The test
var dnsresolver = DnsResolver.new(@[unresponsiveServer.localAddress, server.localAddress])
check await(dnsresolver.resolveIp("status.im", 0.Port, Domain.AF_INET)) ==
mapIt(@["104.22.24.181:0", "172.67.10.161:0", "104.22.25.181:0"], initTAddress(it))
check await(dnsresolver.resolveIp("status.im", 0.Port, Domain.AF_INET)) ==
mapIt(@["104.22.24.181:0", "172.67.10.161:0", "104.22.25.181:0"], initTAddress(it))
await server.closeWait()
await unresponsiveServer.closeWait()
asyncTest "inexisting domain resolving":
var dnsresolver = DnsResolver.new(guessOsNameServers())
let invalid = await dnsresolver.resolveIp("thisdomain.doesnot.exist", 0.Port)
check invalid.len == 0
asyncTest "wrong domain resolving":
var dnsresolver = DnsResolver.new(guessOsNameServers())
let invalid = await dnsresolver.resolveIp("", 0.Port)
check invalid.len == 0
asyncTest "unreachable dns server":
var dnsresolver = DnsResolver.new(@[initTAddress("172.67.10.161:53")])
let invalid = await dnsresolver.resolveIp("google.fr", 0.Port)
check invalid.len == 0