Oberon || Library || Module Index || Search Engine || Definition || Module

Ulm's Oberon Library:


DNSRequests - process DNS requests


TYPE Context = POINTER TO ContextRec;
TYPE ContextRec = RECORD (Disciplines.ObjectRec) END;

TYPE Name = DNSRecords.Name; TYPE Record = DNSRecords.Record; TYPE Query = POINTER TO QueryRec; TYPE QueryRec = RECORD (Objects.ObjectRec) name: Name; type: DNSRecords.RRType; class: DNSRecords.Class; END;

CONST noNameServers = 0; CONST protocolError = 1; CONST tooManyRetries = 2; CONST unexpectedResponse = 3; CONST sendFailed = 4; CONST timeout = 5; CONST formatError = 6; CONST serverFailure = 7; CONST notImplemented = 8; CONST refused = 9; CONST unexpectedResponseCode = 10; CONST errors = 11; TYPE ErrorCode = SHORTINT; (* noNameServers... *) TYPE ErrorEvent = POINTER TO ErrorEventRec; TYPE ErrorEventRec = RECORD (Events.EventRec) errorcode: ErrorCode; query: Query; dnsserver: Name; END; VAR errormsg: ARRAY errors OF Events.Message; VAR error: Events.EventType;

PROCEDURE CreateContext(VAR context: Context); PROCEDURE AddNameServer(context: Context; socket: SysSockets.Socket; name: ARRAY OF CHAR);

PROCEDURE EnableRoundRobin(context: Context); PROCEDURE SetTimeout(context: Context; seconds: INTEGER); PROCEDURE SetNumberOfRetries(context: Context; retries: INTEGER);

PROCEDURE Process(context: Context; name: ARRAY OF CHAR; type: DNSRecords.RRType; class: DNSRecords.Class; VAR header: DNSHeaders.Header; VAR answerRecs, authRecs, addRecs: Record; errors: RelatedEvents.Object) : BOOLEAN;


DNSRequests allows to send DNS requests and to wait synchronously for a response. Each request is to be set in a context which consists of one or more nameservers and some configuration parameters. Any number of requests can be processed in parallel. Within a given context, DNSRequests takes care of load-balancing and sorting out of non-responsive and refusing nameservers.

CreateContext creates a new context with initially no nameservers at all.

AddNameServer adds a nameserver to a context where socket is an already connected socket of type SysSockets.dgram. Note that this socket can be of any protocol family, be it SysSockets.pfINET, SysSockets.pfINET6, or SysSockets.pfUNIX. Additionally, a string name is to be given as printable representation of the nameserver which can be used in error messages. This is usually the printable representation of the socket address.

There is a transitive precedence relation defined over the set of nameservers belonging to a context. Nameservers who refuse to process our requests have a lower precedence than those from which we haven't seen any refusals yet. This being equal, nameservers have a lower precedence if they had more timeouts than others. That also being equal, nameservers are preferred if the queue of pending requests is shorter. Finally, if all other criterias show no difference, nameservers added earlier with AddNameServer take precedence. If a roundrobin mode is selected using EnableRoundRobin, however, the order of nameservers is changed by each of invocation of Process. Note that the maintained counts of refusals and timeouts are reset to 0 as soon successful replies are seen.

If no valid response comes within the given timelimit, or the nameserver responds with a refusal, requests are forwarded to the nameserver with the highest precedence excluding the last nameserver used.

For each of the nameservers added, a task is running (see Tasks). All these tasks are automatically shut down as soon as the context object terminates (see Resources).

SetTimeout sets the timeout for single request attempts. Note that the total time for requests processed by Process can be much longer as this timeout multiplies with the number of maximal retries. The default value is 1.

By default, the maximal number of retries is 2 multiplied with the number of nameservers. SetNumberOfRetries allows to change this factor. This gives usually the number of retries per nameserver but, if precedences play a role because of timeouts and refusals, it might well happen that individual nameservers are queried more often and other nameservers are left out.

Process asks for resource records for name with the given type (e.g. DNSRecords.a) in the given class (usually DNSRecords.classIN). If successful, the header is returned along with the lists of answer, authorization, and additional records. Note that name must be a fully-qualified domain name. Local domain names and search lists are not considered.


DNSRequests is unable to verify that response packets come indeed from the queried nameserver. Any attacker who knows our socket address and the chosen request id, can forge a response packet which just needs to arrive first to be considered. To make it harder for attackers to guess the request id, RandomGenerators.unpredictable is used to generate them. But this provides some limited defense only in case of blind-folded attacks. Note that it is recommended to add name server sockets using AddNameServer whose ports have been selected by EphemeralPortRange.GetRandomPort. As these sockets are continually used during the lifetime of a context, it might be useful to generate new contexts from time to time.


All failures of Process are represented by error events that are related to errors:
No nameservers have yet been added to the given context.
One of the queried nameservers returned a protocol error.
The maximal number of retries was exhausted.
An unexpected response packet was received. This can be the result of responses that come after the timeout or the result of attempts to forge response packets.
The send operation failed for one of the sockets.
A timeout occurred with one of the nameservers.
One of the nameservers was unable to interpret the query.
One of the nameservers returned a server failure.
One of the nameservers indicated that this type of query is not implemented.
One of the nameservers refused to process the query.
An unexpected response code was returned by one of the nameservers.

Note that permanent name errors (i.e. DNSHeaders.nameError) do not lead to error events or to a return of FALSE by Process. They are expected to be handled by the resolver who invokes Process:

IF DNSRequests.Process(context, name,
      rrtype, DNSRecords.classIN,
      header, answerRecs, authRecs, addRecs, errors) THEN
   rcode := DNSHeaders.GetResponseCode(header);
   CASE rcode OF
   | DNSHeaders.noError:
         (* handle successful response *)
   | DNSHeaders.nameError:
         (* permanent error;
            error event is to be generated
            or another attempt is to be made using search lists
            or other rules
   (* temporary error;
      error events have already been generated


DNS header structure as returned by Process.
definition DNS records as returned by Process.

Edited by: borchert, last change: 2004/06/13, revision: 1.2, converted to HTML: 2004/06/13

Oberon || Library || Module Index || Search Engine || Definition || Module