DNSNameService.java :  » 6.0-JDK-Modules-sun » net » sun » net » spi » nameservice » dns » Java Open Source

Java Open Source » 6.0 JDK Modules sun » net 
net » sun » net » spi » nameservice » dns » DNSNameService.java
/*
 * Copyright 2000-2005 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

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 {

    // List of domains specified by property
    private LinkedList domainList = null;

    // JNDI-DNS URL for name servers specified via property
    private String nameProviderUrl = null;

    // Per-thread soft cache of the last temporary context
    private static ThreadLocal contextRef = new ThreadLocal();

    // Simple class to encapsulate the temporary context
    private static class ThreadContext {
  private DirContext dirCtxt;
  private List nsList;

  public ThreadContext(DirContext dirCtxt, List nsList) {
      this.dirCtxt = dirCtxt;
      this.nsList = nsList;
  }

  public DirContext dirContext() {
      return dirCtxt;
  }

  public List nameservers() {
      return nsList;
  }
    }

    // Returns a per-thread DirContext 
    private DirContext getTemporaryContext() throws NamingException {
  SoftReference ref = (SoftReference)contextRef.get();
  ThreadContext thrCtxt = null;
  List 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 = (ThreadContext)ref.get()) != null)) {
      if (nameProviderUrl == null) {
    if (!thrCtxt.nameservers().equals(nsList)) {
        // DNS configuration has changed
        thrCtxt = null;
    }
      }
  }

  // new thread context needs to be created
  if (thrCtxt == null) {
      final Hashtable<String,Object> env = new Hashtable<String,Object>();
            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 = (DirContext)
        java.security.AccessController.doPrivileged(
       new java.security.PrivilegedExceptionAction() {
          public Object run() throws NamingException {
        // Create the DNS context using NamingManager rather than using
                  // the initial context constructor. This avoids having the initial
                  // context constructor call itself.
                  Context ctx = NamingManager.getInitialContext(env);
                  if (!(ctx instanceof DirContext)) {
                    return null; // cannot create a DNS context
                  }
        return 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(thrCtxt));
        }

        return thrCtxt.dirContext();
    }

    /**
     * Resolves the specified entry in DNS.
     *
     * Canonical name records are recursively resolved (to a maximum
     * of 5 to avoid performance hit and potential CNAME loops).
     *
     * @param  ctx  JNDI directory context
     * @param  name  name to resolve
     * @param  ids  record types to search
     * @param  depth  call depth - pass as 0.
     *
     * @return  array list with results (will have at least on entry)
     *
     * @throws  UnknownHostException if lookup fails or other error.
     */
    private ArrayList resolve(final DirContext ctx, final String name, final String[] ids, 
            int depth) throws UnknownHostException
    {
        ArrayList results = new ArrayList();
        Attributes attrs;

  // do the query
        try {
            attrs = (Attributes)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction() {
                        public Object run() throws NamingException {
                            return ctx.getAttributes(name, ids);
                        }
                });
        } catch (java.security.PrivilegedActionException pae) {
            throw new UnknownHostException(pae.getException().getMessage());
        }

        // non-requested type returned so enumeration is empty
        NamingEnumeration ne = attrs.getAll();
        if (!ne.hasMoreElements()) {
            throw new UnknownHostException("DNS record not found");
        }

        // iterate through the returned attributes
        UnknownHostException uhe = null;
        try {
            while (ne.hasMoreElements()) {
                Attribute attr = (Attribute)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) {
                            // canonical name can't be resolved.
                            if (uhe == null)
                                uhe = x;
                        }
                    } else {
                        results.add(addr);
                    }
                }
            }
        } catch (NamingException nx) {
            throw new UnknownHostException(nx.getMessage());
        }

        // pending exception as canonical name could not be resolved.
        if (results.isEmpty() && uhe != null) {
            throw uhe;
        }

        return results;
    }

    public DNSNameService() throws Exception {
    
  // default domain 
        String domain =  (String)AccessController.doPrivileged(
            new GetPropertyAction("sun.net.spi.nameservice.domain"));
        if (domain != null && domain.length() > 0) {
            domainList = new LinkedList();
            domainList.add(domain);
        }

  // name servers
  String nameservers
            = (String)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 nsList = ResolverConfiguration.open().nameservers();
      if (nsList.size() == 0) 
    throw new RuntimeException("no nameservers provided");
      boolean found = false;
      Iterator i = nsList.iterator();
      while (i.hasNext()) {
    String addr = (String)i.next();
    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 {

  // DNS records that we search for
  String[] ids = {"A", "AAAA", "CNAME"};

  // first get directory context
  DirContext ctx;
  try {
      ctx = getTemporaryContext();
  } catch (NamingException nx) {
      throw new Error(nx);
  }

  ArrayList results = null;
  UnknownHostException uhe = null;

  // If host already contains a domain name then just look it up
  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 searchList = null;
      Iterator i;
      boolean usingSearchList = false;

      if (domainList != null) {
    i = domainList.iterator();
      } else {
    searchList = ResolverConfiguration.open().searchlist();
    if (searchList.size() > 1) {
        usingSearchList = true;
    }
    i = searchList.iterator();
      }

      // iterator through each domain suffix
      while (i.hasNext()) {
          String parentDomain = (String)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;
      }

      // devolve
      parentDomain = parentDomain.substring(start+1);
        }
    }
    if (results != null) {
        break;
    }
      }
  }

  // finally try the host if it doesn't have a domain name
        if (results == null && (host.indexOf('.') < 0)) {
            results = resolve(ctx, host, ids, 0);
        }

  // if not found then throw the (last) exception thrown.
        if (results == null) {
      assert uhe != null;
            throw uhe;
        }

  /**
   * Convert the array list into a byte aray list - this
   * filters out any invalid IPv4/IPv6 addresses.
   */
  assert results.size() > 0;
  InetAddress[] addrs = new InetAddress[results.size()];
  int count = 0;
  for (int i=0; i<results.size(); i++) {
      String addrString = (String)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 addresses are filtered then we need to resize the
    * array. Additionally if all addresses are filtered then
   * we throw an exception.
   */
  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;
    }

    /**
     * Reverse lookup code. I.E: find a host name from an IP address.
     * IPv4 addresses are mapped in the IN-ADDR.ARPA. top domain, while
     * IPv6 addresses can be in IP6.ARPA or IP6.INT.
     * In both cases the address has to be converted into a dotted form.
     */
    public String getHostByAddr(byte[] addr) throws UnknownHostException {
  String host = null;
  try {
      String literalip = "";
      String[] ids = { "PTR" };
      DirContext ctx;
      ArrayList results = null;
      try {
    ctx = getTemporaryContext();
      } catch (NamingException nx) {
    throw new Error(nx);
      }
      if (addr.length == 4) { // IPv4 Address
    for (int i = addr.length-1; i >= 0; i--) {
        literalip += (addr[i] & 0xff) +".";
    }
    literalip += "IN-ADDR.ARPA.";

    results = resolve(ctx, literalip, ids, 0);
    host = (String)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 = (String)results.get(0);
    } catch (UnknownHostException e) {
        host = null;
    }
    if (host == null) {
        // IP6.ARPA lookup failed, let's try the older IP6.INT
        ip6lit = literalip + "IP6.INT.";
        results = resolve(ctx, ip6lit, ids, 0);
        host = (String)results.get(0);
    }
      }
  } catch (Exception e) {
      throw new UnknownHostException(e.getMessage());
  }
  // Either we couldn't find it or the address was neither IPv4 or IPv6
  if (host == null)
      throw new UnknownHostException();
  // remove trailing dot
  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 + "] ");
            }
  }
    }

    /*
     * @return String containing the JNDI-DNS provider URL 
     *         corresponding to the supplied List of nameservers.
     */
    private static String createProviderURL(List nsList) {
  Iterator i = nsList.iterator();
  StringBuffer sb = new StringBuffer();
  while (i.hasNext()) {
      appendIfLiteralAddress((String)i.next(), sb);
  }
  return sb.toString();
    }

    /*
     * @return String containing the JNDI-DNS provider URL 
     *         corresponding to the list of nameservers
     *         contained in the provided str.
     */
    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();
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.