1 /*
   2  * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.dns;
  27 
  28 
  29 import javax.naming.*;
  30 
  31 
  32 /**
  33  * The Resolver class performs DNS client operations in support of DnsContext.
  34  *
  35  * <p> Every DnsName instance passed to or returned from a method of
  36  * this class should be fully-qualified and contain a root label (an
  37  * empty component at position 0).
  38  *
  39  * @author Scott Seligman
  40  */
  41 
  42 class Resolver {
  43 
  44     private DnsClient dnsClient;
  45     private int timeout;                // initial timeout on UDP queries in ms
  46     private int retries;                // number of UDP retries
  47 
  48 
  49     /*
  50      * Constructs a new Resolver given its servers and timeout parameters.
  51      * Each server is of the form "server[:port]".
  52      * IPv6 literal host names include delimiting brackets.
  53      * There must be at least one server.
  54      * "timeout" is the initial timeout interval (in ms) for UDP queries,
  55      * and "retries" gives the number of retries per server.
  56      */
  57     Resolver(String[] servers, int timeout, int retries)
  58             throws NamingException {
  59         this.timeout = timeout;
  60         this.retries = retries;
  61         dnsClient = new DnsClient(servers, timeout, retries);
  62     }
  63 
  64     public void close() {
  65         dnsClient.close();
  66         dnsClient = null;
  67     }
  68 
  69 
  70     /*
  71      * Queries resource records of a particular class and type for a
  72      * given domain name.
  73      * Useful values of rrclass are ResourceRecord.[Q]CLASS_xxx.
  74      * Useful values of rrtype are ResourceRecord.[Q]TYPE_xxx.
  75      * If recursion is true, recursion is requested on the query.
  76      * If auth is true, only authoritative responses are accepted.
  77      */
  78     ResourceRecords query(DnsName fqdn, int rrclass, int rrtype,
  79                           boolean recursion, boolean auth)
  80             throws NamingException {
  81         return dnsClient.query(fqdn, rrclass, rrtype, recursion, auth);
  82     }
  83 
  84     /*
  85      * Queries all resource records of a zone given its domain name and class.
  86      * If recursion is true, recursion is requested on the query to find
  87      * the name server (and also on the zone transfer, but it won't matter).
  88      */
  89     ResourceRecords queryZone(DnsName zone, int rrclass, boolean recursion)
  90             throws NamingException {
  91 
  92         DnsClient cl =
  93             new DnsClient(findNameServers(zone, recursion), timeout, retries);
  94         try {
  95             return cl.queryZone(zone, rrclass, recursion);
  96         } finally {
  97             cl.close();
  98         }
  99     }
 100 
 101     /*
 102      * Finds the zone of a given domain name.  The method is to look
 103      * for the first SOA record on the path from the given domain to
 104      * the root.  This search may be partially bypassed if the zone's
 105      * SOA record is received in the authority section of a response.
 106      * If recursion is true, recursion is requested on any queries.
 107      */
 108     DnsName findZoneName(DnsName fqdn, int rrclass, boolean recursion)
 109             throws NamingException {
 110 
 111         fqdn = (DnsName) fqdn.clone();
 112         while (fqdn.size() > 1) {       // while below root
 113             ResourceRecords rrs = null;
 114             try {
 115                 rrs = query(fqdn, rrclass, ResourceRecord.TYPE_SOA,
 116                             recursion, false);
 117             } catch (NameNotFoundException e) {
 118                 throw e;
 119             } catch (NamingException e) {
 120                 // Ignore error and keep searching up the tree.
 121             }
 122             if (rrs != null) {
 123                 if (rrs.answer.size() > 0) {    // found zone's SOA
 124                     return fqdn;
 125                 }
 126                 // Look for an SOA record giving the zone's top node.
 127                 for (int i = 0; i < rrs.authority.size(); i++) {
 128                     ResourceRecord rr = rrs.authority.elementAt(i);
 129                     if (rr.getType() == ResourceRecord.TYPE_SOA) {
 130                         DnsName zone = rr.getName();
 131                         if (fqdn.endsWith(zone)) {
 132                             return zone;
 133                         }
 134                     }
 135                 }
 136             }
 137             fqdn.remove(fqdn.size() - 1);       // one step rootward
 138         }
 139         return fqdn;                    // no SOA found below root, so
 140                                         // return root
 141     }
 142 
 143     /*
 144      * Finds a zone's SOA record.  Returns null if no SOA is found (in
 145      * which case "zone" is not actually a zone).
 146      * If recursion is true, recursion is requested on the query.
 147      */
 148      ResourceRecord findSoa(DnsName zone, int rrclass, boolean recursion)
 149             throws NamingException {
 150 
 151         ResourceRecords rrs = query(zone, rrclass, ResourceRecord.TYPE_SOA,
 152                                     recursion, false);
 153         for (int i = 0; i < rrs.answer.size(); i++) {
 154             ResourceRecord rr = rrs.answer.elementAt(i);
 155             if (rr.getType() == ResourceRecord.TYPE_SOA) {
 156                 return rr;
 157             }
 158         }
 159         return null;
 160     }
 161 
 162     /*
 163      * Finds the name servers of a zone.  <tt>zone</tt> is a fully-qualified
 164      * domain name at the top of a zone.
 165      * If recursion is true, recursion is requested on the query.
 166      */
 167     private String[] findNameServers(DnsName zone, boolean recursion)
 168             throws NamingException {
 169 
 170         // %%% As an optimization, could look in authority section of
 171         // findZoneName() response first.
 172         ResourceRecords rrs =
 173             query(zone, ResourceRecord.CLASS_INTERNET, ResourceRecord.TYPE_NS,
 174                   recursion, false);
 175         String[] ns = new String[rrs.answer.size()];
 176         for (int i = 0; i < ns.length; i++) {
 177             ResourceRecord rr = rrs.answer.elementAt(i);
 178             if (rr.getType() != ResourceRecord.TYPE_NS) {
 179                 throw new CommunicationException("Corrupted DNS message");
 180             }
 181             ns[i] = (String) rr.getRdata();
 182 
 183             // Server name will be passed to InetAddress.getByName(), which
 184             // may not be able to handle a trailing dot.
 185             // assert ns[i].endsWith(".");
 186             ns[i] = ns[i].substring(0, ns[i].length() - 1);
 187         }
 188         return ns;
 189     }
 190 }