1 /*
   2  * Copyright (c) 2002, 2019, 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.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 
 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         // 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 
 214     ResolverConfigurationImpl() {
 215         opts = new OptionsImpl();
 216     }
 217 
 218     @SuppressWarnings("unchecked")
 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")
 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 
 240     public Options options() {
 241         return opts;
 242     }
 243 
 244 
 245     // --- Native methods --
 246 
 247     static native String localDomain0();
 248 
 249     static native String fallbackDomain0();
 250 
 251     static {
 252         jdk.internal.loader.BootLoader.loadLibrary("net");
 253     }
 254 
 255 }
 256 
 257 /**
 258  * Implementation of {@link ResolverConfiguration.Options}
 259  */
 260 class OptionsImpl extends ResolverConfiguration.Options {
 261 }