| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.net.spi.nameservice.dns;  | 
 | 
 | 
 | 
import java.lang.ref.SoftReference;  | 
 | 
import java.net.InetAddress;  | 
 | 
import java.net.UnknownHostException;  | 
 | 
import javax.naming.*;  | 
 | 
import javax.naming.directory.*;  | 
 | 
import javax.naming.spi.NamingManager;  | 
 | 
import java.util.*;  | 
 | 
import sun.net.util.IPAddressUtil;  | 
 | 
import sun.net.dns.ResolverConfiguration;  | 
 | 
import sun.net.spi.nameservice.*;  | 
 | 
import java.security.AccessController;  | 
 | 
import sun.security.action.*;  | 
 | 
 | 
 | 
/*  | 
 | 
 * A name service provider based on JNDI-DNS.  | 
 | 
 */  | 
 | 
 | 
 | 
public final class DNSNameService implements NameService { | 
 | 
 | 
 | 
      | 
 | 
    private LinkedList<String> domainList = null;  | 
 | 
 | 
 | 
      | 
 | 
    private String nameProviderUrl = null;  | 
 | 
 | 
 | 
      | 
 | 
    private static ThreadLocal<SoftReference<ThreadContext>> contextRef =  | 
 | 
            new ThreadLocal<>();  | 
 | 
 | 
 | 
      | 
 | 
    private static class ThreadContext { | 
 | 
        private DirContext dirCtxt;  | 
 | 
        private List<String> nsList;  | 
 | 
 | 
 | 
        public ThreadContext(DirContext dirCtxt, List<String> nsList) { | 
 | 
            this.dirCtxt = dirCtxt;  | 
 | 
            this.nsList = nsList;  | 
 | 
        }  | 
 | 
 | 
 | 
        public DirContext dirContext() { | 
 | 
            return dirCtxt;  | 
 | 
        }  | 
 | 
 | 
 | 
        public List<String> nameservers() { | 
 | 
            return nsList;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private DirContext getTemporaryContext() throws NamingException { | 
 | 
        SoftReference<ThreadContext> ref = contextRef.get();  | 
 | 
        ThreadContext thrCtxt = null;  | 
 | 
        List<String> nsList = null;  | 
 | 
 | 
 | 
        // if no property specified we need to obtain the list of servers  | 
 | 
          | 
 | 
        if (nameProviderUrl == null)  | 
 | 
            nsList = ResolverConfiguration.open().nameservers();  | 
 | 
 | 
 | 
        // if soft reference hasn't been gc'ed no property has been  | 
 | 
        // specified then we need to check if the DNS configuration  | 
 | 
        // has changed.  | 
 | 
          | 
 | 
        if ((ref != null) && ((thrCtxt = ref.get()) != null)) { | 
 | 
            if (nameProviderUrl == null) { | 
 | 
                if (!thrCtxt.nameservers().equals(nsList)) { | 
 | 
                      | 
 | 
                    thrCtxt = null;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (thrCtxt == null) { | 
 | 
            final Hashtable<String,Object> env = new Hashtable<>();  | 
 | 
            env.put("java.naming.factory.initial", | 
 | 
                    "com.sun.jndi.dns.DnsContextFactory");  | 
 | 
 | 
 | 
            // If no nameservers property specified we create provider URL  | 
 | 
            // based on system configured name servers  | 
 | 
              | 
 | 
            String provUrl = nameProviderUrl;  | 
 | 
            if (provUrl == null) { | 
 | 
                provUrl = createProviderURL(nsList);  | 
 | 
                if (provUrl.length() == 0) { | 
 | 
                    throw new RuntimeException("bad nameserver configuration"); | 
 | 
                }  | 
 | 
            }  | 
 | 
            env.put("java.naming.provider.url", provUrl); | 
 | 
 | 
 | 
            // Need to create directory context in privileged block  | 
 | 
            // as JNDI-DNS needs to resolve the name servers.  | 
 | 
              | 
 | 
            DirContext dirCtxt;  | 
 | 
            try { | 
 | 
                dirCtxt = java.security.AccessController.doPrivileged(  | 
 | 
                        new java.security.PrivilegedExceptionAction<DirContext>() { | 
 | 
                            public DirContext run() throws NamingException { | 
 | 
                                // Create the DNS context using NamingManager rather than using  | 
 | 
                                // the initial context constructor. This avoids having the initial  | 
 | 
                                  | 
 | 
                                Context ctx = NamingManager.getInitialContext(env);  | 
 | 
                                if (!(ctx instanceof DirContext)) { | 
 | 
                                    return null;   | 
 | 
                                }  | 
 | 
                                return (DirContext)ctx;  | 
 | 
                            }  | 
 | 
                    });  | 
 | 
            } catch (java.security.PrivilegedActionException pae) { | 
 | 
                throw (NamingException)pae.getException();  | 
 | 
            }  | 
 | 
 | 
 | 
            // create new soft reference to our thread context  | 
 | 
              | 
 | 
            thrCtxt = new ThreadContext(dirCtxt, nsList);  | 
 | 
            contextRef.set(new SoftReference<ThreadContext>(thrCtxt));  | 
 | 
        }  | 
 | 
 | 
 | 
        return thrCtxt.dirContext();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private ArrayList<String> resolve(final DirContext ctx, final String name,  | 
 | 
                                      final String[] ids, int depth)  | 
 | 
            throws UnknownHostException  | 
 | 
    { | 
 | 
        ArrayList<String> results = new ArrayList<>();  | 
 | 
        Attributes attrs;  | 
 | 
 | 
 | 
          | 
 | 
        try { | 
 | 
            attrs = java.security.AccessController.doPrivileged(  | 
 | 
                    new java.security.PrivilegedExceptionAction<Attributes>() { | 
 | 
                        public Attributes run() throws NamingException { | 
 | 
                            return ctx.getAttributes(name, ids);  | 
 | 
                        }  | 
 | 
                });  | 
 | 
        } catch (java.security.PrivilegedActionException pae) { | 
 | 
            throw new UnknownHostException(pae.getException().getMessage());  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        NamingEnumeration<? extends Attribute> ne = attrs.getAll();  | 
 | 
        if (!ne.hasMoreElements()) { | 
 | 
            throw new UnknownHostException("DNS record not found"); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        UnknownHostException uhe = null;  | 
 | 
        try { | 
 | 
            while (ne.hasMoreElements()) { | 
 | 
                Attribute attr = ne.next();  | 
 | 
                String attrID = attr.getID();  | 
 | 
 | 
 | 
                for (NamingEnumeration<?> e = attr.getAll(); e.hasMoreElements();) { | 
 | 
                    String addr = (String)e.next();  | 
 | 
 | 
 | 
                    // for canoncical name records do recursive lookup  | 
 | 
                    // - also check for CNAME loops to avoid stack overflow  | 
 | 
 | 
 | 
                    if (attrID.equals("CNAME")) { | 
 | 
                        if (depth > 4) { | 
 | 
                            throw new UnknownHostException(name + ": possible CNAME loop");  | 
 | 
                        }  | 
 | 
                        try { | 
 | 
                            results.addAll(resolve(ctx, addr, ids, depth+1));  | 
 | 
                        } catch (UnknownHostException x) { | 
 | 
                              | 
 | 
                            if (uhe == null)  | 
 | 
                                uhe = x;  | 
 | 
                        }  | 
 | 
                    } else { | 
 | 
                        results.add(addr);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        } catch (NamingException nx) { | 
 | 
            throw new UnknownHostException(nx.getMessage());  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (results.isEmpty() && uhe != null) { | 
 | 
            throw uhe;  | 
 | 
        }  | 
 | 
 | 
 | 
        return results;  | 
 | 
    }  | 
 | 
 | 
 | 
    public DNSNameService() throws Exception { | 
 | 
 | 
 | 
          | 
 | 
        String domain = AccessController.doPrivileged(  | 
 | 
            new GetPropertyAction("sun.net.spi.nameservice.domain")); | 
 | 
        if (domain != null && domain.length() > 0) { | 
 | 
            domainList = new LinkedList<String>();  | 
 | 
            domainList.add(domain);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        String nameservers = AccessController.doPrivileged(  | 
 | 
            new GetPropertyAction("sun.net.spi.nameservice.nameservers")); | 
 | 
        if (nameservers != null && nameservers.length() > 0) { | 
 | 
            nameProviderUrl = createProviderURL(nameservers);  | 
 | 
            if (nameProviderUrl.length() == 0) { | 
 | 
                throw new RuntimeException("malformed nameservers property"); | 
 | 
            }  | 
 | 
 | 
 | 
        } else { | 
 | 
 | 
 | 
            // no property specified so check host DNS resolver configured  | 
 | 
            // with at least one nameserver in dotted notation.  | 
 | 
              | 
 | 
            List<String> nsList = ResolverConfiguration.open().nameservers();  | 
 | 
            if (nsList.isEmpty()) { | 
 | 
                throw new RuntimeException("no nameservers provided"); | 
 | 
            }  | 
 | 
            boolean found = false;  | 
 | 
            for (String addr: nsList) { | 
 | 
                if (IPAddressUtil.isIPv4LiteralAddress(addr) ||  | 
 | 
                    IPAddressUtil.isIPv6LiteralAddress(addr)) { | 
 | 
                    found = true;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
            if (!found) { | 
 | 
                throw new RuntimeException("bad nameserver configuration"); | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException { | 
 | 
 | 
 | 
          | 
 | 
        String[] ids = {"A", "AAAA", "CNAME"}; | 
 | 
 | 
 | 
          | 
 | 
        DirContext ctx;  | 
 | 
        try { | 
 | 
            ctx = getTemporaryContext();  | 
 | 
        } catch (NamingException nx) { | 
 | 
            throw new Error(nx);  | 
 | 
        }  | 
 | 
 | 
 | 
        ArrayList<String> results = null;  | 
 | 
        UnknownHostException uhe = null;  | 
 | 
 | 
 | 
          | 
 | 
        if (host.indexOf('.') >= 0) { | 
 | 
            try { | 
 | 
                results = resolve(ctx, host, ids, 0);  | 
 | 
            } catch (UnknownHostException x) { | 
 | 
                uhe = x;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        // Here we try to resolve the host using the domain suffix or  | 
 | 
        // the domain suffix search list. If the host cannot be resolved  | 
 | 
        // using the domain suffix then we attempt devolution of  | 
 | 
        // the suffix - eg: if we are searching for "foo" and our  | 
 | 
        // domain suffix is "eng.sun.com" we will try to resolve  | 
 | 
        // "foo.eng.sun.com" and "foo.sun.com".  | 
 | 
        // It's not normal to attempt devolation with domains on the  | 
 | 
        // domain suffix search list - however as ResolverConfiguration  | 
 | 
        // doesn't distinguish domain or search list in the list it  | 
 | 
        // returns we approximate by doing devolution on the domain  | 
 | 
        // suffix if the list has one entry.  | 
 | 
 | 
 | 
        if (results == null) { | 
 | 
            List<String> searchList = null;  | 
 | 
            Iterator<String> i;  | 
 | 
            boolean usingSearchList = false;  | 
 | 
 | 
 | 
            if (domainList != null) { | 
 | 
                i = domainList.iterator();  | 
 | 
            } else { | 
 | 
                searchList = ResolverConfiguration.open().searchlist();  | 
 | 
                if (searchList.size() > 1) { | 
 | 
                    usingSearchList = true;  | 
 | 
                }  | 
 | 
                i = searchList.iterator();  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            while (i.hasNext()) { | 
 | 
                String parentDomain = i.next();  | 
 | 
                int start = 0;  | 
 | 
                while ((start = parentDomain.indexOf(".")) != -1 | 
 | 
                       && start < parentDomain.length() -1) { | 
 | 
                    try { | 
 | 
                        results = resolve(ctx, host+"."+parentDomain, ids, 0);  | 
 | 
                        break;  | 
 | 
                    } catch (UnknownHostException x) { | 
 | 
                        uhe = x;  | 
 | 
                        if (usingSearchList) { | 
 | 
                            break;  | 
 | 
                        }  | 
 | 
 | 
 | 
                          | 
 | 
                        parentDomain = parentDomain.substring(start+1);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                if (results != null) { | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (results == null && (host.indexOf('.') < 0)) { | 
 | 
            results = resolve(ctx, host, ids, 0);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (results == null) { | 
 | 
            assert uhe != null;  | 
 | 
            throw uhe;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        assert results.size() > 0;  | 
 | 
        InetAddress[] addrs = new InetAddress[results.size()];  | 
 | 
        int count = 0;  | 
 | 
        for (int i=0; i<results.size(); i++) { | 
 | 
            String addrString = results.get(i);  | 
 | 
            byte addr[] = IPAddressUtil.textToNumericFormatV4(addrString);  | 
 | 
            if (addr == null) { | 
 | 
                addr = IPAddressUtil.textToNumericFormatV6(addrString);  | 
 | 
            }  | 
 | 
            if (addr != null) { | 
 | 
                addrs[count++] = InetAddress.getByAddress(host, addr);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (count == 0) { | 
 | 
            throw new UnknownHostException(host + ": no valid DNS records");  | 
 | 
        }  | 
 | 
        if (count < results.size()) { | 
 | 
            InetAddress[] tmp = new InetAddress[count];  | 
 | 
            for (int i=0; i<count; i++) { | 
 | 
                tmp[i] = addrs[i];  | 
 | 
            }  | 
 | 
            addrs = tmp;  | 
 | 
        }  | 
 | 
 | 
 | 
        return addrs;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public String getHostByAddr(byte[] addr) throws UnknownHostException { | 
 | 
        String host = null;  | 
 | 
        try { | 
 | 
            String literalip = "";  | 
 | 
            String[] ids = { "PTR" }; | 
 | 
            DirContext ctx;  | 
 | 
            ArrayList<String> results = null;  | 
 | 
            try { | 
 | 
                ctx = getTemporaryContext();  | 
 | 
            } catch (NamingException nx) { | 
 | 
                throw new Error(nx);  | 
 | 
            }  | 
 | 
            if (addr.length == 4) {  | 
 | 
                for (int i = addr.length-1; i >= 0; i--) { | 
 | 
                    literalip += (addr[i] & 0xff) +".";  | 
 | 
                }  | 
 | 
                literalip += "IN-ADDR.ARPA.";  | 
 | 
 | 
 | 
                results = resolve(ctx, literalip, ids, 0);  | 
 | 
                host = results.get(0);  | 
 | 
            } else if (addr.length == 16) { // IPv6 Address | 
 | 
                /**  | 
 | 
                 * Because RFC 3152 changed the root domain name for reverse  | 
 | 
                 * lookups from IP6.INT. to IP6.ARPA., we need to check  | 
 | 
                 * both. I.E. first the new one, IP6.ARPA, then if it fails  | 
 | 
                 * the older one, IP6.INT  | 
 | 
                 */  | 
 | 
 | 
 | 
                for (int i = addr.length-1; i >= 0; i--) { | 
 | 
                    literalip += Integer.toHexString((addr[i] & 0x0f)) +"."  | 
 | 
                        +Integer.toHexString((addr[i] & 0xf0) >> 4) +".";  | 
 | 
                }  | 
 | 
                String ip6lit = literalip + "IP6.ARPA.";  | 
 | 
 | 
 | 
                try { | 
 | 
                    results = resolve(ctx, ip6lit, ids, 0);  | 
 | 
                    host = results.get(0);  | 
 | 
                } catch (UnknownHostException e) { | 
 | 
                    host = null;  | 
 | 
                }  | 
 | 
                if (host == null) { | 
 | 
                      | 
 | 
                    ip6lit = literalip + "IP6.INT.";  | 
 | 
                    results = resolve(ctx, ip6lit, ids, 0);  | 
 | 
                    host = results.get(0);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        } catch (Exception e) { | 
 | 
            throw new UnknownHostException(e.getMessage());  | 
 | 
        }  | 
 | 
          | 
 | 
        if (host == null)  | 
 | 
            throw new UnknownHostException();  | 
 | 
          | 
 | 
        if (host.endsWith(".")) { | 
 | 
            host = host.substring(0, host.length() - 1);  | 
 | 
        }  | 
 | 
        return host;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // ---------  | 
 | 
 | 
 | 
    private static void appendIfLiteralAddress(String addr, StringBuffer sb) { | 
 | 
        if (IPAddressUtil.isIPv4LiteralAddress(addr)) { | 
 | 
            sb.append("dns://" + addr + " "); | 
 | 
        } else { | 
 | 
            if (IPAddressUtil.isIPv6LiteralAddress(addr)) { | 
 | 
                sb.append("dns://[" + addr + "] "); | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static String createProviderURL(List<String> nsList) { | 
 | 
        StringBuffer sb = new StringBuffer();  | 
 | 
        for (String s: nsList) { | 
 | 
            appendIfLiteralAddress(s, sb);  | 
 | 
        }  | 
 | 
        return sb.toString();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static String createProviderURL(String str) { | 
 | 
        StringBuffer sb = new StringBuffer();  | 
 | 
        StringTokenizer st = new StringTokenizer(str, ",");  | 
 | 
        while (st.hasMoreTokens()) { | 
 | 
            appendIfLiteralAddress(st.nextToken(), sb);  | 
 | 
        }  | 
 | 
        return sb.toString();  | 
 | 
    }  | 
 | 
}  |