| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.jndi.dns;  | 
 | 
 | 
 | 
 | 
 | 
import java.util.Enumeration;  | 
 | 
import java.util.Hashtable;  | 
 | 
 | 
 | 
import javax.naming.*;  | 
 | 
import javax.naming.directory.*;  | 
 | 
import javax.naming.spi.DirectoryManager;  | 
 | 
 | 
 | 
import com.sun.jndi.toolkit.ctx.*;  | 
 | 
 | 
 | 
 | 
 | 
/**  | 
 | 
 * A DnsContext is a directory context representing a DNS node.  | 
 | 
 *  | 
 | 
 * @author Scott Seligman  | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
public class DnsContext extends ComponentDirContext { | 
 | 
 | 
 | 
    DnsName domain;               | 
 | 
                                  | 
 | 
    Hashtable<Object,Object> environment;  | 
 | 
    private boolean envShared;    | 
 | 
                                // and so must be copied on write  | 
 | 
    private boolean parentIsDns;          | 
 | 
                                          | 
 | 
    private String[] servers;  | 
 | 
    private Resolver resolver;  | 
 | 
 | 
 | 
    private boolean authoritative;        | 
 | 
    private boolean recursion;            | 
 | 
    private int timeout;                  | 
 | 
    private int retries;                  | 
 | 
 | 
 | 
    static final NameParser nameParser = new DnsNameParser();  | 
 | 
 | 
 | 
    // Timeouts for UDP queries use exponential backoff:  each retry  | 
 | 
    // is for twice as long as the last.  The following constants set  | 
 | 
    // the defaults for the initial timeout (in ms) and the number of  | 
 | 
    // retries, and name the environment properties used to override  | 
 | 
      | 
 | 
    private static final int DEFAULT_INIT_TIMEOUT = 1000;  | 
 | 
    private static final int DEFAULT_RETRIES = 4;  | 
 | 
    private static final String INIT_TIMEOUT =  | 
 | 
                                          "com.sun.jndi.dns.timeout.initial";  | 
 | 
    private static final String RETRIES = "com.sun.jndi.dns.timeout.retries";  | 
 | 
 | 
 | 
    // The resource record type and class to use for lookups, and the  | 
 | 
      | 
 | 
    private CT lookupCT;  | 
 | 
    private static final String LOOKUP_ATTR = "com.sun.jndi.dns.lookup.attr";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String RECURSION = "com.sun.jndi.dns.recursion";  | 
 | 
 | 
 | 
      | 
 | 
    private static final int ANY = ResourceRecord.QTYPE_STAR;  | 
 | 
 | 
 | 
      | 
 | 
    private static final ZoneNode zoneTree = new ZoneNode(null);  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @SuppressWarnings("unchecked") | 
 | 
    public DnsContext(String domain, String[] servers, Hashtable<?,?> environment)  | 
 | 
            throws NamingException { | 
 | 
 | 
 | 
        this.domain = new DnsName(domain.endsWith(".") | 
 | 
                                  ? domain  | 
 | 
                                  : domain + ".");  | 
 | 
        this.servers = (servers == null) ? null : servers.clone();  | 
 | 
        this.environment = (Hashtable<Object,Object>) environment.clone();  | 
 | 
        envShared = false;  | 
 | 
        parentIsDns = false;  | 
 | 
        resolver = null;  | 
 | 
 | 
 | 
        initFromEnvironment();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    DnsContext(DnsContext ctx, DnsName domain) { | 
 | 
        this(ctx);  | 
 | 
        this.domain = domain;  | 
 | 
        parentIsDns = true;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private DnsContext(DnsContext ctx) { | 
 | 
        environment = ctx.environment;    | 
 | 
        envShared = ctx.envShared = true;  | 
 | 
        parentIsDns = ctx.parentIsDns;  | 
 | 
        domain = ctx.domain;  | 
 | 
        servers = ctx.servers;            | 
 | 
        resolver = ctx.resolver;  | 
 | 
        authoritative = ctx.authoritative;  | 
 | 
        recursion = ctx.recursion;  | 
 | 
        timeout = ctx.timeout;  | 
 | 
        retries = ctx.retries;  | 
 | 
        lookupCT = ctx.lookupCT;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void close() { | 
 | 
        if (resolver != null) { | 
 | 
            resolver.close();  | 
 | 
            resolver = null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Environment operations  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected Hashtable<?,?> p_getEnvironment() { | 
 | 
        return environment;  | 
 | 
    }  | 
 | 
 | 
 | 
    public Hashtable<?,?> getEnvironment() throws NamingException { | 
 | 
        return (Hashtable<?,?>) environment.clone();  | 
 | 
    }  | 
 | 
 | 
 | 
    @SuppressWarnings("unchecked") | 
 | 
    public Object addToEnvironment(String propName, Object propVal)  | 
 | 
            throws NamingException { | 
 | 
 | 
 | 
        if (propName.equals(LOOKUP_ATTR)) { | 
 | 
            lookupCT = getLookupCT((String) propVal);  | 
 | 
        } else if (propName.equals(Context.AUTHORITATIVE)) { | 
 | 
            authoritative = "true".equalsIgnoreCase((String) propVal);  | 
 | 
        } else if (propName.equals(RECURSION)) { | 
 | 
            recursion = "true".equalsIgnoreCase((String) propVal);  | 
 | 
        } else if (propName.equals(INIT_TIMEOUT)) { | 
 | 
            int val = Integer.parseInt((String) propVal);  | 
 | 
            if (timeout != val) { | 
 | 
                timeout = val;  | 
 | 
                resolver = null;  | 
 | 
            }  | 
 | 
        } else if (propName.equals(RETRIES)) { | 
 | 
            int val = Integer.parseInt((String) propVal);  | 
 | 
            if (retries != val) { | 
 | 
                retries = val;  | 
 | 
                resolver = null;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (!envShared) { | 
 | 
            return environment.put(propName, propVal);  | 
 | 
        } else if (environment.get(propName) != propVal) { | 
 | 
              | 
 | 
            environment = (Hashtable<Object,Object>) environment.clone();  | 
 | 
            envShared = false;  | 
 | 
            return environment.put(propName, propVal);  | 
 | 
        } else { | 
 | 
            return propVal;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @SuppressWarnings("unchecked") | 
 | 
    public Object removeFromEnvironment(String propName)  | 
 | 
            throws NamingException { | 
 | 
 | 
 | 
        if (propName.equals(LOOKUP_ATTR)) { | 
 | 
            lookupCT = getLookupCT(null);  | 
 | 
        } else if (propName.equals(Context.AUTHORITATIVE)) { | 
 | 
            authoritative = false;  | 
 | 
        } else if (propName.equals(RECURSION)) { | 
 | 
            recursion = true;  | 
 | 
        } else if (propName.equals(INIT_TIMEOUT)) { | 
 | 
            if (timeout != DEFAULT_INIT_TIMEOUT) { | 
 | 
                timeout = DEFAULT_INIT_TIMEOUT;  | 
 | 
                resolver = null;  | 
 | 
            }  | 
 | 
        } else if (propName.equals(RETRIES)) { | 
 | 
            if (retries != DEFAULT_RETRIES) { | 
 | 
                retries = DEFAULT_RETRIES;  | 
 | 
                resolver = null;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (!envShared) { | 
 | 
            return environment.remove(propName);  | 
 | 
        } else if (environment.get(propName) != null) { | 
 | 
              | 
 | 
            environment = (Hashtable<Object,Object>) environment.clone();  | 
 | 
            envShared = false;  | 
 | 
            return environment.remove(propName);  | 
 | 
        } else { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void setProviderUrl(String url) { | 
 | 
          | 
 | 
        environment.put(Context.PROVIDER_URL, url);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private void initFromEnvironment()  | 
 | 
            throws InvalidAttributeIdentifierException { | 
 | 
 | 
 | 
        lookupCT = getLookupCT((String) environment.get(LOOKUP_ATTR));  | 
 | 
        authoritative = "true".equalsIgnoreCase((String)  | 
 | 
                                       environment.get(Context.AUTHORITATIVE));  | 
 | 
        String val = (String) environment.get(RECURSION);  | 
 | 
        recursion = ((val == null) ||  | 
 | 
                     "true".equalsIgnoreCase(val));  | 
 | 
        val = (String) environment.get(INIT_TIMEOUT);  | 
 | 
        timeout = (val == null)  | 
 | 
            ? DEFAULT_INIT_TIMEOUT  | 
 | 
            : Integer.parseInt(val);  | 
 | 
        val = (String) environment.get(RETRIES);  | 
 | 
        retries = (val == null)  | 
 | 
            ? DEFAULT_RETRIES  | 
 | 
            : Integer.parseInt(val);  | 
 | 
    }  | 
 | 
 | 
 | 
    private CT getLookupCT(String attrId)  | 
 | 
            throws InvalidAttributeIdentifierException { | 
 | 
        return (attrId == null)  | 
 | 
            ? new CT(ResourceRecord.CLASS_INTERNET, ResourceRecord.TYPE_TXT)  | 
 | 
            : fromAttrId(attrId);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Naming operations  | 
 | 
 | 
 | 
    public Object c_lookup(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
 | 
 | 
        cont.setSuccess();  | 
 | 
        if (name.isEmpty()) { | 
 | 
            DnsContext ctx = new DnsContext(this);  | 
 | 
            ctx.resolver = new Resolver(servers, timeout, retries);  | 
 | 
                                                  | 
 | 
            return ctx;  | 
 | 
        }  | 
 | 
        try { | 
 | 
            DnsName fqdn = fullyQualify(name);  | 
 | 
            ResourceRecords rrs =  | 
 | 
                getResolver().query(fqdn, lookupCT.rrclass, lookupCT.rrtype,  | 
 | 
                                    recursion, authoritative);  | 
 | 
            Attributes attrs = rrsToAttrs(rrs, null);  | 
 | 
            DnsContext ctx = new DnsContext(this, fqdn);  | 
 | 
            return DirectoryManager.getObjectInstance(ctx, name, this,  | 
 | 
                                                      environment, attrs);  | 
 | 
        } catch (NamingException e) { | 
 | 
            cont.setError(this, name);  | 
 | 
            throw cont.fillInException(e);  | 
 | 
        } catch (Exception e) { | 
 | 
            cont.setError(this, name);  | 
 | 
            NamingException ne = new NamingException(  | 
 | 
                    "Problem generating object using object factory");  | 
 | 
            ne.setRootCause(e);  | 
 | 
            throw cont.fillInException(ne);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public Object c_lookupLink(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        return c_lookup(name, cont);  | 
 | 
    }  | 
 | 
 | 
 | 
    public NamingEnumeration<NameClassPair> c_list(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setSuccess();  | 
 | 
        try { | 
 | 
            DnsName fqdn = fullyQualify(name);  | 
 | 
            NameNode nnode = getNameNode(fqdn);  | 
 | 
            DnsContext ctx = new DnsContext(this, fqdn);  | 
 | 
            return new NameClassPairEnumeration(ctx, nnode.getChildren());  | 
 | 
 | 
 | 
        } catch (NamingException e) { | 
 | 
            cont.setError(this, name);  | 
 | 
            throw cont.fillInException(e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public NamingEnumeration<Binding> c_listBindings(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setSuccess();  | 
 | 
        try { | 
 | 
            DnsName fqdn = fullyQualify(name);  | 
 | 
            NameNode nnode = getNameNode(fqdn);  | 
 | 
            DnsContext ctx = new DnsContext(this, fqdn);  | 
 | 
            return new BindingEnumeration(ctx, nnode.getChildren());  | 
 | 
 | 
 | 
        } catch (NamingException e) { | 
 | 
            cont.setError(this, name);  | 
 | 
            throw cont.fillInException(e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_bind(Name name, Object obj, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_rebind(Name name, Object obj, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_unbind(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_rename(Name oldname, Name newname, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, oldname);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public Context c_createSubcontext(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_destroySubcontext(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public NameParser c_getNameParser(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setSuccess();  | 
 | 
        return nameParser;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Directory operations  | 
 | 
 | 
 | 
    public void c_bind(Name name,  | 
 | 
                       Object obj,  | 
 | 
                       Attributes attrs,  | 
 | 
                       Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_rebind(Name name,  | 
 | 
                         Object obj,  | 
 | 
                         Attributes attrs,  | 
 | 
                         Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public DirContext c_createSubcontext(Name name,  | 
 | 
                                         Attributes attrs,  | 
 | 
                                         Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public Attributes c_getAttributes(Name name,  | 
 | 
                                      String[] attrIds,  | 
 | 
                                      Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
 | 
 | 
        cont.setSuccess();  | 
 | 
        try { | 
 | 
            DnsName fqdn = fullyQualify(name);  | 
 | 
            CT[] cts = attrIdsToClassesAndTypes(attrIds);  | 
 | 
            CT ct = getClassAndTypeToQuery(cts);  | 
 | 
            ResourceRecords rrs =  | 
 | 
                getResolver().query(fqdn, ct.rrclass, ct.rrtype,  | 
 | 
                                    recursion, authoritative);  | 
 | 
            return rrsToAttrs(rrs, cts);  | 
 | 
 | 
 | 
        } catch (NamingException e) { | 
 | 
            cont.setError(this, name);  | 
 | 
            throw cont.fillInException(e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_modifyAttributes(Name name,  | 
 | 
                                   int mod_op,  | 
 | 
                                   Attributes attrs,  | 
 | 
                                   Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void c_modifyAttributes(Name name,  | 
 | 
                                   ModificationItem[] mods,  | 
 | 
                                   Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public NamingEnumeration<SearchResult> c_search(Name name,  | 
 | 
                                      Attributes matchingAttributes,  | 
 | 
                                      String[] attributesToReturn,  | 
 | 
                                      Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        throw new OperationNotSupportedException();  | 
 | 
    }  | 
 | 
 | 
 | 
    public NamingEnumeration<SearchResult> c_search(Name name,  | 
 | 
                                      String filter,  | 
 | 
                                      SearchControls cons,  | 
 | 
                                      Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        throw new OperationNotSupportedException();  | 
 | 
    }  | 
 | 
 | 
 | 
    public NamingEnumeration<SearchResult> c_search(Name name,  | 
 | 
                                      String filterExpr,  | 
 | 
                                      Object[] filterArgs,  | 
 | 
                                      SearchControls cons,  | 
 | 
                                      Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        throw new OperationNotSupportedException();  | 
 | 
    }  | 
 | 
 | 
 | 
    public DirContext c_getSchema(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
    public DirContext c_getSchemaClassDefinition(Name name, Continuation cont)  | 
 | 
            throws NamingException { | 
 | 
        cont.setError(this, name);  | 
 | 
        throw cont.fillInException(  | 
 | 
                new OperationNotSupportedException());  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Name-related operations  | 
 | 
 | 
 | 
    public String getNameInNamespace() { | 
 | 
        return domain.toString();  | 
 | 
    }  | 
 | 
 | 
 | 
    public Name composeName(Name name, Name prefix) throws NamingException { | 
 | 
        Name result;  | 
 | 
 | 
 | 
        // Any name that's not a CompositeName is assumed to be a DNS  | 
 | 
          | 
 | 
        if (!(prefix instanceof DnsName || prefix instanceof CompositeName)) { | 
 | 
            prefix = (new DnsName()).addAll(prefix);  | 
 | 
        }  | 
 | 
        if (!(name instanceof DnsName || name instanceof CompositeName)) { | 
 | 
            name = (new DnsName()).addAll(name);  | 
 | 
        }  | 
 | 
 | 
 | 
        // Each of prefix and name is now either a DnsName or a CompositeName.  | 
 | 
 | 
 | 
          | 
 | 
        if ((prefix instanceof DnsName) && (name instanceof DnsName)) { | 
 | 
            result = (DnsName) (prefix.clone());  | 
 | 
            result.addAll(name);  | 
 | 
            return new CompositeName().add(result.toString());  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        Name prefixC = (prefix instanceof CompositeName)  | 
 | 
            ? prefix  | 
 | 
            : new CompositeName().add(prefix.toString());  | 
 | 
        Name nameC = (name instanceof CompositeName)  | 
 | 
            ? name  | 
 | 
            : new CompositeName().add(name.toString());  | 
 | 
        int prefixLast = prefixC.size() - 1;  | 
 | 
 | 
 | 
          | 
 | 
        if (nameC.isEmpty() || nameC.get(0).equals("") || | 
 | 
                prefixC.isEmpty() || prefixC.get(prefixLast).equals("")) { | 
 | 
            return super.composeName(nameC, prefixC);  | 
 | 
        }  | 
 | 
 | 
 | 
        result = (prefix == prefixC)  | 
 | 
            ? (CompositeName) prefixC.clone()  | 
 | 
            : prefixC;                    | 
 | 
        result.addAll(nameC);  | 
 | 
 | 
 | 
        if (parentIsDns) { | 
 | 
            DnsName dnsComp = (prefix instanceof DnsName)  | 
 | 
                           ? (DnsName) prefix.clone()  | 
 | 
                           : new DnsName(prefixC.get(prefixLast));  | 
 | 
            dnsComp.addAll((name instanceof DnsName)  | 
 | 
                           ? name  | 
 | 
                           : new DnsName(nameC.get(0)));  | 
 | 
            result.remove(prefixLast + 1);  | 
 | 
            result.remove(prefixLast);  | 
 | 
            result.add(prefixLast, dnsComp.toString());  | 
 | 
        }  | 
 | 
        return result;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Helper methods  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private synchronized Resolver getResolver() throws NamingException { | 
 | 
        if (resolver == null) { | 
 | 
            resolver = new Resolver(servers, timeout, retries);  | 
 | 
        }  | 
 | 
        return resolver;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    DnsName fullyQualify(Name name) throws NamingException { | 
 | 
        if (name.isEmpty()) { | 
 | 
            return domain;  | 
 | 
        }  | 
 | 
        DnsName dnsName = (name instanceof CompositeName)  | 
 | 
            ? new DnsName(name.get(0))                    | 
 | 
            : (DnsName) (new DnsName()).addAll(name);     | 
 | 
 | 
 | 
        if (dnsName.hasRootLabel()) { | 
 | 
              | 
 | 
            if (domain.size() == 1) { | 
 | 
                return dnsName;  | 
 | 
            } else { | 
 | 
                throw new InvalidNameException(  | 
 | 
                       "DNS name " + dnsName + " not relative to " + domain);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return (DnsName) dnsName.addAll(0, domain);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static Attributes rrsToAttrs(ResourceRecords rrs, CT[] cts) { | 
 | 
 | 
 | 
        BasicAttributes attrs = new BasicAttributes(true);  | 
 | 
 | 
 | 
        for (int i = 0; i < rrs.answer.size(); i++) { | 
 | 
            ResourceRecord rr = rrs.answer.elementAt(i);  | 
 | 
            int rrtype  = rr.getType();  | 
 | 
            int rrclass = rr.getRrclass();  | 
 | 
 | 
 | 
            if (!classAndTypeMatch(rrclass, rrtype, cts)) { | 
 | 
                continue;  | 
 | 
            }  | 
 | 
 | 
 | 
            String attrId = toAttrId(rrclass, rrtype);  | 
 | 
            Attribute attr = attrs.get(attrId);  | 
 | 
            if (attr == null) { | 
 | 
                attr = new BasicAttribute(attrId);  | 
 | 
                attrs.put(attr);  | 
 | 
            }  | 
 | 
            attr.add(rr.getRdata());  | 
 | 
        }  | 
 | 
        return attrs;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static boolean classAndTypeMatch(int rrclass, int rrtype,  | 
 | 
                                             CT[] cts) { | 
 | 
        if (cts == null) { | 
 | 
            return true;  | 
 | 
        }  | 
 | 
        for (int i = 0; i < cts.length; i++) { | 
 | 
            CT ct = cts[i];  | 
 | 
            boolean classMatch = (ct.rrclass == ANY) ||  | 
 | 
                                 (ct.rrclass == rrclass);  | 
 | 
            boolean typeMatch  = (ct.rrtype == ANY) ||  | 
 | 
                                 (ct.rrtype == rrtype);  | 
 | 
            if (classMatch && typeMatch) { | 
 | 
                return true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static String toAttrId(int rrclass, int rrtype) { | 
 | 
        String attrId = ResourceRecord.getTypeName(rrtype);  | 
 | 
        if (rrclass != ResourceRecord.CLASS_INTERNET) { | 
 | 
            attrId = ResourceRecord.getRrclassName(rrclass) + " " + attrId;  | 
 | 
        }  | 
 | 
        return attrId;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static CT fromAttrId(String attrId)  | 
 | 
            throws InvalidAttributeIdentifierException { | 
 | 
 | 
 | 
        if (attrId.equals("")) { | 
 | 
            throw new InvalidAttributeIdentifierException(  | 
 | 
                    "Attribute ID cannot be empty");  | 
 | 
        }  | 
 | 
        int rrclass;  | 
 | 
        int rrtype;  | 
 | 
        int space = attrId.indexOf(' '); | 
 | 
 | 
 | 
          | 
 | 
        if (space < 0) { | 
 | 
            rrclass = ResourceRecord.CLASS_INTERNET;  | 
 | 
        } else { | 
 | 
            String className = attrId.substring(0, space);  | 
 | 
            rrclass = ResourceRecord.getRrclass(className);  | 
 | 
            if (rrclass < 0) { | 
 | 
                throw new InvalidAttributeIdentifierException(  | 
 | 
                        "Unknown resource record class '" + className + '\'');  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        String typeName = attrId.substring(space + 1);  | 
 | 
        rrtype = ResourceRecord.getType(typeName);  | 
 | 
        if (rrtype < 0) { | 
 | 
            throw new InvalidAttributeIdentifierException(  | 
 | 
                    "Unknown resource record type '" + typeName + '\'');  | 
 | 
        }  | 
 | 
 | 
 | 
        return new CT(rrclass, rrtype);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static CT[] attrIdsToClassesAndTypes(String[] attrIds)  | 
 | 
            throws InvalidAttributeIdentifierException { | 
 | 
        if (attrIds == null) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        CT[] cts = new CT[attrIds.length];  | 
 | 
 | 
 | 
        for (int i = 0; i < attrIds.length; i++) { | 
 | 
            cts[i] = fromAttrId(attrIds[i]);  | 
 | 
        }  | 
 | 
        return cts;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static CT getClassAndTypeToQuery(CT[] cts) { | 
 | 
        int rrclass;  | 
 | 
        int rrtype;  | 
 | 
 | 
 | 
        if (cts == null) { | 
 | 
              | 
 | 
            rrclass = ANY;  | 
 | 
            rrtype  = ANY;  | 
 | 
        } else if (cts.length == 0) { | 
 | 
              | 
 | 
            rrclass = ResourceRecord.CLASS_INTERNET;  | 
 | 
            rrtype  = ANY;  | 
 | 
        } else { | 
 | 
            rrclass = cts[0].rrclass;  | 
 | 
            rrtype  = cts[0].rrtype;  | 
 | 
            for (int i = 1; i < cts.length; i++) { | 
 | 
                if (rrclass != cts[i].rrclass) { | 
 | 
                    rrclass = ANY;  | 
 | 
                }  | 
 | 
                if (rrtype != cts[i].rrtype) { | 
 | 
                    rrtype = ANY;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return new CT(rrclass, rrtype);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Support for list operations  | 
 | 
 | 
 | 
    /*  | 
 | 
     * Synchronization notes:  | 
 | 
     *  | 
 | 
     * Any access to zoneTree that walks the tree, whether it modifies  | 
 | 
     * the tree or not, is synchronized on zoneTree.  | 
 | 
     * [%%% Note:  a read/write lock would allow increased concurrency.]  | 
 | 
     * The depth of a ZoneNode can thereafter be accessed without  | 
 | 
     * further synchronization.  Access to other fields and methods  | 
 | 
     * should be synchronized on the node itself.  | 
 | 
     *  | 
 | 
     * A zone's contents is a NameNode tree that, once created, is never  | 
 | 
     * modified.  The only synchronization needed is to ensure that it  | 
 | 
     * gets flushed into shared memory after being created, which is  | 
 | 
     * accomplished by ZoneNode.populate().  The contents are accessed  | 
 | 
     * via a soft reference, so a ZoneNode may be seen to be populated  | 
 | 
     * one moment and unpopulated the next.  | 
 | 
     */  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private NameNode getNameNode(DnsName fqdn) throws NamingException { | 
 | 
        dprint("getNameNode(" + fqdn + ")"); | 
 | 
 | 
 | 
          | 
 | 
        ZoneNode znode;  | 
 | 
        DnsName zone;  | 
 | 
        synchronized (zoneTree) { | 
 | 
            znode = zoneTree.getDeepestPopulated(fqdn);  | 
 | 
        }  | 
 | 
        dprint("Deepest related zone in zone tree: " + | 
 | 
               ((znode != null) ? znode.getLabel() : "[none]"));  | 
 | 
 | 
 | 
        NameNode topOfZone;  | 
 | 
        NameNode nnode;  | 
 | 
 | 
 | 
        if (znode != null) { | 
 | 
            synchronized (znode) { | 
 | 
                topOfZone = znode.getContents();  | 
 | 
            }  | 
 | 
            // If fqdn is in znode's zone, is not at a zone cut, and  | 
 | 
              | 
 | 
            if (topOfZone != null) { | 
 | 
                nnode = topOfZone.get(fqdn, znode.depth() + 1);   | 
 | 
 | 
 | 
                if ((nnode != null) && !nnode.isZoneCut()) { | 
 | 
                    dprint("Found node " + fqdn + " in zone tree"); | 
 | 
                    zone = (DnsName)  | 
 | 
                        fqdn.getPrefix(znode.depth() + 1);        | 
 | 
                    boolean current = isZoneCurrent(znode, zone);  | 
 | 
                    boolean restart = false;  | 
 | 
 | 
 | 
                    synchronized (znode) { | 
 | 
                        if (topOfZone != znode.getContents()) { | 
 | 
                            // Zone was modified while we were examining it.  | 
 | 
                              | 
 | 
                            restart = true;  | 
 | 
                        } else if (!current) { | 
 | 
                            znode.depopulate();  | 
 | 
                        } else { | 
 | 
                            return nnode;                         | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                    dprint("Zone not current; discarding node"); | 
 | 
                    if (restart) { | 
 | 
                        return getNameNode(fqdn);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        dprint("Adding node " + fqdn + " to zone tree"); | 
 | 
 | 
 | 
          | 
 | 
        zone = getResolver().findZoneName(fqdn, ResourceRecord.CLASS_INTERNET,  | 
 | 
                                          recursion);  | 
 | 
        dprint("Node's zone is " + zone); | 
 | 
        synchronized (zoneTree) { | 
 | 
            znode = (ZoneNode) zoneTree.add(zone, 1);     | 
 | 
        }  | 
 | 
 | 
 | 
        // If znode is now populated we know -- because the first half of  | 
 | 
        // getNodeName() didn't find it -- that it was populated by another  | 
 | 
        // thread during this method call.  Assume then that it's current.  | 
 | 
 | 
 | 
        synchronized (znode) { | 
 | 
            topOfZone = znode.isPopulated()  | 
 | 
                ? znode.getContents()  | 
 | 
                : populateZone(znode, zone);  | 
 | 
        }  | 
 | 
          | 
 | 
        nnode = topOfZone.get(fqdn, zone.size());  | 
 | 
        if (nnode == null) { | 
 | 
            throw new ConfigurationException(  | 
 | 
                    "DNS error: node not found in its own zone");  | 
 | 
        }  | 
 | 
        dprint("Found node in newly-populated zone"); | 
 | 
        return nnode;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private NameNode populateZone(ZoneNode znode, DnsName zone)  | 
 | 
            throws NamingException { | 
 | 
        dprint("Populating zone " + zone); | 
 | 
          | 
 | 
        ResourceRecords rrs =  | 
 | 
            getResolver().queryZone(zone,  | 
 | 
                                    ResourceRecord.CLASS_INTERNET, recursion);  | 
 | 
        dprint("zone xfer complete: " + rrs.answer.size() + " records"); | 
 | 
        return znode.populate(zone, rrs);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean isZoneCurrent(ZoneNode znode, DnsName zone)  | 
 | 
            throws NamingException { | 
 | 
        // former version:  return !znode.isExpired();  | 
 | 
 | 
 | 
        if (!znode.isPopulated()) { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        ResourceRecord soa =  | 
 | 
            getResolver().findSoa(zone, ResourceRecord.CLASS_INTERNET,  | 
 | 
                                  recursion);  | 
 | 
        synchronized (znode) { | 
 | 
            if (soa == null) { | 
 | 
                znode.depopulate();  | 
 | 
            }  | 
 | 
            return (znode.isPopulated() &&  | 
 | 
                    znode.compareSerialNumberTo(soa) >= 0);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    //---------- Debugging  | 
 | 
 | 
 | 
    private static final boolean debug = false;  | 
 | 
 | 
 | 
    private static final void dprint(String msg) { | 
 | 
        if (debug) { | 
 | 
            System.err.println("** " + msg); | 
 | 
        }  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
//----------  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
class CT { | 
 | 
    int rrclass;  | 
 | 
    int rrtype;  | 
 | 
 | 
 | 
    CT(int rrclass, int rrtype) { | 
 | 
        this.rrclass = rrclass;  | 
 | 
        this.rrtype = rrtype;  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
//----------  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
abstract class BaseNameClassPairEnumeration<T> implements NamingEnumeration<T> { | 
 | 
 | 
 | 
    protected Enumeration<NameNode> nodes;      | 
 | 
    protected DnsContext ctx;         | 
 | 
 | 
 | 
    BaseNameClassPairEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) { | 
 | 
        this.ctx = ctx;  | 
 | 
        this.nodes = (nodes != null)  | 
 | 
            ? nodes.elements()  | 
 | 
            : null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public final void close() { | 
 | 
        nodes = null;  | 
 | 
        ctx = null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public final boolean hasMore() { | 
 | 
        boolean more = ((nodes != null) && nodes.hasMoreElements());  | 
 | 
        if (!more) { | 
 | 
            close();  | 
 | 
        }  | 
 | 
        return more;  | 
 | 
    }  | 
 | 
 | 
 | 
    public final boolean hasMoreElements() { | 
 | 
        return hasMore();  | 
 | 
    }  | 
 | 
 | 
 | 
    abstract public T next() throws NamingException;  | 
 | 
 | 
 | 
    public final T nextElement() { | 
 | 
        try { | 
 | 
            return next();  | 
 | 
        } catch (NamingException e) { | 
 | 
            java.util.NoSuchElementException nsee =  | 
 | 
                    new java.util.NoSuchElementException();  | 
 | 
            nsee.initCause(e);  | 
 | 
            throw nsee;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class NameClassPairEnumeration  | 
 | 
        extends BaseNameClassPairEnumeration<NameClassPair>  | 
 | 
        implements NamingEnumeration<NameClassPair> { | 
 | 
 | 
 | 
    NameClassPairEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) { | 
 | 
        super(ctx, nodes);  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public NameClassPair next() throws NamingException { | 
 | 
        if (!hasMore()) { | 
 | 
            throw new java.util.NoSuchElementException();  | 
 | 
        }  | 
 | 
        NameNode nnode = nodes.nextElement();  | 
 | 
        String className = (nnode.isZoneCut() ||  | 
 | 
                            (nnode.getChildren() != null))  | 
 | 
            ? "javax.naming.directory.DirContext"  | 
 | 
            : "java.lang.Object";  | 
 | 
 | 
 | 
        String label = nnode.getLabel();  | 
 | 
        Name compName = (new DnsName()).add(label);  | 
 | 
        Name cname = (new CompositeName()).add(compName.toString());  | 
 | 
 | 
 | 
        NameClassPair ncp = new NameClassPair(cname.toString(), className);  | 
 | 
        ncp.setNameInNamespace(ctx.fullyQualify(cname).toString());  | 
 | 
        return ncp;  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class BindingEnumeration extends BaseNameClassPairEnumeration<Binding>  | 
 | 
                         implements NamingEnumeration<Binding> { | 
 | 
 | 
 | 
    BindingEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) { | 
 | 
        super(ctx, nodes);  | 
 | 
    }  | 
 | 
 | 
 | 
    // Finalizer not needed since it's safe to leave ctx unclosed.  | 
 | 
//  protected void finalize() { | 
 | 
//      close();  | 
 | 
//  }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public Binding next() throws NamingException { | 
 | 
        if (!hasMore()) { | 
 | 
            throw (new java.util.NoSuchElementException());  | 
 | 
        }  | 
 | 
        NameNode nnode = nodes.nextElement();  | 
 | 
 | 
 | 
        String label = nnode.getLabel();  | 
 | 
        Name compName = (new DnsName()).add(label);  | 
 | 
        String compNameStr = compName.toString();  | 
 | 
        Name cname = (new CompositeName()).add(compNameStr);  | 
 | 
        String cnameStr = cname.toString();  | 
 | 
 | 
 | 
        DnsName fqdn = ctx.fullyQualify(compName);  | 
 | 
 | 
 | 
          | 
 | 
        DnsContext child = new DnsContext(ctx, fqdn);  | 
 | 
 | 
 | 
        try { | 
 | 
            Object obj = DirectoryManager.getObjectInstance(  | 
 | 
                    child, cname, ctx, child.environment, null);  | 
 | 
            Binding binding = new Binding(cnameStr, obj);  | 
 | 
            binding.setNameInNamespace(ctx.fullyQualify(cname).toString());  | 
 | 
            return binding;  | 
 | 
        } catch (Exception e) { | 
 | 
            NamingException ne = new NamingException(  | 
 | 
                    "Problem generating object using object factory");  | 
 | 
            ne.setRootCause(e);  | 
 | 
            throw ne;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |