1 /*
   2  * Copyright (c) 2002, 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 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     //
  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                     ll.add(val);
  89                     if (--maxvalues == 0) {
  90                         break;
  91                     }
  92                 }
  93                 if (--maxkeywords == 0) {
  94                     break;
  95                 }
  96             }
  97             in.close();
  98         } catch (IOException ioe) {
  99             // problem reading value
 100         }
 101 
 102         return ll;
 103     }
 104 
 105     private LinkedList<String> searchlist;
 106     private LinkedList<String> nameservers;
 107 
 108 
 109     // Load DNS configuration from OS
 110 
 111     private void loadConfig() {
 112         assert Thread.holdsLock(lock);
 113 
 114         // check if cached settings have expired.
 115         if (lastRefresh >= 0) {
 116             long currTime = System.currentTimeMillis();
 117             if ((currTime - lastRefresh) < TIMEOUT) {
 118                 return;
 119             }
 120         }
 121 
 122         // get the name servers from /etc/resolv.conf
 123         nameservers =
 124             java.security.AccessController.doPrivileged(
 125                 new java.security.PrivilegedAction<LinkedList<String>>() {
 126                     public LinkedList<String> run() {
 127                         // typically MAXNS is 3 but we've picked 5 here
 128                         // to allow for additional servers if required.
 129                         return resolvconf("nameserver", 1, 5);
 130                     } /* run */
 131                 });
 132 
 133         // get the search list (or domain)
 134         searchlist = getSearchList();
 135 
 136         // update the timestamp on the configuration
 137         lastRefresh = System.currentTimeMillis();
 138     }
 139 
 140 
 141     // obtain search list or local domain
 142 
 143     private LinkedList<String> getSearchList() {
 144 
 145         LinkedList<String> sl;
 146 
 147         // first try the search keyword in /etc/resolv.conf
 148 
 149         sl = java.security.AccessController.doPrivileged(
 150                  new java.security.PrivilegedAction<LinkedList<String>>() {
 151                     public LinkedList<String> run() {
 152                         LinkedList<String> ll;
 153 
 154                         // first try search keyword (max 6 domains)
 155                         ll = resolvconf("search", 6, 1);
 156                         if (ll.size() > 0) {
 157                             return ll;
 158                         }
 159 
 160                         return null;
 161 
 162                     } /* run */
 163 
 164                 });
 165         if (sl != null) {
 166             return sl;
 167         }
 168 
 169         // No search keyword so use local domain
 170 
 171 
 172         // LOCALDOMAIN has absolute priority on Solaris
 173 
 174         String localDomain = localDomain0();
 175         if (localDomain != null && localDomain.length() > 0) {
 176             sl = new LinkedList<String>();
 177             sl.add(localDomain);
 178             return sl;
 179         }
 180 
 181         // try domain keyword in /etc/resolv.conf
 182 
 183         sl = java.security.AccessController.doPrivileged(
 184                  new java.security.PrivilegedAction<LinkedList<String>>() {
 185                     public LinkedList<String> run() {
 186                         LinkedList<String> ll;
 187 
 188                         ll = resolvconf("domain", 1, 1);
 189                         if (ll.size() > 0) {
 190                             return ll;
 191                         }
 192                         return null;
 193 
 194                     } /* run */
 195                 });
 196         if (sl != null) {
 197             return sl;
 198         }
 199 
 200         // no local domain so try fallback (RPC) domain or
 201         // hostName
 202 
 203         sl = new LinkedList<>();
 204         String domain = fallbackDomain0();
 205         if (domain != null && domain.length() > 0) {
 206             sl.add(domain);
 207         }
 208 
 209         return sl;
 210     }
 211 
 212 
 213     // ----
 214 
 215     ResolverConfigurationImpl() {
 216         opts = new OptionsImpl();
 217     }
 218 
 219     @SuppressWarnings("unchecked")
 220     public List<String> searchlist() {
 221         synchronized (lock) {
 222             loadConfig();
 223 
 224             // List is mutable so return a shallow copy
 225             return (List<String>)searchlist.clone();
 226         }
 227     }
 228 
 229     @SuppressWarnings("unchecked")
 230     public List<String> nameservers() {
 231         synchronized (lock) {
 232             loadConfig();
 233 
 234             // List is mutable so return a shallow copy
 235 
 236           return (List<String>)nameservers.clone();
 237 
 238         }
 239     }
 240 
 241     public Options options() {
 242         return opts;
 243     }
 244 
 245 
 246     // --- Native methods --
 247 
 248     static native String localDomain0();
 249 
 250     static native String fallbackDomain0();
 251 
 252     static {
 253         java.security.AccessController.doPrivileged(
 254             new java.security.PrivilegedAction<Void>() {
 255                 public Void run() {
 256                     System.loadLibrary("net");
 257                     return null;
 258                 }
 259             });
 260     }
 261 
 262 }
 263 
 264 /**
 265  * Implementation of {@link ResolverConfiguration.Options}
 266  */
 267 class OptionsImpl extends ResolverConfiguration.Options {
 268 }