NetBurner 3.5.8
PDF Version
DNS Name Resolution

DNS Name Resolution

DNS turns a name such as www.netburner.com into an IP address your application can connect to. The NetBurner API offers a blocking call for simple cases and a non-blocking pair for applications built around select().

This page covers what a lookup actually does, so you can size timeouts, read a packet capture, and choose between the two interfaces.

Choosing an Interface

Blocking Non-blocking
┌────────────────────┐ ┌──────────────────────┐
GetHostByName() │ │ fd_dns_part1() │
│ │ vs │ select() │
│ returns an address │ │ fd_dns_processresult()│
└────────────────────┘ └──────────────────────┘
Simple, blocks the task Fits an existing select loop,
you implement the retry policy
bool fd_dns_processresult(int fd, const char *name, IPADDR &addr_out, uint16_t TYPE=DNS_A, uint16_t TYPE2=DNS_AAAA, uint32_t *ttl=0)
Process any responses on the UDP socket opened for DNS.
int fd_dns_part1(const char *name, const IPADDR &dns_server, uint16_t TYPE=DNS_A, uint16_t TYPE2=extra_dns_t, int ifn=-1)
Open a UDP socket and initiate a DNS lookup.
int GetHostByName(const char *name, IPADDR *pIpaddr, const IPADDR &dns_server, const TickTimeout tout, uint16_t TYPE1=DNS_A, uint16_t TYPE2=extra_dns_t, uint32_t *ttl=NULL)
Get the IP address associated with the specified domain name.
Definition dns.h:186
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, unsigned long timeout)
Wait for events to occur on one or more I/O resources associated with a set of file descriptors (fds)...
Definition iosys.h:648

Use GetHostByName() unless your task already runs a select() loop and cannot afford to block. The non-blocking pair gives you the socket file descriptor so you can add it to your own fd_set.


Record Types

By default a lookup asks for both an IPv4 address and an IPv6 address, and returns whichever the host publishes. On an IPv4 only build it asks for IPv4 alone. This suits most applications, which simply want a usable address.

The TYPE and TYPE2 parameters narrow the request. Pass the same type twice to ask for one record type:

// IPv4 only
int rv = GetHostByName("www.netburner.com", &ipAddress, IPADDR::NullIP(),
static IPADDR6 NullIP()
Static function to return a null IPADDR6 object.
#define DNS_A
32-bit IPv4 address
Definition dns.h:49
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49

Pass DNS_AAAA as both types to ask for IPv6 alone. The available record types are listed under DNS Record Types.

The same two parameters apply to fd_dns_part1(), and the types passed there must match the types passed to fd_dns_processresult().


Timeouts and Retries

The timeout you pass is a total timeout for the whole lookup, not a per-query value. Within that budget the resolver retransmits an outstanding query on a backoff, so a query lost in transit is re-sent rather than costing the entire timeout.

Size the budget for the round trip you expect plus room for at least one retry. Ten seconds is reasonable for a public resolver, less for one on the LAN.

IPADDR ipAddress;
ipAddress.SetNull();
int rv = GetHostByName("www.netburner.com", &ipAddress, IPADDR::NullIP(),
if (rv == DNS_OK)
{
// ipAddress is valid
}
void SetNull()
Set the IP address value of an IPADDR6 object to null.
Definition ipv6_addr.h:320
#define DNS_OK
Success.
Definition dns.h:38
IPADDR6 IPADDR
IPADDR Object Type (either v4 or v6).
Definition nettypes.h:568

Return Codes

Code Meaning
DNS_OK A usable address was returned.
DNS_NOSUCHNAME The server answered, and the name has no record of the requested type.
DNS_TIMEOUT No usable answer arrived within the budget. The server may be unreachable, or the name may be unresolvable.
DNS_ERR The request could not be made, for example a null name.

The distinction between DNS_NOSUCHNAME and DNS_TIMEOUT is worth reading carefully when diagnosing a failure. DNS_NOSUCHNAME means something replied. DNS_TIMEOUT means the timeout expired.


Before the First Lookup

DNS needs more than a link. The interface needs an active link and a DNS server address. It needs a gateway address as well if the DNS server is on another subnet.

WaitForActiveNetwork() waits for link only. An application that resolves a name immediately after startup should check the rest:

bool CanInterfaceDoDNS(int ifNumber)
{
if (!InterfaceLinkActive(ifNumber)) return false;
if (InterfaceDNS(ifNumber).IsNull()) return false;
// A DNS server on this subnet is reached directly.
if (IsLocal4(InterfaceDNS(ifNumber), ifNumber)) return true;
// Anything further away needs a gateway.
return InterfaceGate(ifNumber).NotNull();
}
bool NotNull() const
Check if the IP address is not null.
Definition nettypes.h:288
BOOL IsLocal4(IPADDR4 ip, int ifc)
Check if an IPv4 address is on the local network segment.
bool InterfaceLinkActive(int interface)
Returns the link status of the specified network interface.
IPADDR4 InterfaceDNS(int interface)
Returns the IPv4 DNS address of the specified network interface.
IPADDR4 InterfaceGate(int interface)
Returns the IPv4 gateway address of the specified network interface.
Note
Check these prerequisites rather than a fixed delay. A device using a static configuration is ready to resolve names much sooner after boot than one waiting on a DHCP lease, so a delay tuned for one is wrong for the other.

Choosing the Server

Pass IPADDR::NullIP() to use the DNS server the interface already has, whether it came from DHCP or static configuration. This is what most applications want.

// Use the interface's configured DNS server
// Ask a specific server instead
IPADDR server;
server.SetFromAscii("8.8.8.8");
GetHostByName(name, &ip, server, TICKS_PER_SECOND * 10);
void SetFromAscii(const char *cp, bool bembed_v4addresses=true)
Set the IP address value of an IPADDR6 object.

GetHostByNameViaIfNum() restricts the query to one interface on a multi-homed device.


The Response Cache

Successful lookups are cached, so a repeated lookup of the same name returns immediately without network traffic. Entries expire according to the TTL supplied by the DNS server.

A name resolved a moment ago costs nothing to resolve again, so applications are free to call GetHostByName() at the point of use rather than caching the address themselves.

An IP address written as text needs no lookup at all. GetHostByName("10.1.1.50", ...) returns that address directly, so a setting that accepts either a name or an address can be passed straight through.

The cache is enabled by DNS_CACHE in predef.h, and its size is set by NUM_DNS_CACHE in constants.h.


Names Ending in .local

A name ending in .local is not sent to your DNS server. It is resolved by multicast DNS: the query goes to the multicast group 224.0.0.251 on port 5353, and any host on the local network that owns the name answers.

This means .local lookups work with no DNS server configured at all, and equally that a correctly configured DNS server has no effect on them. Only IPv4 .local lookups are supported.


Non-blocking Lookups

fd_dns_part1() sends the query and returns a file descriptor to add to your fd_set. When select() reports the descriptor readable, fd_dns_processresult() parses whatever arrived and returns true once it has an address.

if (fd >= 0)
{
fd_set rd;
FD_ZERO(&rd);
FD_SET(fd, &rd);
if (select(FD_SETSIZE, &rd, (fd_set *)0, (fd_set *)0, timeout) != 0)
{
IPADDR result;
if (fd_dns_processresult(fd, name, result, DNS_A, DNS_AAAA))
{
// result is valid
}
}
close(fd);
}
#define DNS_AAAA
128-bit IPv6 address
Definition dns.h:54
void FD_SET(int fd, fd_set *pfds)
A fd_set (file descriptor set) holds a set of file descriptors (fds). This function sets or adds a sp...
int close(int fd)
Close the specified file descriptor and free the associated resources.
void FD_ZERO(fd_set *pfds)
Clear (set to 0) a fd_set (file descriptor set) so no file descriptors (fds) are selected.

Three responsibilities move to you with this interface:

  • Close the descriptor. Every path out, including failure, must close() it.
  • Implement the timeout. Nothing bounds how long you wait; your application must decide when time out.
  • Match the record types. Pass the same TYPE and TYPE2 to both calls, or you will parse for records you never requested.
Note
fd_dns_processresult() returns true only when it has an address. A false return does not distinguish "still waiting" from "the server said no", so your timeout is what decides when to stop.

When a Lookup Fails

Start with the interface.** CanInterfaceDoDNS() above tests the prerequisites. A missing DNS server address is the common cause, followed by a missing gateway when the DNS server is on another subnet.

Read the return code.** DNS_TIMEOUT points at reachability, DNS_NOSUCHNAME means a server answered and the record does not exist.

Test with a name you have not resolved yet,** so the answer comes from the network rather than the cache.

Narrow the request to one record type** with DNS_A, DNS_A to confirm IPv4 resolution on its own.


See Also