1 /* 2 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.dns; 27 28 29 import java.lang.ref.SoftReference; 30 import java.util.Date; 31 import java.util.Vector; 32 33 34 /** 35 * ZoneNode extends NameNode to represent a tree of the zones in the 36 * DNS namespace, along with any intermediate nodes between zones. 37 * A ZoneNode that represents a zone may be "populated" with a 38 * NameNode tree containing the zone's contents. 39 * 40 * <p> A populated zone's contents will be flagged as having expired after 41 * the time specified by the minimum TTL value in the zone's SOA record. 42 * 43 * <p> Since zone cuts aren't directly modeled by a tree of ZoneNodes, 44 * ZoneNode.isZoneCut() always returns false. 45 * 46 * <p> The synchronization strategy is documented in DnsContext.java. 47 * 48 * <p> The zone's contents are accessed via a soft reference, so its 49 * heap space may be reclaimed when necessary. The zone may be 50 * repopulated later. 51 * 52 * @author Scott Seligman 53 */ 54 55 56 class ZoneNode extends NameNode { 57 58 private SoftReference<NameNode> contentsRef = null; // the zone's namespace 59 private long serialNumber = -1; // the zone data's serial number 60 private Date expiration = null; // time when the zone's data expires 61 62 ZoneNode(String label) { 63 super(label); 64 } 65 66 protected NameNode newNameNode(String label) { 67 return new ZoneNode(label); 68 } 69 70 /* 71 * Clears the contents of this node. If the node was flagged as 72 * expired, it remains so. 73 */ 74 synchronized void depopulate() { 75 contentsRef = null; 76 serialNumber = -1; 77 } 78 79 /* 80 * Is this node currently populated? 81 */ 82 synchronized boolean isPopulated() { 83 return (getContents() != null); 84 } 85 86 /* 87 * Returns the zone's contents, or null if the zone is not populated. 88 */ 89 synchronized NameNode getContents() { 90 return (contentsRef != null) 91 ? contentsRef.get() 92 : null; 93 } 94 95 /* 96 * Has this zone's data expired? 97 */ 98 synchronized boolean isExpired() { 99 return ((expiration != null) && expiration.before(new Date())); 100 } 101 102 /* 103 * Returns the deepest populated zone on the path specified by a 104 * fully-qualified domain name, or null if there is no populated 105 * zone on that path. Note that a node may be depopulated after 106 * being returned. 107 */ 108 ZoneNode getDeepestPopulated(DnsName fqdn) { 109 ZoneNode znode = this; 110 ZoneNode popNode = isPopulated() ? this : null; 111 for (int i = 1; i < fqdn.size(); i++) { // "i=1" to skip root label 112 znode = (ZoneNode) znode.get(fqdn.getKey(i)); 113 if (znode == null) { 114 break; 115 } else if (znode.isPopulated()) { 116 popNode = znode; 117 } 118 } 119 return popNode; 120 } 121 122 /* 123 * Populates (or repopulates) a zone given its own fully-qualified 124 * name and its resource records. Returns the zone's new contents. 125 */ 126 NameNode populate(DnsName zone, ResourceRecords rrs) { 127 // assert zone.get(0).isEmpty(); // zone has root label 128 // assert (zone.size() == (depth() + 1)); // +1 due to root label 129 130 NameNode newContents = new NameNode(null); 131 132 for (int i = 0; i < rrs.answer.size(); i++) { 133 ResourceRecord rr = rrs.answer.elementAt(i); 134 DnsName n = rr.getName(); 135 136 // Ignore resource records whose names aren't within the zone's 137 // domain. Also skip records of the zone's top node, since 138 // the zone's root NameNode is already in place. 139 if ((n.size() > zone.size()) && n.startsWith(zone)) { 140 NameNode nnode = newContents.add(n, zone.size()); 141 if (rr.getType() == ResourceRecord.TYPE_NS) { 142 nnode.setZoneCut(true); 143 } 144 } 145 } 146 // The zone's SOA record is the first record in the answer section. 147 ResourceRecord soa = rrs.answer.firstElement(); 148 synchronized (this) { 149 contentsRef = new SoftReference<NameNode>(newContents); 150 serialNumber = getSerialNumber(soa); 151 setExpiration(getMinimumTtl(soa)); 152 return newContents; 153 } 154 } 155 156 /* 157 * Set this zone's data to expire in {@code secsToExpiration} seconds. 158 */ 159 private void setExpiration(long secsToExpiration) { 160 expiration = new Date(System.currentTimeMillis() + 161 1000 * secsToExpiration); 162 } 163 164 /* 165 * Returns an SOA record's minimum TTL field. 166 */ 167 private static long getMinimumTtl(ResourceRecord soa) { 168 String rdata = (String) soa.getRdata(); 169 int pos = rdata.lastIndexOf(' ') + 1; 170 return Long.parseLong(rdata.substring(pos)); 171 } 172 173 /* 174 * Compares this zone's serial number with that of an SOA record. 175 * Zone must be populated. 176 * Returns a negative, zero, or positive integer as this zone's 177 * serial number is less than, equal to, or greater than the SOA 178 * record's. 179 * See ResourceRecord.compareSerialNumbers() for a description of 180 * serial number arithmetic. 181 */ 182 int compareSerialNumberTo(ResourceRecord soa) { 183 // assert isPopulated(); 184 return ResourceRecord.compareSerialNumbers(serialNumber, 185 getSerialNumber(soa)); 186 } 187 188 /* 189 * Returns an SOA record's serial number. 190 */ 191 private static long getSerialNumber(ResourceRecord soa) { 192 String rdata = (String) soa.getRdata(); 193 194 // An SOA record ends with: serial refresh retry expire minimum. 195 // Set "beg" to the space before serial, and "end" to the space after. 196 // We go "backward" to avoid dealing with escaped spaces in names. 197 int beg = rdata.length(); 198 int end = -1; 199 for (int i = 0; i < 5; i++) { 200 end = beg; 201 beg = rdata.lastIndexOf(' ', end - 1); 202 } 203 return Long.parseLong(rdata.substring(beg + 1, end)); 204 } 205 }