1 /*
2  * Copyright (c) 2002, 2020, 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 sun.net.dns;
27 
28 import java.util.List;
29 import java.util.LinkedList;
30 import java.util.StringTokenizer;
31 import java.io.BufferedReader;
32 import java.io.FileReader;
33 import java.io.IOException;
34 
35 /*
36  * An implementation of ResolverConfiguration for Solaris
37  * and Linux.
38  */
39 
40 public class ResolverConfigurationImpl
41     extends ResolverConfiguration
42 {
43     // Lock helds whilst loading configuration or checking
44     private static Object lock = new Object();
45 
46     // Time of last refresh.
47     private static long lastRefresh = -1;
48 
49     // Cache timeout (300 seconds) - should be converted into property
50     // or configured as preference in the future.
51     private static final int TIMEOUT = 300000;
52 
53     // Resolver options
54     private final Options opts;
55 
56     // Parse /etc/resolv.conf to get the values for a particular
57     // keyword.
58     //
resolvconf(String keyword, int maxperkeyword, int maxkeywords)59     private LinkedList<String> resolvconf(String keyword,
60                                           int maxperkeyword,
61                                           int maxkeywords)
62     {
63         LinkedList<String> ll = new LinkedList<>();
64 
65         try {
66             BufferedReader in =
67                 new BufferedReader(new FileReader("/etc/resolv.conf"));
68             String line;
69             while ((line = in.readLine()) != null) {
70                 int maxvalues = maxperkeyword;
71                 if (line.isEmpty())
72                    continue;
73                 if (line.charAt(0) == '#' || line.charAt(0) == ';')
74                     continue;
75                 if (!line.startsWith(keyword))
76                     continue;
77                 String value = line.substring(keyword.length());
78                 if (value.isEmpty())
79                     continue;
80                 if (value.charAt(0) != ' ' && value.charAt(0) != '\t')
81                     continue;
82                 StringTokenizer st = new StringTokenizer(value, " \t");
83                 while (st.hasMoreTokens()) {
84                     String val = st.nextToken();
85                     if (val.charAt(0) == '#' || val.charAt(0) == ';') {
86                         break;
87                     }
88                     if ("nameserver".equals(keyword)) {
89                         if (val.indexOf(':') >= 0 &&
90                             val.indexOf('.') < 0 && // skip for IPv4 literals with port
91                             val.indexOf('[') < 0 &&
92                             val.indexOf(']') < 0 ) {
93                             // IPv6 literal, in non-BSD-style.
94                             val = "[" + val + "]";
95                         }
96                     }
97                     ll.add(val);
98                     if (--maxvalues == 0) {
99                         break;
100                     }
101                 }
102                 if (--maxkeywords == 0) {
103                     break;
104                 }
105             }
106             in.close();
107         } catch (IOException ioe) {
108             // problem reading value
109         }
110 
111         return ll;
112     }
113 
114     private LinkedList<String> searchlist;
115     private LinkedList<String> nameservers;
116 
117 
118     // Load DNS configuration from OS
119 
loadConfig()120     private void loadConfig() {
121         assert Thread.holdsLock(lock);
122 
123         // check if cached settings have expired.
124         if (lastRefresh >= 0) {
125             long currTime = System.currentTimeMillis();
126             if ((currTime - lastRefresh) < TIMEOUT) {
127                 return;
128             }
129         }
130 
131         // get the name servers from /etc/resolv.conf
132         nameservers =
133             java.security.AccessController.doPrivileged(
134                 new java.security.PrivilegedAction<>() {
135                     public LinkedList<String> run() {
136                         // typically MAXNS is 3 but we've picked 5 here
137                         // to allow for additional servers if required.
138                         return resolvconf("nameserver", 1, 5);
139                     } /* run */
140                 });
141 
142         // get the search list (or domain)
143         searchlist = getSearchList();
144 
145         // update the timestamp on the configuration
146         lastRefresh = System.currentTimeMillis();
147     }
148 
149 
150     // obtain search list or local domain
151 
getSearchList()152     private LinkedList<String> getSearchList() {
153 
154         LinkedList<String> sl;
155 
156         // first try the search keyword in /etc/resolv.conf
157 
158         sl = java.security.AccessController.doPrivileged(
159                  new java.security.PrivilegedAction<>() {
160                     public LinkedList<String> run() {
161                         LinkedList<String> ll;
162 
163                         // first try search keyword (max 6 domains)
164                         ll = resolvconf("search", 6, 1);
165                         if (ll.size() > 0) {
166                             return ll;
167                         }
168 
169                         return null;
170 
171                     } /* run */
172 
173                 });
174         if (sl != null) {
175             return sl;
176         }
177 
178         // No search keyword so use local domain
179 
180         // try domain keyword in /etc/resolv.conf
181 
182         sl = java.security.AccessController.doPrivileged(
183                  new java.security.PrivilegedAction<>() {
184                     public LinkedList<String> run() {
185                         LinkedList<String> ll;
186 
187                         ll = resolvconf("domain", 1, 1);
188                         if (ll.size() > 0) {
189                             return ll;
190                         }
191                         return null;
192 
193                     } /* run */
194                 });
195         if (sl != null) {
196             return sl;
197         }
198 
199         // no local domain so try fallback (RPC) domain or
200         // hostName
201 
202         sl = new LinkedList<>();
203         String domain = fallbackDomain0();
204         if (domain != null && !domain.isEmpty()) {
205             sl.add(domain);
206         }
207 
208         return sl;
209     }
210 
211 
212     // ----
213 
ResolverConfigurationImpl()214     ResolverConfigurationImpl() {
215         opts = new OptionsImpl();
216     }
217 
218     @SuppressWarnings("unchecked")
searchlist()219     public List<String> searchlist() {
220         synchronized (lock) {
221             loadConfig();
222 
223             // List is mutable so return a shallow copy
224             return (List<String>)searchlist.clone();
225         }
226     }
227 
228     @SuppressWarnings("unchecked")
nameservers()229     public List<String> nameservers() {
230         synchronized (lock) {
231             loadConfig();
232 
233             // List is mutable so return a shallow copy
234 
235           return (List<String>)nameservers.clone();
236 
237         }
238     }
239 
options()240     public Options options() {
241         return opts;
242     }
243 
244 
245     // --- Native methods --
246 
fallbackDomain0()247     static native String fallbackDomain0();
248 
249     static {
250         jdk.internal.loader.BootLoader.loadLibrary("net");
251     }
252 
253 }
254 
255 /**
256  * Implementation of {@link ResolverConfiguration.Options}
257  */
258 class OptionsImpl extends ResolverConfiguration.Options {
259 }
260