4632N/A<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
4632N/A - Copyright (C) 1999-2001, 2004, 2007, 2016 Internet Systems Consortium, Inc. ("ISC") 4632N/A - This Source Code Form is subject to the terms of the Mozilla Public 4632N/A - License, v. 2.0. If a copy of the MPL was not distributed with this 4632N/A <
TITLE>Adding new RDATA type</
TITLE>
4632N/AThe dns rdata routines (<
CODE>dns_rdata_fromtext()</
CODE>,
4632N/A<
CODE>dns_rdata_totext()</
CODE>, <
CODE>dns_rdata_fromwire()</
CODE>,
4632N/A<
CODE>dns_rdata_towire()</
CODE> <
CODE>dns_rdata_fromstruct()</
CODE>,
4632N/A<
CODE>dns_rdata_tostruct()</
CODE> and <
CODE>dns_rdata_compare()</
CODE>)
4632N/Aare designed to provide a single set of routines
4632N/Afor encoding, decoding and comparing dns data preventing the problems that
4632N/Aoccurred in BIND
8.x and earlier where there were multiple places in the
4632N/Adecoded wire format to internal format or compared rdata sometimes with
4632N/Asubtly different behaviour (bugs) or didn't support a particular type leading
4632N/AEach of these generic routines calls type specific routines that provide
4632N/AFrom time to time new types are defined and it is necessary to add these types
5276N/Ainto the existing structure.
5276N/AThis document is written to provide instruction on how to do this.
5276N/A<
H2>Adding new RDATA types</
H2>
4632N/AAdding a new rdata type requires determining if the new rdata type is class
4632N/AWriting code to perform the following set of operations
4632N/Aand then integrating it into the build by placing the code into the rdata
4632N/Ahierachy at the correct place.
4632N/ARunning <
CODE>make clean</
CODE> followed <
CODE>make</
CODE> in
4632N/A<
CODE>
lib/
dns</
CODE> will cause the new rdata type to be picked up.
4632N/AEach rdata module must perform the following operations:
4632N/A<
DT>Convert from text format to internal format</
DT>
4632N/A<
DT>Convert from internal format to text format</
DT>
4632N/A<
DT>Convert from wire format to internal format</
DT>
5276N/A<
DT>Convert from internal format to wire format</
DT>
4632N/A<
DT>Convert from a structure to internal format</
DT>
4632N/A<
DT>Convert from internal format to a structure</
DT>
4632N/A<
DT>Compare two rdata in internal format<
DT>
4632N/AThere is an additional set of support <
A HREF="#functions">functions</
A> and
4632N/A<
A HREF="#macros">macros</
A> only available to
4632N/AThe <
CODE>rdata</
CODE> hierarchy has the following format.
4632N/A <
I>typename_typenumber</
I>.h
4632N/A <
I>classname_classnumber</
I>/
4632N/A <
I>typename_typenumber</
I>.h
4632N/A<
H2>CLASSNAME and TYPENAME</
H2>
4632N/AClass and type names must be from the following alphabet and less that 11
4632N/Acharacters in length or otherwise they will be ignored.
4632N/APermissible alphabet: a to z, 0 to 9 and dash (-).
4632N/ADash is mapped to underscore (_) for the C function names below.
4632N/AThe internal format chosen is DNS wire format without any compression being
4632N/Aapplied to domain names in the rdata.
4632N/A<
H2>Convert from text format to internal format</
H2>
4632N/AThe functions to convert from text format has the following call formats and
4632N/Ais declared as follows for class generic functions.
4632N/Afromtext_<
I>typename</
I>(dns_rdataclass_t class, dns_rdatatype_t type,
4632N/A isc_lex_t *lexer, dns_name_t *origin,
4632N/A isc_boolean_t downcase, isc_buffer_t *target);</
CODE>
4632N/AClass specific functions contain the class name in addition to the
4632N/Afromtext_<
I>classname_typename</
I>(dns_rdataclass_t class, dns_rdatatype_t type,
4670N/A isc_lex_t *lexer, dns_name_t *origin,
4670N/A isc_boolean_t downcase, isc_buffer_t *target);</
CODE>
4632N/AThis argument should be ignored when used with a class generic RR type
4632N/Aotherwise <
CODE>REQUIRE(class == #)</
CODE> should be present at the start
4632N/AThis should be tested with a <
CODE>REQUIRE(type == #)</
CODE> statement at
4632N/Athe begining of the function.
4632N/AThis is used to read the input text stream.
4632N/A<
DT><
CODE>origin</
CODE></
DT>
4632N/AThis is a absolute name used to qualify unqualified / partially qualified
4632N/Adomainnames in the text stream.
4632N/AIt is passed to the name parsing routines.
4632N/A<
DT><
CODE>downcase</
CODE></
DT>
4632N/AThis is passed to the name parsing routines to determine whether to downcase
4632N/Athe names it generates or leave them in the case they are pesented in.
4632N/A<
DT><
CODE>target</
CODE></
DT>
4632N/AThis is a <
CODE>BINARY</
CODE> buffer used to write the internal format of the rdata record being read in to.
4632N/A<
CODE>fromtext_<
I>typename</
I>()</
CODE> reads tokens from <
CODE>lexer</
CODE>,
4632N/Aup to but not including the end of line (EOL) token or end of file (EOF) token.
4632N/AIf the EOL / EOF token is read it should be returned to the input stream.
4632N/A<
A HREF="#gettoken"><
CODE>gettoken()</
CODE></
A>
4632N/Ashould be used to read the next token from the input stream and
4632N/Awill return EOL / EOF tokens
4632N/Athey are specifcally requested.
4632N/A<
CODE>isc_lex_ungettoken()</
CODE> should
4632N/Abe used to return EOL / EOF (or any other token) to the input stream if
4632N/Athe EOL / EOF token is read.
4632N/AUnused tokens will cause <
CODE>dns_rdata_fromtext()</
CODE> to return
4632N/A<
CODE>DNS_R_EXTRATOKEN</
CODE> if <
CODE>fromtext_<
I>typename</
I>()</
CODE> was successful.
4632N/A<
CODE>fromtext_<
I>typename</
I>()</
CODE> reads external input and as such is a high security area and must be paranoid about its input.
4632N/A<
H2>Convert from internal format to text format</
H2>
4632N/Atotext_<
I>typename</
I>(dns_rdata_t *rdata, dns_name_t *origin,
4632N/A isc_buffer_t *target);</
CODE>
4632N/Atotext_<
I>classname_typename</
I>(dns_rdata_t *rdata, dns_name_t *origin,
4632N/A isc_buffer_t *target);</
CODE>
4632N/AThis is the rdata record to be converted from internal format to text.
4632N/A<
CODE>rdata->type</
CODE> and <
CODE>rdata->class</
CODE> for class specific
4632N/ARR types should be checked at the start of the function with
4632N/A<
CODE>REQUIRE(rdata->type ==�#)</
CODE> statements.
4632N/A<
DT><
CODE>origin</
CODE></
DT>
4632N/AIf this in non <
CODE>NULL</
CODE> then any domainnames with this suffix
4632N/Ashould be written out unqualified.
4632N/A<
A HREF="#name_prefix"><
CODE>name_prefix()</
CODE></
A> can be used to
4632N/Acheck if <
CODE>origin</
CODE> is <
CODE>NULL</
CODE> and provide the correct
4632N/Aarguments to the name conversion routines.
4632N/A<
DT><
CODE>target</
CODE></
DT>
4632N/AThis is a <
CODE>TEXT</
CODE> buffer used to hold the output.
4632N/A<
H2>Convert from wire format to internal format</
H2>
4632N/Afromwire_<
I>typename</
I>(dns_rdataclass_t class, dns_rdatatype_t type,
4632N/A isc_buffer_t *source, dns_decompress_t *dctx,
4632N/A isc_boolean_t downcase, isc_buffer_t *target);</
CODE>
4632N/Afromwire_<
I>classname_typename</
I>(dns_rdataclass_t class, dns_rdatatype_t type,
4632N/A isc_buffer_t *source, dns_decompress_t *dctx,
4632N/A isc_boolean_t downcase, isc_buffer_t *target);</
CODE>
4632N/A<
CODE>fromwire_<
I>classname_typename</
I>()</
CODE> is required to set the valid
4632N/Adecompression methods if there is a domain name in the rdata.
4632N/A<
CODE>if (dns_decompress_edns(dctx) >= # || !dns_decompress_strict(dctx))
4632N/A dns_decompress_setmethods(dctx, DNS_COMPRESS_ALL);
4632N/A dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);</
CODE>
4632N/AThis argument should be ignored when used with a class generic RR type
4632N/Aotherwise <
CODE>REQUIRE(class == #)</
CODE> should be present at the start
4632N/AThis should be tested with a <
CODE>REQUIRE(type == #)</
CODE> statement at
4632N/Athe begining of the function.
5276N/A<
DT><
CODE>source</
CODE></
DT>
4632N/AThis is a <
CODE>BINARY</
CODE> buffer with the <
CODE>active</
CODE> region
4632N/Acontaining a RR record in wire format.
4632N/AThis is the decompression context and is passed to
4632N/A<
CODE>dns_name_fromwire()</
CODE>,
4632N/Aalong with <
CODE>downcase</
CODE>, to enable a compressed domain name
4632N/Ato be extracted from the source.
4632N/A<
DT><
CODE>downcase</
CODE></
DT>
4632N/AThis is passed to <
CODE>dns_name_fromwire()</
CODE> to say whether the
4632N/Aextracted domainname should be downcased during the extraction.
4632N/A<
DT><
CODE>target</
CODE></
DT>
4632N/AThis is a <
CODE>BINARY</
CODE> buffer where the decompressed and checked
4632N/A<
CODE>fromwire_<
I>typename</
I>()</
CODE> is a security sensitive routine
4632N/Aas it reads external data and should take extreme care to ensure that
4632N/Athe input data matches its description.
4632N/AIf the <
CODE>active</
CODE> buffer is not empty at completion and
4632N/A<
CODE>fromwire_<
I>typename</
I>()</
CODE> was otherwise successful
4632N/A<
CODE>dns_rdata_fromwire()</
CODE> will return <
CODE>DNS_R_EXTRADATA</
CODE>.
4632N/A<
H2>Convert from internal format to wire format</
H2>
4639N/Atowire_<
I>typename</
I>(dns_rdata_t *rdata, dns_compress_t *cctx,
4639N/A isc_buffer_t *target);</
CODE>
4639N/Atowire_<
I>classname_typename</
I>(dns_rdata_t *rdata, dns_compress_t *cctx,
4639N/A isc_buffer_t *target);<
CODE>
4632N/A<
CODE>towire_<
I>classname_typename</
I>()</
CODE> is required to set the
4632N/Aallowed name compression methods based on EDNS version if there is a
4632N/A<
CODE>if (dns_compress_getedns(cctx) >= #)
4632N/A dns_compress_setmethods(cctx, DNS_COMPRESS_ALL);
4632N/A dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);</
CODE>
4632N/AThis is the rdata record to be converted from internal format to text.
4632N/A<
CODE>rdata->type</
CODE> and <
CODE>rdata->class</
CODE> for class specific
4632N/ARR types should be checked at the start of the function with
4632N/A<
CODE>REQUIRE(rdata->type ==�#)</
CODE> statements.
4632N/AThis is the compression context, it should be passed to <
CODE>dns_name_towire()</
CODE> when putting domainnames on the wire.
4632N/A<
DT><
CODE>target</
CODE></
DT>
4632N/AThis is a <
CODE>BINARY</
CODE> buffer used to write the rdata to.
4632N/ASimple RR types without domainnames can use the following code to
4632N/Atransfer the contents of the <
CODE>rdata</
CODE> to the target buffer.
4632N/A <
CODE>return (<
A HREF="#mem_tobuffer">mem_tobuffer</
A>(target, rdata->data, rdata->length));</
CODE>
4632N/A<
H2>Convert from a structure to internal format</
H2>
4632N/Afromstruct_<
I>typename</
I>(dns_rdataclass_t class, dns_rdatatype_t type,
4632N/A void *source, isc_buffer_t *target);</
CODE>
4632N/Afromstruct_<
I>classname_typename</
I>(dns_rdataclass_t class, dns_rdatatype_t type,
4632N/A void *source, isc_buffer_t *target);</
CODE>
4632N/AThis argument should be ignored when used with a class generic RR type
4632N/Aotherwise <
CODE>REQUIRE(class == #)</
CODE> should be present at the start
4632N/AThis should be tested with a <
CODE>REQUIRE(type == #)</
CODE> statement at
4632N/Athe beginning of the function.
4632N/A<
DT><
CODE>source</
CODE></
DT>
4632N/AThis points to a type specific structure.
4632N/A<
DT><
CODE>target</
CODE></
DT>
4632N/AThis is a <
CODE>BINARY</
CODE> buffer used to write the internal format of the rdata record being read in to.
4632N/A<
H2>Convert from internal format to a structure</
H2>
4632N/Atostruct_<
I>typename</
I>(dns_rdata_t *rdata, void *target);</
CODE>
4632N/Atostruct_<
I>classname_typename</
I>(dns_rdata_t *rdata, void *target);</
CODE>
4632N/AThis is the rdata record to be converted from internal format to a structure.
4632N/A<
CODE>rdata->type</
CODE> and <
CODE>rdata->class</
CODE> for class specific
4632N/ARR types should be checked at the start of the function with
4632N/A<
CODE>REQUIRE(rdata->type ==�#)</
CODE> statements.
4632N/A<
DT><
CODE>target</
CODE></
DT>
4632N/APointer to a type specific structure.
4632N/A<
H2>Compare two rdata in internal format</
H2>
4632N/Acompare_<
I>typename</
I>(dns_rdata_t *rdata1, dns_rdata_t *rdata2);</
CODE>
4632N/Acompare_<
I>classname_typename</
I>(dns_rdata_t *rdata1, dns_rdata_t *rdata2);</
CODE>
4632N/ACompares <
CODE>rdata1</
CODE> and <
CODE>rdata2<
CODE> as required for DNSSEC
4632N/Aordering. The routine should
4632N/Aensure that the <
CODE>type</
CODE> and <
CODE>class</
CODE> of the two rdata
4632N/Amatch with <
CODE>REQUIRE(rdata1->type == rdata2->type);</
CODE> and
4632N/A<
CODE>REQUIRE(rdata1->class == rdata2->class);</
CODE> statements. The
4632N/A<
CODE>rdata->type</
CODE> should also be verified and if the RR type is
4632N/Aclass specific the <
CODE>rdata->class</
CLASS>.
4632N/A<
CODE>compare_<
I>classname_typename</
I>()</
CODE> returns -1, 0, 1.
4632N/A<
H2><
A NAME="functions">Support Functions</
A></
H2>
4632N/AThe following static support functions are available to use.
4632N/A<
DT><
CODE>static unsigned int<
BR>
4632N/Aname_length(dns_name_t *name);</
CODE></
DT>
4632N/AReturns the length of <
CODE>name</
CODE>.
4632N/A<
DT><
CODE>static dns_result_t<
BR>
4632N/Atxt_totext(isc_region_t *source, isc_buffer_t *target);</
CODE></
DT>
4632N/AExtracts the octet length tagged text string at the start of
4632N/A<
CODE>source</
CODE> and writes it as a quoted string to <
CODE>target</
CODE>.
4632N/A<
CODE>source</
CODE> is adjusted so that it points to first octet after the
4632N/AReturns <
CODE>DNS_R_NOSPACE</
CODE> or <
CODE>DNS_R_SUCCESS</
CODE>.
4632N/A<
DT><
CODE>static dns_result_t<
BR>
4632N/Atxt_fromtext(isc_textregion_t *source, isc_buffer_t *target);</
CODE></
DT>
4632N/ATake the text region <
CODE>source</
CODE> and convert it to a length tagged
4632N/Atext string writing it to <
CODE>target</
CODE>.
4632N/AReturns <
CODE>DNS_R_NOSPACE</
CODE>, <
CODE>DNS_R_TEXTTOLONG</
CODE>
4632N/Aor <
CODE>DNS_R_SUCCESS</
CODE>.
4632N/A<
DT><
CODE>static dns_result_t<
BR>
4632N/Atxt_fromwire(isc_buffer_t *source, isc_buffer_t *target);</
CODE></
DT>
4632N/ARead a octet length tagged text string from <
CODE>source</
CODE> and
4632N/Awrite it to <
CODE>target</
CODE>.
4632N/AEnsures that octet length tagged text string was wholly within the active
4632N/Aarea of <
CODE>source</
CODE>.
4632N/AAdjusts the active area of <
CODE>source</
CODE> so that it refers to the first
4632N/Aoctet after the octet length tagged text string.
4632N/AReturns <
CODE>DNS_R_UNEXPECTEDEND</
CODE>, <
CODE>DNS_R_NOSPACE</
CODE> or
4632N/A<
DT><
A NAME="name_prefix"><
CODE>static isc_boolean_t<
BR>
4632N/Aname_prefix(dns_name_t *name, dns_name_t *origin, dns_name_t *target);</
CODE>
4632N/AIf <
CODE>origin</
CODE> is NULL or the root label set <
CODE>target<
CODE> to
4632N/Arefer to <
CODE>name</
CODE> and return <
CODE>ISC_FALSE</
CODE>.
4632N/AOtherwise see if <
CODE>name</
CODE> is a sub domain of <
CODE>origin</
CODE>
4632N/AIf so make <
CODE>target</
CODE> refer to the prefix of <
CODE>name</
CODE> and
4632N/Areturn <
CODE>ISC_TRUE</
CODE>.
4632N/AOtherwise make <
CODE>target</
CODE> refer to <
CODE>name</
CODE> and return
4632N/Atotext_<
I>typename</
I>(dns_rdata_t *rdata, dns_name_t *origin,
4632N/A dns_name_init(&name, NULL);
4632N/A dns_name_init(&prefix, NULL);
4632N/A dns_rdata_toregion(rdata, &region);
4632N/A dns_name_fromregion(&name, &region);
4632N/A sub = <
B>name_prefix</
B>(&name, origin, &prefix);
4632N/A return (dns_name_totext(&prefix, sub, target));
4632N/A<
DT><
CODE>static dns_result_t<
BR>
4632N/Astr_totext(char *source, isc_buffer_t *target);</
CODE></
DT>
4632N/AThis adds the <
CODE>NULL</
CODE> terminated string <
CODE>source</
CODE>
4632N/Aup to but not including <
CODE>NULL</
CODE> to <
CODE>target</
CODE>.
4632N/AReturns <
CODE>DNS_R_NOSPACE</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
4632N/A<
DT><
CODE>static isc_boolean_t<
BR>
4632N/Abuffer_empty(isc_buffer_t *source);</
CODE></
DT>
4632N/AReturns <
CODE>ISC_TRUE</
CODE> if the active region of <
CODE>source</
CODE> is
4632N/Aempty otherwise <
CODE>ISC_FALSE</
CODE>.
4632N/Abuffer_fromregion(isc_buffer_t *buffer, isc_region_t *region,
4632N/Aunsigned int type);</
CODE></
DT>
4632N/AMake <
CODE>buffer</
CODE> refer to the memory in <
CODE>region</
CODE> and
4632N/A<
DT><
CODE>static dns_result_t<
BR>
4632N/Auint32_tobuffer(isc_uint32_t value, isc_buffer_t *target);</
CODE></
DT>
4632N/AWrite the 32 bit <
CODE>value</
CODE> in network order to <
CODE>target</
CODE>.
5276N/AReturns <
CODE>DNS_R_NOSPACE</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
5276N/A<
DT><
CODE>static dns_result_t<
BR>
5276N/Auint16_tobuffer(isc_uint32_t value, isc_buffer_t *target);</
CODE></
DT>
5276N/AWrite them 16 bit <
CODE>value</
CODE> in network order to <
CODE>target</
CODE>.
5276N/AReturns <
CODE>ISC_R_RANGE</
CODE>, <
CODE>DNS_R_NOSPACE</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
5276N/A<
DT><
CODE>static isc_uint32_t<
BR>
5276N/Auint32_fromregion(isc_region_t *region);</
CODE></
DT>
5276N/AReturns the 32 bit at the start of <
CODE>region</
CODE> in host order.
5276N/ARequires <
CODE>(region->length >= 4)</
CODE>.
5276N/A<
DT><
CODE>static isc_uint16_t<
BR>
5276N/Auint16_fromregion(isc_region_t *region);</
CODE></
DT>
5276N/AReturns the 16 bit at the start of <
CODE>region</
CODE> in host order.
5276N/ARequires <
CODE>(region->length >= 2)</
CODE>.
5276N/A<
DT><
CODE>static dns_result_t<
BR>
5276N/A<
A NAME="gettoken">gettoken</
A>(isc_lex_t *lexer, isc_token_t *token, isc_tokentype_t expect, isc_boolean_t eol);</
CODE></
DT>
5276N/AGets the next token from the input stream <
CODE>lexer</
CODE>. Ensure that the
5276N/Areturned token matches <
CODE>expect</
CODE> (isc_tokentype_qstring can also
5276N/Areturn isc_tokentype_string), or isc_tokentype_eol and isc_tokentype_eof if
5276N/A<
CODE>eol</
CODE> is <
CODE>ISC_TRUE</
CODE>.
5276N/AReturns <
CODE>DNS_R_UNEXPECTED</
CODE>, <
CODE>DNS_R_UNEXPECTEDEND</
CODE>,
5276N/A<
CODE>DNS_R_UNEXPECTEDTOKEN</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
5276N/A<
DT><
CODE>static dns_result_t<
BR>
5276N/A<
A NAME="mem_tobuffer">mem_tobuffer</
A>(isc_buffer_t *target, void *base, unsigned int length);</
CODE></
DT>
5276N/AAdd the memory referred to by <
CODE>base</
CODE> to <
CODE>target</
CODE>.
5276N/AReturns <
CODE>DNS_R_NOSPACE</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
compare_region(isc_region_t *r1, isc_region_t *r2)</
CODE></
DT>
Compares two regions returning -1, 0, 1 based on their DNSSEC ordering.
hexvalue(char value);</
CODE></
DT>
Returns the hexadecimal value of <
CODE>value</
CODE> or -1 if not
decvalue(char value);</
CODE></
DT>
Returns the decimal value of <
CODE>value</
CODE> or -1 if not
<
DT><
CODE>static dns_result_t<
BR>
base64_totext(isc_region_t *source, isc_buffer_t *target);</
CODE></
DT>
Convert the region referred to by <
CODE>source</
CODE> to base64 encoded text
and put it into <
CODE>target</
CODE>.
Returns <
CODE>DNS_R_NOSPACE</
CODE> or <
CODE>DNS_R_SUCCESS</
CODE>.
<
DT><
CODE>static dns_result_t<
BR>
base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target,
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> >= 0)
bytes have been read or base64 pad characters are seen.
If <
CODE>length</
CODE> < 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.
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>.
<
DT><
CODE>static dns_result_t<
BR>
time_totext(unsigned long value, isc_buffer_t *target);</
CODE></
DT>
Convert the date represented by <
CODE>value</
CODE> into YYYYMMDDHHMMSS format
taking into account the active epochs. This code is Y2K and Y2038 compliant.
Returns <
CODE>DNS_R_NOSPACE</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
<
DT><
CODE>static dns_result_t<
BR>
time_tobuffer(char *source, isc_buffer_t *target);</
CODE></
DT>
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
Returns <
CODE>ISC_R_RANGE</
CODE>, <
CODE>DNS_R_SYNTAX</
CODE>,
<
CODE>DNS_R_NOSPACE</
CODE> and <
CODE>DNS_R_SUCCESS</
CODE>.
<
H2><
A NAME="macros">Support Macros<
A></
H2>
The following macro is available:
<
DT><
CODE>RETERR(x)</
CODE><
DT>
Evaluate <
CODE>x</
CODE> and call <
CODE>return (<
I><value of x></
I>);</
CODE> if the result is not <
CODE>DNS_R_SUCCESS</
CODE>.