Dynamic DNS Update

Jump to: navigation, search

Table of Contents >> Application Reference


The Domain Name System (DNS) was originally designed as a distributed database for retrieving static information about network names. The data was solely maintained by domain administrators in master files that were edited by hand. Over the years, the data has grown and become more dynamic, making manual edits infeasible for some domain names.

RFC 2136 defines a method that allows a remote host to update records in a master file by contacting the authoritative DNS server directly, thereby eliminating the need for human interaction. Treck supports RFC 2136 in a way that is functionally similar to the nsupdate program that is included with the BIND tools.

Some important information about Treck's implementation of Dynamic DNS Update:

  • Treck implements the requestor portion of RFC 2136, but not the server portion.
  • The application is free to choose UDP or TCP messaging and blocking or non-blocking mode of execution (the send function call either waits or does not wait for a response from the server).
  • Treck supports the Internet record class (IN) only.
  • RFC 2136 does not include any security features, nor does Treck implement any at this time.

Code Configuration

To enable the Treck Dynamic DNS Update code, uncomment the following compile time macro in trsystem.h. By default, the option is disabled and the code is absent.

#define TM_USE_DNS_UPDATE

User Interface

The following functions provide support for Dynamic DNS Update. The API follows a session/transaction model in which the requestor (your application) uses a session descriptor to build and send one or more update transactions to a DNS server. If the specified server is authoritative for more than one zone, you may update records in all such zones in the same session.

Initialization

tfDnsInit()
Initialize the Treck DNS service. This must be called before calling any other Treck DNS function. This call determines whether the DNS update request is sent in blocking or non-blocking mode (see tfDnsUpdateSend() below).
tfDnsSetOption()
Set a global option that affect the operation of DNS. Treck Dynamic DNS Update supports two global options: TM_DNS_OPTION_RETRIES and TM_DNS_OPTION_TIMEOUT.
For UDP messages, these options determine the number of retransmissions (assuming no server response) and the delay between each retransmission. The maximum delay before the session is finally terminated is (retries + 1) * timeout.
Since TCP inherently supports retransmission, these options are used to determine the maximum delay ((retries + 1) * timeout) before terminating the request.

Session Management

The following functions create and manage DNS update sessions.

tfDnsUpdateOpen()
Create a new session for communication with a DNS server. A descriptor handle is returned that you must provide to each of the functions below.
tfDnsUpdateClose()
End a session.
tfDnsUpdateLocal()
Set a local IP address and port from which to send the requests. By default, Treck will choose an appropriate local IP address and port. This function allows you to override the default action.
tfDnsUpdateZone()
Set the zone name that you are updating. This information is sent to the DNS server as part of the request. By default, Treck will attempt to derive the zone name from the domain name you specify in your first call to tfDnsUpdateAdd() or tfDnsUpdateDelete(). If the DNS server is authoritative for more than one zone, you can use this call to change zones between update requests.
tfDnsUpdateSetSockOpt()
Change the characteristics of the underlying socket used by the session. This is a wrapper for setsockopt() and will not be required for most applications.
tfDnsUpdateSetCallback()
Set a pointer to a function that you provide for the purpose of event notification. This is only useful if you are running in non-blocking mode. The callback function API is discussed below.

Specifying Prerequisites

The following function is used to specify a prerequisite that will be sent as part of the request. Prerequisites are not required when updating DNS records. However, they can be used to resolve conflicts between multiple hosts concurrently making updates. The DNS server must check that the specified prerequisites are true before it commits the updates.

tfDnsUpdatePrereq()
Add a prerequisite to the request. This function can be called zero or more times prior to sending the request.

Specifying Updates

The following functions allow you to add or delete DNS records in a single zone. You must make at least one call to a function in this section before sending the request. You may specify any combination of add or delete requests. Multiple requests are handled by the DNS server in the order that you issue them. So, you may delete an existing record and add a replacement within the same request.

tfDnsUpdateAdd()
Add a DNS record to the current zone.
tfDnsUpdateDelete()
Delete a DNS record from the current zone.

Sending the Request

The following functions send the request and process the response. The send is considered complete when a valid response from the server is received or the send times out. A valid response will indicate that the update succeeded or failed (determined by the response code). After the send completes, you may be able to start another update transaction with the same session handle, depending on the send result.

tfDnsUpdateSend()
Send the update request to the DNS server. If TCP is used, a connection will be established if the socket is not already connected as a result of a prior send. If blocking mode is used, this function will not return until the send/response sequence is complete (successful or not). If non-blocking mode is used, this function will return after putting the request in the socket's outgoing queue. The return code should indicate that the send is busy (TM_EWOULDBLOCK). In that case, tfDnsUpdateExecute() must be called periodically until the send is complete.
tfDnsUpdateExecute()
Call this function periodically in non-blocking mode while a send is in progress. A return code other than TM_EWOULDBLOCK indicates the send is complete, with the return code indicating the result. If you wish, you can set a callback function via tfDnsUpdateSetCallback() to receive notification of when you need to call tfDnsUpdateExecute().

Callback Notification of Events

When you are running DNS in non-blocking mode (see tfDnsInit()), you need to call tfDnsUpdateExecute() periodically after calling tfDnsUpdateSend(). If you want to know when you need to call tfDnsUpdateExecute() (for better responsiveness perhaps), you can create a callback function and call tfDnsUpdateSetCallback(). The pointer to your callback function is of type ttUserDnsUpdateCBFuncPtr(). The callback event types passed to your function are as follows:

Callback Event Types
Name Meaning
TM_DNS_UPDATE_EVENT_SOCKET Either there was data received on the socket, or there is room in the socket to send more data, or there was a socket error. If using TCP, other possible events include connection completion and socket closed or reset by the remote peer. An incoming ICMP error message, like destination port unreachable can also trigger a socket event.
TM_DNS_UPDATE_EVENT_TIMER Timer expired. If using UDP, this will cause Treck to resend the update request up to the maximum retry limit, at which point a TM_ETIMEDOUT return code will be returned from the API. If using TCP, this event always results in a TM_ETIMEDOUT return code.

You may call tfDnsUpdateExecute() from within your callback function, but doing so would place a burden on the calling context, namely the receive task or timer task. One of the main reasons for choosing non-blocking mode is so that you can do the processing at a lower priority.

Resource Record Types

Resource record types are 16-bit values defined in the IANA dns-parameters.txt document. They indicate the type of data stored in the resource record. The resource record type is a parameter required by some of the API functions listed above. Predefined macros exist in trsocket.h that map to many common values. Here are a few:

Macro Type Value Meaning
TM_DNS_RR_TYPE_A A 1 IPv4 address
TM_DNS_RR_TYPE_NS NS 2 Authoritative name server
TM_DNS_RR_TYPE_CNAME CNAME 5 Canonical name for an alias
TM_DNS_RR_TYPE_MX MX 15 Mail exchange
TM_DNS_RR_TYPE_TXT TXT 16 Text strings
TM_DNS_RR_TYPE_AAAA AAAA 28 IPv6 address
TM_DNS_RR_TYPE_SRV SRV 33 Location of services
TM_DNS_RR_TYPE_DHCID DHCID 49 DHCP host identification
TM_DNS_RR_TYPE_ANY * 255 Any/all records
TM_DNS_RR_TYPE_PRIVATE   65280 Private record types (65280 to 65534)

Sample Code

The following example shows two transactions with callback notification. In this example, you would provide the definitions for NO_BLOCKING and myDnsUpdateSemaphore. Your operating system would provide the KernelSignalWait() and KernelSignal() functions.

void myDnsUpdate(struct sockaddr_storage * serverAddrPtr)
{
    ttDnsUpdateHandle   dnsUpdateHandle;
    int                 errorCode;
 
/* Initialize Treck DNS */
#ifdef NO_BLOCKING
    errorCode = tfDnsInit(TM_BLOCKING_OFF);
#else
    errorCode = tfDnsInit(TM_BLOCKING_ON);
#endif
    assert(errorCode == TM_ENOERROR);
 
/* Start a UDP session */
    dnsUpdateHandle = tfDnsUpdateOpen(serverAddrPtr, 0, &errorCode);
    assert(dnsUpdateHandle != TM_DNS_UPDATE_INVALID_HANDLE);
 
#ifdef NO_BLOCKING
/* Notify me of events */
    errorCode = tfDnsUpdateSetCallback(dnsUpdateHandle, myDnsUpdateCB, NULL);
    assert(errorCode == TM_ENOERROR);
#endif
 
/* Transaction 1: Create IPv4 and IPv6 addresses for a non-existent domain */
    errorCode = tfDnsUpdatePrereq(dnsUpdateHandle,
                                  TM_DNS_UPDATE_PREREQ_NXDOMAIN,
                                  "ftp.example.com",
                                  0,
                                  NULL,
                                  0,
                                  0);
    assert(errorCode == TM_ENOERROR);
 
    errorCode = tfDnsUpdateAdd(dnsUpdateHandle,
                               "ftp.example.com",
                               300,
                               TM_DNS_RR_TYPE_A,
                               "10.0.0.1",
                               -1,
                               TM_DNS_UPDATE_DATA_IPV4);
    assert(errorCode == TM_ENOERROR);
 
    errorCode = tfDnsUpdateAdd(dnsUpdateHandle,
                               "ftp.example.com",
                               300,
                               TM_DNS_RR_TYPE_AAAA,
                               "2001:db8::1",
                               -1,
                               TM_DNS_UPDATE_DATA_IPV6);
    assert(errorCode == TM_ENOERROR);
 
/* Send the request and wait for the reply */
    errorCode = tfDnsUpdateSend(dnsUpdateHandle);
#ifdef NO_BLOCKING
    while (errorCode == TM_EWOULDBLOCK)
    {
        KernelSignalWait(myDnsUpdateSemaphore);
        errorCode = tfDnsUpdateExecute(dnsUpdateHandle);
    }
#endif
    assert(errorCode == TM_ENOERROR);
 
/* Transaction 2: Change a text record from 'red' to 'blue' */
    errorCode = tfDnsUpdatePrereq(dnsUpdateHandle,
                                  TM_DNS_UPDATE_PREREQ_YXRRSET,
                                  "www.example.com",
                                  TM_DNS_RR_TYPE_TXT,
                                  "red",
                                  -1,
                                  TM_DNS_UPDATE_DATA_TEXT);
    assert(errorCode == TM_ENOERROR);
 
    errorCode = tfDnsUpdateDelete(dnsUpdateHandle,
                                  "www.example.com",
                                  TM_DNS_RR_TYPE_TXT,
                                  NULL,
                                  0,
                                  0);
    assert(errorCode == TM_ENOERROR);
 
    errorCode = tfDnsUpdateAdd(dnsUpdateHandle,
                               "www.example.com",
                               300,
                               TM_DNS_RR_TYPE_TXT,
                               "blue",
                               -1,
                               TM_DNS_UPDATE_DATA_TEXT);
    assert(errorCode == TM_ENOERROR);
 
/* Send the request and wait for the reply */
    errorCode = tfDnsUpdateSend(dnsUpdateHandle);
#ifdef NO_BLOCKING
    while (errorCode == TM_EWOULDBLOCK)
    {
        KernelSignalWait(myDnsUpdateSemaphore);
        errorCode = tfDnsUpdateExecute(dnsUpdateHandle);
    }
#endif
    assert(errorCode == TM_ENOERROR);
 
/* End of session */
    errorCode = tfDnsUpdateClose(dnsUpdateHandle);
    assert(errorCode == TM_ENOERROR);
}
 
 
#ifdef NO_BLOCKING
/*
 * Callback function to receive notification of DNS update events.
 */
void myDnsUpdateCB(
    ttDnsUpdateHandle   dnsUpdateHandle,
    void *              userDataPtr,
    int                 eventId)
{
    KernelSignal(myDnsUpdateSemaphore);
}
#endif

Function Calls


Table of Contents >> Application Reference