rdata.html revision 1ef8965366d91e02a4672c35a187d30aa4a4c72c
2N/A<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2N/A<HTML>
2N/A<HEAD>
2N/A <TITLE>Adding new RDATA type</TITLE>
2N/A</HEAD>
2N/A<BODY>
2N/A<H2>Overview</H2>
2N/AThe dns rdata routines (<CODE>dns_rdata_fromtext()</CODE>,
2N/A<CODE>dns_rdata_totext()</CODE>, <CODE>dns_rdata_fromwire()</CODE>,
2N/A<CODE>dns_rdata_towire()</CODE> <CODE>dns_rdata_fromstruct()</CODE>,
2N/A<CODE>dns_rdata_tostruct()</CODE> and <CODE>dns_rdata_compare()</CODE>)
2N/Aare designed to provide a single set of routines
2N/Afor encoding, decoding and comparing dns data preventing the problems that
2N/Aoccurred in BIND 8.x and earlier where there were multiple places in the
2N/Acode base that
2N/Adecoded wire format to internal format or compared rdata sometimes with
2N/Asubtly different behaviour (bugs) or didn't support a particular type leading
2N/Ato internal inconsistancy.
2N/A<P>
2N/AEach of these generic routines calls type specific routines that provide
2N/Athe type specific details.
2N/A<P>
2N/AFrom time to time new types are defined and it is necessary to add these types
2N/Ainto the existing structure.
2N/AThis document is written to provide instruction on how to do this.
2N/A<H2>Adding new RDATA types</H2>
2N/A
2N/AAdding a new rdata type requires determining if the new rdata type is class
2N/Aspecific or generic.
2N/AWriting code to perform the following set of operations
2N/Aand then integrating it into the build by placing the code into the rdata
2N/Ahierachy at the correct place.
2N/ARunning <CODE>make clean</CODE> followed <CODE>make</CODE> in
2N/A<CODE>lib/dns</CODE> will cause the new rdata type to be picked up.
2N/A<P>
2N/AEach rdata module must perform the following operations:
2N/A<DL>
2N/A<DT>Convert from text format to internal format</DT>
2N/A<DT>Convert from internal format to text format</DT>
2N/A<DT>Convert from wire format to internal format</DT>
2N/A<DT>Convert from internal format to wire format</DT>
2N/A<DT>Convert from a structure to internal format</DT>
2N/A<DT>Convert from internal format to a structure</DT>
2N/A<DT>Compare two rdata in internal format<DT>
2N/A</DL>
2N/A<P>
2N/AThere is an additional set of support <A HREF="#functions">functions</A> and
2N/A<A HREF="#macros">macros</A> only available to
2N/Ato rdata code.
2N/A<H2>RDATA Hierarchy</H2>
2N/AThe <CODE>rdata</CODE> hierarchy has the following format.
2N/A<PRE>
rdata/
generic/
<I>typename_typenumber</I>.h
<I>classname_classnumber</I>/
<I>typename_typenumber</I>.h
<PRE>
<P>
Initial rdata hierarchy:
<P>
<PRE>
rdata/
generic/
ns_2.h
md_3.h
mf_4.h
cname_5.h
soa_6.h
mb_7.h
mg_8.h
mr_9.h
null_10.h
ptr_12.h
hinfo_13.h
minfo_14.h
mx_15.h
txt_16.h
rp_17.h
afsdb_18.h
x25_19.h
isdn_20.h
rt_21.h
sig_24.h
key_25.h
gpos_27.h
loc_29.h
nxt_30.h
cert_37.h
dname_39.h
unspec_103.h
tkey_249.h
in_1/
a_1.h
wks_11.h
nsap_22.h
nsap-ptr_23.h
px_26.h
aaaa_28.h
srv_33.h
naptr_35.h
kx_36.h
a6_38.h
any_255/
tsig_250.h
</PRE>
<H2>CLASSNAME and TYPENAME</H2>
Class and type names must be from the following alphabet and less that 11
characters in length or otherwise they will be ignored.
Permissible alphabet: a to z, 0 to 9 and dash (-).
Dash is mapped to underscore (_) for the C function names below.
<H2>Internal Format</H2>
The internal format choosen is DNS wire format without any compression being
applied to domain names in the rdata.
<H2>Convert from text format to internal format</H2>
The functions to convert from text format has the following call formats and
is declared as follows for class generic functions.
<PRE>
<CODE>static dns_result_t
fromtext_<I>typename</I>(dns_rdataclass_t class, dns_rdatatype_t type,
isc_lex_t *lexer, dns_name_t *origin,
isc_boolean_t downcase, isc_buffer_t *target);</CODE>
</PRE>
Class specific functions contain the class name in addition to the
type name.
<PRE>
<CODE>static dns_result_t
fromtext_<I>classname_typename</I>(dns_rdataclass_t class, dns_rdatatype_t type,
isc_lex_t *lexer, dns_name_t *origin,
isc_boolean_t downcase, isc_buffer_t *target);</CODE>
</PRE>
<DL>
<DT><CODE>class</CODE></DT>
<DD>
This argument should be ignored when used with a class generic RR type
otherwise <CODE>REQUIRE(class == #)</CODE> should be present at the start
of the function.
<DT><CODE>type</CODE></DT>
<DD>
This should be tested with a <CODE>REQUIRE(type == #)</CODE> statement at
the begining of the function.
<DT><CODE>lexer</CODE></DT>
<DD>
This is used to read the input text stream.
<DT><CODE>origin</CODE></DT>
<DD>
This is a absolute name used to qualify unqualified / partially qualified
domainnames in the text stream.
It is passed to the name parsing routines.
<DT><CODE>downcase</CODE></DT>
<DD>
This is passed to the name parsing routines to determine whether to downcase
the names it generates or leave them in the case they are pesented in.
<DT><CODE>target</CODE></DT>
<DD>
This is a <CODE>BINARY</CODE> buffer used to write the internal format of the rdata record being read in to.
</DL>
<CODE>fromtext_<I>typename</I>()</CODE> reads tokens from <CODE>lexer</CODE>,
up to but not including the end of line (EOL) token or end of file (EOF) token.
If the EOL / EOF token is read it should be returned to the input stream.
<A HREF="#gettoken"><CODE>gettoken()</CODE></A>
should be used to read the next token from the input stream and
will return EOL / EOF tokens
automatically unless
they are specifcally requested.
<CODE>isc_lex_ungettoken()</CODE> should
be used to return EOL / EOF (or any other token) to the input stream if
the EOL / EOF token is read.
Unused tokens will cause <CODE>dns_rdata_fromtext()</CODE> to return
<CODE>DNS_R_EXTRATOKEN</CODE> if <CODE>fromtext_<I>typename</I>()</CODE> was successful.
<P>
<CODE>fromtext_<I>typename</I>()</CODE> reads external input and as such is a high security area and must be paranoid about its input.
<CODE>fromtext_<I>typename</I>()</CODE> reads external input and as such is a high security area and must be paranoid about its input.
<H2>Convert from internal format to text format</H2>
<PRE>
<CODE>static dns_result_t
totext_<I>typename</I>(dns_rdata_t *rdata, dns_name_t *origin,
isc_buffer_t *target);</CODE>
</PRE>
<PRE>
<CODE>static dns_result_t
totext_<I>classname_typename</I>(dns_rdata_t *rdata, dns_name_t *origin,
isc_buffer_t *target);</CODE>
</PRE>
<DL>
<DT><CODE>rdata</CODE></DT>
<DD>
This is the rdata record to be converted from internal format to text.
<CODE>rdata->type</CODE> and <CODE>rdata->class</CODE> for class specific
RR types should be checked at the start of the function with
<CODE>REQUIRE(rdata->type ==�#)</CODE> statements.
<DT><CODE>origin</CODE></DT>
<DD>
If this in non <CODE>NULL</CODE> then any domainnames with this suffix
should be written out unqualified.
<A HREF="#name_prefix"><CODE>name_prefix()</CODE></A> can be used to
check if <CODE>origin</CODE> is <CODE>NULL</CODE> and provide the correct
arguments to the name conversion routines.
<DT><CODE>target</CODE></DT>
<DD>
This is a <CODE>TEXT</CODE> buffer used to hold the output.
</DL>
<H2>Convert from wire format to internal format</H2>
<PRE>
<CODE>static dns_result_t
fromwire_<I>typename</I>(dns_rdataclass_t class, dns_rdatatype_t type,
isc_buffer_t *source, dns_decompress_t *dctx,
isc_boolean_t downcase, isc_buffer_t *target);</CODE>
</PRE>
<PRE>
<CODE>static dns_result_t
fromwire_<I>classname_typename</I>(dns_rdataclass_t class, dns_rdatatype_t type,
isc_buffer_t *source, dns_decompress_t *dctx,
isc_boolean_t downcase, isc_buffer_t *target);</CODE>
</PRE>
<P>
<CODE>fromwire_<I>classname_typename</I>()</CODE> is required to set the valid
decompression methods if there is a domain name in the rdata.
<PRE>
<CODE>if (dns_decompress_edns(dctx) >= # || !dns_decompress_strict(dctx))
dns_decompress_setmethods(dctx, DNS_COMPRESS_ALL);
else
dns_decompress_setmethods(dctx, DNS_COMPRESS_LOCAL);</CODE>
</PRE>
<DL>
<DT><CODE>class</CODE></DT>
<DD>
This argument should be ignored when used with a class generic RR type
otherwise <CODE>REQUIRE(class == #)</CODE> should be present at the start
of the function.
<DT><CODE>type</CODE></DT>
<DD>
This should be tested with a <CODE>REQUIRE(type == #)</CODE> statement at
the begining of the function.
<DT><CODE>source</CODE></DT>
<DD>
This is a <CODE>BINARY</CODE> buffer with the <CODE>active</CODE> region
containing a RR record in wire format.
<DT><CODE>dctx</CODE></DT>
<DD>
This is the decompression context and is passed to
<CODE>dns_name_fromwire()</CODE>,
along with <CODE>downcase</CODE>, to enable a compressed domain name
to be extracted from the source.
<DT><CODE>downcase</CODE></DT>
<DD>
This is passed to <CODE>dns_name_fromwire()</CODE> to say whether the
extracted domainname should be downcased during the extraction.
<DT><CODE>target</CODE></DT>
<DD>
This is a <CODE>BINARY</CODE> buffer where the decompressed and checked
RR record is written.
</DL>
<CODE>fromwire_<I>typename</I>()</CODE> is a security sensitive routine
as it reads external data and should take extreme care to ensure that
the input data matches its description.
<P>
If the <CODE>active</CODE> buffer is not empty at completion and
<CODE>fromwire_<I>typename</I>()</CODE> was otherwise successful
<CODE>dns_rdata_fromwire()</CODE> will return <CODE>DNS_R_EXTRADATA</CODE>.
<H2>Convert from internal format to wire format</H2>
<PRE>
<CODE>static dns_result_t
towire_<I>typename</I>(dns_rdata_t *rdata, dns_compress_t *cctx,
isc_buffer_t *target);</CODE>
</PRE>
<PRE>
<CODE>static dns_result_t
towire_<I>classname_typename</I>(dns_rdata_t *rdata, dns_compress_t *cctx,
isc_buffer_t *target);<CODE>
</PRE>
<P>
<CODE>towire_<I>classname_typename</I>()</CODE> is required to set the
allowed name compression methods based on EDNS version if there is a
domain name in the rdata.
<PRE>
<CODE>if (dns_compress_getedns(cctx) >= #)
dns_compress_setmethods(cctx, DNS_COMPRESS_ALL);
else
dns_compress_setmethods(cctx, DNS_COMPRESS_LOCAL);</CODE>
</PRE>
<DL>
<DT><CODE>rdata</CODE></DT>
<DD>
This is the rdata record to be converted from internal format to text.
<CODE>rdata->type</CODE> and <CODE>rdata->class</CODE> for class specific
RR types should be checked at the start of the function with
<CODE>REQUIRE(rdata->type ==�#)</CODE> statements.
<DT><CODE>cctx</CODE></DT>
<DD>
This is the compression context, it should be passed to <CODE>dns_name_towire()</CODE> when putting domainnames on the wire.
<DT><CODE>target</CODE></DT>
<DD>
This is a <CODE>BINARY</CODE> buffer used to write the rdata to.
</DL>
Simple RR types without domainnames can use the following code to
transfer the contents of the <CODE>rdata</CODE> to the target buffer.
<PRE>
<CODE>return (<A HREF="#mem_tobuffer">mem_tobuffer</A>(target, rdata->data, rdata->length));</CODE>
</PRE>
<H2>Convert from a structure to internal format</H2>
<PRE>
<CODE>static dns_result_t
fromstruct_<I>typename</I>(dns_rdataclass_t class, dns_rdatatype_t type,
void *source, isc_buffer_t *target);</CODE>
</PRE>
<PRE>
<CODE>static dns_result_t
fromstruct_<I>classname_typename</I>(dns_rdataclass_t class, dns_rdatatype_t type,
void *source, isc_buffer_t *target);</CODE>
</PRE>
<DL>
<DT><CODE>class</CODE></DT>
<DD>
This argument should be ignored when used with a class generic RR type
otherwise <CODE>REQUIRE(class == #)</CODE> should be present at the start
of the function.
<DT><CODE>type</CODE></DT>
<DD>
This should be tested with a <CODE>REQUIRE(type == #)</CODE> statement at
the beginning of the function.
<DT><CODE>source</CODE></DT>
<DD>
This points to a type specific structure.
<DT><CODE>target</CODE></DT>
<DD>
This is a <CODE>BINARY</CODE> buffer used to write the internal format of the rdata record being read in to.
</DL>
<H2>Convert from internal format to a structure</H2>
<PRE>
<CODE>static dns_result_t
tostruct_<I>typename</I>(dns_rdata_t *rdata, void *target);</CODE>
</PRE>
<PRE>
<CODE>static dns_result_t
tostruct_<I>classname_typename</I>(dns_rdata_t *rdata, void *target);</CODE>
</PRE>
<DL>
<DT><CODE>rdata</CODE></DT>
<DD>
This is the rdata record to be converted from internal format to text.
<CODE>rdata->type</CODE> and <CODE>rdata->class</CODE> for class specific
RR types should be checked at the start of the function with
<CODE>REQUIRE(rdata->type ==�#)</CODE> statements.
<DT><CODE>target</CODE></DT>
<DD>
Pointer to a type specific structure.
</DL>
<H2>Compare two rdata in internal format</H2>
<PRE>
<CODE>static int
compare_<I>typename</I>(dns_rdata_t *rdata1, dns_rdata_t *rdata2);</CODE>
</PRE>
<PRE>
<CODE>static int
compare_<I>classname_typename</I>(dns_rdata_t *rdata1, dns_rdata_t *rdata2);</CODE>
</PRE>
Compares <CODE>rdata1</CODE> and <CODE>rdata2<CODE> as required for DNSSEC
ordering. The routine should
ensure that the <CODE>type</CODE> and <CODE>class</CODE> of the two rdata
match with <CODE>REQUIRE(rdata1->type == rdata2->type);</CODE> and
<CODE>REQUIRE(rdata1->class == rdata2->class);</CODE> statements. The
<CODE>rdata->type</CODE> should also be verified and if the RR type is
class specific the <CODE>rdata->class</CLASS>.
<P>
<CODE>compare_<I>classname_typename</I>()</CODE> returns -1, 0, 1.
<H2><A NAME="functions">Support Functions</A></H2>
The following static support functions are available to use.
<DL>
<DT><CODE>static unsigned int<BR>
name_length(dns_name_t *name);</CODE></DT>
<DD>
<P>
Returns the length of <CODE>name</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
txt_totext(isc_region_t *source, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Extracts the octet length tagged text string at the start of
<CODE>source</CODE> and writes it as a quoted string to <CODE>target</CODE>.
<CODE>source</CODE> is adjusted so that it points to first octet after the
text string.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE> or <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
txt_fromtext(isc_textregion_t *source, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Take the text region <CODE>source</CODE> and convert it to a length tagged
text string writing it to <CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE>, <CODE>DNS_R_TEXTTOLONG</CODE>
or <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
txt_fromwire(isc_buffer_t *source, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Read a octet length tagged text string from <CODE>source</CODE> and
write it to <CODE>target</CODE>.
Ensures that octet length tagged text string was wholly within the active
area of <CODE>source</CODE>.
Adjusts the active area of <CODE>source</CODE> so that it refers to the first
octet after the octet length tagged text string.
<P>
Returns <CODE>DNS_R_UNEXPECTEDEND</CODE>, <CODE>DNS_R_NOSPACE</CODE> or
<CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><A NAME="name_prefix"><CODE>static isc_boolean_t<BR>
name_prefix(dns_name_t *name, dns_name_t *origin, dns_name_t *target);</CODE>
</A></DT>
<DD>
<P>
If <CODE>origin</CODE> is NULL or the root label set <CODE>target<CODE> to
refer to <CODE>name</CODE> and return <CODE>ISC_FALSE</CODE>.
Otherwise see if <CODE>name</CODE> is a sub domain of <CODE>origin</CODE>
and are not equal.
If so make <CODE>target</CODE> refer to the prefix of <CODE>name</CODE> and
return <CODE>ISC_TRUE</CODE>.
Otherwise make <CODE>target</CODE> refer to <CODE>name</CODE> and return
<CODE>ISC_FALSE</CODE>.
<P>
Typical use:
<PRE><CODE>
static dns_result_t
totext_<I>typename</I>(dns_rdata_t *rdata, dns_name_t *origin,
isc_buffer_t * target)
{
isc_region_t region;
dns_name_t name, prefix;
isc_boolean_t sub;
dns_name_init(&name, NULL);
dns_name_init(&prefix, NULL);
dns_rdata_toregion(rdata, &region);
dns_name_fromregion(&name, &region);
sub = <B>name_prefix</B>(&name, origin, &prefix);
return (dns_name_totext(&prefix, sub, target));
}
</CODE></PRE>
<DT><CODE>static dns_result_t<BR>
str_totext(char *source, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
This adds the <CODE>NULL</CODE> terminated string <CODE>source</CODE>
up to but not including <CODE>NULL</CODE> to <CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE> and <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static isc_boolean_t<BR>
buffer_empty(isc_buffer_t *source);</CODE></DT>
<DD>
<P>
Returns <CODE>ISC_TRUE</CODE> if the active region of <CODE>source</CODE> is
empty otherwise <CODE>ISC_FALSE</CODE>.
<P>
<DT><CODE>static void<BR>
buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region,
unsigned int type);</CODE></DT>
<DD>
<P>
Make <CODE>buffer</CODE> refer to the memory in <CODE>region</CODE> and
make it active.
<P>
<DT><CODE>static dns_result_t<BR>
uint32_tobuffer(isc_uint32_t value, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Write them 32 bit <CODE>value</CODE> in network order to <CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE> and <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
uint16_tobuffer(isc_uint32_t value, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Write them 16 bit <CODE>value</CODE> in network order to <CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_RANGE</CODE>, <CODE>DNS_R_NOSPACE</CODE> and <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static isc_uint32_t<BR>
uint32_fromregion(isc_region_t *region);</CODE></DT>
<DD>
<P>
Returns the 32 bit at the start of <CODE>region</CODE> in host order.
<P>
Requires <CODE>(region->length >= 4)</CODE>.
<P>
<DT><CODE>static isc_uint16_t<BR>
uint16_fromregion(isc_region_t *region);</CODE></DT>
<DD>
<P>
Returns the 16 bit at the start of <CODE>region</CODE> in host order.
<P>
Requires <CODE>(region->length >= 2)</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
<A NAME="gettoken">gettoken</A>(isc_lex_t *lexer, isc_token_t *token, isc_tokentype_t expect, isc_boolean_t eol);</CODE></DT>
<DD>
<P>
Gets the next token from the input stream <CODE>lexer</CODE>. Ensure that the
returned token matches <CODE>expect</CODE> (isc_tokentype_qstring can also
return isc_tokentype_string), or isc_tokentype_eol and isc_tokentype_eof if
<CODE>eol</CODE> is <CODE>ISC_TRUE</CODE>.
<P>
Returns <CODE>DNS_R_UNEXPECTED</CODE>, <CODE>DNS_R_UNEXPECTEDEND</CODE>,
<CODE>DNS_R_UNEXPECTEDTOKEN</CODE> and <CODE>DNS_R_SUCCESS</CODE>.
<P>
</DT>
<DT><CODE>static dns_result_t<BR>
<A NAME="mem_tobuffer">mem_tobuffer</A>(isc_buffer_t *target, void *base, unsigned int length);</CODE></DT>
<DD>
<P>
Add the memory referred to by <CODE>base</CODE> to <CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE> and <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static int<BR>
compare_region(isc_region_t *r1, isc_region_t *r2)</CODE></DT>
<DD>
<P>
Compares to regions returning -1, 0, 1 based on their DNSSEC ordering.
<P>
<DT><CODE>static int<BR>
hexvalue(char value);</CODE></DT>
<DD>
<P>
Returns the hexadecimal value of <CODE>value</CODE> or -1 if not
a hexadecimal character.
<P>
<DT><CODE>static int<BR>
decvalue(char value);</CODE></DT>
<DD>
<P>
Returns the decimal value of <CODE>value</CODE> or -1 if not
a decimal character.
<P>
<DT><CODE>static dns_result_t<BR>
base64_totext(isc_region_t *source, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Convert the region referred to by <CODE>source</CODE> to base64 encode text
and put it into <CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE> or <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target,
int length);</CODE></DT>
<DD>
<P>
Read a series of tokens from <CODE>lexer</CODE> that containing base64 data
until one of end of line, <CODE>length</CODE> (<CODE>length</CODE> &gt;= 0)
bytes have been read or base64 pad characters are seen.
If <CODE>length</CODE> &lt; 0 it is ignored otherwise it is an error if there
are not <CODE>length</CODE> octets of data or when processing a token
<CODE>length</CODE> octets would have been exceeded.
<P>
Returns <CODE>DNS_R_BADBASE64</CODE>, <CODE>DNS_R_UNEXPECTED</CODE>,
<CODE>DNS_R_UNEXPECTEDEND</CODE>, <CODE>DNS_R_UNEXPECTEDTOKEN</CODE>
and <CODE>DNS_R_SUCCESS</CODE>.
<P>
<DT><CODE>static dns_result_t<BR>
time_totext(unsigned long value, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Convert the date represented by <CODE>value</CODE> into YYYYMMDDHHMMSS format
taking into account the active epochs. This code is Y2K and Y2038 compliant.
<P>
Returns <CODE>DNS_R_NOSPACE</CODE> and <DNS_R_SUCCESS>.
<DT><CODE>static dns_result_t<BR>
time_tobuffer(char *source, isc_buffer_t *target);</CODE></DT>
<DD>
<P>
Take the date in <CODE>source</CODE> and convert it seconds since January 1,
1970 (ignoring leap seconds) and place the least significant 32 bits into
<CODE>target</CODE>.
<P>
Returns <CODE>DNS_R_RANGE</CODE>, <CODE>DNS_R_SYNTAX</CODE>,
<CODE>DNS_R_NOSPACE</CODE> and <CODE>DNS_R_SUCCESS</CODE>.
</DL>
<H2><A NAME="macros">Support Macros<A></H2>
The following macro is available:
<DL>
<DT><CODE>RETERR(x)</CODE><DT>
<DD>
<P>
Evaluate <CODE>x</CODE> and call <CODE>return (<I>&lt;value of x&gt;</I>);</CODE> if the result is not <CODE>DNS_R_SUCCESS</CODE>.
</DL>
</BODY>
</HTML>