Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/jndi/dns/DnsContext.java
38924 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.dns;262728import java.util.Enumeration;29import java.util.Hashtable;3031import javax.naming.*;32import javax.naming.directory.*;33import javax.naming.spi.DirectoryManager;3435import com.sun.jndi.toolkit.ctx.*;363738/**39* A DnsContext is a directory context representing a DNS node.40*41* @author Scott Seligman42*/434445public class DnsContext extends ComponentDirContext {4647DnsName domain; // fully-qualified domain name of this context,48// with a root (empty) label at position 049Hashtable<Object,Object> environment;50private boolean envShared; // true if environment is possibly shared51// and so must be copied on write52private boolean parentIsDns; // was this DnsContext created by53// another? see composeName()54private String[] servers;55private Resolver resolver;5657private boolean authoritative; // must all responses be authoritative?58private boolean recursion; // request recursion on queries?59private int timeout; // initial timeout on UDP queries in ms60private int retries; // number of UDP retries6162static final NameParser nameParser = new DnsNameParser();6364// Timeouts for UDP queries use exponential backoff: each retry65// is for twice as long as the last. The following constants set66// the defaults for the initial timeout (in ms) and the number of67// retries, and name the environment properties used to override68// these defaults.69private static final int DEFAULT_INIT_TIMEOUT = 1000;70private static final int DEFAULT_RETRIES = 4;71private static final String INIT_TIMEOUT =72"com.sun.jndi.dns.timeout.initial";73private static final String RETRIES = "com.sun.jndi.dns.timeout.retries";7475// The resource record type and class to use for lookups, and the76// property used to modify them77private CT lookupCT;78private static final String LOOKUP_ATTR = "com.sun.jndi.dns.lookup.attr";7980// Property used to disallow recursion on queries81private static final String RECURSION = "com.sun.jndi.dns.recursion";8283// ANY == ResourceRecord.QCLASS_STAR == ResourceRecord.QTYPE_STAR84private static final int ANY = ResourceRecord.QTYPE_STAR;8586// The zone tree used for list operations87private static final ZoneNode zoneTree = new ZoneNode(null);888990/**91* Returns a DNS context for a given domain and servers.92* Each server is of the form "server[:port]".93* IPv6 literal host names include delimiting brackets.94* There must be at least one server.95* The environment must not be null; it is cloned before being stored.96*/97@SuppressWarnings("unchecked")98public DnsContext(String domain, String[] servers, Hashtable<?,?> environment)99throws NamingException {100101this.domain = new DnsName(domain.endsWith(".")102? domain103: domain + ".");104this.servers = (servers == null) ? null : servers.clone();105this.environment = (Hashtable<Object,Object>) environment.clone();106envShared = false;107parentIsDns = false;108resolver = null;109110initFromEnvironment();111}112113/*114* Returns a clone of a DNS context, just like DnsContext(DnsContext)115* but with a different domain name and with parentIsDns set to true.116*/117DnsContext(DnsContext ctx, DnsName domain) {118this(ctx);119this.domain = domain;120parentIsDns = true;121}122123/*124* Returns a clone of a DNS context. The context's modifiable125* private state is independent of the original's (so closing one126* context, for example, won't close the other). The two contexts127* share <tt>environment</tt>, but it's copy-on-write so there's128* no conflict.129*/130private DnsContext(DnsContext ctx) {131environment = ctx.environment; // shared environment, copy-on-write132envShared = ctx.envShared = true;133parentIsDns = ctx.parentIsDns;134domain = ctx.domain;135servers = ctx.servers; // shared servers, no write operation136resolver = ctx.resolver;137authoritative = ctx.authoritative;138recursion = ctx.recursion;139timeout = ctx.timeout;140retries = ctx.retries;141lookupCT = ctx.lookupCT;142}143144public void close() {145if (resolver != null) {146resolver.close();147resolver = null;148}149}150151152//---------- Environment operations153154/*155* Override default with a noncloning version.156*/157protected Hashtable<?,?> p_getEnvironment() {158return environment;159}160161public Hashtable<?,?> getEnvironment() throws NamingException {162return (Hashtable<?,?>) environment.clone();163}164165@SuppressWarnings("unchecked")166public Object addToEnvironment(String propName, Object propVal)167throws NamingException {168169if (propName.equals(LOOKUP_ATTR)) {170lookupCT = getLookupCT((String) propVal);171} else if (propName.equals(Context.AUTHORITATIVE)) {172authoritative = "true".equalsIgnoreCase((String) propVal);173} else if (propName.equals(RECURSION)) {174recursion = "true".equalsIgnoreCase((String) propVal);175} else if (propName.equals(INIT_TIMEOUT)) {176int val = Integer.parseInt((String) propVal);177if (timeout != val) {178timeout = val;179resolver = null;180}181} else if (propName.equals(RETRIES)) {182int val = Integer.parseInt((String) propVal);183if (retries != val) {184retries = val;185resolver = null;186}187}188189if (!envShared) {190return environment.put(propName, propVal);191} else if (environment.get(propName) != propVal) {192// copy on write193environment = (Hashtable<Object,Object>) environment.clone();194envShared = false;195return environment.put(propName, propVal);196} else {197return propVal;198}199}200201@SuppressWarnings("unchecked")202public Object removeFromEnvironment(String propName)203throws NamingException {204205if (propName.equals(LOOKUP_ATTR)) {206lookupCT = getLookupCT(null);207} else if (propName.equals(Context.AUTHORITATIVE)) {208authoritative = false;209} else if (propName.equals(RECURSION)) {210recursion = true;211} else if (propName.equals(INIT_TIMEOUT)) {212if (timeout != DEFAULT_INIT_TIMEOUT) {213timeout = DEFAULT_INIT_TIMEOUT;214resolver = null;215}216} else if (propName.equals(RETRIES)) {217if (retries != DEFAULT_RETRIES) {218retries = DEFAULT_RETRIES;219resolver = null;220}221}222223if (!envShared) {224return environment.remove(propName);225} else if (environment.get(propName) != null) {226// copy-on-write227environment = (Hashtable<Object,Object>) environment.clone();228envShared = false;229return environment.remove(propName);230} else {231return null;232}233}234235/*236* Update PROVIDER_URL property. Call this only when environment237* is not being shared.238*/239void setProviderUrl(String url) {240// assert !envShared;241environment.put(Context.PROVIDER_URL, url);242}243244/*245* Read environment properties and set parameters.246*/247private void initFromEnvironment()248throws InvalidAttributeIdentifierException {249250lookupCT = getLookupCT((String) environment.get(LOOKUP_ATTR));251authoritative = "true".equalsIgnoreCase((String)252environment.get(Context.AUTHORITATIVE));253String val = (String) environment.get(RECURSION);254recursion = ((val == null) ||255"true".equalsIgnoreCase(val));256val = (String) environment.get(INIT_TIMEOUT);257timeout = (val == null)258? DEFAULT_INIT_TIMEOUT259: Integer.parseInt(val);260val = (String) environment.get(RETRIES);261retries = (val == null)262? DEFAULT_RETRIES263: Integer.parseInt(val);264}265266private CT getLookupCT(String attrId)267throws InvalidAttributeIdentifierException {268return (attrId == null)269? new CT(ResourceRecord.CLASS_INTERNET, ResourceRecord.TYPE_TXT)270: fromAttrId(attrId);271}272273274//---------- Naming operations275276public Object c_lookup(Name name, Continuation cont)277throws NamingException {278279cont.setSuccess();280if (name.isEmpty()) {281DnsContext ctx = new DnsContext(this);282ctx.resolver = new Resolver(servers, timeout, retries);283// clone for parallelism284return ctx;285}286try {287DnsName fqdn = fullyQualify(name);288ResourceRecords rrs =289getResolver().query(fqdn, lookupCT.rrclass, lookupCT.rrtype,290recursion, authoritative);291Attributes attrs = rrsToAttrs(rrs, null);292DnsContext ctx = new DnsContext(this, fqdn);293return DirectoryManager.getObjectInstance(ctx, name, this,294environment, attrs);295} catch (NamingException e) {296cont.setError(this, name);297throw cont.fillInException(e);298} catch (Exception e) {299cont.setError(this, name);300NamingException ne = new NamingException(301"Problem generating object using object factory");302ne.setRootCause(e);303throw cont.fillInException(ne);304}305}306307public Object c_lookupLink(Name name, Continuation cont)308throws NamingException {309return c_lookup(name, cont);310}311312public NamingEnumeration<NameClassPair> c_list(Name name, Continuation cont)313throws NamingException {314cont.setSuccess();315try {316DnsName fqdn = fullyQualify(name);317NameNode nnode = getNameNode(fqdn);318DnsContext ctx = new DnsContext(this, fqdn);319return new NameClassPairEnumeration(ctx, nnode.getChildren());320321} catch (NamingException e) {322cont.setError(this, name);323throw cont.fillInException(e);324}325}326327public NamingEnumeration<Binding> c_listBindings(Name name, Continuation cont)328throws NamingException {329cont.setSuccess();330try {331DnsName fqdn = fullyQualify(name);332NameNode nnode = getNameNode(fqdn);333DnsContext ctx = new DnsContext(this, fqdn);334return new BindingEnumeration(ctx, nnode.getChildren());335336} catch (NamingException e) {337cont.setError(this, name);338throw cont.fillInException(e);339}340}341342public void c_bind(Name name, Object obj, Continuation cont)343throws NamingException {344cont.setError(this, name);345throw cont.fillInException(346new OperationNotSupportedException());347}348349public void c_rebind(Name name, Object obj, Continuation cont)350throws NamingException {351cont.setError(this, name);352throw cont.fillInException(353new OperationNotSupportedException());354}355356public void c_unbind(Name name, Continuation cont)357throws NamingException {358cont.setError(this, name);359throw cont.fillInException(360new OperationNotSupportedException());361}362363public void c_rename(Name oldname, Name newname, Continuation cont)364throws NamingException {365cont.setError(this, oldname);366throw cont.fillInException(367new OperationNotSupportedException());368}369370public Context c_createSubcontext(Name name, Continuation cont)371throws NamingException {372cont.setError(this, name);373throw cont.fillInException(374new OperationNotSupportedException());375}376377public void c_destroySubcontext(Name name, Continuation cont)378throws NamingException {379cont.setError(this, name);380throw cont.fillInException(381new OperationNotSupportedException());382}383384public NameParser c_getNameParser(Name name, Continuation cont)385throws NamingException {386cont.setSuccess();387return nameParser;388}389390391//---------- Directory operations392393public void c_bind(Name name,394Object obj,395Attributes attrs,396Continuation cont)397throws NamingException {398cont.setError(this, name);399throw cont.fillInException(400new OperationNotSupportedException());401}402403public void c_rebind(Name name,404Object obj,405Attributes attrs,406Continuation cont)407throws NamingException {408cont.setError(this, name);409throw cont.fillInException(410new OperationNotSupportedException());411}412413public DirContext c_createSubcontext(Name name,414Attributes attrs,415Continuation cont)416throws NamingException {417cont.setError(this, name);418throw cont.fillInException(419new OperationNotSupportedException());420}421422public Attributes c_getAttributes(Name name,423String[] attrIds,424Continuation cont)425throws NamingException {426427cont.setSuccess();428try {429DnsName fqdn = fullyQualify(name);430CT[] cts = attrIdsToClassesAndTypes(attrIds);431CT ct = getClassAndTypeToQuery(cts);432ResourceRecords rrs =433getResolver().query(fqdn, ct.rrclass, ct.rrtype,434recursion, authoritative);435return rrsToAttrs(rrs, cts);436437} catch (NamingException e) {438cont.setError(this, name);439throw cont.fillInException(e);440}441}442443public void c_modifyAttributes(Name name,444int mod_op,445Attributes attrs,446Continuation cont)447throws NamingException {448cont.setError(this, name);449throw cont.fillInException(450new OperationNotSupportedException());451}452453public void c_modifyAttributes(Name name,454ModificationItem[] mods,455Continuation cont)456throws NamingException {457cont.setError(this, name);458throw cont.fillInException(459new OperationNotSupportedException());460}461462public NamingEnumeration<SearchResult> c_search(Name name,463Attributes matchingAttributes,464String[] attributesToReturn,465Continuation cont)466throws NamingException {467throw new OperationNotSupportedException();468}469470public NamingEnumeration<SearchResult> c_search(Name name,471String filter,472SearchControls cons,473Continuation cont)474throws NamingException {475throw new OperationNotSupportedException();476}477478public NamingEnumeration<SearchResult> c_search(Name name,479String filterExpr,480Object[] filterArgs,481SearchControls cons,482Continuation cont)483throws NamingException {484throw new OperationNotSupportedException();485}486487public DirContext c_getSchema(Name name, Continuation cont)488throws NamingException {489cont.setError(this, name);490throw cont.fillInException(491new OperationNotSupportedException());492}493494public DirContext c_getSchemaClassDefinition(Name name, Continuation cont)495throws NamingException {496cont.setError(this, name);497throw cont.fillInException(498new OperationNotSupportedException());499}500501502//---------- Name-related operations503504public String getNameInNamespace() {505return domain.toString();506}507508public Name composeName(Name name, Name prefix) throws NamingException {509Name result;510511// Any name that's not a CompositeName is assumed to be a DNS512// compound name. Convert each to a DnsName for syntax checking.513if (!(prefix instanceof DnsName || prefix instanceof CompositeName)) {514prefix = (new DnsName()).addAll(prefix);515}516if (!(name instanceof DnsName || name instanceof CompositeName)) {517name = (new DnsName()).addAll(name);518}519520// Each of prefix and name is now either a DnsName or a CompositeName.521522// If we have two DnsNames, simply join them together.523if ((prefix instanceof DnsName) && (name instanceof DnsName)) {524result = (DnsName) (prefix.clone());525result.addAll(name);526return new CompositeName().add(result.toString());527}528529// Wrap compound names in composite names.530Name prefixC = (prefix instanceof CompositeName)531? prefix532: new CompositeName().add(prefix.toString());533Name nameC = (name instanceof CompositeName)534? name535: new CompositeName().add(name.toString());536int prefixLast = prefixC.size() - 1;537538// Let toolkit do the work at namespace boundaries.539if (nameC.isEmpty() || nameC.get(0).equals("") ||540prefixC.isEmpty() || prefixC.get(prefixLast).equals("")) {541return super.composeName(nameC, prefixC);542}543544result = (prefix == prefixC)545? (CompositeName) prefixC.clone()546: prefixC; // prefixC is already a clone547result.addAll(nameC);548549if (parentIsDns) {550DnsName dnsComp = (prefix instanceof DnsName)551? (DnsName) prefix.clone()552: new DnsName(prefixC.get(prefixLast));553dnsComp.addAll((name instanceof DnsName)554? name555: new DnsName(nameC.get(0)));556result.remove(prefixLast + 1);557result.remove(prefixLast);558result.add(prefixLast, dnsComp.toString());559}560return result;561}562563564//---------- Helper methods565566/*567* Resolver is not created until needed, to allow time for updates568* to the environment.569*/570private synchronized Resolver getResolver() throws NamingException {571if (resolver == null) {572resolver = new Resolver(servers, timeout, retries);573}574return resolver;575}576577/*578* Returns the fully-qualified domain name of a name given579* relative to this context. Result includes a root label (an580* empty component at position 0).581*/582DnsName fullyQualify(Name name) throws NamingException {583if (name.isEmpty()) {584return domain;585}586DnsName dnsName = (name instanceof CompositeName)587? new DnsName(name.get(0)) // parse name588: (DnsName) (new DnsName()).addAll(name); // clone & check syntax589590if (dnsName.hasRootLabel()) {591// Be overly generous and allow root label if we're in root domain.592if (domain.size() == 1) {593return dnsName;594} else {595throw new InvalidNameException(596"DNS name " + dnsName + " not relative to " + domain);597}598}599return (DnsName) dnsName.addAll(0, domain);600}601602/*603* Converts resource records to an attribute set. Only resource604* records in the answer section are used, and only those that605* match the classes and types in cts (see classAndTypeMatch()606* for matching rules).607*/608private static Attributes rrsToAttrs(ResourceRecords rrs, CT[] cts) {609610BasicAttributes attrs = new BasicAttributes(true);611612for (int i = 0; i < rrs.answer.size(); i++) {613ResourceRecord rr = rrs.answer.elementAt(i);614int rrtype = rr.getType();615int rrclass = rr.getRrclass();616617if (!classAndTypeMatch(rrclass, rrtype, cts)) {618continue;619}620621String attrId = toAttrId(rrclass, rrtype);622Attribute attr = attrs.get(attrId);623if (attr == null) {624attr = new BasicAttribute(attrId);625attrs.put(attr);626}627attr.add(rr.getRdata());628}629return attrs;630}631632/*633* Returns true if rrclass and rrtype match some element of cts.634* A match occurs if corresponding classes and types are equal,635* or if the array value is ANY. If cts is null, then any class636* and type match.637*/638private static boolean classAndTypeMatch(int rrclass, int rrtype,639CT[] cts) {640if (cts == null) {641return true;642}643for (int i = 0; i < cts.length; i++) {644CT ct = cts[i];645boolean classMatch = (ct.rrclass == ANY) ||646(ct.rrclass == rrclass);647boolean typeMatch = (ct.rrtype == ANY) ||648(ct.rrtype == rrtype);649if (classMatch && typeMatch) {650return true;651}652}653return false;654}655656/*657* Returns the attribute ID for a resource record given its class658* and type. If the record is in the internet class, the659* corresponding attribute ID is the record's type name (or the660* integer type value if the name is not known). If the record is661* not in the internet class, the class name (or integer class662* value) is prepended to the attribute ID, separated by a space.663*664* A class or type value of ANY represents an indeterminate class665* or type, and is represented within the attribute ID by "*".666* For example, the attribute ID "IN *" represents667* any type in the internet class, and "* NS" represents an NS668* record of any class.669*/670private static String toAttrId(int rrclass, int rrtype) {671String attrId = ResourceRecord.getTypeName(rrtype);672if (rrclass != ResourceRecord.CLASS_INTERNET) {673attrId = ResourceRecord.getRrclassName(rrclass) + " " + attrId;674}675return attrId;676}677678/*679* Returns the class and type values corresponding to an attribute680* ID. An indeterminate class or type is represented by ANY. See681* toAttrId() for the format of attribute IDs.682*683* @throws InvalidAttributeIdentifierException684* if class or type is unknown685*/686private static CT fromAttrId(String attrId)687throws InvalidAttributeIdentifierException {688689if (attrId.equals("")) {690throw new InvalidAttributeIdentifierException(691"Attribute ID cannot be empty");692}693int rrclass;694int rrtype;695int space = attrId.indexOf(' ');696697// class698if (space < 0) {699rrclass = ResourceRecord.CLASS_INTERNET;700} else {701String className = attrId.substring(0, space);702rrclass = ResourceRecord.getRrclass(className);703if (rrclass < 0) {704throw new InvalidAttributeIdentifierException(705"Unknown resource record class '" + className + '\'');706}707}708709// type710String typeName = attrId.substring(space + 1);711rrtype = ResourceRecord.getType(typeName);712if (rrtype < 0) {713throw new InvalidAttributeIdentifierException(714"Unknown resource record type '" + typeName + '\'');715}716717return new CT(rrclass, rrtype);718}719720/*721* Returns an array of the classes and types corresponding to a722* set of attribute IDs. See toAttrId() for the format of723* attribute IDs, and classAndTypeMatch() for the format of the724* array returned.725*/726private static CT[] attrIdsToClassesAndTypes(String[] attrIds)727throws InvalidAttributeIdentifierException {728if (attrIds == null) {729return null;730}731CT[] cts = new CT[attrIds.length];732733for (int i = 0; i < attrIds.length; i++) {734cts[i] = fromAttrId(attrIds[i]);735}736return cts;737}738739/*740* Returns the most restrictive resource record class and type741* that may be used to query for records matching cts.742* See classAndTypeMatch() for matching rules.743*/744private static CT getClassAndTypeToQuery(CT[] cts) {745int rrclass;746int rrtype;747748if (cts == null) {749// Query all records.750rrclass = ANY;751rrtype = ANY;752} else if (cts.length == 0) {753// No records are requested, but we need to ask for something.754rrclass = ResourceRecord.CLASS_INTERNET;755rrtype = ANY;756} else {757rrclass = cts[0].rrclass;758rrtype = cts[0].rrtype;759for (int i = 1; i < cts.length; i++) {760if (rrclass != cts[i].rrclass) {761rrclass = ANY;762}763if (rrtype != cts[i].rrtype) {764rrtype = ANY;765}766}767}768return new CT(rrclass, rrtype);769}770771772//---------- Support for list operations773774/*775* Synchronization notes:776*777* Any access to zoneTree that walks the tree, whether it modifies778* the tree or not, is synchronized on zoneTree.779* [%%% Note: a read/write lock would allow increased concurrency.]780* The depth of a ZoneNode can thereafter be accessed without781* further synchronization. Access to other fields and methods782* should be synchronized on the node itself.783*784* A zone's contents is a NameNode tree that, once created, is never785* modified. The only synchronization needed is to ensure that it786* gets flushed into shared memory after being created, which is787* accomplished by ZoneNode.populate(). The contents are accessed788* via a soft reference, so a ZoneNode may be seen to be populated789* one moment and unpopulated the next.790*/791792/*793* Returns the node in the zone tree corresponding to a794* fully-qualified domain name. If the desired portion of the795* tree has not yet been populated or has been outdated, a zone796* transfer is done to populate the tree.797*/798private NameNode getNameNode(DnsName fqdn) throws NamingException {799dprint("getNameNode(" + fqdn + ")");800801// Find deepest related zone in zone tree.802ZoneNode znode;803DnsName zone;804synchronized (zoneTree) {805znode = zoneTree.getDeepestPopulated(fqdn);806}807dprint("Deepest related zone in zone tree: " +808((znode != null) ? znode.getLabel() : "[none]"));809810NameNode topOfZone;811NameNode nnode;812813if (znode != null) {814synchronized (znode) {815topOfZone = znode.getContents();816}817// If fqdn is in znode's zone, is not at a zone cut, and818// is current, we're done.819if (topOfZone != null) {820nnode = topOfZone.get(fqdn, znode.depth() + 1); // +1 for root821822if ((nnode != null) && !nnode.isZoneCut()) {823dprint("Found node " + fqdn + " in zone tree");824zone = (DnsName)825fqdn.getPrefix(znode.depth() + 1); // +1 for root826boolean current = isZoneCurrent(znode, zone);827boolean restart = false;828829synchronized (znode) {830if (topOfZone != znode.getContents()) {831// Zone was modified while we were examining it.832// All bets are off.833restart = true;834} else if (!current) {835znode.depopulate();836} else {837return nnode; // cache hit!838}839}840dprint("Zone not current; discarding node");841if (restart) {842return getNameNode(fqdn);843}844}845}846}847848// Cache miss... do it the expensive way.849dprint("Adding node " + fqdn + " to zone tree");850851// Find fqdn's zone and add it to the tree.852zone = getResolver().findZoneName(fqdn, ResourceRecord.CLASS_INTERNET,853recursion);854dprint("Node's zone is " + zone);855synchronized (zoneTree) {856znode = (ZoneNode) zoneTree.add(zone, 1); // "1" to skip root857}858859// If znode is now populated we know -- because the first half of860// getNodeName() didn't find it -- that it was populated by another861// thread during this method call. Assume then that it's current.862863synchronized (znode) {864topOfZone = znode.isPopulated()865? znode.getContents()866: populateZone(znode, zone);867}868// Desired node should now be in znode's populated zone. Find it.869nnode = topOfZone.get(fqdn, zone.size());870if (nnode == null) {871throw new ConfigurationException(872"DNS error: node not found in its own zone");873}874dprint("Found node in newly-populated zone");875return nnode;876}877878/*879* Does a zone transfer to [re]populate a zone in the zone tree.880* Returns the zone's new contents.881*/882private NameNode populateZone(ZoneNode znode, DnsName zone)883throws NamingException {884dprint("Populating zone " + zone);885// assert Thread.holdsLock(znode);886ResourceRecords rrs =887getResolver().queryZone(zone,888ResourceRecord.CLASS_INTERNET, recursion);889dprint("zone xfer complete: " + rrs.answer.size() + " records");890return znode.populate(zone, rrs);891}892893/*894* Determine if a ZoneNode's data is current.895* We base this on a comparison between the cached serial896* number and the latest SOA record.897*898* If there is no SOA record, znode is not (or is no longer) a zone:899* depopulate znode and return false.900*901* Since this method may perform a network operation, it is best902* to call it with znode unlocked. Caller must then note that the903* result may be outdated by the time this method returns.904*/905private boolean isZoneCurrent(ZoneNode znode, DnsName zone)906throws NamingException {907// former version: return !znode.isExpired();908909if (!znode.isPopulated()) {910return false;911}912ResourceRecord soa =913getResolver().findSoa(zone, ResourceRecord.CLASS_INTERNET,914recursion);915synchronized (znode) {916if (soa == null) {917znode.depopulate();918}919return (znode.isPopulated() &&920znode.compareSerialNumberTo(soa) >= 0);921}922}923924925//---------- Debugging926927private static final boolean debug = false;928929private static final void dprint(String msg) {930if (debug) {931System.err.println("** " + msg);932}933}934}935936937//----------938939/*940* A pairing of a resource record class and a resource record type.941* A value of ANY in either field represents an indeterminate value.942*/943class CT {944int rrclass;945int rrtype;946947CT(int rrclass, int rrtype) {948this.rrclass = rrclass;949this.rrtype = rrtype;950}951}952953954//----------955956/*957* Common base class for NameClassPairEnumeration and BindingEnumeration.958*/959abstract class BaseNameClassPairEnumeration<T> implements NamingEnumeration<T> {960961protected Enumeration<NameNode> nodes; // nodes to be enumerated, or null if none962protected DnsContext ctx; // context being enumerated963964BaseNameClassPairEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) {965this.ctx = ctx;966this.nodes = (nodes != null)967? nodes.elements()968: null;969}970971/*972* ctx will be set to null when no longer needed by the enumeration.973*/974public final void close() {975nodes = null;976ctx = null;977}978979public final boolean hasMore() {980boolean more = ((nodes != null) && nodes.hasMoreElements());981if (!more) {982close();983}984return more;985}986987public final boolean hasMoreElements() {988return hasMore();989}990991abstract public T next() throws NamingException;992993public final T nextElement() {994try {995return next();996} catch (NamingException e) {997java.util.NoSuchElementException nsee =998new java.util.NoSuchElementException();999nsee.initCause(e);1000throw nsee;1001}1002}1003}10041005/*1006* An enumeration of name/classname pairs.1007*1008* Nodes that have children or that are zone cuts are returned with1009* classname DirContext. Other nodes are returned with classname1010* Object even though they are DirContexts as well, since this might1011* make the namespace easier to browse.1012*/1013final class NameClassPairEnumeration1014extends BaseNameClassPairEnumeration<NameClassPair>1015implements NamingEnumeration<NameClassPair> {10161017NameClassPairEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) {1018super(ctx, nodes);1019}10201021@Override1022public NameClassPair next() throws NamingException {1023if (!hasMore()) {1024throw new java.util.NoSuchElementException();1025}1026NameNode nnode = nodes.nextElement();1027String className = (nnode.isZoneCut() ||1028(nnode.getChildren() != null))1029? "javax.naming.directory.DirContext"1030: "java.lang.Object";10311032String label = nnode.getLabel();1033Name compName = (new DnsName()).add(label);1034Name cname = (new CompositeName()).add(compName.toString());10351036NameClassPair ncp = new NameClassPair(cname.toString(), className);1037ncp.setNameInNamespace(ctx.fullyQualify(cname).toString());1038return ncp;1039}1040}10411042/*1043* An enumeration of Bindings.1044*/1045final class BindingEnumeration extends BaseNameClassPairEnumeration<Binding>1046implements NamingEnumeration<Binding> {10471048BindingEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) {1049super(ctx, nodes);1050}10511052// Finalizer not needed since it's safe to leave ctx unclosed.1053// protected void finalize() {1054// close();1055// }10561057@Override1058public Binding next() throws NamingException {1059if (!hasMore()) {1060throw (new java.util.NoSuchElementException());1061}1062NameNode nnode = nodes.nextElement();10631064String label = nnode.getLabel();1065Name compName = (new DnsName()).add(label);1066String compNameStr = compName.toString();1067Name cname = (new CompositeName()).add(compNameStr);1068String cnameStr = cname.toString();10691070DnsName fqdn = ctx.fullyQualify(compName);10711072// Clone ctx to create the child context.1073DnsContext child = new DnsContext(ctx, fqdn);10741075try {1076Object obj = DirectoryManager.getObjectInstance(1077child, cname, ctx, child.environment, null);1078Binding binding = new Binding(cnameStr, obj);1079binding.setNameInNamespace(ctx.fullyQualify(cname).toString());1080return binding;1081} catch (Exception e) {1082NamingException ne = new NamingException(1083"Problem generating object using object factory");1084ne.setRootCause(e);1085throw ne;1086}1087}1088}108910901091