Here at Nominet, we have two interfaces to the registry database:
- The automaton, an email system which uses PGP signed lists of key-value pairs (KVPs), e.g:
operation: domain request
key: auto-example.co.uk
account-id: 1000001
dns0: ns0.auto-example.co.uk 96.152.146.245
dns1: ns1.auto-example.co.uk 96.152.146.246 1234::1234
- and EPP, an XML based system. The above request would essentially be written in XML as:
<epp>
<create>
<domain:create>
<domain:name>auto-example.co.uk</domain:name>
<domain:account>
<domain:account-id>1000001</domain:account-id>
</domain:account>
<nameservers>
<ns:create>
<ns:name>ns0.auto-example.co.uk</ns:name>
<ns:addr ip="v4">96.152.146.245</ns:addr>
</ns:create>
<ns:create>
<ns:name>ns0.auto-example.co.uk</ns:name>
<ns:addr ip="v4">96.152.146.246</ns:addr>
<ns:addr ip="v6">1234::1234</ns:addr>
</ns:create>
</nameservers>
</domain:create>
</create>
</epp>
These are two separate processes but basically do the same thing - they write to a database but with a different interface, so we were keen to use as much common code as possible in the system. One way to facilitate this is to use an XSL transformation (using hardware XML acceleration for performance) to convert the XML into KVPs.
Generally, the XSL for this looks like:
<xsl:template match="xml-element-name">key-name: <xsl:apply-templates/>
<xsl:text>
</xsl:text>
</xsl:template>
This tells the XSL processor “whenever you see xml-element-name write out the corresponding key-name and then process the rest of the element”. For leaf elements, processing the rest of the element will get you the XML value added to the output. The 
 adds a linebreak.
So, for example:
<xsl:template match="domain:name">key: <xsl:apply-templates/>
<xsl:text>
</xsl:text>
</xsl:template>
converts the
<domain:name>auto-example.co.uk</domain:name>
line in the EPP request into a corresponding KVP:
key:auto-example.co.uk
This is fairly straightforward for most fields in the requests. Dealing with nameserver fields is however a little more complicated as the KVPs need to be enumerated and the name and ip addresses need to be included in the same value. To do this, the XSL needs to output the ‘dns#: ‘ part when it encounters an <ns:create> element and then add the name and ip addresses when it encounters the child elements.
To find where the entry sits in the dns enumeration, it is necessary to use XPath within an <xsl:value-of> element to count the position of the <ns:create> amongst its sibling elements:
<xsl:template match="ns:create">dns<xsl:value-of select="position()-1"/>
<xsl:text>: </xsl:text>
<xsl:apply-templates/>
</xsl:template>
This works ok for nameservers until you consider that there is no restriction on the order of the ip addresses in the XML. The order of different elements in EPP requests is specified by the schema, but <ns:addr> elements are distinguished only by their attributes and there are no restrictions on the order. So, it is possible for an ipv6 address to be provided before the ipv4 address and the resultant KVP in the above example:
dns0: ns0.auto-example.co.uk 1234::1234 96.152.146.246
would confuse the KVP processing code. To get around this, we need to tell the XSL processor to go and find the ip addresses in the correct order when it finds the name. Again, we need to use XPath:
<xsl:template match="ns:name">
<!-- Add the name-->
<xsl:apply-templates/>
<!-- Go find the ip addresses-->
<xsl:if test=
"count(following-sibling::*[local-name()='addr' and @ip='v4']) > 0">
<xsl:text> </xsl:text>
<xsl:value-of select=
"following-sibling::*[local-name()='addr' and @ip='v4']"/>
</xsl:if>
<xsl:if test=
"count(following-sibling::*[local-name()='addr' and @ip='v6']) > 0">
<xsl:text> </xsl:text>
<xsl:value-of select=
"following-sibling::*[local-name()='addr' and @ip='v6']"/>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:template>
We don’t need to worry about the key for the KVP here as this has already been done at the <ns:create> element.
So, to find the ipv4 address, we count the number of following siblings with name *:addr and ip attribute of ‘v4‘. If there is one then add it’s value to the KVP.