0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: *
0015: * See the License for the specific language governing permissions and
0016: * limitations under the License.
0017: */
0018:
0019: /**
0020: * @author Alexei Y. Zakharov
0021: * @version $Revision: 1.1.2.5 $
0022: */package org.apache.harmony.jndi.provider.dns;
0023:
0024: import java.net.InetAddress;
0025: import java.net.SocketTimeoutException;
0026: import java.util.ArrayList;
0027: import java.util.Enumeration;
0028: import java.util.HashSet;
0029: import java.util.Hashtable;
0030: import java.util.Iterator;
0031: import java.util.Random;
0032: import java.util.Set;
0033: import java.util.StringTokenizer;
0034: import java.util.Vector;
0035:
0036: import javax.naming.NameNotFoundException;
0037: import javax.naming.NamingException;
0038: import javax.naming.ServiceUnavailableException;
0039:
0040: import org.apache.harmony.jndi.internal.nls.Messages;
0041: import org.apache.harmony.jndi.provider.dns.SList.Server;
0042:
0043: /**
0044: * This class implements the functionality of a simple DNS resolver.<br>
0045: * Following DNS Resource Records are supported:
0046: * <ul>
0047: * <li>A</li>
0048: * <li>NS</li>
0049: * <li>CNAME</li>
0050: * <li>SOA</li>
0051: * <li>PTR</li>
0052: * <li>MX</li>
0053: * <li>TXT</li>
0054: * <li>HINFO</li>
0055: * <li>AAAA (TODO)</li>
0056: * <li>NAPTR (TODO)</li>
0057: * <li>SRV</li>
0058: * </ul>
0059: * Following DNS classes are supported:
0060: * <ul>
0061: * <li>IN</li>
0062: * <li>HS (TODO)</li>
0063: * </ul>
0064: * <br>
0065: * TODO: do we need broadcasting and IP multicasting to obtain initial name
0066: * server address? TODO: network-preference feature for choosing most welcome
0067: * address for multihomed hosts (RFC 1123 point 6.1.3.4) TODO: add general IPv6
0068: * support and support for AAAA resource record
0069: */
0070: public class Resolver implements Runnable {
0071:
0072: private static final int MSG_MAX_BYTES = 512;
0073:
0074: /**
0075: * Entry of the resolver thread list.
0076: */
0077: private static class ThreadListEntry {
0078: Thread thread;
0079:
0080: String serverNameToResolve;
0081:
0082: int dnsClass;
0083: }
0084:
0085: private static final Random rndGen = new Random();
0086:
0087: // resolver configuration
0088: private int initialTimeout;
0089:
0090: private int timeoutRetries;
0091:
0092: private boolean authoritativeAnswerDesired;
0093:
0094: private boolean recursionDesired;
0095:
0096: // maximum number of active threads
0097: private int threadNumberLimit;
0098:
0099: // vector with currently running Resolver threads
0100: private final ArrayList<ThreadListEntry> resolverThreads = new ArrayList<ThreadListEntry>();
0101:
0102: // the list of host names that should be resolved
0103: private final ArrayList<ThreadListEntry> hostnamesToResolve = new ArrayList<ThreadListEntry>();
0104:
0105: // semaphore that controls access to both lists above
0106: private class ThreadListSemaphore {
0107: }
0108:
0109: private final Object threadListSemaphore = new ThreadListSemaphore();
0110:
0111: /**
0112: * Constructs a <code>Resolver</code> object with default initial timeout
0113: * (1 second), default timeout retries (4 times), default recursion desired
0114: * switch (true) and default authoritative answer desired
0115: *
0116: * @see #Resolver(int, int)
0117: */
0118: public Resolver() {
0119: this (ProviderConstants.DEFAULT_INITIAL_TIMEOUT,
0120: ProviderConstants.DEFAULT_TIMEOUT_RETRIES,
0121: ProviderConstants.DEFAULT_MAX_THREADS,
0122: ProviderConstants.DEFAULT_AUTHORITATIVE,
0123: ProviderConstants.DEFAULT_RECURSION);
0124: }
0125:
0126: /**
0127: * Constructs a <code>Resolver</code> object with given initial timeout
0128: * and timeout retries. Initially the resolver will try to access DNS
0129: * servers with timeout set to initial timeout. If none of servers answer it
0130: * will double the timeout and perform the second round. The process will
0131: * continue for <code>timeoutRetries</code> rounds. If there is no answer
0132: * still the resolver will give up.
0133: *
0134: * @param initialTimeout
0135: * the initial timeout that is used during the first round (in
0136: * milliseconds)
0137: * @param timeoutRetries
0138: * number of rounds the Resolver should perform before giving up
0139: * @param authoritativeAnswerDesired
0140: * do we want to receive only authoritative answers
0141: * @param recursionDesired
0142: * do we want our outgoing packages to have RD but set
0143: */
0144: public Resolver(int initialTimeout, int timeoutRetries,
0145: int maxThreads, boolean authoritativeAnswerDesired,
0146: boolean recursionDesired) {
0147: this .initialTimeout = initialTimeout;
0148: this .timeoutRetries = timeoutRetries;
0149: this .threadNumberLimit = maxThreads;
0150: this .authoritativeAnswerDesired = authoritativeAnswerDesired;
0151: this .recursionDesired = recursionDesired;
0152: }
0153:
0154: /**
0155: * @return Returns the threadNumberLimit.
0156: */
0157: public int getThreadNumberLimit() {
0158: return threadNumberLimit;
0159: }
0160:
0161: /**
0162: * @param threadNumberLimit
0163: * The threadNumberLimit to set.
0164: */
0165: public void setThreadNumberLimit(int threadNumberLimit) {
0166: this .threadNumberLimit = threadNumberLimit;
0167: }
0168:
0169: /**
0170: * @return Returns the authoritativeAnswerDesired.
0171: */
0172: public boolean isAuthoritativeAnswerDesired() {
0173: return authoritativeAnswerDesired;
0174: }
0175:
0176: /**
0177: * @param authoritativeAnswerDesired
0178: * The authoritativeAnswerDesired to set.
0179: */
0180: public void setAuthoritativeAnswerDesired(
0181: boolean authoritativeAnswerDesired) {
0182: this .authoritativeAnswerDesired = authoritativeAnswerDesired;
0183: }
0184:
0185: /**
0186: * @return Returns the initialTimeout.
0187: */
0188: public int getInitialTimeout() {
0189: return initialTimeout;
0190: }
0191:
0192: /**
0193: * @param initialTimeout
0194: * The initialTimeout to set.
0195: */
0196: public void setInitialTimeout(int initialTimeout) {
0197: this .initialTimeout = initialTimeout;
0198: }
0199:
0200: /**
0201: * @return Returns the recursionDesired.
0202: */
0203: public boolean isRecursionDesired() {
0204: return recursionDesired;
0205: }
0206:
0207: /**
0208: * @param recursionDesired
0209: * The recursionDesired to set.
0210: */
0211: public void setRecursionDesired(boolean recursionDesired) {
0212: this .recursionDesired = recursionDesired;
0213: }
0214:
0215: /**
0216: * @return Returns the timeoutRetries.
0217: */
0218: public int getTimeoutRetries() {
0219: return timeoutRetries;
0220: }
0221:
0222: /**
0223: * @param timeoutRetries
0224: * The timeoutRetries to set.
0225: */
0226: public void setTimeoutRetries(int timeoutRetries) {
0227: this .timeoutRetries = timeoutRetries;
0228: }
0229:
0230: /**
0231: * Checks available name servers if they have any resource records related
0232: * to given name & type & class combination. Standard DNS lookup algorithm
0233: * is used.
0234: *
0235: * @param name
0236: * well-formed domain name
0237: * @param types
0238: * an array of types; only records that have such types will be
0239: * returned
0240: * @param classes
0241: * @return enumeration with found resource records
0242: * @throws SecurityException
0243: * if the resolver is not allowed to use a network subsystem
0244: * @throws NameNotFoundException
0245: * if authoritative server for desired zone was contacted but
0246: * given name has not been found in that zone
0247: * @throws ServiceUnavailableException
0248: * if no authoritative server for desired name was found or all
0249: * servers are dead or malfunction
0250: * @throws DomainProtocolException
0251: * if some DNS specific error has occurred
0252: */
0253: public Enumeration<ResourceRecord> lookup(String name, int[] types,
0254: int[] classes) throws SecurityException,
0255: NameNotFoundException, ServiceUnavailableException,
0256: DomainProtocolException {
0257:
0258: // Algorithm:
0259: // 1. Set workZone to the parent of qName; clear queriedServers.
0260: // 2. Try to get a complete answer for the workZone from the servers
0261: // currently available in SLIST exclude servers from queriedServers.
0262: // 3. update queriedServers with "visited servers" info.
0263: // 4. If the complete answer was received - return it to the user;exit.
0264: // 5. If the delegation was received:
0265: // a) If we already have this server & zone pair in SLIST - skip it.
0266: // b) If we don't have - put it into SLIST
0267: // c) If we haven't received any new delegations - goto step (7)
0268: // d) If some new delegation has been received:
0269: // 1) from delegations: found the zone with the best matching count
0270: // with qName
0271: // 2) if this matching count is bigger than matching count between
0272: // workZone and qName:
0273: // - set workZone to zone with biggest matching count determined
0274: // at step (5.d.1)
0275: // - goto step (2)
0276: // 3) if it doesn't then goto step 2 with the same workZone
0277: // 6. If ALIAS was received ...
0278: // 7. If no answer has been received:
0279: // a) Check if the workZone is the root zone.
0280: // b) If so - give up; return empty result to the user.
0281: // c) If it isn't, set workZone to parent of workZone. Goto step (2).
0282:
0283: // SList slist = SList.getInstance();
0284: ResolverCache cache = ResolverCache.getInstance();
0285:
0286: Vector<QuestionRecord> questions = new Vector<QuestionRecord>();
0287: Vector<ResourceRecord> answers = new Vector<ResourceRecord>();
0288:
0289: if (name == null) {
0290: // jndi.2E=The name is null
0291: throw new NullPointerException(Messages
0292: .getString("jndi.2E")); //$NON-NLS-1$
0293: }
0294: if (types == null) {
0295: // jndi.6B=types is null
0296: throw new NullPointerException(Messages
0297: .getString("jndi.6B")); //$NON-NLS-1$
0298: }
0299: if (classes == null) {
0300: // jndi.6C=classes is null
0301: throw new NullPointerException(Messages
0302: .getString("jndi.6C")); //$NON-NLS-1$
0303: }
0304: for (int element : classes) {
0305: for (int element0 : types) {
0306: QuestionRecord quest = new QuestionRecord(name,
0307: element0, element);
0308: questions.addElement(quest);
0309: }
0310: }
0311: // iterate over question records
0312: for (int i = 0; i < questions.size(); i++) {
0313: QuestionRecord curQuestion = questions.elementAt(i);
0314: String qName = curQuestion.getQName();
0315: Message mesToSend = null;
0316: Message receivedMes = null;
0317: AnalysisReport report = null;
0318: String workZone;
0319: Hashtable<Server, Object> visitedServers = new Hashtable<Server, Object>();
0320:
0321: // if (LogConst.DEBUG) {
0322: // ProviderMgr.logger.fine("Current question: " +
0323: // curQuestion.toString());
0324: // }
0325: // look in cache
0326: if (curQuestion.getQType() != ProviderConstants.ANY_QTYPE
0327: && curQuestion.getQClass() != ProviderConstants.ANY_QCLASS) {
0328: Enumeration<ResourceRecord> recEnum = cache
0329: .get(curQuestion);
0330:
0331: if (recEnum.hasMoreElements()) {
0332: while (recEnum.hasMoreElements()) {
0333: answers.addElement(recEnum.nextElement());
0334: }
0335: // we don't need to query any servers since the information
0336: // we want has been found in the local cache
0337: // if (LogConst.DEBUG) {
0338: // ProviderMgr.logger.fine(
0339: // "Information was gathered from cache");
0340: // }
0341: continue;
0342: }
0343: }
0344:
0345: // query remote DNS servers
0346:
0347: // determine work zone
0348: if (qName != null && !qName.equals(".")) { //$NON-NLS-1$
0349: workZone = qName;
0350: // support for SRV-style qNames
0351: while (workZone.startsWith("_")) { //$NON-NLS-1$
0352: workZone = ProviderMgr.getParentName(workZone);
0353: }
0354: } else {
0355: workZone = "."; //$NON-NLS-1$
0356: }
0357: // if (LogConst.DEBUG) {
0358: // ProviderMgr.logger.fine("Lookup: new workZone is " +
0359: // "\"" + workZone + "\"");
0360: // }
0361: // construct request message
0362: try {
0363: mesToSend = createMessageForSending(qName, curQuestion
0364: .getQType(), curQuestion.getQClass());
0365: // if (LogConst.DEBUG) {
0366: // ProviderMgr.logger.finest("Message to send:\n" +
0367: // mesToSend.toString());
0368: // }
0369:
0370: } catch (DomainProtocolException e) {
0371: throw e;
0372: }
0373: while (true) {
0374: boolean noIdea = false;
0375:
0376: try {
0377: receivedMes = queryServers(mesToSend, workZone,
0378: visitedServers, false);
0379: if (receivedMes != null) {
0380: report = analyzeAnswer(mesToSend, receivedMes);
0381: if (!report.messageWasTruncated) {
0382: // Put all extra records into the cache for
0383: // future use
0384: for (int k = 0; k < report.extraRecords
0385: .size(); k++) {
0386: ResourceRecord rec = report.extraRecords
0387: .elementAt(k);
0388:
0389: cache.put(rec);
0390: }
0391: } else {
0392: // Truncated message MUST NOT be cached and later
0393: // used in such a way that the fact that they are
0394: // truncated is lost (RFC 1123 point 6.1.3.2).
0395: }
0396: // examine the report
0397: if (report.completeAnswerWasReceived) {
0398: // complete answer
0399: // if (LogConst.DEBUG) {
0400: // ProviderMgr.logger.fine(
0401: // "Lookup: a complete answer was received");
0402: // }
0403: for (int k = 0; k < report.records.size(); k++) {
0404: ResourceRecord rec = report.records
0405: .elementAt(k);
0406: answers.addElement(rec);
0407: // we are sure that the answer section has not
0408: // been truncated so we can put the record
0409: // into the cache
0410: cache.put(rec);
0411: }
0412: // exit the loop
0413: break;
0414: } else if (report.nameError) {
0415: // name error
0416: // if (LogConst.DEBUG) {
0417: // ProviderMgr.logger.fine("Lookup: name error");
0418: // }
0419: // jndi.6D=Name {0} was not found
0420: throw new NameNotFoundException(Messages
0421: .getString("jndi.6D", name)); //$NON-NLS-1$
0422: } else if (report.aliasInfoWasReceived) {
0423: // alias received
0424: // QuestionRecord newQuestion = new
0425: // QuestionRecord();
0426:
0427: // if (LogConst.DEBUG) {
0428: // ProviderMgr.logger.fine(
0429: // "Lookup: an alias was received");
0430: // }
0431: qName = report.newName;
0432: curQuestion.setQName(qName);
0433: // look in cache
0434: if (curQuestion.getQType() != ProviderConstants.ANY_QTYPE
0435: && curQuestion.getQClass() != ProviderConstants.ANY_QCLASS) {
0436: Enumeration<ResourceRecord> recEnum = cache
0437: .get(curQuestion);
0438:
0439: if (recEnum.hasMoreElements()) {
0440: while (recEnum.hasMoreElements()) {
0441: answers.addElement(recEnum
0442: .nextElement());
0443: }
0444: // We don't need to query any more servers
0445: // since the information we want has been
0446: // found in the local cache.
0447: // Let's switch to next question if any.
0448: break;
0449: }
0450: }
0451: if (qName != null && !qName.equals(".")) //$NON-NLS-1$
0452: {
0453: workZone = qName;
0454: } else {
0455: workZone = "."; //$NON-NLS-1$
0456: }
0457: visitedServers = new Hashtable<Server, Object>();
0458: for (int k = 0; k < report.records.size(); k++) {
0459: answers.addElement(report.records
0460: .elementAt(k));
0461: }
0462: // construct a new request message
0463: try {
0464: mesToSend = createMessageForSending(
0465: qName, curQuestion.getQType(),
0466: curQuestion.getQClass());
0467: } catch (DomainProtocolException e) {
0468: throw e;
0469: }
0470: // if (LogConst.DEBUG) {
0471: // ProviderMgr.logger.fine("Lookup: new name is " +
0472: // "\"" + qName + "\"");
0473: // ProviderMgr.logger.fine(
0474: // "Lookup: new workZone is " +
0475: // "\"" + workZone + "\"");
0476: // }
0477: } else if (report.delegationArrived) {
0478: // new delegation, probably need to query once again
0479: int k17 = -1;
0480: int matchingCount = ProviderMgr
0481: .getMatchingCount(qName, workZone);
0482:
0483: // if (LogConst.DEBUG) {
0484: // ProviderMgr.logger.fine(
0485: // "Lookup: delegation arrived");
0486: // }
0487: for (int k = 0; k < report.delegationZones
0488: .size(); k++) {
0489: String curZone = report.delegationZones
0490: .elementAt(k);
0491: int tmpMatchingCount = ProviderMgr
0492: .getMatchingCount(qName,
0493: curZone);
0494:
0495: if (tmpMatchingCount > matchingCount) {
0496: k17 = k;
0497: matchingCount = tmpMatchingCount;
0498: }
0499:
0500: }
0501: if (k17 != -1) {
0502: // better delegation was received
0503: workZone = report.delegationZones
0504: .elementAt(k17);
0505: // if (LogConst.DEBUG) {
0506: // ProviderMgr.logger.fine(
0507: // "Lookup: better delegation was found");
0508: // }
0509: } else {
0510: // no better delegation
0511: // do nothing, just query the next server of
0512: // the current workZone
0513: }
0514: } else {
0515: noIdea = true;
0516: }
0517: } // end of if report != null block
0518: else {
0519: noIdea = true;
0520: }
0521: if (noIdea) {
0522: // Resolver has no idea how to get info about
0523: // desired host while querying master hosts of the
0524: // current workZone.
0525: // Let's make one step up to the root.
0526: // if (LogConst.DEBUG) {
0527: // ProviderMgr.logger.fine("Lookup: no idea");
0528: // }
0529: if (!workZone.equals(".")) { //$NON-NLS-1$
0530: workZone = ProviderMgr
0531: .getParentName(workZone);
0532: // if (LogConst.DEBUG) {
0533: // ProviderMgr.logger.fine(
0534: // "Lookup: new work zone is " +
0535: // "\"" + workZone + "\"");
0536: // }
0537: } else {
0538: // give up
0539: break;
0540: // throw new ServiceUnavailableException(
0541: // "Unable to " +
0542: // "contact authoritative server for " +
0543: // qName +
0544: // " and no other results were found");
0545: }
0546: }
0547: } catch (NameNotFoundException e) {
0548: throw e;
0549: } catch (DomainProtocolException e) {
0550: throw e;
0551: }
0552: } // query servers loop
0553:
0554: } // questions loop
0555: return answers.elements();
0556: }
0557:
0558: /**
0559: * Lists entire DNS zone using zone transfer mechanism.
0560: *
0561: * @param name
0562: * DNS zone name
0563: * @return enumeration with found <code>ResourceRecord</code> objects
0564: * @throws SecurityException
0565: * if the resolver is not allowed to use a network subsystem
0566: * @throws NameNotFoundException
0567: * if authoritative server(s) was not found
0568: * @throws ServiceUnavailableException
0569: * if none of found servers permits zone transfers
0570: * @throws DomainProtocolException
0571: * if some DNS specific error has occured
0572: */
0573: public Enumeration<ResourceRecord> list(String name)
0574: throws NamingException {
0575: final int OUT_BUF_SIZE = 512;
0576: final int IN_BUF_SIZE = 65536;
0577:
0578: Vector<ResourceRecord> answerVect = new Vector<ResourceRecord>();
0579: Message mesToSend = null;
0580: Message receivedMes = null;
0581: Enumeration<ResourceRecord> enum1;
0582: // String zoneMasterServer = null;
0583: // Vector authoritativeServerIPs = new Vector();
0584: HashSet<Object> authoritativeServers = new HashSet<Object>();
0585: Iterator<Object> authServersIter;
0586: int qClassArr[] = new int[1];
0587: byte outBuf[] = new byte[OUT_BUF_SIZE];
0588: int outLen;
0589: byte inBuf[] = new byte[IN_BUF_SIZE];
0590: boolean received = false;
0591: boolean completeAnswer = false;
0592: String proto = null;
0593:
0594: ResolverCache cache = ResolverCache.getInstance();
0595: // SList slist = SList.getInstance();
0596:
0597: if (name == null) {
0598: // jndi.2E=The name is null
0599: throw new NullPointerException(Messages
0600: .getString("jndi.2E")); //$NON-NLS-1$
0601: }
0602: // if given name is SRV style name where domain name is prefixed
0603: // with _Proto
0604: if (name.startsWith("_")) { //$NON-NLS-1$
0605: int n = name.indexOf('.');
0606:
0607: if (n != -1) {
0608: proto = name.substring(0, n);
0609: if (name.length() > n) {
0610: name = name.substring(n + 1, name.length());
0611: } else {
0612: // nonsense
0613: name = "."; //$NON-NLS-1$
0614: }
0615: } else {
0616: // nonsense
0617: name = "."; //$NON-NLS-1$
0618: }
0619: }
0620: enum1 = lookup(name, new int[] { ProviderConstants.NS_TYPE },
0621: new int[] { ProviderConstants.ANY_QTYPE });
0622: mesToSend = createMessageForSending(name,
0623: ProviderConstants.AXFR_QTYPE,
0624: ProviderConstants.ANY_QCLASS);
0625: outLen = mesToSend.writeBytes(outBuf, 0);
0626: // determine the list of zone authoritative servers
0627: while (enum1.hasMoreElements()) {
0628: ResourceRecord rr = enum1.nextElement();
0629:
0630: if (rr.getRRType() == ProviderConstants.NS_TYPE) {
0631: authoritativeServers.add(rr.getRData());
0632: // assertion: all authoritative servers should have the same
0633: // DNS class
0634: qClassArr[0] = rr.getRRClass();
0635: } else if (rr.getRRType() == ProviderConstants.SOA_TYPE) {
0636: StringTokenizer st = new StringTokenizer((String) rr
0637: .getRData(), " "); //$NON-NLS-1$
0638:
0639: if (st.hasMoreTokens()) {
0640: authoritativeServers.add(st.nextToken());
0641: qClassArr[0] = rr.getRRClass();
0642: break;
0643: }
0644: }
0645: }
0646: // try to perform a zone transfer
0647: authServersIter = authoritativeServers.iterator();
0648: authServersLoop: while (authServersIter.hasNext()) {
0649: String authServerName = (String) authServersIter.next();
0650: Enumeration<ResourceRecord> addrEnum = lookup(
0651: authServerName,
0652: new int[] { ProviderConstants.A_TYPE }, qClassArr);
0653:
0654: while (addrEnum.hasMoreElements()) {
0655: ResourceRecord curRR = addrEnum.nextElement();
0656: String ip = (String) curRR.getRData();
0657:
0658: try {
0659: // if (LogConst.DEBUG) {
0660: // ProviderMgr.logger.fine(
0661: // "Initiating zone transfer, IP=" + ip);
0662: // }
0663: TransportMgr.sendReceiveTCP(ip,
0664: ProviderConstants.DEFAULT_DNS_PORT, outBuf,
0665: outLen, inBuf, IN_BUF_SIZE,
0666: this .initialTimeout * this .timeoutRetries);
0667: received = true;
0668: } catch (SocketTimeoutException e) {
0669:
0670: // if (LogConst.DEBUG) {
0671: // ProviderMgr.logger.fine("Socket timeout");
0672: // }
0673: } catch (DomainProtocolException e) {
0674: // some problem was encountered
0675: // ProviderMgr.logger.log(Level.WARNING,
0676: // "Connection failure", e);
0677: }
0678: if (received) {
0679: receivedMes = new Message();
0680: try {
0681: int rCode;
0682:
0683: Message.parseMessage(inBuf, 0, receivedMes);
0684: rCode = receivedMes.getRCode();
0685: switch (rCode) {
0686: case ProviderConstants.NO_ERROR:
0687: // put all received records into Resolver's
0688: // cache
0689: for (int k = 0; k < 3; k++) {
0690: switch (k) {
0691: case 0:
0692: enum1 = receivedMes.getAnswerRRs();
0693: break;
0694: case 1:
0695: enum1 = receivedMes
0696: .getAuthorityRRs();
0697: break;
0698: case 2:
0699: enum1 = receivedMes
0700: .getAdditionalRRs();
0701: break;
0702: }
0703: while (enum1.hasMoreElements()) {
0704: ResourceRecord rr = enum1
0705: .nextElement();
0706:
0707: cache.put(rr);
0708: if (k == 0) {
0709: answerVect.addElement(rr);
0710: }
0711: }
0712: }
0713: completeAnswer = true;
0714: break;
0715: case ProviderConstants.NAME_ERROR:
0716: // jndi.6D=Name {0} was not found
0717: throw new NameNotFoundException(Messages
0718: .getString("jndi.6D", name)); //$NON-NLS-1$
0719: case ProviderConstants.SERVER_FAILURE:
0720: case ProviderConstants.FORMAT_ERROR:
0721: case ProviderConstants.NOT_IMPLEMENTED:
0722: case ProviderConstants.REFUSED:
0723: default:
0724: }
0725: } catch (DomainProtocolException e) {
0726: // ProviderMgr.logger.log(Level.WARNING,
0727: // "Error while parsing of DNS message", e);
0728: }
0729: } // if received
0730: if (completeAnswer) {
0731: // if (LogConst.DEBUG) {
0732: // ProviderMgr.logger.fine(
0733: // "list: Complete answer received");
0734: // }
0735: break authServersLoop;
0736: }
0737: } // address loop
0738: } // authoritative servers loop
0739:
0740: if (!completeAnswer) {
0741: // found nothing
0742: // jndi.6E=Unable to perform zone transfer
0743: throw new ServiceUnavailableException(Messages
0744: .getString("jndi.6E")); //$NON-NLS-1$
0745: }
0746: // SRV _Proto prefix support - filter all records that don't have given
0747: // _Proto field
0748: if (proto != null) {
0749: Vector<ResourceRecord> answerVect2 = new Vector<ResourceRecord>();
0750:
0751: for (int i = 0; i < answerVect.size(); i++) {
0752: ResourceRecord rr = answerVect.elementAt(i);
0753: StringTokenizer st = new StringTokenizer(rr.getName(),
0754: "."); //$NON-NLS-1$
0755: String token = null;
0756: boolean valid = false;
0757:
0758: if (st.hasMoreTokens()) {
0759: token = st.nextToken();
0760: if (token.length() > 0 && token.charAt(0) == '_'
0761: && st.hasMoreTokens()) {
0762: token = st.nextToken();
0763: if (token.equalsIgnoreCase(proto)) {
0764: valid = true;
0765: }
0766: }
0767: }
0768: if (valid) {
0769: answerVect2.addElement(rr);
0770: }
0771: }
0772: answerVect = answerVect2;
0773: }
0774: return answerVect.elements();
0775: }
0776:
0777: /**
0778: * Adds initial DNS server the resolver should start with. Trying underlying
0779: * OS services to determine IP from name.
0780: *
0781: * @param name
0782: * server's name
0783: * @param ip
0784: * server's IP address
0785: * @param port
0786: * port on server
0787: */
0788: public void addInitialServer(String name, String ip, int port,
0789: String zoneName) {
0790: SList.Server server = new SList.Server(name, ip, port);
0791: SList slist = SList.getInstance();
0792:
0793: if (name == null && ip == null) {
0794: // jndi.6F=Both name and IP are null
0795: throw new NullPointerException(Messages
0796: .getString("jndi.6F")); //$NON-NLS-1$
0797: }
0798: if (zoneName == null) {
0799: // jndi.70=zoneName is null
0800: throw new NullPointerException(Messages
0801: .getString("jndi.70")); //$NON-NLS-1$
0802: }
0803: // if IP is not given and we don't know this server yet
0804: // try to determine IP from underlying OS services
0805: if (ip == null && !slist.hasServer(name)) {
0806: InetAddress addrObj = TransportMgr.getIPByName_OS(name);
0807:
0808: if (addrObj != null) {
0809: server
0810: .setIP(ProviderMgr.getIpStr(addrObj
0811: .getAddress()));
0812: }
0813: }
0814: // add given zone <-> server pair
0815: if (!slist.hasServer(zoneName, server)) {
0816: slist.updateEntry(zoneName, server, SList.UNKNOWN);
0817: }
0818: }
0819:
0820: /**
0821: * Query available DNS servers for desired information. This method doesn't
0822: * look into the local cache. Drops all answers that contains "server fail"
0823: * and "not implemented" answer codes and returns the first "good" answer.
0824: *
0825: * @param request
0826: * a DNS message that contains the request record
0827: * @param workZone
0828: * a zone that is closest known (grand-) parent of the desired
0829: * name
0830: * @param visitedServers
0831: * the hash list of servers, that should not be examined; this
0832: * method also appends to this list all server that have been
0833: * visited during execution of this method
0834: * @param tcpOnly
0835: * <code>true</code> if we want to use TCP protocol only;
0836: * otherwise UDP will be tried first
0837: * @return the message received; <code>null</code> if none found
0838: * @throws DomainProtocolException
0839: * some domain protocol related error occured
0840: * @throws SecurityException
0841: * if the resolver doesn't have the permission to use sockets
0842: */
0843: Message queryServers(Message request, String workZone,
0844: Hashtable<Server, Object> visitedServers, boolean tcpOnly)
0845: throws DomainProtocolException, SecurityException {
0846: QuestionRecord qRecord;
0847:
0848: SList slist = SList.getInstance();
0849: SList.Server curServer;
0850: byte[] outBuf = new byte[MSG_MAX_BYTES];
0851: int outBufLen;
0852: byte[] inBuf = new byte[MSG_MAX_BYTES];
0853: Message receivedMes = null;
0854: int idx = 0;
0855: int curTimeout = this .initialTimeout;
0856: boolean received = false;
0857: boolean parsed = false;
0858: boolean correctAnswer = false;
0859: int rCode = -1;
0860:
0861: // determine a question
0862: if (!request.getQuestionRecords().hasMoreElements()) {
0863: // jndi.71=no question record
0864: throw new IllegalArgumentException(Messages
0865: .getString("jndi.71")); //$NON-NLS-1$
0866: }
0867: qRecord = request.getQuestionRecords().nextElement();
0868: // preparing a domain protocol message
0869: outBufLen = request.writeBytes(outBuf, 0);
0870:
0871: // sending message and trying to receive an answer
0872: for (int round = 0; round < this .timeoutRetries; round++) {
0873: Set<Server> queriedServers = new HashSet<Server>();
0874:
0875: // start of round
0876: while (true) {
0877: int responseTime = 0;
0878:
0879: received = false;
0880: parsed = false;
0881: rCode = -1;
0882: // get next server
0883: curServer = slist
0884: .getBestGuess(workZone, visitedServers);
0885: if (curServer == null
0886: || queriedServers.contains(curServer)) {
0887: // end of round
0888: break;
0889: }
0890: if (curServer.getIP() == null) {
0891: // if we don't know IP lets start background resolving
0892: // thread
0893: startResolvingThread(curServer.getName(), qRecord
0894: .getQClass());
0895: slist.updateEntry(workZone, curServer,
0896: SList.NETWORK_FAILURE);
0897: queriedServers.add(curServer);
0898: continue;
0899: }
0900: // send the message and receive the answer
0901: try {
0902: // if (LogConst.DEBUG) {
0903: // ProviderMgr.logger.fine("Timeout is set to " +
0904: // curTimeout);
0905: // ProviderMgr.logger.fine("Querying server \"" +
0906: // curServer + "\"");
0907: // }
0908: // timeBeforeSending = System.currentTimeMillis();
0909: if (tcpOnly) {
0910: TransportMgr.sendReceiveTCP(curServer.getIP(),
0911: curServer.getPort(), outBuf, outBufLen,
0912: inBuf, inBuf.length, curTimeout);
0913: } else {
0914: TransportMgr.sendReceiveUDP(curServer.getIP(),
0915: curServer.getPort(), outBuf, outBufLen,
0916: inBuf, inBuf.length, curTimeout);
0917: }
0918: // responseTime = (int) (System.currentTimeMillis() -
0919: // timeBeforeSending);
0920: // ProviderMgr.logger.fine("Answer received in " +
0921: // responseTime + " milliseconds");
0922: received = true;
0923: } catch (SocketTimeoutException e) {
0924: slist.updateEntry(workZone, curServer,
0925: SList.TIMEOUT);
0926: // if (LogConst.DEBUG) {
0927: // ProviderMgr.logger.fine("Socket timeout");
0928: // }
0929: } catch (DomainProtocolException e) {
0930: // problems with receiving the message
0931: // skipping this server
0932: slist.updateEntry(workZone, curServer,
0933: SList.NETWORK_FAILURE);
0934: // ProviderMgr.logger.log(Level.WARNING,
0935: // "Connection failure", e);
0936: }
0937: // parse the message
0938: if (received) {
0939: try {
0940: boolean answerSectionIsTruncated = false;
0941:
0942: receivedMes = new Message();
0943: idx = 0;
0944: idx = Message.parseMessage(inBuf, idx,
0945: receivedMes);
0946:
0947: // if (LogConst.DEBUG) {
0948: // ProviderMgr.logger.finest("Received message:\n" +
0949: // receivedMes.toString());
0950: // }
0951: parsed = true;
0952: // handle a truncation
0953: if (receivedMes.isTc() && !tcpOnly) {
0954: // The Message is truncated.
0955: // Let's try to establish a TCP connection
0956: // and retransmit the message over that connection.
0957: // if (LogConst.DEBUG) {
0958: // ProviderMgr.logger.fine("Message is truncated");
0959: // ProviderMgr.logger.fine("Trying to establish " +
0960: // "a connection over TCP");
0961: // }
0962: try {
0963: Message receivedMesTcp;
0964: int idx2;
0965:
0966: TransportMgr.sendReceiveTCP(curServer
0967: .getIP(), curServer.getPort(),
0968: outBuf, outBufLen, inBuf,
0969: inBuf.length, curTimeout);
0970: receivedMesTcp = new Message();
0971: idx2 = Message.parseMessage(inBuf, 0,
0972: receivedMesTcp);
0973: // complete message was received
0974: if (!receivedMesTcp.isTc()) {
0975: receivedMes = receivedMesTcp;
0976: idx = idx2;
0977: }
0978: } catch (Exception e) {
0979: // ProviderMgr.logger.log(Level.WARNING,
0980: // "Receiving a complete message" +
0981: // " over TCP failed", e);
0982: // if (LogConst.DEBUG) {
0983: // ProviderMgr.logger.fine(
0984: // "Parsing the message " +
0985: // "previously received over UDP");
0986: // }
0987: }
0988: }
0989: // Is the message still truncated?
0990: // (It is possible in case if TCP connection failed)
0991: if (receivedMes.isTc()) {
0992: // check if the ANSWER section is truncated
0993: // or not
0994: if (!receivedMes.getAuthorityRRs()
0995: .hasMoreElements()
0996: && !receivedMes.getAdditionalRRs()
0997: .hasMoreElements()) {
0998: answerSectionIsTruncated = true;
0999: }
1000: }
1001: rCode = receivedMes.getRCode();
1002: if (rCode == ProviderConstants.NO_ERROR) {
1003: // correct message has been received
1004: slist.updateEntry(workZone, curServer,
1005: responseTime);
1006: visitedServers.put(curServer, new Object()); // $NON-LOCK-1$
1007: if (!answerSectionIsTruncated) {
1008: correctAnswer = true;
1009: break;
1010: }
1011: } else if (rCode == ProviderConstants.SERVER_FAILURE) {
1012: // removing server from list
1013: // ProviderMgr.logger.warning("Server failure. " +
1014: // errMsg);
1015: slist.updateEntry(workZone, curServer,
1016: SList.SERVER_FAILURE);
1017: visitedServers.put(curServer, new Object()); // $NON-LOCK-1$
1018: } else if (rCode == ProviderConstants.FORMAT_ERROR) {
1019: // removing server from list
1020: // ProviderMgr.logger.warning("Format error. " +
1021: // errMsg);
1022: slist.updateEntry(workZone, curServer,
1023: SList.SERVER_FAILURE);
1024: visitedServers.put(curServer, new Object()); // $NON-LOCK-1$
1025: } else if (rCode == ProviderConstants.NAME_ERROR) {
1026: // ProviderMgr.logger.warning("Name error. " +
1027: // errMsg);
1028: if (receivedMes.isAA()) {
1029: slist.updateEntry(workZone, curServer,
1030: responseTime);
1031: visitedServers.put(curServer,
1032: new Object()); // $NON-LOCK-1$
1033: correctAnswer = true;
1034: // if (LogConst.DEBUG) {
1035: // ProviderMgr.logger.fine(
1036: // "Return name error to user");
1037: // }
1038: break;
1039: }
1040: // This server is not authoritative server for
1041: // this zone. It should not answer with a
1042: // name error. Probably it is misconfigured.
1043: slist.updateEntry(workZone, curServer,
1044: SList.SERVER_FAILURE);
1045: visitedServers.put(curServer, new Object()); // $NON-LOCK-1$
1046: // if (LogConst.DEBUG) {
1047: // ProviderMgr.logger.fine(
1048: // "Not authoritative answer. " +
1049: // "Skip it.");
1050: // }
1051: } else if (rCode == ProviderConstants.NOT_IMPLEMENTED) {
1052: // ProviderMgr.logger.warning("Not implemented. " +
1053: // errMsg);
1054: slist.updateEntry(workZone, curServer,
1055: SList.SERVER_FAILURE);
1056: visitedServers.put(curServer, new Object()); // $NON-LOCK-1$
1057: } else if (rCode == ProviderConstants.REFUSED) {
1058: // ProviderMgr.logger.warning("Refused. " +
1059: // errMsg);
1060: slist.updateEntry(workZone, curServer,
1061: SList.SERVER_FAILURE);
1062: visitedServers.put(curServer, new Object()); // $NON-LOCK-1$
1063: }
1064: } catch (DomainProtocolException e) {
1065: // removing this server from SLIST
1066: slist.dropServer(workZone, curServer);
1067: // ProviderMgr.logger.warning("Unknown error.");
1068: } catch (IndexOutOfBoundsException e) {
1069: // bad message received
1070: slist.dropServer(workZone, curServer);
1071: // ProviderMgr.logger.warning("Bad message received: " +
1072: // " IndexOutOfBoundsException.");
1073: }
1074: }
1075: queriedServers.add(curServer);
1076: }
1077: // end of round
1078:
1079: if (received & parsed & correctAnswer) {
1080: // correct answer received
1081: return receivedMes;
1082: }
1083: curTimeout *= 2;
1084: }
1085: // give up - no correct message has been received
1086: return null;
1087: }
1088:
1089: /**
1090: * Analyzes the answer message and constructs an analysis report.
1091: *
1092: * @param request
1093: * the request has been send to the server
1094: * @param answer
1095: * the answer has been received
1096: * @param workZone
1097: * the current resolver's work zone
1098: * @return analysis report TODO may be optimized
1099: */
1100: AnalysisReport analyzeAnswer(Message request, Message answer)
1101: throws DomainProtocolException {
1102: Enumeration<QuestionRecord> questions = request
1103: .getQuestionRecords();
1104: Enumeration<ResourceRecord> answerRRs = answer.getAnswerRRs();
1105: Enumeration<ResourceRecord> authorityRRs = answer
1106: .getAuthorityRRs();
1107: Enumeration<ResourceRecord> additionalRRs;
1108: QuestionRecord question;
1109: Resolver.AnalysisReport report = new AnalysisReport();
1110:
1111: // Check the ID.
1112: if (request.getId() != answer.getId()) {
1113: // jndi.72=Request and Answer have different ids
1114: throw new DomainProtocolException(Messages
1115: .getString("jndi.72")); //$NON-NLS-1$
1116: }
1117:
1118: // Determine a question.
1119: if (questions.hasMoreElements()) {
1120: question = questions.nextElement();
1121: } else {
1122: // jndi.73=no question record
1123: throw new IllegalArgumentException(Messages
1124: .getString("jndi.73")); //$NON-NLS-1$
1125: }
1126: // If name error occurred - no extra processing needed.
1127: if (answer.getRCode() == ProviderConstants.NAME_ERROR) {
1128: report.nameError = true;
1129: return report;
1130: }
1131: // check truncation, truncated message should not be cached
1132: if (answer.isTc()) {
1133: report.messageWasTruncated = true;
1134: }
1135: // Analyze answer section.
1136: while (answerRRs.hasMoreElements()) {
1137: ResourceRecord curRec = answerRRs.nextElement();
1138:
1139: if (question.getQClass() == curRec.getRRClass()
1140: || question.getQClass() == ProviderConstants.ANY_QCLASS) {
1141: if (question.getQType() == ProviderConstants.ANY_QTYPE
1142: && ProviderMgr.namesAreEqual(curRec.getName(),
1143: question.getQName())) {
1144: // If we query for ANY record types and the server returns
1145: // some record for the SAME domain name we will collect
1146: // all of such records and treat
1147: // this situation as a complete answer for this query.
1148: // We will not perform any more attempts to obtain more
1149: // records.
1150:
1151: report.records.addElement(curRec);
1152: // if (LogConst.DEBUG) {
1153: // ProviderMgr.logger.fine("Adding " +
1154: // ProviderConstants.rrTypeNames[
1155: // curRec.getRRType()]);
1156: // }
1157: if (curRec.getRRType() == ProviderConstants.CNAME_TYPE) {
1158: report.aliasInfoWasReceived = true;
1159: report.newName = (String) curRec.getRData();
1160: // if (LogConst.DEBUG) {
1161: // ProviderMgr.logger.fine("Alias \"" +
1162: // report.newName + "\" was received");
1163: // }
1164: } else {
1165: // XXX have we received a complete set of records?
1166: report.completeAnswerWasReceived = true;
1167: }
1168: } else if (question.getQType() == curRec.getRRType()
1169: && ProviderMgr.namesAreEqual(question
1170: .getQName(), curRec.getName())) {
1171: // This is a situation when we get the record with the
1172: // name and type exactly matching to that we have asked for.
1173: // We will treat this as a complete answer.
1174:
1175: report.records.addElement(curRec);
1176: // if (LogConst.DEBUG) {
1177: // ProviderMgr.logger.fine("Adding " +
1178: // ProviderConstants.rrTypeNames[
1179: // curRec.getRRType()]);
1180: // }
1181: report.completeAnswerWasReceived = true;
1182: } else if (curRec.getRRType() == ProviderConstants.CNAME_TYPE
1183: && ProviderMgr.namesAreEqual(curRec.getName(),
1184: question.getQName())) {
1185: // This is the case of an alias. If we received an alias for
1186: // the name we have asked the information for then we need
1187: // to change the desired name to this newly received name.
1188: // Then we will try to find necessary information for
1189: // this new name in the current answer. If we fail then
1190: // we will continue our general lookup algorithm with the
1191: // new name instead of an old one. We will query servers
1192: // from the SLIST with this new name.
1193:
1194: // TODO this is not effective
1195: Enumeration<ResourceRecord> answerRRs2 = answer
1196: .getAnswerRRs();
1197: Enumeration<ResourceRecord> additionalRRs2 = answer
1198: .getAdditionalRRs();
1199:
1200: report.aliasInfoWasReceived = true;
1201: report.newName = (String) curRec.getRData();
1202: report.extraRecords.addElement(curRec);
1203: // if (LogConst.DEBUG) {
1204: // ProviderMgr.logger.fine("Alias \"" + report.newName +
1205: // "\" was received");
1206: // }
1207: // if we find the one of desired records in the
1208: // current answer then we will treat the answer as complete
1209: while (answerRRs2.hasMoreElements()) {
1210:
1211: // Try to look for info about newly received name
1212: // in ANSWER section.
1213:
1214: ResourceRecord tmpRec = answerRRs2
1215: .nextElement();
1216:
1217: // if (LogConst.DEBUG) {
1218: // ProviderMgr.logger.fine(
1219: // "Look for an answer in ANSWER section");
1220: // }
1221: if (tmpRec.getRRType() == question.getQType()
1222: && ProviderMgr.namesAreEqual(tmpRec
1223: .getName(), report.newName)) {
1224: // the answer is founded in ANSWER section
1225: report.records.addElement(tmpRec);
1226: // if (LogConst.DEBUG) {
1227: // ProviderMgr.logger.fine("Adding " +
1228: // ProviderConstants.rrTypeNames[
1229: // tmpRec.getRRType()]);
1230: // }
1231: report.completeAnswerWasReceived = true;
1232: }
1233: }
1234: while (additionalRRs2.hasMoreElements()) {
1235: // Try to look for info about newly received name
1236: // in ADDITIONAL section.
1237:
1238: ResourceRecord tmpRec = additionalRRs2
1239: .nextElement();
1240:
1241: // if (LogConst.DEBUG) {
1242: // ProviderMgr.logger.fine("Look for an answer in " +
1243: // "ADDITIONAL section");
1244: // }
1245: if (tmpRec.getRRType() == question.getQType()
1246: && ProviderMgr.namesAreEqual(tmpRec
1247: .getName(), report.newName)) {
1248: // the answer is founded in ADDITIONAL section
1249: report.records.addElement(tmpRec);
1250: // if (LogConst.DEBUG) {
1251: // ProviderMgr.logger.fine("Adding " +
1252: // ProviderConstants.rrTypeNames[
1253: // tmpRec.getRRType()]);
1254: // }
1255: report.completeAnswerWasReceived = true;
1256: }
1257: }
1258: // if (report.completeAnswerWasReceived) {
1259: // if (LogConst.DEBUG) {
1260: // ProviderMgr.logger.fine("Complete answer received");
1261: // }
1262: // }
1263: } else {
1264: // We have received some extra records. Let's save it for
1265: // future use.
1266:
1267: // we will treat authoritative answer as a complete answer
1268: // and in no case will perform further actions
1269: if (answer.isAA()) {
1270: report.completeAnswerWasReceived = true;
1271: }
1272: report.extraRecords.addElement(curRec);
1273: // if (LogConst.DEBUG) {
1274: // ProviderMgr.logger.fine("Adding additional record " +
1275: // ProviderConstants.rrTypeNames[
1276: // curRec.getRRType()]);
1277: // }
1278: }
1279: } else {
1280: // The record from another DNS class arrived. Just ignore it.
1281: // if (LogConst.DEBUG) {
1282: // ProviderMgr.logger.fine("Ignore records from DNS class " +
1283: // curRec.getRRClass());
1284: // }
1285: }
1286: }
1287:
1288: // analyze authority section
1289: // 1. Store all info from authority NS records; try to locate NS IPs
1290: // from additional records in case if it is not present in SLIST;
1291: // start new background lookup process if not found in additional
1292: // section
1293: // TODO current implementation isn't effective
1294: while (authorityRRs.hasMoreElements()) {
1295: ResourceRecord curRec = authorityRRs.nextElement();
1296: SList slist = SList.getInstance();
1297:
1298: // save record for future use
1299: report.extraRecords.addElement(curRec);
1300: // analyze
1301: if (curRec.getRRType() == ProviderConstants.NS_TYPE) {
1302: String serverName = (String) curRec.getRData();
1303: SList.Server server2 = new SList.Server(serverName,
1304: null, ProviderConstants.DEFAULT_DNS_PORT);
1305: SList.Server server = slist.getServerByServer(curRec
1306: .getName(), server2);
1307:
1308: report.delegationArrived = true;
1309: if (server == null) {
1310: // not found in SLIST
1311: slist.updateEntry(curRec.getName(), server2,
1312: SList.UNKNOWN);
1313: report.delegationZones.addElement(curRec.getName());
1314: server = server2;
1315: }
1316: if (server != null && server.getIP() == null) {
1317: // try to search additional records to obtain server's IP
1318: additionalRRs = answer.getAdditionalRRs();
1319: while (additionalRRs.hasMoreElements()) {
1320: ResourceRecord addRec = additionalRRs
1321: .nextElement();
1322:
1323: if (ProviderMgr.namesAreEqual(addRec.getName(),
1324: serverName)
1325: && addRec.getRRType() == ProviderConstants.A_TYPE) {
1326: server.setIP((String) addRec.getRData());
1327: }
1328: }
1329: if (server.getIP() == null) {
1330: // IP was not found in additional section
1331: // start resolving process in the background
1332: this .startResolvingThread(server.getName(),
1333: curRec.getRRClass());
1334: }
1335: }
1336: // if (LogConst.DEBUG) {
1337: // ProviderMgr.logger.fine("Delegation \"" + server +
1338: // "\" arrived");
1339: // }
1340: } // end of NS type analysis
1341: } // end of authority section analysis
1342:
1343: // analyze additional section
1344: additionalRRs = answer.getAdditionalRRs();
1345: while (additionalRRs.hasMoreElements()) {
1346: ResourceRecord addRec = additionalRRs.nextElement();
1347:
1348: report.extraRecords.addElement(addRec);
1349: // if (LogConst.DEBUG) {
1350: // ProviderMgr.logger.fine("Adding additional record " +
1351: // ProviderConstants.rrTypeNames[addRec.getRRType()]);
1352: // }
1353: }
1354:
1355: // Fixing RRSet TTL issue.
1356: // If TTL fields in RRSet are not all the same then we need to set
1357: // all TTLs to lowest found value.
1358: // See RFC 2181 point 5.2
1359:
1360: // checking report.records and report.extraRecords
1361: for (int k = 0; k < 2; k++) {
1362: Vector<ResourceRecord> records = null;
1363: HashSet<String> processed = new HashSet<String>();
1364:
1365: switch (k) {
1366: case 0:
1367: records = report.records;
1368: break;
1369: case 1:
1370: records = report.extraRecords;
1371: break;
1372: }
1373: for (int i = 0; i < records.size(); i++) {
1374: ResourceRecord rr = records.elementAt(i);
1375: String key = rr.getName()
1376: + " " + rr.getRRClass() + " " + //$NON-NLS-1$ //$NON-NLS-2$
1377: rr.getRRType();
1378: long ttl = rr.getTtl();
1379: Vector<ResourceRecord> objToUpdateTTL = new Vector<ResourceRecord>();
1380:
1381: if (processed.contains(key)) {
1382: continue;
1383: }
1384: objToUpdateTTL.addElement(rr);
1385: // look forward for records with the same NAME CLASS TYPE
1386: for (int j = i; j < records.size(); j++) {
1387: ResourceRecord rr2 = records.elementAt(j);
1388: String key2 = rr2.getName()
1389: + " " + rr2.getRRClass() + " " + //$NON-NLS-1$ //$NON-NLS-2$
1390: rr2.getRRType();
1391: long ttl2 = rr2.getTtl();
1392:
1393: if (processed.contains(key2)) {
1394: continue;
1395: }
1396: if (key.equals(key2)) {
1397: if (ttl > ttl2) {
1398: ttl = ttl2;
1399: }
1400: objToUpdateTTL.addElement(rr2);
1401: }
1402: }
1403: // update TTL if necessary
1404: for (int j = 0; j < objToUpdateTTL.size(); j++) {
1405: ResourceRecord rr2 = objToUpdateTTL.elementAt(j);
1406:
1407: if (rr2.getTtl() != ttl) {
1408: rr2.setTtl(ttl);
1409: }
1410: }
1411: // don't process such NAME CLASS TYPE combination any more
1412: processed.add(key);
1413: }
1414: } // fixing RRSet TTL issue
1415:
1416: return report;
1417: }
1418:
1419: /**
1420: * Creates a new <code>Message</code> object and fills some of it's
1421: * standard fields.
1422: *
1423: * @return created <code>Message</code> object
1424: */
1425: static Message createMessageForSending(String desiredName,
1426: int recType, int recClass) throws DomainProtocolException {
1427: Message mes = new Message();
1428: QuestionRecord qr = new QuestionRecord();
1429:
1430: mes.setId(rndGen.nextInt() & 0xffff);
1431: mes.setQR(ProviderConstants.QR_QUERY);
1432: mes.setOpCode(ProviderConstants.QUERY);
1433: mes.setRD(true);
1434: mes.setQDCount(1);
1435: qr.setQName(desiredName);
1436: qr.setQType(recType);
1437: qr.setQClass(recClass);
1438: mes.addQuestionRecord(qr);
1439: return mes;
1440: }
1441:
1442: /**
1443: * Starts new resolver thread that will be searching for IP of the given
1444: * hostname.
1445: *
1446: * @param hostname
1447: * hostname to resolve
1448: * @param dnsClass
1449: * DNS class of host
1450: */
1451: void startResolvingThread(String hostname, int dnsClass) {
1452: Thread newThread;
1453: Resolver.ThreadListEntry newEntry;
1454: int classes[] = new int[1];
1455:
1456: synchronized (threadListSemaphore) {
1457: // check that no currently running thread looks for this hostname
1458: for (int i = 0; i < resolverThreads.size(); i++) {
1459: Resolver.ThreadListEntry entry = resolverThreads.get(i);
1460: if (ProviderMgr.namesAreEqual(hostname,
1461: entry.serverNameToResolve)
1462: && entry.dnsClass == dnsClass) {
1463: // this hostname is already under investigation
1464: // exiting
1465: return;
1466: }
1467: }
1468: // check if the hostname is already scheduled for resolving
1469: for (int i = 0; i < hostnamesToResolve.size(); i++) {
1470: Resolver.ThreadListEntry entry = hostnamesToResolve
1471: .get(i);
1472: if (ProviderMgr.namesAreEqual(hostname,
1473: entry.serverNameToResolve)
1474: && entry.dnsClass == dnsClass) {
1475: // this hostname is already scheduled for resolving
1476: // exiting
1477: return;
1478: }
1479: }
1480: // check that the maximum number of threads is not exceeded
1481: if (resolverThreads.size() >= threadNumberLimit) {
1482: // maximum possible number of threads is reached already
1483: return;
1484: }
1485: classes[0] = dnsClass;
1486: newEntry = new Resolver.ThreadListEntry();
1487: newEntry.serverNameToResolve = hostname;
1488: newEntry.dnsClass = dnsClass;
1489: hostnamesToResolve.add(newEntry);
1490: // starting new thread that should make further updates by itself
1491: newThread = new Thread(this );
1492: // if (LogConst.DEBUG) {
1493: // ProviderMgr.logger.fine("Starting new resolver thread," +
1494: // " target hostname: " + hostname);
1495: // }
1496: newThread.start();
1497: }
1498: }
1499:
1500: /**
1501: * Start background search of the address of next unresolved server hostname
1502: */
1503: public void run() {
1504: SList slist = SList.getInstance();
1505: Enumeration<ResourceRecord> foundRecords;
1506: Resolver.ThreadListEntry entryToProcess;
1507: int[] classes = new int[1];
1508:
1509: // update lists
1510: synchronized (threadListSemaphore) {
1511: if (hostnamesToResolve.size() > 0) {
1512: entryToProcess = hostnamesToResolve.get(0);
1513: hostnamesToResolve.remove(0);
1514: entryToProcess.thread = Thread.currentThread();
1515: resolverThreads.add(entryToProcess);
1516: } else {
1517: // ProviderMgr.logger.warning(
1518: // "Resolver thread: no host name to resolve");
1519: return;
1520: }
1521: }
1522: // lookup
1523: try {
1524: classes[0] = entryToProcess.dnsClass;
1525: foundRecords = lookup(entryToProcess.serverNameToResolve,
1526: new int[] { ProviderConstants.A_TYPE }, classes);
1527: while (foundRecords != null
1528: && foundRecords.hasMoreElements()) {
1529: // we will take all A records and store all of them in SLIST
1530: ResourceRecord rr = foundRecords.nextElement();
1531:
1532: if (rr.getRRType() == ProviderConstants.A_TYPE) {
1533: slist.setServerIP(
1534: entryToProcess.serverNameToResolve,
1535: (String) rr.getRData());
1536: }
1537: }
1538: } catch (NamingException e) {
1539: // just ignore it
1540: }
1541: // update resolver threads list, remove info about current thread
1542: synchronized (threadListSemaphore) {
1543: for (int i = 0; i < resolverThreads.size(); i++) {
1544: Resolver.ThreadListEntry entry = resolverThreads.get(i);
1545:
1546: if (ProviderMgr.namesAreEqual(
1547: entryToProcess.serverNameToResolve,
1548: entry.serverNameToResolve)
1549: && entryToProcess.dnsClass == entry.dnsClass) {
1550: resolverThreads.remove(i);
1551: break;
1552: }
1553: }
1554: }
1555: // exiting
1556: }
1557:
1558: /**
1559: * Analysis report.
1560: *
1561: * @see Resolver#analyzeAnswer(Message, Message)
1562: */
1563: static class AnalysisReport {
1564:
1565: boolean completeAnswerWasReceived = false;
1566:
1567: boolean nameError = false;
1568:
1569: boolean delegationArrived = false;
1570:
1571: boolean aliasInfoWasReceived = false;
1572:
1573: boolean messageWasTruncated = false;
1574:
1575: Vector<ResourceRecord> records;
1576:
1577: Vector<String> delegationZones;
1578:
1579: String newName = null;
1580:
1581: Vector<ResourceRecord> extraRecords;
1582:
1583: AnalysisReport() {
1584: records = new Vector<ResourceRecord>();
1585: delegationZones = new Vector<String>();
1586: extraRecords = new Vector<ResourceRecord>();
1587: }
1588:
1589: }
1590: }
|