Chapter 170. DNS

170.1. DNS API

The DNS client uses the normal BSD API for performing lookups: gethostbyname(), gethostbyaddr(), getaddrinfo(), getnameinfo().

There are a few restrictions:

  • If the DNS server returns multiple authoritive records for a host name to gethostbyname, the hostent will only contain a record for the first entry. If multiple records are desired, use getaddrinfo, which will return multiple results.
  • The code has been made thread safe. ie multiple threads may call gethostbyname() without causing problems to the hostent structure returned. What is not safe is one thread using both gethostbyname() and gethostbyaddr(). A call to one will destroy the results from the previous call to the other function. getaddrinfo() and getnameinfo() are thread safe and so these are the preferred interfaces. They are also address family independent so making it easier to port code to IPv6.
  • The DNS client will only return IPv4 addresses to RedBoot. At the moment this is not really a limitation, since RedBoot only supports IPv4 and not IPv6.

To initialise the DNS client the following function must be called:

#include <network.h>
int cyg_dns_res_start(char * dns_server)

Where dns_server is the address of the DNS server. The address must be in numeric form and can be either an IPv4 or an IPv6 address.

There also exists a deprecated function to start the DNS client:

int cyg_dns_res_init(struct in_addr *dns_server)

where dns_server is the address of the DNS server the client should query. The address should be in network order and can only be an IPv4 address.

On error both this function returns -1, otherwise 0 for success. If lookups are attemped before this function has been called, they will fail and return NULL, unless numeric host addresses are passed. In this cause, the address will be converted and returned without the need for a lookup.

A default, hard coded, server may be specified in the CDL option CYGDAT_NS_DNS_DEFAULT_SERVER. The use of this is controlled by CYGPKG_NS_DNS_DEFAULT. If this is enabled, init_all_network_interfaces() will initialize the resolver with the hard coded address. The DHCP client or user code my override this address by calling cyg_dns_res_init again.

The DNS client understands the concepts of the target being in a domain. By default no domain will be used. Host name lookups should be for fully qualified names. The domain name can be set and retrieved using the functions:

int getdomainname(char *name, size_t len);

int setdomainname(const char *name, size_t len);

Alternatively, a hard coded domain name can be set using CDL. The boolean CYGPKG_NS_DNS_DOMAINNAME enables this and the domain name is taken from CYGPKG_NS_DNS_DOMAINNAME_NAME.

Once set, the DNS client will use some simple heuristics when deciding how to use the domainname. If the name given to the client ends with a "." it is assumed to be a FQDN and the domain name will not be used. If the name contains a "." somewhere within it, first a lookup will be performed without the domainname. If that fails the domainname will be appended and looked up. If the name does not contain a ".", the domainname is appended and used for the first query. If that fails, the unadorned name is lookup.

The getaddrinfo will return both IPv4 and IPv6 addresses for a given host name, when IPv6 is enabled in the eCos configuration. The CDL option CYGOPT_NS_DNS_FIRST_FAMILY controls the order IPv6 and IPv4 addresses are returned in the linked list of addrinfo structures. If the value AF_INET is used, the IPv4 addresses will be first. If the value AF_INET6, which is the default, is used, IPv6 address will be first. This ordering will control how clients attempt to connect to servers, ie using IPv6 or IPv4 first.

170.2. DNS Client Testing

The DNS client has a test program, dns1.c, which tests many of the features of the DNS client and the functions gethostbyname(), gethostbyaddr(), getaddrinfo(), getnameinfo().

In order for this test to work, a DNS server must be configured with a number of names and addresses. The following is an example forward address resolution database for bind v9, which explains the requirements.

@         1D IN SOA       @ hostmaster.ecoscentric.com. (
                        2017022501      ; serial
                        3H              ; refresh
                        2H              ; retry
                        2W              ; expiry
                        1D )            ; minimum

          1D IN NS        ns0
          1D IN NS        ns1
          1D IN NS        dns1.zoneedit.com.
          1D IN NS        dns2.zoneedit.com.

albus     1D IN A         212.13.207.200
barn      1D IN A         87.127.120.188
farm      1D IN A         88.97.17.238
fawkes    1D IN A         212.13.207.202
www       1D IN CNAME     albus
www2      1D IN CNAME     fawkes

The actual names and addresses do not matter, since they are configurable in the test. What is important is the relationship between the names and the addresses and there family. ie hostnamev4 should map to one IPv4 address. hostnamev46 should map to both an IPv4 and an IPv6 address. cnamev4 should be a CNAME record for hostname4. Reverse lookup information is also needed by the test.

The information placed into the DNS server is also need in the test case. A structure is defined to hold this information:

struct test_info_s {
    char * dns_server_v4;
    char * dns_server_v6;
    char * domain_name;
    char * hostname_v4;
    char * cname_v4;
    char * ip_addr_v4;
    char * hostname_v6;
    char * cname_v6;
    char * ip_addr_v6;
    char * hostname_v46;
    char * cname_v46;
    char * ip_addr_v46_v4;
    char * ip_addr_v46_v6;
};

The test program may hold a number of such structures for different DNS server. The test will use each structure in turn to perform the tests. If IPv6 is not enabled in the eCos configuration, the entries which use IPv6 may be assigned to NULL.