1/*
2 * Copyright (c) 2002, 2012, 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
26package sun.net.dns;
27
28import java.util.List;
29import java.util.LinkedList;
30import java.util.StringTokenizer;
31import java.io.BufferedReader;
32import java.io.FileReader;
33import java.io.IOException;
34
35/*
36 * An implementation of ResolverConfiguration for Solaris
37 * and Linux.
38 */
39
40public 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    //
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.length() == 0)
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.length() == 0)
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
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
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
181        // LOCALDOMAIN has absolute priority on Solaris
182
183        String localDomain = localDomain0();
184        if (localDomain != null && localDomain.length() > 0) {
185            sl = new LinkedList<>();
186            sl.add(localDomain);
187            return sl;
188        }
189
190        // try domain keyword in /etc/resolv.conf
191
192        sl = java.security.AccessController.doPrivileged(
193                 new java.security.PrivilegedAction<>() {
194                    public LinkedList<String> run() {
195                        LinkedList<String> ll;
196
197                        ll = resolvconf("domain", 1, 1);
198                        if (ll.size() > 0) {
199                            return ll;
200                        }
201                        return null;
202
203                    } /* run */
204                });
205        if (sl != null) {
206            return sl;
207        }
208
209        // no local domain so try fallback (RPC) domain or
210        // hostName
211
212        sl = new LinkedList<>();
213        String domain = fallbackDomain0();
214        if (domain != null && domain.length() > 0) {
215            sl.add(domain);
216        }
217
218        return sl;
219    }
220
221
222    // ----
223
224    ResolverConfigurationImpl() {
225        opts = new OptionsImpl();
226    }
227
228    @SuppressWarnings("unchecked")
229    public List<String> searchlist() {
230        synchronized (lock) {
231            loadConfig();
232
233            // List is mutable so return a shallow copy
234            return (List<String>)searchlist.clone();
235        }
236    }
237
238    @SuppressWarnings("unchecked")
239    public List<String> nameservers() {
240        synchronized (lock) {
241            loadConfig();
242
243            // List is mutable so return a shallow copy
244
245          return (List<String>)nameservers.clone();
246
247        }
248    }
249
250    public Options options() {
251        return opts;
252    }
253
254
255    // --- Native methods --
256
257    static native String localDomain0();
258
259    static native String fallbackDomain0();
260
261    static {
262        java.security.AccessController.doPrivileged(
263            new java.security.PrivilegedAction<>() {
264                public Void run() {
265                    System.loadLibrary("net");
266                    return null;
267                }
268            });
269    }
270
271}
272
273/**
274 * Implementation of {@link ResolverConfiguration.Options}
275 */
276class OptionsImpl extends ResolverConfiguration.Options {
277}
278