<?xml version="1.0" encoding="US-ASCII"?>
<!-- $Id: respsize.xml,v 1.8 2008/07/14 20:39:32 kato Exp $  -->
<!DOCTYPE rfc SYSTEM "rfc2629.dtd">
<?xml-stylesheet type='text/xsl' href='rfc2629.xslt' ?>
<!-- used by XSLT processors -->
<?rfc strict="yes" ?>
<?rfc toc="no"?>
<?rfc tocdepth="4"?>
<?rfc symrefs="yes"?>
<?rfc sortrefs="yes" ?>
<?rfc compact="yes" ?>
<?rfc subcompact="yes" ?>
<rfc category="info" docName="draft-ietf-dnsop-respsize-11" ipr="full3978">
  <!-- category values: std, bcp, info, exp, and historic
     ipr values: full3667, noModification3667, noDerivatives3667
     you can add the attributes updates="NNNN" and obsoletes="NNNN" 
     they will automatically be output with "(if approved)" -->

  <!-- ***** FRONT MATTER ***** -->

  <front>

    <title abbrev="DNS Referral Response Size">
	DNS Referral Response Size Issues
    </title>

    <!-- add 'role="editor"' below for the editors if appropriate -->

    <!-- Another author who claims to be an editor -->

    <author fullname="Paul Vixie" initials="P" role="" surname="Vixie">
      <organization>Internet Systems Consortium</organization>
      <address>
        <postal>
          <street>950 Charter Street</street>
          <city>Redwood City</city>
          <region>CA</region>
          <code>94063</code>
          <country>US</country>
        </postal>
        <phone>+1 650 423 1300</phone>
        <email>paul@vix.com</email>
      </address>
    </author>
    <author fullname="Akira Kato" initials="A" role="" surname="Kato">
      <organization>Keio University/WIDE Project</organization>
      <address>
        <postal>
          <street>Graduate School of Media Design, 4-1-1 Hiyoshi</street>
          <city>Kohoku</city>
          <region>Yokohama</region>
          <code>223-8526</code>
          <country>JP</country>
        </postal>
        <phone>+81 3 5418 6419</phone>
        <email>kato@wide.ad.jp</email>
      </address>
    </author>

    <date month="July" year="2008" />

    <!-- If the month and year are both specified and are the current ones, xml2rfc will fill 
         in the current day for you. If only the current year is specified, xml2rfc will fill 
	 in the current day and month for you. If the year is not the current one, it is 
	 necessary to specify at least a month (xml2rfc assumes day="1" if not specified for the 
	 purpose of calculating the expiry date).  With drafts it is normally sufficient to 
	 specify just the year. -->

    <area>General</area>	<!-- XXXX -->

    <workgroup>Internet Engineering Task Force</workgroup>

    <keyword>DNS</keyword>
    <keyword>delegation</keyword>
    <keyword>packet size</keyword>
    <keyword>AAAA record</keyword>

    <!-- Keywords will be incorporated into HTML output
         files in a meta tag but they have no effect on text or nroff
         output. If you submit your draft to the RFC Editor, the
         keywords will be used for the search engine. -->

    <abstract>
      <t>
	With a mandated default minimum maximum UDP message size of
	512 octets, the DNS protocol presents some special problems
	for zones wishing to expose a moderate or high number of
	authority servers (NS RRs).
	This document explains the operational issues caused by,
	or related to this response size limit, and suggests ways
	to optimize the use of this limited space.
	Guidance is offered to DNS server implementors and to DNS zone
	operators.
      </t>
    </abstract>
  </front>

  <middle>

<!-- ##################################################################### -->

    <section title="Introduction and Overview">	<!-- 1 -->
<!--      <section title="Introduction and Overview">  -->
	<t>	<!-- 1.1 -->
	The DNS standard limits UDP message size to 512 octets
	(see <xref target="RFC1035"/> 4.2.1).
	Even though this limitation was due to the required minimum
	IP reassembly limit for IPv4, it became a hard DNS protocol limit
	and is not implicitly relaxed by changes in a network layer protocol,
	for example to IPv6.
	</t>
	<t>	<!-- 1.2 -->
	The EDNS (Extension Mechanisms for DNS) protocol extension starting
	with version 0 permits larger
	responses by mutual agreement of the requester and responder
	(see <xref target="RFC2671"/> 2.3, 4.5),
	and it is recommended to support EDNS.
	The 512 octets UDP message size limit will remain in
	practical effect until virtually all DNS servers and resolvers
	support EDNS.
	</t>
	<t>	<!-- 1.3 -->
	Since DNS responses include a copy of the request, the space
	available for response data is somewhat less than the full 512 octets.
	Negative responses are quite small, but for positive and referral
	responses, every octet must be carefully and sparingly allocated.
	While the response size of positive responses is 
	also a concern in <xref target="RFC3226"/>,
	this document specifically addresses referral response size.
	</t>
<!--      </section> -->

<!--
      <section title="Requirements Language">
        <t>
	The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
        "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
        document are to be interpreted as described in
	<xref target="RFC2119">RFC 2119</xref>.
	</t>
      </section>
-->
    </section>

<!-- ##################################################################### -->

    <section anchor="delegation_details" title="Delegation Details">
      <section anchor="aa" title="Relevant Protocol Elements">	<!-- 2.1 -->
      <t>	<!-- 2.1.1 -->
	A positive delegation response will include the following elements:
	<vspace blankLines="1" />
	<list hangIndent="4" style="empty">
          <t>Header Section: fixed length (12 octets)</t>
	  <t>Question Section: original query (name, class, type)</t>
	  <t>Answer Section: empty, or a CNAME/DNAME chain</t>
	  <t>Authority Section: NS RRset (nameserver names)</t>
	  <t>Additional Section: A and AAAA RRsets (nameserver addresses)</t>
	</list>
	Note: CNAME defines a canonical name (<xref target="RFC1034"/>)
	while DNAME maps an entire subtree to another domain
	(<xref target="RFC2672"/>).
      </t>
      <t>	<!-- 2.1.2 -->
	If the total size of the UDP response exceeds 512 octets or
	the size advertised in EDNS, 
	and if the data that does not fit was "required",
	then the TC bit will be set (indicating truncation).
	This will usually cause the requester to retry using TCP,
	depending on what information was desired and what information
	was omitted.
	For example, truncation in the authority section is of no interest
	to a stub resolver who only plans to consume the answer section.
	If a retry using TCP is needed, the total cost of the transaction
	is much higher.
	See <xref target="RFC1123"/> 6.1.3.2 for details on the requirement
	that UDP be attempted before falling back to TCP.
      </t>
      <t>	<!-- 2.1.3 -->
	RRsets (Resource Record Set, see <xref target="RFC2136"/>)
	are never sent partially unless the TC bit is set to
	indicate truncation.
	When the TC bit is set, the final apparent RRset in the final
	non-empty section must be considered "possibly damaged"
	(see <xref target="RFC1035"/> 6.2, <xref target="RFC2181"/> 9).
      </t>
      <t>	<!-- 2.1.4 -->
	With or without truncation, the glue present in the additional
	data section should be considered "possibly incomplete",
	and requesters should be prepared to re-query for any damaged
	or missing RRsets.
	Note that truncation of the additional data section might not be
	signaled via the TC bit since additional data is often optional
	(see discussion in <xref target="RFC4472"/> B).
      </t>
      <t>	<!-- 2.1.5 -->
	DNS label compression allows the component labels of a domain
	name to be instantiated exactly once per DNS message, and then
	referenced with a two-octet "pointer" from other locations in
	that same DNS message
	(see <xref target="RFC1035"/> 4.1.4).
	If all nameserver names in a message share a common parent
	(for example, all of them are in "ROOT-SERVERS.NET." zone),
	then more space will be available for incompressible data
	(such as nameserver addresses).
      </t>
      <t>	<!-- 2.1.6 -->
	The query name can be as long as 255 octets of network data.
	In this worst case scenario, the question section will be 259
	octets in size, which would leave only 240 octets for the
	authority and additional sections
	(after deducting 12 octets for the fixed length header)
	in a referral.
      </t>
      </section>

      <section title="Advice to Zone Owners">
      <t>	<!-- 2.2.1 -->
	Average and maximum question section sizes can be predicted by
	the zone owner, since they will know what names actually exist
	and can measure which ones are queried for most often.
	Note that if the zone contains any wildcards, it is possible
	for maximum length queries to require positive responses,
	but that it is reasonable to expect truncation and TCP retry
	in that case.
	For cost and performance reasons, the majority of requests
	should be satisfied without truncation or TCP retry.
      </t>
      <t>	<!-- 2.2.2 -->
	Some queries to non-existing names can be large, but this is not
	a problem because negative responses need not contain any answer,
	authority or additional records.
	See <xref target="RFC2308"/> 2.1 for more information about
	the format of negative responses.
      </t>
      <t>	<!-- 2.2.3 -->
	The minimum useful number of name servers is two, for redundancy
	(see <xref target="RFC1034"/> 4.1).
	A zone's name servers should be reachable by all IP protocols
	versions (e.g., IPv4 and IPv6) in common use.
	As long as the servers are well managed, the server serving IPv6
	might be different from the server serving IPv4 sharing the same
	server name.
      </t>
      <t>	<!-- 2.2.4 -->
	The best case is no truncation at all.
	This is because many requesters will retry using TCP immediately,
	or will automatically requery for RRsets that are possibly truncated,
	without considering whether the omitted data was actually necessary.
      </t>
      <t>	<!-- 2.2.5 -->
	Anycasting <xref target="RFC3258"/> is a useful tool for performance
	and reliability without increasing the size of referral responses.
      </t>
      <t>	<!-- 2.2.6 -->
	While it is irrelevant to the response size issue, all zones have
	to be served via IPv4 as well to avoid
	name space fragmentation <xref target="RFC3901"/>.
      </t>
      </section>

      <section title="Advice to Server Implementors">	<!-- 2.3 -->
      <t>	<!-- 2.3.1 -->
	Each NS RR for a zone will add 12 fixed octets
	(name, type, class, ttl, and rdlen) plus 2 to 255 variable octets
	(for the NSDNAME).
	Each A RR will require 16 octets, and each AAAA RR will require 28
	octets.
      </t>
      <t>	<!-- 2.3.2 -->
	While DNS distinguishes between necessary and optional resource
	records, this distinction is according to protocol elements
	necessary to signify facts, and takes no official notice of
	protocol content necessary to ensure correct operation.
	For example, a nameserver name that is in or below the zone cut
	being described by a delegation is "necessary content",
	since there is no way to reach that zone unless the parent zone's
	delegation includes "glue records" describing
	that name server's addresses.
      </t>
      <t>	<!-- 2.3.3 -->
	Recall that the TC bit is only set when a required RRset can
	not be included in its entirety
	(see <xref target="RFC2181"/> 9).
	Even when some of the RRsets to be included in the additional
	section don't fit in the response size, the TC bit isn't set.
	These RRsets may be important for a referral.
	Some DNS implementations try to resolve these missing glue
	records separately which will introduce extra queries and
	extra time to resolve a given name.
      </t>
      <t>	<!-- 2.3.4 -->
	A delegation response should prioritize glue records as follows.
	<vspace blankLines="1" />
	first:
	<list hangIndent="4" style="empty">
	  <t>
	  All glue RRsets for one name server whose name is in or below the
	  zone being delegated, or which has multiple address RRsets
	  (currently A and AAAA), or preferably both;
	  </t>
	</list>
	second:
	<list hangIndent="4" style="empty">
	  <t>
	  Alternate between adding all glue RRsets for any name servers whose
	  names are in or below the zone being delegated, and all glue RRsets
	  for any name servers who have multiple address RRsets
	  (currently A and AAAA);
	  </t>
	</list>
	thence:
	<list hangIndent="4" style="empty">
	  <t>
	  All other glue RRsets, in any order.
	  </t>
	</list>
	<vspace blankLines="1" />
	Whenever there are multiple candidates for a position in this priority
	scheme, one should be chosen on a round-robin or fully random basis.

	The goal of this priority scheme is to offer "necessary" glue first
	to fill into the response if possible.
<!--	,avoiding silent truncation for this glue if possible. -->
      </t>
      <t>	<!-- 2.3.5 -->
	If any "necessary" content cannot be fit in the response,
	then it is
	advisable that the TC bit be set in order to force a TCP retry,
	rather than have the zone be unreachable.
	Note that a parent server's proper response to a query for
	in-child glue or below-child glue is a referral rather
	than an answer, and that this referral must be able to
	contain the in-child or below-child glue,
	and that in outlying cases, only EDNS or TCP will be large enough
	to contain that data.
      </t>
      <t>
	The glue record order should be independent to the version of
	IP used in the query because the DNS server just see a query
	from an intermediate server rather than the query from the
	original client.
      </t>
      </section>
    </section>

<!-- ##################################################################### -->

    <section title="Analysis">	<!-- 3 -->
    <t>		<!-- 3.1 -->
	An instrumented protocol trace of a best case delegation response
	is shown in <xref target="sample_response"/>.
	Note that 13 servers are named, and 13 addresses are given.
	This query was artificially designed to exactly reach
	the 512 octets limit.
    </t>

   <figure align="center" anchor="sample_response">
     <preamble></preamble>
     <artwork align="left"><![CDATA[
   ;; flags: qr rd; QUERY: 1, ANS: 0, AUTH: 13, ADDIT: 13
   ;; QUERY SECTION:
   ;;  [23456789.123456789.123456789.\
        123456789.123456789.123456789.com A IN]        ;; @80

   ;; AUTHORITY SECTION:
   com.                 86400 NS  E.GTLD-SERVERS.NET.  ;; @112
   com.                 86400 NS  F.GTLD-SERVERS.NET.  ;; @128
   com.                 86400 NS  G.GTLD-SERVERS.NET.  ;; @144
   com.                 86400 NS  H.GTLD-SERVERS.NET.  ;; @160
   com.                 86400 NS  I.GTLD-SERVERS.NET.  ;; @176
   com.                 86400 NS  J.GTLD-SERVERS.NET.  ;; @192
   com.                 86400 NS  K.GTLD-SERVERS.NET.  ;; @208
   com.                 86400 NS  L.GTLD-SERVERS.NET.  ;; @224
   com.                 86400 NS  M.GTLD-SERVERS.NET.  ;; @240
   com.                 86400 NS  A.GTLD-SERVERS.NET.  ;; @256
   com.                 86400 NS  B.GTLD-SERVERS.NET.  ;; @272
   com.                 86400 NS  C.GTLD-SERVERS.NET.  ;; @288
   com.                 86400 NS  D.GTLD-SERVERS.NET.  ;; @304


   ;; ADDITIONAL SECTION:
   A.GTLD-SERVERS.NET.  86400 A   192.5.6.30           ;; @320
   B.GTLD-SERVERS.NET.  86400 A   192.33.14.30         ;; @336
   C.GTLD-SERVERS.NET.  86400 A   192.26.92.30         ;; @352
   D.GTLD-SERVERS.NET.  86400 A   192.31.80.30         ;; @368
   E.GTLD-SERVERS.NET.  86400 A   192.12.94.30         ;; @384
   F.GTLD-SERVERS.NET.  86400 A   192.35.51.30         ;; @400
   G.GTLD-SERVERS.NET.  86400 A   192.42.93.30         ;; @416
   H.GTLD-SERVERS.NET.  86400 A   192.54.112.30        ;; @432
   I.GTLD-SERVERS.NET.  86400 A   192.43.172.30        ;; @448
   J.GTLD-SERVERS.NET.  86400 A   192.48.79.30         ;; @464
   K.GTLD-SERVERS.NET.  86400 A   192.52.178.30        ;; @480
   L.GTLD-SERVERS.NET.  86400 A   192.41.162.30        ;; @496
   M.GTLD-SERVERS.NET.  86400 A   192.55.83.30         ;; @512

   ;; MSG SIZE  sent: 80  rcvd: 512

      ]]></artwork>
        <postamble></postamble>
      </figure>
      <t>
	For longer query names, the number of address records supplied will
	be lower.
	Furthermore, it is only by using a common parent name
	(which is "GTLD-SERVERS.NET." in this example)
	that all 13 addresses are able to fit, due to the use of
	DNS compression pointers in the last 12 occurrences of
	the parent domain name.
	The outputs from the response simulator
	in <xref target="code"/> (written in perl <xref target="PERL"/>)
	shown in
	<xref target="resp_analysis1"/> and
	<xref target="resp_analysis2"/> 
	demonstrate these properties.
      </t>
      <figure align="center" anchor="resp_analysis1">
        <preamble></preamble>
        <artwork align="left"><![CDATA[
   % perl respsize.pl a.dns.br b.dns.br c.dns.br d.dns.br
   a.dns.br requires 10 bytes
   b.dns.br requires 4 bytes
   c.dns.br requires 4 bytes
   d.dns.br requires 4 bytes
   # of NS: 4
   For maximum size query (255 byte):
       only A is considered:        # of A is 4 (green)
       A and AAAA are considered:   # of A+AAAA is 3 (yellow)
       preferred-glue A is assumed: # of A is 4, # of AAAA is 3 (yellow)
   For average size query (64 byte):
       only A is considered:        # of A is 4 (green)
       A and AAAA are considered:   # of A+AAAA is 4 (green)
       preferred-glue A is assumed: # of A is 4, # of AAAA is 4 (green)

       ]]></artwork>
       <postamble></postamble>
      </figure>

      <figure align="center" anchor="resp_analysis2">
        <preamble></preamble>
        <artwork align="left"><![CDATA[
   % perl respsize.pl ns-ext.isc.org ns.psg.com ns.ripe.net ns.eu.int
   ns-ext.isc.org requires 16 bytes
   ns.psg.com requires 12 bytes
   ns.ripe.net requires 13 bytes
   ns.eu.int requires 11 bytes
   # of NS: 4
   For maximum size query (255 byte):
       only A is considered:        # of A is 4 (green)
       A and AAAA are considered:   # of A+AAAA is 3 (yellow)
       preferred-glue A is assumed: # of A is 4, # of AAAA is 2 (yellow)
   For average size query (64 byte):
       only A is considered:        # of A is 4 (green)
       A and AAAA are considered:   # of A+AAAA is 4 (green)
       preferred-glue A is assumed: # of A is 4, # of AAAA is 4 (green)
       ]]></artwork>
        <postamble></postamble>
      </figure>
      <t>
	Here we use the term "green" if all address records could fit, or
	"yellow" if two or more could fit, or "orange" if only one could fit,
	or "red" if no address record could fit.
	It's clear that without a common parent for nameserver names,
	much space would be lost.
	For these examples we use an average/common name size of 15 octets,
	befitting our assumption of "GTLD-SERVERS.NET."
	as our common parent name.
      </t>
      <t>
	We're assuming a medium query name size of 64 since that is
	the typical size seen in trace data at the time of this writing.
	If Internationalized Domain Name (IDN) or any other technology which
	results in larger query names be deployed significantly
	in advance of EDNS, then new measurements and new estimates
	will have to be made.
      </t>
    </section>

<!-- ##################################################################### -->

    <section title="Conclusions">

      <t>
	The current practice of giving all nameserver names a common parent
	(such as "GTLD-SERVERS.NET." or "ROOT-SERVERS.NET.") saves space in DNS
	responses and allows for more nameservers to be enumerated than would
	otherwise be possible, since the common parent domain name only appears
	once in a DNS message and is referred to via "compression pointers"
	thereafter.
      </t>
      <t>
	If all nameserver names for a zone share a common parent,
	then it is operationally advisable to make all servers for
	the zone thus served also be authoritative for the zone of
	that common parent.
	For example, the root name servers (?.ROOT-SERVERS.NET.) can answer
	authoritatively for the ROOT-SERVERS.NET. zone.
	This is to ensure that the zone's servers always have the zone's
	nameservers' glue available when delegating, and will be able
	to respond with answers rather than referrals if a requester
	who wants that glue comes back asking for it.
	In this case the name server will likely be a
	"stealth server" -- authoritative but unadvertised in the glue
	zone's NS RRset.
	See <xref target="RFC1996"/> 2 for more
	information about stealth servers.
      </t>
      <t>
	Thirteen (13) is the effective maximum number of nameserver names
	usable with traditional (non-extended) DNS,
	assuming a common parent domain name, and given that implicit
	referral response truncation is undesirable in the average case.
      </t>
      <t>
	More than one address record in a protocol family per server is
	inadvisable since the necessary glue RRsets (A or AAAA) are
	atomically indivisible, and will be larger than a single
	resource record.
	Larger RRsets are more likely to lead to or encounter truncation.
      </t>
      <t>
	More than one address record across protocol families is less
	likely to lead to or encounter truncation, partly because
	multiprotocol clients, which are required to handle larger
	RRsets such as AAAA RRs, are more likely to speak EDNS
	which can use a larger UDP response size limit,
	and partly because the resource records (A and AAAA) are in
	different RRsets and are therefore divisible from each other.
      </t>
      <t>
	Name server names which are at or below the zone they serve are
	more sensitive to referral response truncation,
	and glue records for them should be considered "more important"
	than other glue records, in the assembly of referral responses.
      </t>
<!--
      <t>
	If a zone is served by thirteen (13) name servers having a common
	parent name (such as ?.ROOT-SERVERS.NET.) and each such name server
	has a single address record in some protocol family
	(e.g., an A RR), then all thirteen name servers or any subset
	thereof could have address records in a second protocol family
	by adding a second address record (e.g., an AAAA RR)
	without reducing the reachability of the zone thus served.
      </t>
-->

     </section>

<!-- ##################################################################### -->

    <section title="Security Considerations">
      <t>
	The recommendations contained in this document have
	no known security implications.
      </t>
    </section>

<!-- ##################################################################### -->

    <section title="IANA Considerations">
      <t>
	This document does not call for changes or additions to any IANA
	registry.
      </t>
    </section>

<!-- ##################################################################### -->

    <section title="Acknowledgement">
      <t>
	The authors thank Peter Koch, Rob Austein, Joe Abley, Mark Andrews,
	Kenji Rikitake, Stephane Bortzmeyer, Olafur Gudmundsson,
	Alfred Hoenes, and Alexander Mayrhofer
	for their valuable comments and suggestions.
      </t>
      <t>
	This work was supported by the US National Science Foundation
	(research grant SCI-0427144) and DNS-OARC.
      </t>
    </section>

<!-- ##################################################################### -->
    <!-- This PI places the pagebreak correctly (before the section title) in the text output. -->

<!--    <?rfc needLines="8" ?> -->

  </middle>

  <!--  *****BACK MATTER ***** -->

  <back>
    <!-- References split into informative and normative -->

    <references title="Normative References">	<!-- XXXX -->
      <?rfc include="reference.RFC.1034.xml"?>
      <?rfc include="reference.RFC.1035.xml"?>
      <?rfc include="reference.RFC.2181.xml"?>

    </references>

    <references title="Informative References">
      <!-- Here we use entities that we defined at the beginning. -->

<!--      <?rfc include="reference.RFC.3552.xml"?> -->

      <?rfc include="reference.RFC.1123.xml"?>
      <?rfc include="reference.RFC.1996.xml"?>
<!--  <?rfc include="reference.RFC.2119.xml"?> -->
      <?rfc include="reference.RFC.2136.xml"?>
      <?rfc include="reference.RFC.2308.xml"?>
      <?rfc include="reference.RFC.2671.xml"?>
      <?rfc include="reference.RFC.2672.xml"?>
      <?rfc include="reference.RFC.3226.xml"?>
      <?rfc include="reference.RFC.3258.xml"?>
      <?rfc include="reference.RFC.3901.xml"?>
      <?rfc include="reference.RFC.4472.xml"?>

      <reference anchor="PERL">
        <front>
          <title>Programming Perl, 3rd ed.</title>
          <author initials="L" surname="Wall">
            <organization></organization>
          </author>
          <author initials="T" surname="Christiansen">
            <organization></organization>
          </author>
          <author initials="J" surname="Orwant">
            <organization></organization>
          </author>
          <date month="July" year="2000" />
        </front>
        <seriesInfo name='ISBN' value='0-596-00027-8' />
      </reference>
   </references>

<!-- ##################################################################### -->

    <section anchor="code" title="The response simulator program">
      <figure>
        <preamble></preamble>

        <artwork><![CDATA[
#!/usr/bin/perl
#
# SYNOPSIS
#    respsize.pl [ -z zone ] fqdn_ns1 fqdn_ns2 ...
#        if all queries are assumed to have a same zone suffix,
#     such as "jp" in JP TLD servers, specify it in -z option
#
use strict;
use Getopt::Std;

my ($sz_msg) = (512);
my ($sz_header, $sz_ptr, $sz_rr_a, $sz_rr_aaaa) = (12, 2, 16, 28);
my ($sz_type, $sz_class, $sz_ttl, $sz_rdlen) = (2, 2, 4, 2);
my (%namedb, $name, $nssect, %opts, $optz);
my $n_ns = 0;

getopt('z', %opts);
if (defined($opts{'z'})) {
    server_name_len($opts{'z'}); # just register it
}

foreach $name (@ARGV) {
    my $len;
    $n_ns++;
    $len = server_name_len($name);
    print "$name requires $len bytes\n";
    $nssect += $sz_ptr + $sz_type + $sz_class + $sz_ttl
            +  $sz_rdlen + $len;
}
print "# of NS: $n_ns\n";
arsect(255, $nssect, $n_ns, "maximum");
arsect(64, $nssect, $n_ns, "average");

sub server_name_len {
    my ($name) = @_;
    my (@labels, $len, $n, $suffix);

    $name =~ tr/A-Z/a-z/;
    @labels = split(/\./, $name);
    $len = length(join('.', @labels)) + 2;
    for ($n = 0; $#labels >= 0; $n++, shift @labels) {
        $suffix = join('.', @labels);
        return length($name) - length($suffix) + $sz_ptr
            if (defined($namedb{$suffix}));
        $namedb{$suffix} = 1;
    }
    return $len;
}

sub arsect {
    my ($sz_query, $nssect, $n_ns, $cond) = @_;
    my ($space, $n_a, $n_a_aaaa, $n_p_aaaa, $ansect);
    $ansect = $sz_query + $sz_type + $sz_class;
    $space = $sz_msg - $sz_header - $ansect - $nssect;
    $n_a = atmost(int($space / $sz_rr_a), $n_ns);
    $n_a_aaaa = atmost(int($space
                           / ($sz_rr_a + $sz_rr_aaaa)), $n_ns);
    $n_p_aaaa = atmost(int(($space - $sz_rr_a * $n_ns)
                           / $sz_rr_aaaa), $n_ns);
    printf "For %s size query (%d byte):\n", $cond, $sz_query;
    printf "    only A is considered:        ";
    printf "# of A is %d (%s)\n", $n_a, &judge($n_a, $n_ns);
    printf "    A and AAAA are considered:   ";
    printf "# of A+AAAA is %d (%s)\n",
           $n_a_aaaa, &judge($n_a_aaaa, $n_ns);
    printf "    preferred-glue A is assumed: ";
    printf "# of A is %d, # of AAAA is %d (%s)\n",
        $n_a, $n_p_aaaa, &judge($n_p_aaaa, $n_ns);
}

sub judge {
    my ($n, $n_ns) = @_;
    return "green" if ($n >= $n_ns);
    return "yellow" if ($n >= 2);
    return "orange" if ($n == 1);
    return "red";
}

sub atmost {
    my ($a, $b) = @_;
    return 0 if ($a < 0);
    return $b if ($a > $b);
    return $a;
}
            ]]></artwork>
      </figure>
    </section>

  </back>
</rfc>
