1 /*
2  * Copyright (c) 2000, 2011, 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      */
Resolver(String[] servers, int timeout, int retries)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 
close()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      */
query(DnsName fqdn, int rrclass, int rrtype, boolean recursion, boolean auth)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      */
queryZone(DnsName zone, int rrclass, boolean recursion)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      */
findZoneName(DnsName fqdn, int rrclass, boolean recursion)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      */
findSoa(DnsName zone, int rrclass, boolean recursion)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      */
findNameServers(DnsName zone, boolean recursion)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 }
191