aboutsummaryrefslogtreecommitdiff
path: root/src/org/xbill/DNS/Zone.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/xbill/DNS/Zone.java')
-rw-r--r--src/org/xbill/DNS/Zone.java559
1 files changed, 559 insertions, 0 deletions
diff --git a/src/org/xbill/DNS/Zone.java b/src/org/xbill/DNS/Zone.java
new file mode 100644
index 0000000..866be77
--- /dev/null
+++ b/src/org/xbill/DNS/Zone.java
@@ -0,0 +1,559 @@
+// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
+
+package org.xbill.DNS;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * A DNS Zone. This encapsulates all data related to a Zone, and provides
+ * convenient lookup methods.
+ *
+ * @author Brian Wellington
+ */
+
+public class Zone implements Serializable {
+
+private static final long serialVersionUID = -9220510891189510942L;
+
+/** A primary zone */
+public static final int PRIMARY = 1;
+
+/** A secondary zone */
+public static final int SECONDARY = 2;
+
+private Map data;
+private Name origin;
+private Object originNode;
+private int dclass = DClass.IN;
+private RRset NS;
+private SOARecord SOA;
+private boolean hasWild;
+
+class ZoneIterator implements Iterator {
+ private Iterator zentries;
+ private RRset [] current;
+ private int count;
+ private boolean wantLastSOA;
+
+ ZoneIterator(boolean axfr) {
+ synchronized (Zone.this) {
+ zentries = data.entrySet().iterator();
+ }
+ wantLastSOA = axfr;
+ RRset [] sets = allRRsets(originNode);
+ current = new RRset[sets.length];
+ for (int i = 0, j = 2; i < sets.length; i++) {
+ int type = sets[i].getType();
+ if (type == Type.SOA)
+ current[0] = sets[i];
+ else if (type == Type.NS)
+ current[1] = sets[i];
+ else
+ current[j++] = sets[i];
+ }
+ }
+
+ public boolean
+ hasNext() {
+ return (current != null || wantLastSOA);
+ }
+
+ public Object
+ next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ if (current == null) {
+ wantLastSOA = false;
+ return oneRRset(originNode, Type.SOA);
+ }
+ Object set = current[count++];
+ if (count == current.length) {
+ current = null;
+ while (zentries.hasNext()) {
+ Map.Entry entry = (Map.Entry) zentries.next();
+ if (entry.getKey().equals(origin))
+ continue;
+ RRset [] sets = allRRsets(entry.getValue());
+ if (sets.length == 0)
+ continue;
+ current = sets;
+ count = 0;
+ break;
+ }
+ }
+ return set;
+ }
+
+ public void
+ remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
+private void
+validate() throws IOException {
+ originNode = exactName(origin);
+ if (originNode == null)
+ throw new IOException(origin + ": no data specified");
+
+ RRset rrset = oneRRset(originNode, Type.SOA);
+ if (rrset == null || rrset.size() != 1)
+ throw new IOException(origin +
+ ": exactly 1 SOA must be specified");
+ Iterator it = rrset.rrs();
+ SOA = (SOARecord) it.next();
+
+ NS = oneRRset(originNode, Type.NS);
+ if (NS == null)
+ throw new IOException(origin + ": no NS set specified");
+}
+
+private final void
+maybeAddRecord(Record record) throws IOException {
+ int rtype = record.getType();
+ Name name = record.getName();
+
+ if (rtype == Type.SOA && !name.equals(origin)) {
+ throw new IOException("SOA owner " + name +
+ " does not match zone origin " +
+ origin);
+ }
+ if (name.subdomain(origin))
+ addRecord(record);
+}
+
+/**
+ * Creates a Zone from the records in the specified master file.
+ * @param zone The name of the zone.
+ * @param file The master file to read from.
+ * @see Master
+ */
+public
+Zone(Name zone, String file) throws IOException {
+ data = new TreeMap();
+
+ if (zone == null)
+ throw new IllegalArgumentException("no zone name specified");
+ Master m = new Master(file, zone);
+ Record record;
+
+ origin = zone;
+ while ((record = m.nextRecord()) != null)
+ maybeAddRecord(record);
+ validate();
+}
+
+/**
+ * Creates a Zone from an array of records.
+ * @param zone The name of the zone.
+ * @param records The records to add to the zone.
+ * @see Master
+ */
+public
+Zone(Name zone, Record [] records) throws IOException {
+ data = new TreeMap();
+
+ if (zone == null)
+ throw new IllegalArgumentException("no zone name specified");
+ origin = zone;
+ for (int i = 0; i < records.length; i++)
+ maybeAddRecord(records[i]);
+ validate();
+}
+
+private void
+fromXFR(ZoneTransferIn xfrin) throws IOException, ZoneTransferException {
+ data = new TreeMap();
+
+ origin = xfrin.getName();
+ List records = xfrin.run();
+ for (Iterator it = records.iterator(); it.hasNext(); ) {
+ Record record = (Record) it.next();
+ maybeAddRecord(record);
+ }
+ if (!xfrin.isAXFR())
+ throw new IllegalArgumentException("zones can only be " +
+ "created from AXFRs");
+ validate();
+}
+
+/**
+ * Creates a Zone by doing the specified zone transfer.
+ * @param xfrin The incoming zone transfer to execute.
+ * @see ZoneTransferIn
+ */
+public
+Zone(ZoneTransferIn xfrin) throws IOException, ZoneTransferException {
+ fromXFR(xfrin);
+}
+
+/**
+ * Creates a Zone by performing a zone transfer to the specified host.
+ * @see ZoneTransferIn
+ */
+public
+Zone(Name zone, int dclass, String remote)
+throws IOException, ZoneTransferException
+{
+ ZoneTransferIn xfrin = ZoneTransferIn.newAXFR(zone, remote, null);
+ xfrin.setDClass(dclass);
+ fromXFR(xfrin);
+}
+
+/** Returns the Zone's origin */
+public Name
+getOrigin() {
+ return origin;
+}
+
+/** Returns the Zone origin's NS records */
+public RRset
+getNS() {
+ return NS;
+}
+
+/** Returns the Zone's SOA record */
+public SOARecord
+getSOA() {
+ return SOA;
+}
+
+/** Returns the Zone's class */
+public int
+getDClass() {
+ return dclass;
+}
+
+private synchronized Object
+exactName(Name name) {
+ return data.get(name);
+}
+
+private synchronized RRset []
+allRRsets(Object types) {
+ if (types instanceof List) {
+ List typelist = (List) types;
+ return (RRset []) typelist.toArray(new RRset[typelist.size()]);
+ } else {
+ RRset set = (RRset) types;
+ return new RRset [] {set};
+ }
+}
+
+private synchronized RRset
+oneRRset(Object types, int type) {
+ if (type == Type.ANY)
+ throw new IllegalArgumentException("oneRRset(ANY)");
+ if (types instanceof List) {
+ List list = (List) types;
+ for (int i = 0; i < list.size(); i++) {
+ RRset set = (RRset) list.get(i);
+ if (set.getType() == type)
+ return set;
+ }
+ } else {
+ RRset set = (RRset) types;
+ if (set.getType() == type)
+ return set;
+ }
+ return null;
+}
+
+private synchronized RRset
+findRRset(Name name, int type) {
+ Object types = exactName(name);
+ if (types == null)
+ return null;
+ return oneRRset(types, type);
+}
+
+private synchronized void
+addRRset(Name name, RRset rrset) {
+ if (!hasWild && name.isWild())
+ hasWild = true;
+ Object types = data.get(name);
+ if (types == null) {
+ data.put(name, rrset);
+ return;
+ }
+ int rtype = rrset.getType();
+ if (types instanceof List) {
+ List list = (List) types;
+ for (int i = 0; i < list.size(); i++) {
+ RRset set = (RRset) list.get(i);
+ if (set.getType() == rtype) {
+ list.set(i, rrset);
+ return;
+ }
+ }
+ list.add(rrset);
+ } else {
+ RRset set = (RRset) types;
+ if (set.getType() == rtype)
+ data.put(name, rrset);
+ else {
+ LinkedList list = new LinkedList();
+ list.add(set);
+ list.add(rrset);
+ data.put(name, list);
+ }
+ }
+}
+
+private synchronized void
+removeRRset(Name name, int type) {
+ Object types = data.get(name);
+ if (types == null) {
+ return;
+ }
+ if (types instanceof List) {
+ List list = (List) types;
+ for (int i = 0; i < list.size(); i++) {
+ RRset set = (RRset) list.get(i);
+ if (set.getType() == type) {
+ list.remove(i);
+ if (list.size() == 0)
+ data.remove(name);
+ return;
+ }
+ }
+ } else {
+ RRset set = (RRset) types;
+ if (set.getType() != type)
+ return;
+ data.remove(name);
+ }
+}
+
+private synchronized SetResponse
+lookup(Name name, int type) {
+ int labels;
+ int olabels;
+ int tlabels;
+ RRset rrset;
+ Name tname;
+ Object types;
+ SetResponse sr;
+
+ if (!name.subdomain(origin))
+ return SetResponse.ofType(SetResponse.NXDOMAIN);
+
+ labels = name.labels();
+ olabels = origin.labels();
+
+ for (tlabels = olabels; tlabels <= labels; tlabels++) {
+ boolean isOrigin = (tlabels == olabels);
+ boolean isExact = (tlabels == labels);
+
+ if (isOrigin)
+ tname = origin;
+ else if (isExact)
+ tname = name;
+ else
+ tname = new Name(name, labels - tlabels);
+
+ types = exactName(tname);
+ if (types == null)
+ continue;
+
+ /* If this is a delegation, return that. */
+ if (!isOrigin) {
+ RRset ns = oneRRset(types, Type.NS);
+ if (ns != null)
+ return new SetResponse(SetResponse.DELEGATION,
+ ns);
+ }
+
+ /* If this is an ANY lookup, return everything. */
+ if (isExact && type == Type.ANY) {
+ sr = new SetResponse(SetResponse.SUCCESSFUL);
+ RRset [] sets = allRRsets(types);
+ for (int i = 0; i < sets.length; i++)
+ sr.addRRset(sets[i]);
+ return sr;
+ }
+
+ /*
+ * If this is the name, look for the actual type or a CNAME.
+ * Otherwise, look for a DNAME.
+ */
+ if (isExact) {
+ rrset = oneRRset(types, type);
+ if (rrset != null) {
+ sr = new SetResponse(SetResponse.SUCCESSFUL);
+ sr.addRRset(rrset);
+ return sr;
+ }
+ rrset = oneRRset(types, Type.CNAME);
+ if (rrset != null)
+ return new SetResponse(SetResponse.CNAME,
+ rrset);
+ } else {
+ rrset = oneRRset(types, Type.DNAME);
+ if (rrset != null)
+ return new SetResponse(SetResponse.DNAME,
+ rrset);
+ }
+
+ /* We found the name, but not the type. */
+ if (isExact)
+ return SetResponse.ofType(SetResponse.NXRRSET);
+ }
+
+ if (hasWild) {
+ for (int i = 0; i < labels - olabels; i++) {
+ tname = name.wild(i + 1);
+
+ types = exactName(tname);
+ if (types == null)
+ continue;
+
+ rrset = oneRRset(types, type);
+ if (rrset != null) {
+ sr = new SetResponse(SetResponse.SUCCESSFUL);
+ sr.addRRset(rrset);
+ return sr;
+ }
+ }
+ }
+
+ return SetResponse.ofType(SetResponse.NXDOMAIN);
+}
+
+/**
+ * Looks up Records in the Zone. This follows CNAMEs and wildcards.
+ * @param name The name to look up
+ * @param type The type to look up
+ * @return A SetResponse object
+ * @see SetResponse
+ */
+public SetResponse
+findRecords(Name name, int type) {
+ return lookup(name, type);
+}
+
+/**
+ * Looks up Records in the zone, finding exact matches only.
+ * @param name The name to look up
+ * @param type The type to look up
+ * @return The matching RRset
+ * @see RRset
+ */
+public RRset
+findExactMatch(Name name, int type) {
+ Object types = exactName(name);
+ if (types == null)
+ return null;
+ return oneRRset(types, type);
+}
+
+/**
+ * Adds an RRset to the Zone
+ * @param rrset The RRset to be added
+ * @see RRset
+ */
+public void
+addRRset(RRset rrset) {
+ Name name = rrset.getName();
+ addRRset(name, rrset);
+}
+
+/**
+ * Adds a Record to the Zone
+ * @param r The record to be added
+ * @see Record
+ */
+public void
+addRecord(Record r) {
+ Name name = r.getName();
+ int rtype = r.getRRsetType();
+ synchronized (this) {
+ RRset rrset = findRRset(name, rtype);
+ if (rrset == null) {
+ rrset = new RRset(r);
+ addRRset(name, rrset);
+ } else {
+ rrset.addRR(r);
+ }
+ }
+}
+
+/**
+ * Removes a record from the Zone
+ * @param r The record to be removed
+ * @see Record
+ */
+public void
+removeRecord(Record r) {
+ Name name = r.getName();
+ int rtype = r.getRRsetType();
+ synchronized (this) {
+ RRset rrset = findRRset(name, rtype);
+ if (rrset == null)
+ return;
+ if (rrset.size() == 1 && rrset.first().equals(r))
+ removeRRset(name, rtype);
+ else
+ rrset.deleteRR(r);
+ }
+}
+
+/**
+ * Returns an Iterator over the RRsets in the zone.
+ */
+public Iterator
+iterator() {
+ return new ZoneIterator(false);
+}
+
+/**
+ * Returns an Iterator over the RRsets in the zone that can be used to
+ * construct an AXFR response. This is identical to {@link #iterator} except
+ * that the SOA is returned at the end as well as the beginning.
+ */
+public Iterator
+AXFR() {
+ return new ZoneIterator(true);
+}
+
+private void
+nodeToString(StringBuffer sb, Object node) {
+ RRset [] sets = allRRsets(node);
+ for (int i = 0; i < sets.length; i++) {
+ RRset rrset = sets[i];
+ Iterator it = rrset.rrs();
+ while (it.hasNext())
+ sb.append(it.next() + "\n");
+ it = rrset.sigs();
+ while (it.hasNext())
+ sb.append(it.next() + "\n");
+ }
+}
+
+/**
+ * Returns the contents of the Zone in master file format.
+ */
+public synchronized String
+toMasterFile() {
+ Iterator zentries = data.entrySet().iterator();
+ StringBuffer sb = new StringBuffer();
+ nodeToString(sb, originNode);
+ while (zentries.hasNext()) {
+ Map.Entry entry = (Map.Entry) zentries.next();
+ if (!origin.equals(entry.getKey()))
+ nodeToString(sb, entry.getValue());
+ }
+ return sb.toString();
+}
+
+/**
+ * Returns the contents of the Zone as a string (in master file format).
+ */
+public String
+toString() {
+ return toMasterFile();
+}
+
+}