001: // Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
002:
003: package org.xbill.DNS;
004:
005: import java.io.*;
006: import java.util.*;
007:
008: /**
009: * A representation of a $GENERATE statement in a master file.
010: *
011: * @author Brian Wellington
012: */
013:
014: public class Generator {
015:
016: /** The start of the range. */
017: public long start;
018:
019: /** The end of the range. */
020: public long end;
021:
022: /** The step value of the range. */
023: public long step;
024:
025: /** The pattern to use for generating record names. */
026: public final String namePattern;
027:
028: /** The type of the generated records. */
029: public final int type;
030:
031: /** The class of the generated records. */
032: public final int dclass;
033:
034: /** The ttl of the generated records. */
035: public final long ttl;
036:
037: /** The pattern to use for generating record data. */
038: public final String rdataPattern;
039:
040: /** The origin to append to relative names. */
041: public final Name origin;
042:
043: private long current;
044:
045: /**
046: * Indicates whether generation is supported for this type.
047: * @throws InvalidTypeException The type is out of range.
048: */
049: public static boolean supportedType(int type) {
050: Type.check(type);
051: return (type == Type.PTR || type == Type.CNAME
052: || type == Type.DNAME || type == Type.A
053: || type == Type.AAAA || type == Type.NS);
054: }
055:
056: /**
057: * Creates a specification for generating records, as a $GENERATE
058: * statement in a master file.
059: * @param start The start of the range.
060: * @param end The end of the range.
061: * @param step The step value of the range.
062: * @param namePattern The pattern to use for generating record names.
063: * @param type The type of the generated records. The supported types are
064: * PTR, CNAME, DNAME, A, AAAA, and NS.
065: * @param dclass The class of the generated records.
066: * @param ttl The ttl of the generated records.
067: * @param rdataPattern The pattern to use for generating record data.
068: * @param origin The origin to append to relative names.
069: * @throws IllegalArgumentException The range is invalid.
070: * @throws IllegalArgumentException The type does not support generation.
071: * @throws IllegalArgumentException The dclass is not a valid class.
072: */
073: public Generator(long start, long end, long step,
074: String namePattern, int type, int dclass, long ttl,
075: String rdataPattern, Name origin) {
076: if (start < 0 || end < 0 || start > end || step <= 0)
077: throw new IllegalArgumentException(
078: "invalid range specification");
079: if (!supportedType(type))
080: throw new IllegalArgumentException("unsupported type");
081: DClass.check(dclass);
082:
083: this .start = start;
084: this .end = end;
085: this .step = step;
086: this .namePattern = namePattern;
087: this .type = type;
088: this .dclass = dclass;
089: this .ttl = ttl;
090: this .rdataPattern = rdataPattern;
091: this .origin = origin;
092: this .current = start;
093: }
094:
095: private String substitute(String spec, long n) throws IOException {
096: boolean escaped = false;
097: byte[] str = spec.getBytes();
098: StringBuffer sb = new StringBuffer();
099:
100: for (int i = 0; i < str.length; i++) {
101: char c = (char) (str[i] & 0xFF);
102: if (escaped) {
103: sb.append(c);
104: escaped = false;
105: } else if (c == '\\') {
106: if (i + 1 == str.length)
107: throw new TextParseException(
108: "invalid escape character");
109: escaped = true;
110: } else if (c == '$') {
111: boolean negative = false;
112: long offset = 0;
113: long width = 0;
114: long base = 10;
115: boolean wantUpperCase = false;
116: if (i + 1 < str.length && str[i + 1] == '$') {
117: // '$$' == literal '$' for backwards
118: // compatibility with old versions of BIND.
119: c = (char) (str[++i] & 0xFF);
120: sb.append(c);
121: continue;
122: } else if (i + 1 < str.length && str[i + 1] == '{') {
123: // It's a substitution with modifiers.
124: i++;
125: if (i + 1 < str.length && str[i + 1] == '-') {
126: negative = true;
127: i++;
128: }
129: while (i + 1 < str.length) {
130: c = (char) (str[++i] & 0xFF);
131: if (c == ',' || c == '}')
132: break;
133: if (c < '0' || c > '9')
134: throw new TextParseException(
135: "invalid offset");
136: c -= '0';
137: offset *= 10;
138: offset += c;
139: }
140: if (negative)
141: offset = -offset;
142:
143: if (c == ',') {
144: while (i + 1 < str.length) {
145: c = (char) (str[++i] & 0xFF);
146: if (c == ',' || c == '}')
147: break;
148: if (c < '0' || c > '9')
149: throw new TextParseException(
150: "invalid width");
151: c -= '0';
152: width *= 10;
153: width += c;
154: }
155: }
156:
157: if (c == ',') {
158: if (i + 1 == str.length)
159: throw new TextParseException("invalid base");
160: c = (char) (str[++i] & 0xFF);
161: if (c == 'o')
162: base = 8;
163: else if (c == 'x')
164: base = 16;
165: else if (c == 'X') {
166: base = 16;
167: wantUpperCase = true;
168: } else if (c != 'd')
169: throw new TextParseException("invalid base");
170: }
171:
172: if (i + 1 == str.length || str[i + 1] != '}')
173: throw new TextParseException(
174: "invalid modifiers");
175: i++;
176: }
177: long v = n + offset;
178: if (v < 0)
179: throw new TextParseException(
180: "invalid offset expansion");
181: String number;
182: if (base == 8)
183: number = Long.toOctalString(v);
184: else if (base == 16)
185: number = Long.toHexString(v);
186: else
187: number = Long.toString(v);
188: if (wantUpperCase)
189: number = number.toUpperCase();
190: if (width != 0 && width > number.length()) {
191: int zeros = (int) width - number.length();
192: while (zeros-- > 0)
193: sb.append('0');
194: }
195: sb.append(number);
196: } else {
197: sb.append(c);
198: }
199: }
200: return sb.toString();
201: }
202:
203: /**
204: * Constructs and returns the next record in the expansion.
205: * @throws IOException The name or rdata was invalid after substitutions were
206: * performed.
207: */
208: public Record nextRecord() throws IOException {
209: if (current > end)
210: return null;
211: String namestr = substitute(namePattern, current);
212: Name name = Name.fromString(namestr, origin);
213: String rdata = substitute(rdataPattern, current);
214: current += step;
215: return Record
216: .fromString(name, type, dclass, ttl, rdata, origin);
217: }
218:
219: /**
220: * Constructs and returns all records in the expansion.
221: * @throws IOException The name or rdata of a record was invalid after
222: * substitutions were performed.
223: */
224: public Record[] expand() throws IOException {
225: List list = new ArrayList();
226: for (long i = start; i < end; i += step) {
227: String namestr = substitute(namePattern, current);
228: Name name = Name.fromString(namestr, origin);
229: String rdata = substitute(rdataPattern, current);
230: list.add(Record.fromString(name, type, dclass, ttl, rdata,
231: origin));
232: }
233: return (Record[]) list.toArray(new Record[list.size()]);
234: }
235:
236: /**
237: * Converts the generate specification to a string containing the corresponding
238: * $GENERATE statement.
239: */
240: public String toString() {
241: StringBuffer sb = new StringBuffer();
242: sb.append("$GENERATE ");
243: sb.append(start + "-" + end);
244: if (step > 1)
245: sb.append("/" + step);
246: sb.append(" ");
247: sb.append(namePattern + " ");
248: sb.append(ttl + " ");
249: if (dclass != DClass.IN || !Options.check("noPrintIN"))
250: sb.append(DClass.string(dclass) + " ");
251: sb.append(Type.string(type) + " ");
252: sb.append(rdataPattern + " ");
253: return sb.toString();
254: }
255:
256: }
|