ToC | DocOverview | CGDoc | RelNotes | FAQ | Index | PermutedIndex
| Allegro CL version 10.1 Unrevised from 10.0 to 10.1. 10.0 version

# The Domain Naming System Utility

This document contains the following sections:

1.0 The Allegro CL Domain Naming System Package
2.0 Nameserver
3.0 The DNS API
3.1 Address queries
3.2 Inverse queries
3.3 MX queries
3.4 TXT queries
3.5 SRV queries
3.6 Undecoded responses
3.7 Repeat and timeout

## 1.0 The Allegro CL Domain Naming System Package

The DNS utility implements the client side of the dns protocol defined in rfc1035. dns is a distributed database that records information about computer names and IP addresses. The symbols naming objects in this utility are in the `acl-socket` package. See socket.htm for more information on sockets in Allegro CL.

There are three kinds of information this package can extract at this time (in the future we may add other functionality):

1. Determine the IP address or IP addresses of a given hostname
2. Determine the hostname associated with a given IP address
3. Determine to which machine email addressed to a certain machine should be sent.

Typically programs access dns information using a set of C library functions. Unfortunately, these functions return only a portion of the information needed and they work in a way that can interact poorly with a multi-threaded Lisp application. The acldns utility addresses these shortcomings.

The acldns module may not be loaded into your image. Evaluate

    (require :acldns)

to ensure the functionality is available.

The variable `*dns-mode*` selects whether the C library functions or acldns functions are used by the Lisp dns lookup functions lookup-hostname and ipaddr-to-hostname.

## 2.0 Nameserver

A nameserver is a program that implements the server side of the dns protocol. A nameserver is also the authority about the machines on a small section (or zone) of the network. When given a dns query, a nameserver will check to see if that's information it knows directly, and if not a nameserver will use the dns client protocol to find out which nameserver does know the answer to the query and it will ask that nameserver. Thus a nameserver gives the appearance of knowing everything when in fact the information is distributed across the internet.

There is a small but important set of nameservers that will not seek out answers to questions they can't answer directly. These are the root nameservers, the ones that answer queries for information in the top level domains (".com", ".edu", .etc).

In order to use this dns utility, you must specify one or more nameservers, and these nameservers must be willing to seek out answers to questions they don't know. You can't choose one of the root nameservers for this purpose. The best nameservers to select are the ones that your computer is setup to use for other network programs. The configure-dns function handles this configuration task.

## 3.0 The DNS API

The functions in the DNS API are:

- dns-query
- dns-lookup-hostname
- dns-ipaddr-to-hostname
- configure-dns

The main function is socket:dns-query. Two easier to use functions are socket:dns-lookup-hostname and socket:dns-ipaddr-to-hostname which are designed to replace socket:lookup-hostname and socket:ipaddr-to-hostname.

socket:dns-query supports IPv6 (Internat Protocol version 6) addresses.

The variables are:

- `*dns-mode*`
- `*nameservers*`
- `*dns-domain*`
- `*domain-search-list*`

In the following subsections, there are examples of using socket:dns-query in a variety of ways. If when running the examples you get an error about dns not being configured, then see configure-dns.

## 3.1 Address queries

The default query is to determine the IP address associated with a given name. Here we chose the popular web site name www.cnn.com. This name has a large number of associated IP addresses. We arbitrarily pick one of the addresses to return as the first value, and put the rest of the addresses in a list to return as the third value. The second value, 300, is the number of seconds that this response is valid.

    user(5): (socket:dns-query "www.cnn.com")
    3474540534
    300
    (3474540293 3474540294 3474540295 3474540296 3474540297 3474540300 3474540308
    3474540310 3474540311 3474540312 ...)
    user(6):

We didn't specify a value for queue which means that it defaulted to true. This means that the request was first checked against our cache and if not found there the request was sent out to the nameservers. Once the response was found it was stored in the cache.

To see the effect of the cache, let's run the query a second time:

    user(6): (socket:dns-query "www.cnn.com")
    3474540534
    190
    (3474540293 3474540294 3474540295 3474540296 3474540297 3474540300 3474540308
    3474540310 3474540311 3474540312 ...)
    user(7):

Note that all that has changed is the second value. In this case our query was found in our local cache and we have 190 seconds of valid time remaining out of the original 300. Now what if we wait about 200 seconds and ask again:

    user(7): (socket:dns-query "www.cnn.com")
    3474540534
    -10
    (3474540293 3474540294 3474540295 3474540296 3474540297 3474540300 3474540308
    3474540310 3474540311 3474540312 ...)
    user(8):

Now all of the sudden we have a negative valid time. What has happened is that the cache was consulted and the entry found, however the valid time had expired. What socket:dns-query decided was that while the entry was no longer valid, it wasn't that far from valid (in fact it was less than `socket::*stale-entry-remove-time*` seconds invalid (initially 30 minutes)). As a result socket:dns-query decided that this entry was worth returning. So it did two things: it returned the stale entry with a negative valid time (to denote a stale entry) and it sent off a dns request to update the entry. Thus the next time this request is made it will likely already have fresher information. Given the slow rate at which data can change in dns, you should not be concerned about using data returned by socket:dns-query if it is denoted as stale by having a negative valid time.

Suppose you ask for the IP address of a machine that doesn't exist:

    user(8): (socket:dns-query "blorp.cnn.com")
    nil
    10400
    nil
    user(9):

The first value returned in `nil`, meaning there is no such machine, and the second value is how long you can believe that there is no machine named blorp.cnn.com.

There is one more case:

    user(10): (socket:dns-query "www.bogusmachine.com")
    nil
    user(11):

The distinguishing feature is that the implicit second value returned (the valid time) is `nil`. This means that we cannot determine whether or not this machine exists. This is due to the nameserver responsible for supplying that information not responding to our queries. When faced with this response the best thing to do is to try again later.

## 3.2 Inverse queries

A second type of query is where you like to determine the name associated with an IP address. This information is stored in different tables than the ones mapping names to IP addresses. Thus this query may say that there is no name that can map to an IP address when in fact there is, or it may return a name that can't in fact map to an IP address.

    user(11): (socket:dns-query 3474540310 :type :ptr)
    "www2.cnn.com"
    86400
    nil
    user(12):

An inverse query is done by specifying the type value of `:ptr`. The IP address can also be specified symbolically as in

    user(14): (socket:dns-query "207.25.71.22" :type :ptr)
    "www2.cnn.com"
    86248
    nil
    user(15):

If an ipv6 address (in structure or dotted form) is supplied and type is `:ptr`, then a reverse DNS lookup in the ip6.arpa domain is used.

#### An example IPv6 inverse query

    cl-user(5): (socket:dns-query "2001:5c0:0:2::24" :type :ptr)
    "www.6bone.net"
    259200
    nil
    (:recursion-requested :recursion-ok :response)

## 3.3 MX queries

The third type of query is for the Mail eXchanger record (MX record). Many machines designate other machines as their email servers. Thus if you want to mail to www.cnn.com you can't just connect to the SMTP port of one of the www.cnn.com IP addresses we determined above. You first have to check if there is any MX record for the machine.

When making an mx query there are three possibilities. One is that there is no mx record (which means that mail for this host should be sent to this host, not some other host)

    cl-user(3): (socket:dns-query "franz.com" :type :mx)
    nil
    120
    nil

The second possibility is that there is an mx record and the responding nameserver is kind enough to supply the IP address as well as the name of the mx host:

    cl-user(4): (socket:dns-query "tiger.franz.com" :type :mx)
    ("tiger.franz.com" 322990064 201)
    		   ^^^^^^^^^------------------ the ip address
    120
    nil

The third possibility is that there is an mx record to a host but the IP address of that host isn't returned:

    cl-user(4): (socket:dns-query "tiger.franz.com" :type :mx)
    ("tiger.franz.com" nil 201)
    		   ^^^------------------ no ip address
    120
    nil

The example above is constructed since the IP address was returned, as shown in the example above it (the IP address there is not real, however). This third case should be rare in practice, but can happen. Here is another example:

    user(2): (socket:dns-query "www.cnn.com" :type :mx)
    ("atlmail1.turner.com" 3327256260 10)
    10132
    (("atlmail2.turner.com" 3327256261 20) ("nymail1.turner.com" 3474478538 30))
    user(3):

In this case we find that there are three machines willing to be the mail server. For each machine we have three pieces of information. The first is the name of the machine. The second is the IP address of the machine. The third is the priority of the machine. If possible mail should be sent to the machine with the lowest priority value. socket:dns-query will always sort the hosts by priority value when returning the list.

If a machine doesn't have an MX record, then you can send mail directly to the machine.

## 3.4 TXT queries

A fourth type of query is a TXT query. It gets the TXT records (if any). DNS TXT records are strings containing information about the named service.

You obtain TXT information by specifying `:txt` as the value of the type keyword argument to socket:dns-query. The answer will be a list of one or more strings (because TXT records can be composed of multiple strings), or `nil` if there are no TXT records. If there is more than one TXT record, then the third return value of socket:dns-query will contain a list of the remaining answers.

Here is an example showing the form of the call and possible responses (these may not still work if you try them as the sites listed may have changed in this regard):

    (socket:dns-query "nytimes.com" :type :txt)
    ;; returns:
    ("v=spf1 mx ptr ip4:199.239.138.0/24 include:attglobal.net ~all")
    300
    nil
    (:recursion-requested :recursion-ok :authoritative :response)

    (dns-query "franz.com" :type :txt)
    ;; should return:
    nil
    120
    nil
    (:recursion-requested :recursion-ok :authoritative :response)

## 3.5 SRV queries

SRV queries are also handled by socket:dns-query and dns-response-answer can be used to extract the SRV record data from the `dns-response` object:

    (let ((ans (socket:dns-response-answer
    	     (socket:dns-query "srv.record.test.franz.com" :type :srv))))
      (socket:dns-rr-answer (car ans)))

    RETURNS
    (33 44 5555 "srvtest-target.franz.com")

The return value above is a list of the priority, weight, port and target for the `:srv` query. (See the SRV queries link above for a complete description.)

## 3.6 Undecoded responses

In the examples above we've let the decode argument take on it's default value of true. This causes the return values to be appropriate to the query. If you're interested in seeing what the nameserver returned you can set decode to nil. This will bypass the cache and send the request to the nameserver.

    user(5): (socket:dns-query "www.whitehouse.gov" :decode nil)
    #S(dns-response :id 3
         :flags
           (:recursion-requested :recursion-ok :authoritative :response)
         :flags-value 34176
         :answer
           (#S(dns-rr :name "www.whitehouse.gov" :type :a :class 1
                      :time-to-live 900 :answer 3330928732)
            #S(dns-rr :name "www.whitehouse.gov" :type :a :class 1
                      :time-to-live 900 :answer 3330928731))
         :authority
           (#S(dns-rr :name "whitehouse.gov" :type :ns :class 1
                      :time-to-live 10800 :answer "sec1.dns.psi.net")
            #S(dns-rr :name "whitehouse.gov" :type :ns :class 1
                      :time-to-live 10800 :answer "sec2.dns.psi.net"))
                      :additional nil)
    user(6):

The response from a socket:dns-query is either nil (if no nameserver responded) or is a dns-response object. Each response is returned with 16 bits of flags, and we convert that to a list of symbols accessible via the accessor dns-response-flags See rfc1035 for a description of the flags. (Links to rfc1035 are in Section 1.0 The Allegro CL Domain Naming System Package above.) The dns-response-answer accessor returns a list of dns-rr records. Here we see that there are two IP addresses for www.whitehouse.gov. The dns-response-authority field is a list of the nameservers which hold the information on which the answer is based. The dns-response-additional is a list of other dns-rr objects that might prove useful. For example when an MX query is done and mail exchanger machine names are returned, the additional field usually contains the IP addresses associated with those machine names, thus saving you the trouble of sending another query to retrieve those IP addresses.

## 3.7 Repeat and timeout

In the examples above we've used the default value of repeat and timeout (which are 5 and 5). This means that each query is sent up to 5 times and we wait 5 seconds for a response each time. Thus in the case of a non-responsive nameserver the socket:dns-query function could take 25 seconds to return. You can control this possible delay by setting repeat and timeout. Some interesting possibilities are:

- `:repeat 0` -- in this case there will be no delay and a value will be returned only if it is already cached. A dns query will not be sent to a nameserver unless there is a value in the cache and it is slightly stale, in which case a query will be sent to refresh that entry.
- `:timeout 0` -- if the value is in the cache then return it. If the value is not in the cache then send a request exactly once to the nameserver (regardless of the value of repeat). socket:dns-query will not wait for the response. At this point socket:dns-query will most likely return nil meaning no response from the server, however if the nameserver is quick at responding and the Lisp scheduler happens to immediately run the Lisp process receiving nameserver responses, there is a chance that socket:dns-query will return the answer to the query. In any event the nameserver will likely respond to the query within a few seconds and if socket:dns-query were to be called again with the same arguments, it would find the answer in the cache and return it.

Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page was not revised from the 10.0 page.
Created 2019.8.20.

ToC | DocOverview | CGDoc | RelNotes | FAQ | Index | PermutedIndex
| Allegro CL version 10.1 Unrevised from 10.0 to 10.1. 10.0 version