+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
-<!--
- - Copyright (C) 1999-2001, 2004, 2007, 2016 Internet Systems Consortium, Inc. ("ISC")
- -
- - This Source Code Form is subject to the terms of the Mozilla Public
- - License, v. 2.0. If a copy of the MPL was not distributed with this
- - file, You can obtain one at http://mozilla.org/MPL/2.0/.
--->
-
-<!-- $Id: rdata.html,v 1.15 2007/06/19 23:47:13 tbox Exp $ -->
-
-<HTML>
-<HEAD>
- <TITLE>Adding new RDATA type</TITLE>
-</HEAD>
-<BODY>
-<H2>Overview</H2>
-The dns rdata routines (<CODE>dns_rdata_fromtext()</CODE>,
-<CODE>dns_rdata_totext()</CODE>, <CODE>dns_rdata_fromwire()</CODE>,
-<CODE>dns_rdata_towire()</CODE> <CODE>dns_rdata_fromstruct()</CODE>,
-<CODE>dns_rdata_tostruct()</CODE> and <CODE>dns_rdata_compare()</CODE>)
-are designed to provide a single set of routines
-for encoding, decoding and comparing dns data preventing the problems that
-occurred in BIND 8.x and earlier where there were multiple places in the
-code base that
-decoded wire format to internal format or compared rdata sometimes with
-subtly different behaviour (bugs) or didn't support a particular type leading
-to internal inconsistancy.
-<P>
-Each of these generic routines calls type specific routines that provide
-the type specific details.
-<P>
-From time to time new types are defined and it is necessary to add these types
-into the existing structure.
-This document is written to provide instruction on how to do this.
-<H2>Adding new RDATA types</H2>
-
-Adding a new rdata type requires determining if the new rdata type is class
-specific or generic.
-Writing code to perform the following set of operations
-and then integrating it into the build by placing the code into the rdata
-hierachy at the correct place.
-Running <CODE>make clean</CODE> followed <CODE>make</CODE> in
-<CODE>lib/dns</CODE> will cause the new rdata type to be picked up.
-<P>
-Each rdata module must perform the following operations:
-<DL>
-<DT>Convert from text format to internal format</DT>
-<DT>Convert from internal format to text format</DT>
-<DT>Convert from wire format to internal format</DT>
-<DT>Convert from internal format to wire format</DT>
-<DT>Convert from a structure to internal format</DT>
-<DT>Convert from internal format to a structure</DT>
-<DT>Compare two rdata in internal format<DT>
-</DL>
-<P>
-There is an additional set of support <A HREF="#functions">functions</A> and
-<A HREF="#macros">macros</A> only available to
-to rdata code.
-<H2>RDATA Hierarchy</H2>
-The <CODE>rdata</CODE> hierarchy has the following format.
-<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 chosen 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.
-<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_GLOBAL14);</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_GLOBAL14);</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 a structure.
-<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 the 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>ISC_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 two 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 encoded 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> >= 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.
-<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 <CODE>DNS_R_SUCCESS</CODE>.
-<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>ISC_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><value of x></I>);</CODE> if the result is not <CODE>DNS_R_SUCCESS</CODE>.
-</DL>
-</BODY>
-</HTML>
--- /dev/null
+<!--
+ - Copyright (C) 1999-2001, 2004, 2007, 2016, 2017 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+
+## RDATA Types
+
+### Overview
+
+The dns rdata routines (`dns_rdata_fromtext()`,
+`dns_rdata_totext()`, `dns_rdata_fromwire()`,
+`dns_rdata_towire()` `dns_rdata_fromstruct()`,
+`dns_rdata_tostruct()` and `dns_rdata_compare()`)
+are designed to provide a single set of routines
+for encoding, decoding and comparing dns data preventing the problems that
+occurred in BIND 8.x and earlier, in which there were multiple places in the
+code base that decoded wire format to internal format or compared rdata,
+sometimes with subtly different behaviour (bugs), and sometimes failing to
+support a particular type, leading to internal inconsistancy.
+
+Each of these generic routines calls type-specific routines that provide
+the type-specific details.
+
+From time to time new types are defined and it is necessary to add these types
+into the existing structure. This document is written to provide instruction
+on how to do this.
+
+### Adding new RDATA types
+
+Adding a new rdata type requires determining whether the new rdata type is
+class-specific or generic, writing code to perform the rdata operations for the
+type, then integrating it into the build by placing the code into the rdata
+hierachy at the correct location under `lib/dns/rdata`. Running `make clean`
+followed by `make` in `lib/dns` will cause the new rdata type to be picked up
+and compiled.
+
+Each rdata module must perform the following operations:
+
+* Convert from text format to internal format
+* Convert from internal format to text format
+* Convert from wire format to internal format
+* Convert from internal format to wire format
+* Convert from a structure to internal format
+* Convert from internal format to a structure
+* Compare two rdata in internal format
+
+There is an additional set of support functions and macros only available to
+rdata code.
+
+#### RDATA Hierarchy
+
+The `rdata` hierarchy has the following format.
+
+ rdata/
+ generic/
+ typename_typenumber.h
+ classname_classnumber/
+ typename_typenumber.h
+
+Initial rdata hierarchy:
+
+ 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
+
+#### CLASSNAME and TYPENAME
+
+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.
+
+#### Internal Format
+
+The internal format chosen is DNS wire format without any compression being
+applied to domain names in the rdata.
+
+#### Converting from text format to internal format
+
+The functions to convert from text format has the following call formats and
+is declared as follows for class-generic functions.
+
+ static dns_result_t
+ fromtext_typename(dns_rdataclass_t class, dns_rdatatype_t type,
+ isc_lex_t *lexer, dns_name_t *origin,
+ isc_boolean_t downcase, isc_buffer_t *target);
+
+Class specific functions contain the class name in addition to the
+type name.
+
+ static dns_result_t
+ fromtext_classname_typename(dns_rdataclass_t class,
+ dns_rdatatype_t type,
+ isc_lex_t *lexer,
+ dns_name_t *origin,
+ isc_boolean_t downcase,
+ isc_buffer_t *target);
+
+|Parameter|Description |
+|---------|-----------------------|
+|`class`|This argument should be ignored when used with a class-generic RR type, otherwise `REQUIRE(class == <value>)` should be present at the start of the function.|
+|`type`|This should be tested with a `REQUIRE(type == <value>)` statement at the begining of the function.|
+|`lexer`|This is used to read the input text stream.|
+|`origin`|This is a absolute name used to qualify unqualified / partially qualified domain names in the text stream. It is passed to the name parsing routines.|
+|`downcase`|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 presented in.|
+|`target`|This is a `BINARY` buffer into which to write the internal format of the rdata record being read.|
+
+`fromtext_typename()` reads tokens from `lexer`,
+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.
+
+`gettoken()` should be used to read the next token from the input stream.
+
+`isc_lex_ungettoken()` 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 `dns_rdata_fromtext()` to return `DNS_R_EXTRATOKEN` if
+`fromtext_typename()` was successful.
+
+`fromtext_typename()` reads external input and as such is a high
+security area and must be paranoid about its input.
+
+#### Converting from internal format to text format
+
+ static dns_result_t
+ totext_typename(dns_rdata_t *rdata, dns_name_t *origin,
+ isc_buffer_t *target);
+
+ static dns_result_t
+ totext_classname_typename(dns_rdata_t *rdata,
+ dns_name_t *origin, isc_buffer_t *target);
+
+|Parameter|Description |
+|---------|-----------------------|
+|`rdata`|This is the rdata record to be converted from internal format to text. `rdata->type` (and `rdata->class` for class-specific RR types) should be checked at the start of the function with `REQUIRE` statements.|
+|`origin`|If this is not `NULL`, then any domain names with this suffix should be written out as unqualified subdomains. `name_prefix()` can be used to check whether `origin` is `NULL` and provide the correct arguments to the name conversion routines.|
+|`target`|This is a `TEXT` buffer into which to write the output.|
+
+#### Converting from wire format to internal format
+
+ static dns_result_t
+ fromwire_typename(dns_rdataclass_t class,
+ dns_rdatatype_t type,
+ isc_buffer_t *source,
+ dns_decompress_t *dctx,
+ isc_boolean_t downcase,
+ isc_buffer_t *target);
+
+ static dns_result_t
+ fromwire_classname_typename(dns_rdataclass_t class,
+ dns_rdatatype_t type,
+ isc_buffer_t *source,
+ dns_decompress_t *dctx,
+ isc_boolean_t downcase,
+ isc_buffer_t *target);
+
+`fromwire_classname_typename()` is required to set the valid
+decompression methods if there is a domain name in the rdata.
+
+ if (dns_decompress_edns(dctx) >= # || !dns_decompress_strict(dctx))
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_ALL);
+ else
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+|Parameter|Description |
+|---------|-----------------------|
+|`class`|This argument should be ignored when used with a class-generic RR type otherwise `REQUIRE(class == <value>)` should be present at the start of the function.|
+|`type`|This should be tested with a `REQUIRE(type == <value>)` statement at the begining of the function.|
+|`source`|This is a `BINARY` buffer with the `active` region containing a resource record in wire format.|
+|`dctx`|This is the decompression context and is passed to `dns_name_fromwire()`, along with `downcase`, to enable a compressed domain name to be extracted from the source.|
+|`downcase`|This is passed to `dns_name_fromwire()` to say whether the extracted domain name should be downcased during the extraction.|
+|`target`|This is a `BINARY` buffer into which the decompressed and checked resource record is written.|
+
+`fromwire_typename()` is a security sensitive routine
+as it reads external data, and should take extreme care to ensure that
+the input data matches its description.
+
+If the `active` buffer is not empty at completion and
+`fromwire_typename()` was otherwise successful, `dns_rdata_fromwire()`
+will return `DNS_R_EXTRADATA`.
+
+#### Converting from internal format to wire format
+
+ static dns_result_t
+ towire_typename(dns_rdata_t *rdata,
+ dns_compress_t *cctx,
+ isc_buffer_t *target);
+
+ static dns_result_t
+ towire_classname_typename(dns_rdata_t *rdata,
+ dns_compress_t *cctx,
+ isc_buffer_t *target);
+
+`towire_classname_typename()` is required to set the
+allowed name compression methods based on the EDNS version, if there
+is a domain name in the rdata.
+
+ if (dns_compress_getedns(cctx) >= #)
+ dns_compress_setmethods(cctx, DNS_COMPRESS_ALL);
+ else
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+|Parameter|Description |
+|---------|-----------------------|
+|`rdata`|This is the rdata record to be converted from internal format to text. `rdata->type` (and `rdata->class` for class-specific RR types) should be checked at the start of the function with `REQUIRE` statements.|
+|`cctx`|This is the compression context. It should be passed to `dns_name_towire()` when putting domain names on the wire.|
+|`target`|This is a `BINARY` buffer into which to write the rdata|
+
+Simple RR types without domain names can use the following code to
+transfer the contents of the `rdata` to the target buffer.
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+
+#### Converting from a structure to internal format
+
+ static dns_result_t
+ fromstruct_typename(dns_rdataclass_t class,
+ dns_rdatatype_t type,
+ void *source,
+ isc_buffer_t *target);
+
+ static dns_result_t
+ fromstruct_classname_typename(dns_rdataclass_t class,
+ dns_rdatatype_t type,
+ void *source,
+ isc_buffer_t *target);
+
+|Parameter|Description |
+|---------|-----------------------|
+|`class`|This argument should be ignored when used with a class-generic RR type otherwise `REQUIRE(class == <value>)` should be present at the start of the function.|
+|`type`|This should be tested with a `REQUIRE(type == <value>)` statement at the beginning of the function.|
+|`source`|This points to a type-specific structure.|
+|`target`|This is a `BINARY` buffer into which to write the internal format of the rdata record being read in.|
+
+#### Converting from internal format to a structure
+
+ static dns_result_t
+ tostruct_typename(dns_rdata_t *rdata, void *target);
+
+ static dns_result_t
+ tostruct_classname_typename(dns_rdata_t *rdata, void *target);
+
+|Parameter|Description |
+|---------|-----------------------|
+|`rdata`|This is the rdata record to be converted from internal format to a structure. `rdata->type` (and `rdata->class` for class-specific RR types) should be checked at the start of the function with `REQUIRE` statements.|
+|`target`|Pointer to a type-specific structure.|
+
+#### Comparing two rdata in internal format
+
+ static int
+ compare_typename(dns_rdata_t *rdata1,
+ dns_rdata_t *rdata2);
+
+ static int
+ compare_classname_typename(dns_rdata_t *rdata1,
+ dns_rdata_t *rdata2);
+
+This function compares `rdata1` and `rdata2` as required for DNSSEC
+ordering. The routine should ensure that the `type` and `class` of the
+two rdata match with `REQUIRE(rdata1->type == rdata2->type);` and
+`REQUIRE(rdata1->class == rdata2->class);` statements. The
+`rdata->type` should also be verified, and if the RR type is
+class-specific, also the `rdata->class`.
+
+`compare_classname_typename()` returns -1, 0, 1.
+
+#### Support Functions
+
+The following static support functions are available to use.
+
+ static unsigned int
+ name_length(dns_name_t *name);
+
+Returns the length of `name`.
+
+ static dns_result_t
+ txt_totext(isc_region_t *source, isc_buffer_t *target);
+
+Extracts the octet-length-tagged text string at the start of
+`source` and writes it as a quoted string to `target`.
+`source` is adjusted so that it points to first octet after the
+text string.
+
+Returns `DNS_R_NOSPACE` or `DNS_R_SUCCESS`.
+
+ static dns_result_t
+ txt_fromtext(isc_textregion_t *source, isc_buffer_t *target);
+
+Take the text region `source` and convert it to a length-tagged
+text string, writing it to `target`.
+
+Returns `DNS_R_NOSPACE`, `DNS_R_TEXTTOLONG` or `DNS_R_SUCCESS`.
+
+ static dns_result_t
+ txt_fromwire(isc_buffer_t *source, isc_buffer_t *target);
+
+Read an octet-length-tagged text string from `source` and write it to `target`.
+Ensures that octet-length-tagged text string was wholly within the active area
+of `source`. Adjusts the active area of `source` so that it refers to the
+first octet after the octet-length-tagged text string.
+
+Returns `DNS_R_UNEXPECTEDEND`, `DNS_R_NOSPACE` or `DNS_R_SUCCESS`.
+
+ static isc_boolean_t
+ name_prefix(dns_name_t *name, dns_name_t *origin, dns_name_t *target);
+
+If `origin` is NULL or the root label, set `target` to refer to `name` and
+return `ISC_FALSE`. Otherwise, see if `name` is a subdomain of `origin` and
+not equal to it. If so, make `target` refer to the prefix of `name` and return
+`ISC_TRUE`. Otherwise, make `target` refer to `name` and return `ISC_FALSE`.
+
+Typical use:
+
+ static dns_result_t
+ totext_typename(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, ®ion);
+ dns_name_fromregion(&name, ®ion);
+ sub = name_prefix(&name, origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+ }
+
+static dns_result_t
+str_totext(char *source, isc_buffer_t *target);
+
+Adds the `NULL`-terminated string `source`, up to but not including `NULL`,
+to `target`.
+
+Returns `DNS_R_NOSPACE` and `DNS_R_SUCCESS`.
+
+ static isc_boolean_t
+ buffer_empty(isc_buffer_t *source);
+
+Returns `ISC_TRUE` if the active region of `source` is
+empty otherwise `ISC_FALSE`.
+
+ static void
+ buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region,
+ unsigned int type);
+
+Make `buffer` refer to the memory in `region` and make it active.
+
+ static dns_result_t
+ uint32_tobuffer(isc_uint32_t value, isc_buffer_t *target);
+
+Write the 32 bit `value` in network order to `target`.
+
+Returns `DNS_R_NOSPACE` and `DNS_R_SUCCESS`.
+
+static dns_result_t
+uint16_tobuffer(isc_uint32_t value, isc_buffer_t *target);
+
+Write them 16 bit `value` in network order to `target`.
+
+Returns `ISC_R_RANGE`, `DNS_R_NOSPACE` and `DNS_R_SUCCESS`.
+
+ static isc_uint32_t
+ uint32_fromregion(isc_region_t *region);
+
+Returns the 32 bit at the start of `region` in host byte order.
+
+Requires `(region->length >= 4)`.
+
+ static isc_uint16_t
+ uint16_fromregion(isc_region_t *region);
+
+Returns the 16 bit at the start of `region` in host byte order.
+
+Requires `(region->length >= 2)`.
+
+ static dns_result_t
+ gettoken(isc_lex_t *lexer, isc_token_t *token,
+ isc_tokentype_t expect, isc_boolean_t eol);
+
+Gets the next token from the input stream `lexer`. Ensures that the returned
+token matches `expect` (isc_tokentype_qstring can also return
+isc_tokentype_string), or isc_tokentype_eol and isc_tokentype_eof if `eol` is
+`ISC_TRUE`.
+
+Returns `DNS_R_UNEXPECTED`, `DNS_R_UNEXPECTEDEND`, `DNS_R_UNEXPECTEDTOKEN` and
+`DNS_R_SUCCESS`.
+
+ static dns_result_t
+ mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
+
+Add the memory referred to by `base` to `target`.
+
+Returns `DNS_R_NOSPACE` and `DNS_R_SUCCESS`.
+
+ static int
+ compare_region(isc_region_t *r1, isc_region_t *r2)
+
+Compares two regions, returning -1, 0, 1 based on their DNSSEC ordering.
+
+ static int
+ hexvalue(char value);
+
+Returns the hexadecimal value of `value`, or -1 if not a hexadecimal character.
+
+ static int
+ decvalue(char value);
+
+Returns the decimal value of `value`, or -1 if not a decimal character.
+
+ static dns_result_t
+ base64_totext(isc_region_t *source, isc_buffer_t *target);
+
+Convert the region referred to by `source` to Base64 encoded text and put it
+into `target`.
+
+Returns `DNS_R_NOSPACE` or `DNS_R_SUCCESS`.
+
+ static dns_result_t
+ base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+
+Read a series of tokens from `lexer` that containing base64 data until one of
+end of line, `length` (`length` >= 0) bytes have been read or base64 pad
+characters are seen. If `length` < 0 it is ignored; otherwise, it is an
+error if there are not `length` octets of data or if when processing a
+token, `length` octets would have been exceeded.
+
+Returns `DNS_R_BADBASE64`, `DNS_R_UNEXPECTED`, `DNS_R_UNEXPECTEDEND`,
+`DNS_R_UNEXPECTEDTOKEN` and `DNS_R_SUCCESS`.
+
+ static dns_result_t
+ time_totext(unsigned long value, isc_buffer_t *target);`
+
+Convert the date represented by `value` into YYYYMMDDHHMMSS format
+taking into account the active epochs. This code is Y2K and Y2038 compliant.
+
+Returns `DNS_R_NOSPACE` and `DNS_R_SUCCESS`.
+
+ static dns_result_t
+ time_tobuffer(char *source, isc_buffer_t *target);
+
+Take the date in `source` and convert it to seconds since January 1, 1970
+(ignoring leap seconds) and place the least significant 32 bits into `target`.
+
+Returns `ISC_R_RANGE`, `DNS_R_SYNTAX`, `DNS_R_NOSPACE` and `DNS_R_SUCCESS`.
+
+#### Support Macros
+
+The following macro is available:
+
+`RETERR(x)`
+
+Evaluate `x` and call `return (<value of x>);` if the result is
+not `ISC_R_SUCCESS`.