Implementation of IPv6 DNS Resolver Part 6

Handle Exceptions

Listing 3-40

Listing 3-40

861—872 If the response provides a Response Code of Server Failure (SERVFAIL), Not Implemented (NOTIMP), or Refused (REFUSED), each indicates some error, the current caching server is marked as bad, and the next server will be tried.

873—887 If the TC (TrunCation) bit is on in the response, it means the response was too large to fit in a UDP packet. In this case, the resolver will fall back to TCP transport. Since it rarely happens in a usual environment, and the callback mechanism complicates the code, it is omitted in this description.

Exiting

Listing 3-41

Listing 3-41

939—942 If the response looks valid so far, the transaction succeeds in the level of res_send(). The length of the response is returned to the caller. The response packet is stored in the buffer pointed to by ans, which was provided by the caller.


943—952 If all retries for all caching servers have failed and the socket is still open, the socket is closed, and —1 is returned to indicate the failure.

Functions get_nsaddr() and res_close() are shown below, which are subroutines called from the res_send() function.

Listing 3-42

Listing 3-42

199—213 If the nth address is an IPv4 address, it is stored in the nsaddr_list of the base _res{} structure; otherwise, it is stored in the external _res_ext{} structure. Function get_nsaddr() returns an appropriate pointer to the socket address structure according to the value of the sin_family member in the _res{} structure.

Listing 3-43

Listing 3-43

964—970 The global socket variable s is closed if it is still open, and is reset to —1. Other global parameters are also reinitialized.

IPv6 Reverse Lookup: _dns_ghbyaddr() Function

The _dns_ghbyaddr() function is called from getipnodebyaddr() as a backend of getnameinfo(). It constructs DNS query names for reverse mapping of a given IPv4 or IPv6 address, sends queries, and waits for a response through the res_query() function.

Ignore Link-Local Addresses

Listing 3-44

Listing 3-44

1464—1465 If the given address is an IPv6 link-local address, a negative response is returned without sending DNS queries. This check is actually meaningless, because all callers of the function do the same check in this implementation.

Set Up the TLD

Listing 3-45

Listing 3-45

1468—1479 The variable tld is an array of strings, whose elements specify the common domain name suffix(es) for reverse lookups. For IPv6, ip6.arpa is first used, and then ip6.int will be tried. For IPv4, in-addr.arpa is used.

It does not make sense to try ip6.int anymore because the use of this domain has been deprecated as explained in Section 3.3.6. Any query about the name under this domain simply results in a negative response. The resolver library provided in recent versions of BSD variants, including FreeBSD, now only tries the ip6.arpa domain.

Set Up the Resolver Context

Listing 3-46

Listing 3-46

1481—1486 If this is the first time the resolver library is used, res_init() is called to initialize the resolver context, _res{} and _res_ext{}.

Initialize Other Variables

Listing 3-47

Listing 3-47

1487—1491 The variable hbuf is an instance of the standard hostent{} structure to store a temporary result within this function. The variable na is actually unused and can be ignored.

1493—1497 The variable buf points to a querybuf{} union, which is the same as the one used for the _dns_getaddrinfo() function.

Build and Send Queries

Listing 3-48

Listing 3-48

 

 

 

 

Listing 3-48

1498-1505 The for loop goes through all possible domain suffixes for reverse lookups; bp points to the head of the buffer containing the query name; cp is set to point to the end of the address.

1506—1517 If an IPv6 address is given, it is converted into the nibble format explained in Section 3.3.2 as follows: each single byte of the address is divided into the uppermost 4 bits and the lowermost 4 bits. Then each 4-bit chunk (i.e., a nibble) is converted into the corresponding ASCII hexadecimal character. Finally, the two characters are concatenated with periods as delimiters, where the lower part of the character leads. For example, one byte of 0x8a is converted to the string "a.8." in the inner for loop. The for loop repeats this process for the entire address, concatenating the latest string to the previous result. Then the well-known domain string is appended at the end of the name.

Note that the inner for loop starts at the end of the IPv6 address and the pointer cp moves backward as the loop continues. Figures 3-22 and 3-23 illustrate an example of this process for the IPv6 address 2 0 01 :db8:…:abcd. Figure 3-22 shows the intermediate state when the last 2 bytes of the address are parsed, which corresponds to the first 2 cycles of the loop. Figure 3-23 depicts the final query name at the completion of the process with the domain suffix of ip6.arpa.

It is redundant to repeat the process of the inner for loop in every iteration of the outer for loop because af and addr are invariant throughout the outer loop. This should be done as common initialization before entering the outer for loop.

1519—1531 A similar process is taken for an IPv4 address except that each byte is converted into a decimal number. For example, it converts an IPv4 address 203.178.141.194 into the domain name 194.141.178.203.in-addr.arpa.

FIGURE 3-22

FIGURE 3-22

FIGURE 3-23

FIGURE 3-23

1533 res_query() constructs a DNS query message, sends it to caching servers, and waits for a response. The actual process is done in the backend functions, res_mkquery() and res_send().As shown in the arguments, the class of the query is IN and the type is PTR.

Collect Answer

Listing 3-49

Listing 3-49

 

 

 

Listing 3-49

1534-1536 If res_query() fails for some reason, it should return a negative value and h_errno should have an error code. The error code is temporarily stored in *errp, and the next top-level domain will be tried, if any.

One common error here is "host not found," which corresponds to the Name Error code of the DNS. In this case, h_errno should have been set to HOST_NOT_FOUND. The details of the res_query() function are omitted here, but the logic is almost the same as that of res_queryN() (Section 3.4.3).

1538-1546 The getanswer() function analyzes the response and constructs the result in a hostent{} structure passed from _dns_ghbyaddr() as the fifth argument (a pointer to hbuf). It returns a non-NULL pointer (which should actually be the pointer to hbuf) on success, in which case the hostent{} structure is filled with other parameters. Finally, _hpcopy() will allocate memory for a new hostent{} structure, copy the result stored in hbuf to the new structure, and return the pointer to the new one. The resulting pointer is then the return value of _dns_ghbyaddr() itself.

Here is a serious bug. The temporary buffer pointed to by buf must be released before exiting. A memory leak occurs every time this function is called. In particular, every call to getnameinfo( ) that involves DNS reverse lookups can cause a memory leak. This has been fixed in later versions of the library code.

Error Handling

Listing 3-50

Listing 3-50

1548-1549 If queries failed for all possible domain suffixes, the temporary buffer to store DNS responses is released and a NULL pointer is returned to indicate the failure.

Next post:

Previous post: