The code in DNS.fth
implements a simple DNS client.
The focus of the design is on minimising overall RAM usage.
All data is transferred using UDP.
The code is compiled after the BSD layer because it makes heavy use of that layer.
The main documentation for DNS is contained in RFCs 1034 and 1035. The language is abstruse at best.
These equates and data are only used if not already defined.
1 equ DNSdebug? \ -- x
Set this non-zero to display DNS debug information during
execution.
#53 equ DNSport# \ -- port#
The standard DNS port number on the server.
3 equ #DNS \ -- u
Maximum number of DNS attempts if no response.
5000 equ /DNSms \ -- ms
Maximum time (in milliseconds) allowed for a DNS transaction.
#512 equ /DNS \ -- u
Maximum size of the DNS payload.
variable DNSserver \ -- addr
Holds DNS server or 0 if not configured yet.
For the full details of DNS queries and responses, see RFCs 1034 and 1035. What follows in this documentation is a small subset.
The header (RFC 1035, 4.1.1) contains the following fields:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
A query section contains the following fields, QNAME (RFC 1035, 4.1.2), QTYPE (RFC 1035, 3.2.2) and QCLASS (RFC 1035, 3.2.4/5).
The question section is used to carry the "question" in most queries, i.e., the parameters that define what is being asked. The section contains QDCOUNT (usually 1) entries, each of the following format:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ QNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
QNAME |
a domain name represented as a sequence of labels, where each label consists of a length octet followed by that number of octets. The domain name terminates with the zero length octet for the null label of the root. Note that this field may be an odd number of octets; no padding is used. |
QTYPE |
a two octet code which specifies the type of the query. The values for this field include all codes valid for a TYPE field, together with some more general codes which can match more than one type of RR. |
QCLASS |
a two octet code that specifies the class of the query. For example, the QCLASS field is IN for the Internet. |
The answer, authority, and additional sections all share the same format: a variable number of resource records, where the number of records is specified in the corresponding count field in the header. Each resource record has the following format:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
NAME |
a domain name to which this resource record pertains. |
TYPE |
two octets containing one of the RR type codes. This field specifies the meaning of the data in the RDATA field. |
CLASS |
two octets which specify the class of the data in the RDATA field. |
TTL |
a 32 bit unsigned integer that specifies the time interval (in seconds) that the resource record may be cached before it should be discarded. Zero values are interpreted to mean that the RR can only be used for the transaction in progress, and should not be cached. |
RDLENGTH |
an unsigned 16 bit integer that specifies the length in octets of the RDATA field. |
RDATA |
a variable length string of octets that describes the resource. The format of this information varies according to the TYPE and CLASS of the resource record. For example, the if the TYPE is A and the CLASS is IN, the RDATA field is a 4 octet ARPA Internet address. |
In order to reduce the size of messages, the domain system utilizes a compression scheme which eliminates the repetition of domain names in a message. In this scheme, an entire domain name or a list of labels at the end of a domain name is replaced with a pointer to a prior occurance of the same name.
The pointer takes the form of a two octet sequence:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1 1| OFFSET |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The first two bits are ones. This allows a pointer to be distinguished from a label, since the label must begin with two zero bits because labels are restricted to 63 octets or less. (The 10 and 01 combinations are reserved for future use.) The OFFSET field specifies an offset from the start of the message (i.e., the first octet of the ID field in the domain header). A zero offset specifies the first byte of the ID field, etc.
4 buffer: DNSid \ -- addr
Holds the DNS transaction number. The number is incremented
before use so that a good response should have the same
number.
create DNSQtemplate \ -- addr
Template for a DNS query.
#12 equ /DNSQtemplate \ -- len
Length of the DNS query template.
: genDNSQhead \ addr -- addr
Generate a DNS query header at addr and return the
address just after the header.
: c!++ \ addr b -- addr+1
Saves b at addr and increments addr.
: genLabel \ caddr len addr -- addr'
Add a label (n + n bytes) to the text at addr, returning
the next label address.
: genDNSname \ caddr len addr -- addr'
Generate a name as a sequence of labels.
: genDNSquestion \ caddr len addr -- addr'
Generate the question section of a query.
: skipDNSQ \ caddr len -- caddr' len'
Skip the query portion of a DNS reponse.
: skipRRname \ caddr len -- caddr' len'
Step over the name field in a resource record (RR).
: cleanDNSq \ hs buffer --
Clean up the query by releasing the buffer and closing the
socket.
After acquisition of the DNS server's IP address, either by specification or by DHCP, the only function provided is to convert a domain name string, e.g. www.google.com into an IPv4 address.
: DNSquery \ caddr len -- ip|0
Send a UDP DNS query message requesting the IP address of the
given domain name. Returns the IP address for success, or zero
on failure.