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;
}