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.ArrayList;
  29 import java.util.List;
  30 import java.util.concurrent.TimeUnit;
  31 
  32 /*
  33  * An implementation of sun.net.ResolverConfiguration for Windows.
  34  */
  35 
  36 public class ResolverConfigurationImpl
  37     extends ResolverConfiguration
  38 {
  39     // Lock held whilst loading configuration or checking
  40     private static Object lock = new Object();
  41 
  42     // Resolver options
  43     private final Options opts;
  44 
  45     // Addresses have changed. We default to true to make sure we
  46     // resolve the first time it is requested.
  47     private static boolean changed = true;
  48 
  49     // Time of last refresh.
  50     private static long lastRefresh;
  51 
  52     // Cache timeout (120 seconds) - should be converted into property
  53     // or configured as preference in the future.
  54     private static final long TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(120);
  55 
  56     // DNS suffix list and name servers populated by native method
  57     private static String os_searchlist;
  58     private static String os_nameservers;
  59 
  60     // Cached lists
  61     private static ArrayList<String> searchlist;
  62     private static ArrayList<String> nameservers;
  63 
  64     // Parse string that consists of token delimited by comma
  65     // and return ArrayList. Refer to ResolverConfigurationImpl.c and
  66     // strappend to see how the string is created.
  67     private ArrayList<String> stringToList(String str) {
  68         // String is delimited by comma.
  69         String[] tokens = str.split(",");
  70         ArrayList<String> l = new ArrayList<>(tokens.length);
  71         for (String s : tokens) {
  72             if (!s.isEmpty() && !l.contains(s)) {
  73                 l.add(s);
  74             }
  75         }
  76         l.trimToSize();
  77         return l;
  78     }
  79 
  80     // Parse string that consists of token delimited by comma
  81     // and return ArrayList.  Refer to ResolverConfigurationImpl.c and
  82     // strappend to see how the string is created.
  83     // In addition to splitting the string, converts IPv6 addresses to
  84     // BSD-style.
  85     private ArrayList<String> addressesToList(String str) {
  86         // String is delimited by comma
  87         String[] tokens = str.split(",");
  88         ArrayList<String> l = new ArrayList<>(tokens.length);
  89 
  90         for (String s : tokens) {
  91             if (!s.isEmpty()) {
  92                 if (s.indexOf(':') >= 0 && s.charAt(0) != '[') {
  93                     // Not BSD style
  94                     s = '[' + s + ']';
  95                 }
  96                 if (!s.isEmpty() && !l.contains(s)) {
  97                     l.add(s);
  98                 }
  99             }
 100         }
 101         l.trimToSize();
 102         return l;
 103     }
 104 
 105     // Load DNS configuration from OS
 106 
 107     private void loadConfig() {
 108         assert Thread.holdsLock(lock);
 109 
 110         // A change in the network address of the machine usually indicates
 111         // a change in DNS configuration too so we always refresh the config
 112         // after such a change.
 113         if (changed) {
 114             changed = false;
 115         } else {
 116             // Otherwise we refresh if TIMEOUT_NANOS has passed since last
 117             // load.
 118             long currTime = System.nanoTime();
 119             // lastRefresh will always have been set once because we start with
 120             // changed = true.
 121             if ((currTime - lastRefresh) < TIMEOUT_NANOS) {
 122                 return;
 123             }
 124         }
 125 
 126         // Native code that uses Windows API to find out the DNS server
 127         // addresses and search suffixes. It builds a comma-delimited string
 128         // of nameservers and domain suffixes and sets them to the static
 129         // os_nameservers and os_searchlist. We then split these into Java
 130         // Lists here.
 131         loadDNSconfig0();
 132 
 133         // Record the time of update and refresh the lists of addresses /
 134         // domain suffixes.
 135         lastRefresh = System.nanoTime();
 136         searchlist = stringToList(os_searchlist);
 137         nameservers = addressesToList(os_nameservers);
 138         os_searchlist = null;                       // can be GC'ed
 139         os_nameservers = null;
 140     }
 141 
 142     ResolverConfigurationImpl() {
 143         opts = new OptionsImpl();
 144     }
 145 
 146     @SuppressWarnings("unchecked") // clone()
 147     public List<String> searchlist() {
 148         synchronized (lock) {
 149             loadConfig();
 150 
 151             // List is mutable so return a shallow copy
 152             return (List<String>)searchlist.clone();
 153         }
 154     }
 155 
 156     @SuppressWarnings("unchecked") // clone()
 157     public List<String> nameservers() {
 158         synchronized (lock) {
 159             loadConfig();
 160 
 161             // List is mutable so return a shallow copy
 162             return (List<String>)nameservers.clone();
 163          }
 164     }
 165 
 166     public Options options() {
 167         return opts;
 168     }
 169 
 170     // --- Address Change Listener
 171 
 172     static class AddressChangeListener extends Thread {
 173         public void run() {
 174             for (;;) {
 175                 // wait for configuration to change
 176                 if (notifyAddrChange0() != 0)
 177                     return;
 178                 synchronized (lock) {
 179                     changed = true;
 180                 }
 181             }
 182         }
 183     }
 184 
 185 
 186     // --- Native methods --
 187 
 188     static native void init0();
 189 
 190     static native void loadDNSconfig0();
 191 
 192     static native int notifyAddrChange0();
 193 
 194     static {
 195         jdk.internal.loader.BootLoader.loadLibrary("net");
 196         init0();
 197 
 198         // start the address listener thread
 199         AddressChangeListener thr = new AddressChangeListener();
 200         thr.setDaemon(true);
 201         thr.start();
 202     }
 203 }
 204 
 205 /**
 206  * Implementation of {@link ResolverConfiguration.Options}
 207  */
 208 class OptionsImpl extends ResolverConfiguration.Options {
 209 }