+ /**
+ * This routine normalizes a response. This includes removing "irrelevant"
+ * records from the answer and additional sections and (re)synthesizing
+ * CNAMEs from DNAMEs, if present.
+ *
+ * @param response
+ */
+ private SMessage normalize(SMessage m) {
+ if (m == null) return m;
+
+ if (m.getRcode() != Rcode.NOERROR && m.getRcode() != Rcode.NXDOMAIN) {
+ return m;
+ }
+
+ Name qname = m.getQuestion().getName();
+ int qtype = m.getQuestion().getType();
+
+ Name sname = qname;
+
+ // For the ANSWER section, remove all "irrelevant" records and add
+ // synthesized CNAMEs from DNAMEs
+ // This will strip out-of-order CNAMEs as well.
+ List<SRRset> rrset_list = m.getSectionList(Section.ANSWER);
+ Set<Name> additional_names = new HashSet<Name>();
+
+ for (ListIterator<SRRset> i = rrset_list.listIterator(); i.hasNext();) {
+ SRRset rrset = i.next();
+ int type = rrset.getType();
+ Name n = rrset.getName();
+
+ // Handle DNAME synthesis; DNAME synthesis does not occur at the
+ // DNAME name itself.
+ if (type == Type.DNAME && ValUtils.strictSubdomain(sname, n)) {
+ if (rrset.size() > 1) {
+ // log.debug("Found DNAME rrset with size > 1: " + rrset);
+ // return Util.errorMessage(m, Rcode.SERVFAIL);
+ return null; // FIXME
+ }
+ DNAMERecord dname = (DNAMERecord) rrset.first();
+ try {
+ Name cname_alias = sname.fromDNAME(dname);
+ // Note that synthesized CNAMEs should have a TTL of zero.
+
+ CNAMERecord cname = new CNAMERecord(sname,
+ dname.getDClass(), 0, cname_alias);
+ SRRset cname_rrset = new SRRset();
+ cname_rrset.addRR(cname);
+ i.add(cname_rrset);
+
+ sname = cname_alias;
+ } catch (NameTooLongException e) {
+// log.debug("not adding synthesized CNAME -- "
+// + "generated name is too long", e);
+ }
+ continue;
+ }
+
+ // The only records in the ANSWER section not allowed to
+ if (!n.equals(sname)) {
+// log.debug("normalize: removing irrelevant rrset: " + rrset);
+ i.remove();
+ continue;
+ }
+
+ // Follow the CNAME chain.
+ if (type == Type.CNAME) {
+ if (rrset.size() > 1) {
+// log.debug("Found CNAME rrset with size > 1: " + rrset);
+// return Util.errorMessage(m, Rcode.SERVFAIL);
+ return null; // FIXME
+ }
+
+ CNAMERecord cname = (CNAMERecord) rrset.first();
+ sname = cname.getAlias();
+ continue;
+ }
+
+ // Otherwise, make sure that the RRset matches the qtype.
+ if (qtype != Type.ANY && qtype != type) {
+// log.debug("normalize: removing irrelevant rrset: " + rrset);
+ i.remove();
+ }
+
+ // Otherwise, fetch the additional names from the relevant rrset.
+ rrsetAdditionalNames(additional_names, rrset);
+ }
+
+ // Get additional names from AUTHORITY
+ rrset_list = m.getSectionList(Section.AUTHORITY);
+ for (SRRset rrset : rrset_list) {
+ rrsetAdditionalNames(additional_names, rrset);
+ }
+
+ // For each record in the additional section, remove it if it is an
+ // address record and not in the collection of additional names found in
+ // ANSWER and AUTHORITY.
+ rrset_list = m.getSectionList(Section.ADDITIONAL);
+ for (Iterator<SRRset> i = rrset_list.iterator(); i.hasNext();) {
+ SRRset rrset = i.next();
+ int type = rrset.getType();
+ if ((type == Type.A || type == Type.AAAA)
+ && !additional_names.contains(rrset.getName())) {
+ i.remove();
+ }
+ // FIXME: what about other types?
+ }
+
+ return m;
+ }
+
+ /**
+ * Extract additional names from the records in an rrset.
+ *
+ * @param additional_names
+ * The set to add the additional names to, if any.
+ * @param rrset
+ * The rrset to extract from.
+ */
+ private void rrsetAdditionalNames(Set<Name> additional_names, SRRset rrset) {
+ if (rrset == null) return;
+
+ for (Iterator<Record> i = rrset.rrs(); i.hasNext();) {
+ Record r = i.next();
+ Name add_name = r.getAdditionalName();
+ if (add_name != null) {
+ additional_names.add(add_name);
+ }
+ }
+ }
+