src/jdk.dns.client/windows/classes/jdk/dns/conf/DnsResolverConfiguration.java
author aefimov
Thu, 14 Nov 2019 23:13:47 +0000
branchaefimov-dns-client-branch
changeset 59100 b92aac38b046
parent 58870 35c438a6d45c
permissions -rw-r--r--
aefimov-dns-client-branch: fix windows builds

/*
 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.dns.conf;

import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.locks.ReentrantLock;

public class DnsResolverConfiguration {
    // Lock held whilst loading configuration or checking
    private static ReentrantLock lock = new ReentrantLock();

    // Addresses have changed
    private static boolean changed = false;

    // Time of last refresh.
    private static long lastRefresh = -1;

    // Cache timeout (120 seconds in nanoseconds) - should be converted into property
    // or configured as preference in the future.
    private static final long TIMEOUT = 120_000_000_000L;

    // DNS suffix list and name servers populated by native method
    private static String os_searchlist;
    private static String os_nameservers;

    // Cached lists
    private static LinkedList<String> searchlist;
    private static LinkedList<String> nameservers;
    private volatile String domain = "";

    // Parse string that consists of token delimited by space or commas
    // and return LinkedHashMap
    private LinkedList<String> stringToList(String str) {
        LinkedList<String> ll = new LinkedList<>();

        // comma and space are valid delimiters
        StringTokenizer st = new StringTokenizer(str, ", ");
        while (st.hasMoreTokens()) {
            String s = st.nextToken();
            if (!ll.contains(s)) {
                ll.add(s);
            }
        }
        return ll;
    }

    public static String getDefaultHostsFileLocation() {
        return Paths.get(System.getenv("SystemRoot"))
                .resolve("System32")
                .resolve("drivers")
                .resolve("etc")
                .resolve("hosts")
                .toString();
    }

    // Load DNS configuration from OS

    private void loadConfig() {
        assert lock.isHeldByCurrentThread();

        // if address have changed then DNS probably changed as well;
        // otherwise check if cached settings have expired.
        //
        if (changed) {
            changed = false;
        } else {
            if (lastRefresh >= 0) {
                long currTime = System.nanoTime();
                if ((currTime - lastRefresh) < TIMEOUT) {
                    return;
                }
            }
        }

        // load DNS configuration, update timestamp, create
        // new HashMaps from the loaded configuration
        //
        loadDNSconfig0();

        lastRefresh = System.nanoTime();
        searchlist = stringToList(os_searchlist);
        if (searchlist.size() > 0) {
            domain = searchlist.get(0);
        } else {
            domain = "";
        }
        nameservers = stringToList(os_nameservers);
        os_searchlist = null;                       // can be GC'ed
        os_nameservers = null;
    }

    public DnsResolverConfiguration() {
    }

    @SuppressWarnings("unchecked") // clone()
    public List<String> searchlist() {
        lock.lock();
        try {
            loadConfig();

            // List is mutable so return a shallow copy
            return (List<String>) searchlist.clone();
        } finally {
            lock.unlock();
        }
    }

    @SuppressWarnings("unchecked") // clone()
    public List<String> nameservers() {
        lock.lock();
        try {
            loadConfig();

            // List is mutable so return a shallow copy
            return (List<String>) nameservers.clone();
        } finally {
            lock.unlock();
        }
    }

    public String domain() {
        lock.lock();
        try {
            loadConfig();
            return domain;
        } finally {
            lock.unlock();
        }
    }

    // --- Address Change Listener

    static class AddressChangeListener extends Thread {
        public void run() {
            for (; ; ) {
                // wait for configuration to change
                if (notifyAddrChange0() != 0)
                    return;
                lock.lock();
                try {
                    changed = true;
                } finally {
                    lock.unlock();
                }
            }
        }
    }


    // --- Native methods --

    static native void init0();

    static native void loadDNSconfig0();

    static native int notifyAddrChange0();

    static {
        System.loadLibrary("resolver");
        init0();

        // start the address listener thread
        AddressChangeListener thr = new AddressChangeListener();
        thr.setDaemon(true);
        thr.start();
    }
}