Skip to content

Commit

Permalink
chore: resolving DNS IP and publishing it when no extIp is provided (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielmer authored and ABresting committed Sep 30, 2023
1 parent 2e76165 commit f5ae19d
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 7 deletions.
40 changes: 36 additions & 4 deletions apps/wakunode2/internal_config.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import
chronicles,
chronos,
libp2p/crypto/crypto,
libp2p/multiaddress,
libp2p/nameresolving/dnsresolver,
std/options,
stew/shims/net,
stew/results,
libp2p/crypto/crypto,
libp2p/multiaddress
stew/shims/net
import
../../waku/common/utils/nat,
../../waku/node/config,
Expand All @@ -18,6 +21,23 @@ proc validateExtMultiAddrs*(vals: seq[string]):
multiaddrs.add(multiaddr)
return ok(multiaddrs)

proc dnsResolve*(domain: string, conf: WakuNodeConf): Future[Result[string, string]] {.async} =

# Use conf's DNS servers
var nameServers: seq[TransportAddress]
for ip in conf.dnsAddrsNameServers:
nameServers.add(initTAddress(ip, Port(53))) # Assume all servers use port 53

let dnsResolver = DnsResolver.new(nameServers)

# Resolve domain IP
let resolved = await dnsResolver.resolveIp(domain, 0.Port, Domain.AF_UNSPEC)

if resolved.len > 0:
return ok(resolved[0].host) # Use only first answer
else:
return err("Could not resolve IP from DNS: empty response")

proc networkConfiguration*(conf: WakuNodeConf,
clientId: string,
): NetConfigResult =
Expand All @@ -30,7 +50,7 @@ proc networkConfiguration*(conf: WakuNodeConf,
if natRes.isErr():
return err("failed to setup NAT: " & $natRes.error)

let (extIp, extTcpPort, _) = natRes.get()
var (extIp, extTcpPort, _) = natRes.get()

let
dns4DomainName = if conf.dns4DomainName != "": some(conf.dns4DomainName)
Expand Down Expand Up @@ -69,6 +89,18 @@ proc networkConfiguration*(conf: WakuNodeConf,
relay = conf.relay
)

# Resolve and use DNS domain IP
if dns4DomainName.isSome() and extIp.isNone():
try:
let dnsRes = waitFor dnsResolve(conf.dns4DomainName, conf)

if dnsRes.isErr():
return err($dnsRes.error) # Pass error down the stack

extIp = some(ValidIpAddress.init(dnsRes.get()))
except CatchableError:
return err("Could not update extIp to resolved DNS IP: " & getCurrentExceptionMsg())

# Wrap in none because NetConfig does not have a default constructor
# TODO: We could change bindIp in NetConfig to be something less restrictive
# than ValidIpAddress, which doesn't allow default construction
Expand Down
61 changes: 60 additions & 1 deletion tests/test_wakunode.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{.used.}

import
std/sequtils,
std/[sequtils,strutils],
stew/byteutils,
stew/shims/net as stewNet,
testutils/unittests,
Expand Down Expand Up @@ -215,6 +215,65 @@ suite "WakuNode":
node.announcedAddresses.len == 1
node.announcedAddresses.contains(expectedDns4Addr)

asyncTest "Node uses dns4 resolved ip in announced addresses if no extIp is provided":
let
nodeKey = generateSecp256k1Key()
bindIp = ValidIpAddress.init("0.0.0.0")
bindPort = Port(0)

domainName = "status.im"
node = newTestWakuNode(
nodeKey,
bindIp, bindPort,
dns4DomainName = some(domainName))

var ipStr = ""
var enrIp = node.enr.tryGet("ip", array[4, byte])

if enrIp.isSome():
ipStr &= $ipv4(enrIp.get())

# Check that the IP filled is the one received by the DNS lookup
# As IPs may change, we check that it's not empty, not the 0 IP and not localhost
check:
ipStr.len() > 0
not ipStr.contains("0.0.0.0")
not ipStr.contains("127.0.0.1")

asyncTest "Node creation fails when invalid dns4 address is provided":
let
nodeKey = generateSecp256k1Key()
bindIp = ValidIpAddress.init("0.0.0.0")
bindPort = Port(0)

inexistentDomain = "thisdomain.doesnot.exist"
invalidDomain = ""
expectedError = "Could not resolve IP from DNS: empty response"

var inexistentDomainErr, invalidDomainErr: string = ""

# Create node with inexistent domain
try:
let node = newTestWakuNode(
nodeKey,
bindIp, bindPort,
dns4DomainName = some(inexistentDomain))
except Exception as e:
inexistentDomainErr = e.msg

# Create node with invalid domain
try:
let node = newTestWakuNode(
nodeKey,
bindIp, bindPort,
dns4DomainName = some(invalidDomain))
except Exception as e:
invalidDomainErr = e.msg

# Check that exceptions were raised in both cases
check:
inexistentDomainErr == expectedError
invalidDomainErr == expectedError

asyncTest "Agent string is set and advertised correctly":
let
Expand Down
23 changes: 22 additions & 1 deletion tests/testlib/wakunode.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import
../../../waku/node/peer_manager,
../../../waku/waku_enr,
../../../waku/waku_discv5,
../../apps/wakunode2/internal_config,
../wakunode2/test_app,
./common


Expand All @@ -38,10 +40,29 @@ proc newTestWakuNode*(nodeKey: crypto.PrivateKey,
discv5UdpPort = none(Port),
agentString = none(string),
peerStoreCapacity = none(int)): WakuNode =

var resolvedExtIp = extIp

# Update extPort to default value if it's missing and there's an extIp or a DNS domain
let extPort = if (extIp.isSome() or dns4DomainName.isSome()) and
extPort.isNone():
some(Port(60000))
else:
extPort

if dns4DomainName.isSome() and extIp.isNone():
let conf = defaultTestWakuNodeConf()
# If there's an error resolving the IP, an exception is thrown and test fails
let dnsRes = waitFor dnsResolve(dns4DomainName.get(), conf)
if dnsRes.isErr():
raise newException(Defect, $dnsRes.error)
else:
resolvedExtIp = some(ValidIpAddress.init(dnsRes.get()))

let netConfigRes = NetConfig.init(
bindIp = bindIp,
bindPort = bindPort,
extIp = extIp,
extIp = resolvedExtIp,
extPort = extPort,
extMultiAddrs = extMultiAddrs,
wsBindPort = wsBindPort,
Expand Down
3 changes: 2 additions & 1 deletion tests/wakunode2/test_app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import
../testlib/common,
../testlib/wakucore

proc defaultTestWakuNodeConf(): WakuNodeConf =
proc defaultTestWakuNodeConf*(): WakuNodeConf =
WakuNodeConf(
listenAddress: ValidIpAddress.init("127.0.0.1"),
rpcAddress: ValidIpAddress.init("127.0.0.1"),
restAddress: ValidIpAddress.init("127.0.0.1"),
metricsServerAddress: ValidIpAddress.init("127.0.0.1"),
dnsAddrsNameServers: @[ValidIpAddress.init("1.1.1.1"), ValidIpAddress.init("1.0.0.1")],
nat: "any",
maxConnections: 50,
topics: @["/waku/2/default-waku/proto"],
Expand Down

0 comments on commit f5ae19d

Please sign in to comment.