| Allegro CL version 10.1 Unrevised from 10.0 to 10.1. 10.0 version |
This document contains the following sections:
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):
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.
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.
The functions in the DNS API are:
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:
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.
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.
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.
cl-user(5): (socket:dns-query "2001:5c0:0:2::24" :type :ptr) "www.6bone.net" 259200 nil (:recursion-requested :recursion-ok :response)
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.
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)
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.)
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.
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.
| Allegro CL version 10.1 Unrevised from 10.0 to 10.1. 10.0 version |