AnalysisReport analyzeAnswer()

in modules/jndi/src/main/java/org/apache/harmony/jndi/provider/dns/Resolver.java [1072:1379]


    AnalysisReport analyzeAnswer(Message request, Message answer)
            throws DomainProtocolException {
        Enumeration<QuestionRecord> questions = request.getQuestionRecords();
        Enumeration<ResourceRecord> answerRRs = answer.getAnswerRRs();
        Enumeration<ResourceRecord> authorityRRs = answer.getAuthorityRRs();
        Enumeration<ResourceRecord> additionalRRs;
        QuestionRecord question;
        Resolver.AnalysisReport report = new AnalysisReport();

        // Check the ID.
        if (request.getId() != answer.getId()) {
            // jndi.72=Request and Answer have different ids
            throw new DomainProtocolException(Messages.getString("jndi.72")); //$NON-NLS-1$
        }

        // Determine a question.
        if (questions.hasMoreElements()) {
            question = questions.nextElement();
        } else {
            // jndi.73=no question record
            throw new IllegalArgumentException(Messages.getString("jndi.73")); //$NON-NLS-1$
        }
        // If name error occurred - no extra processing needed.
        if (answer.getRCode() == ProviderConstants.NAME_ERROR) {
            report.nameError = true;
            return report;
        }
        // check truncation, truncated message should not be cached
        if (answer.isTc()) {
            report.messageWasTruncated = true;
        }
        // Analyze answer section.
        while (answerRRs.hasMoreElements()) {
            ResourceRecord curRec = answerRRs.nextElement();

            if (question.getQClass() == curRec.getRRClass()
                    || question.getQClass() == ProviderConstants.ANY_QCLASS) {
                if (question.getQType() == ProviderConstants.ANY_QTYPE
                        && ProviderMgr.namesAreEqual(curRec.getName(), question
                                .getQName())) {
                    // If we query for ANY record types and the server returns
                    // some record for the SAME domain name we will collect
                    // all of such records and treat
                    // this situation as a complete answer for this query.
                    // We will not perform any more attempts to obtain more
                    // records.

                    report.records.addElement(curRec);
                    // if (LogConst.DEBUG) {
                    // ProviderMgr.logger.fine("Adding " +
                    // ProviderConstants.rrTypeNames[
                    // curRec.getRRType()]);
                    // }
                    if (curRec.getRRType() == ProviderConstants.CNAME_TYPE) {
                        report.aliasInfoWasReceived = true;
                        report.newName = (String) curRec.getRData();
                        // if (LogConst.DEBUG) {
                        // ProviderMgr.logger.fine("Alias \"" +
                        // report.newName + "\" was received");
                        // }
                    } else {
                        // XXX have we received a complete set of records?
                        report.completeAnswerWasReceived = true;
                    }
                } else if (question.getQType() == curRec.getRRType()
                        && ProviderMgr.namesAreEqual(question.getQName(),
                                curRec.getName())) {
                    // This is a situation when we get the record with the
                    // name and type exactly matching to that we have asked for.
                    // We will treat this as a complete answer.

                    report.records.addElement(curRec);
                    // if (LogConst.DEBUG) {
                    // ProviderMgr.logger.fine("Adding " +
                    // ProviderConstants.rrTypeNames[
                    // curRec.getRRType()]);
                    // }
                    report.completeAnswerWasReceived = true;
                } else if (curRec.getRRType() == ProviderConstants.CNAME_TYPE
                        && ProviderMgr.namesAreEqual(curRec.getName(), question
                                .getQName())) {
                    // This is the case of an alias. If we received an alias for
                    // the name we have asked the information for then we need
                    // to change the desired name to this newly received name.
                    // Then we will try to find necessary information for
                    // this new name in the current answer. If we fail then
                    // we will continue our general lookup algorithm with the
                    // new name instead of an old one. We will query servers
                    // from the SLIST with this new name.

                    // TODO this is not effective
                    Enumeration<ResourceRecord> answerRRs2 = answer
                            .getAnswerRRs();
                    Enumeration<ResourceRecord> additionalRRs2 = answer
                            .getAdditionalRRs();

                    report.aliasInfoWasReceived = true;
                    report.newName = (String) curRec.getRData();
                    report.extraRecords.addElement(curRec);
                    // if (LogConst.DEBUG) {
                    // ProviderMgr.logger.fine("Alias \"" + report.newName +
                    // "\" was received");
                    // }
                    // if we find the one of desired records in the
                    // current answer then we will treat the answer as complete
                    while (answerRRs2.hasMoreElements()) {

                        // Try to look for info about newly received name
                        // in ANSWER section.

                        ResourceRecord tmpRec = answerRRs2.nextElement();

                        // if (LogConst.DEBUG) {
                        // ProviderMgr.logger.fine(
                        // "Look for an answer in ANSWER section");
                        // }
                        if (tmpRec.getRRType() == question.getQType()
                                && ProviderMgr.namesAreEqual(tmpRec.getName(),
                                        report.newName)) {
                            // the answer is founded in ANSWER section
                            report.records.addElement(tmpRec);
                            // if (LogConst.DEBUG) {
                            // ProviderMgr.logger.fine("Adding " +
                            // ProviderConstants.rrTypeNames[
                            // tmpRec.getRRType()]);
                            // }
                            report.completeAnswerWasReceived = true;
                        }
                    }
                    while (additionalRRs2.hasMoreElements()) {
                        // Try to look for info about newly received name
                        // in ADDITIONAL section.

                        ResourceRecord tmpRec = additionalRRs2.nextElement();

                        // if (LogConst.DEBUG) {
                        // ProviderMgr.logger.fine("Look for an answer in " +
                        // "ADDITIONAL section");
                        // }
                        if (tmpRec.getRRType() == question.getQType()
                                && ProviderMgr.namesAreEqual(tmpRec.getName(),
                                        report.newName)) {
                            // the answer is founded in ADDITIONAL section
                            report.records.addElement(tmpRec);
                            // if (LogConst.DEBUG) {
                            // ProviderMgr.logger.fine("Adding " +
                            // ProviderConstants.rrTypeNames[
                            // tmpRec.getRRType()]);
                            // }
                            report.completeAnswerWasReceived = true;
                        }
                    }
                    // if (report.completeAnswerWasReceived) {
                    // if (LogConst.DEBUG) {
                    // ProviderMgr.logger.fine("Complete answer received");
                    // }
                    // }
                } else {
                    // We have received some extra records. Let's save it for
                    // future use.

                    // we will treat authoritative answer as a complete answer
                    // and in no case will perform further actions
                    if (answer.isAA()) {
                        report.completeAnswerWasReceived = true;
                    }
                    report.extraRecords.addElement(curRec);
                    // if (LogConst.DEBUG) {
                    // ProviderMgr.logger.fine("Adding additional record " +
                    // ProviderConstants.rrTypeNames[
                    // curRec.getRRType()]);
                    // }
                }
            } else {
                // The record from another DNS class arrived. Just ignore it.
                // if (LogConst.DEBUG) {
                // ProviderMgr.logger.fine("Ignore records from DNS class " +
                // curRec.getRRClass());
                // }
            }
        }

        // analyze authority section
        // 1. Store all info from authority NS records; try to locate NS IPs
        // from additional records in case if it is not present in SLIST;
        // start new background lookup process if not found in additional
        // section
        // TODO current implementation isn't effective
        while (authorityRRs.hasMoreElements()) {
            ResourceRecord curRec = authorityRRs.nextElement();
            SList slist = SList.getInstance();

            // save record for future use
            report.extraRecords.addElement(curRec);
            // analyze
            if (curRec.getRRType() == ProviderConstants.NS_TYPE) {
                String serverName = (String) curRec.getRData();
                SList.Server server2 = new SList.Server(serverName, null,
                        ProviderConstants.DEFAULT_DNS_PORT);
                SList.Server server = slist.getServerByServer(curRec.getName(),
                        server2);

                report.delegationArrived = true;
                if (server == null) {
                    // not found in SLIST
                    slist.updateEntry(curRec.getName(), server2, SList.UNKNOWN);
                    report.delegationZones.addElement(curRec.getName());
                    server = server2;
                }
                if (server != null && server.getIP() == null) {
                    // try to search additional records to obtain server's IP
                    additionalRRs = answer.getAdditionalRRs();
                    while (additionalRRs.hasMoreElements()) {
                        ResourceRecord addRec = additionalRRs.nextElement();

                        if (ProviderMgr.namesAreEqual(addRec.getName(),
                                serverName)
                                && addRec.getRRType() == ProviderConstants.A_TYPE) {
                            server.setIP((String) addRec.getRData());
                        }
                    }
                    if (server.getIP() == null) {
                        // IP was not found in additional section
                        // start resolving process in the background
                        this.startResolvingThread(server.getName(), curRec
                                .getRRClass());
                    }
                }
                // if (LogConst.DEBUG) {
                // ProviderMgr.logger.fine("Delegation \"" + server +
                // "\" arrived");
                // }
            } // end of NS type analysis
        } // end of authority section analysis

        // analyze additional section
        additionalRRs = answer.getAdditionalRRs();
        while (additionalRRs.hasMoreElements()) {
            ResourceRecord addRec = additionalRRs.nextElement();

            report.extraRecords.addElement(addRec);
            // if (LogConst.DEBUG) {
            // ProviderMgr.logger.fine("Adding additional record " +
            // ProviderConstants.rrTypeNames[addRec.getRRType()]);
            // }
        }

        // Fixing RRSet TTL issue.
        // If TTL fields in RRSet are not all the same then we need to set
        // all TTLs to lowest found value.
        // See RFC 2181 point 5.2

        // checking report.records and report.extraRecords
        for (int k = 0; k < 2; k++) {
            Vector<ResourceRecord> records = null;
            HashSet<String> processed = new HashSet<String>();

            switch (k) {
                case 0:
                    records = report.records;
                    break;
                case 1:
                    records = report.extraRecords;
                    break;
            }
            for (int i = 0; i < records.size(); i++) {
                ResourceRecord rr = records.elementAt(i);
                String key = rr.getName() + " " + rr.getRRClass() + " " + //$NON-NLS-1$ //$NON-NLS-2$
                        rr.getRRType();
                long ttl = rr.getTtl();
                Vector<ResourceRecord> objToUpdateTTL = new Vector<ResourceRecord>();

                if (processed.contains(key)) {
                    continue;
                }
                objToUpdateTTL.addElement(rr);
                // look forward for records with the same NAME CLASS TYPE
                for (int j = i; j < records.size(); j++) {
                    ResourceRecord rr2 = records.elementAt(j);
                    String key2 = rr2.getName() + " " + rr2.getRRClass() + " " + //$NON-NLS-1$ //$NON-NLS-2$
                            rr2.getRRType();
                    long ttl2 = rr2.getTtl();

                    if (processed.contains(key2)) {
                        continue;
                    }
                    if (key.equals(key2)) {
                        if (ttl > ttl2) {
                            ttl = ttl2;
                        }
                        objToUpdateTTL.addElement(rr2);
                    }
                }
                // update TTL if necessary
                for (int j = 0; j < objToUpdateTTL.size(); j++) {
                    ResourceRecord rr2 = objToUpdateTTL.elementAt(j);

                    if (rr2.getTtl() != ttl) {
                        rr2.setTtl(ttl);
                    }
                }
                // don't process such NAME CLASS TYPE combination any more
                processed.add(key);
            }
        } // fixing RRSet TTL issue

        return report;
    }