blob: a08d34352528a7baf9fe52a5232808d3a91cbdd4 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.util.*;
7
8/**
9 * A representation of a $GENERATE statement in a master file.
10 *
11 * @author Brian Wellington
12 */
13
14public class Generator {
15
16/** The start of the range. */
17public long start;
18
19/** The end of the range. */
20public long end;
21
22/** The step value of the range. */
23public long step;
24
25/** The pattern to use for generating record names. */
26public final String namePattern;
27
28/** The type of the generated records. */
29public final int type;
30
31/** The class of the generated records. */
32public final int dclass;
33
34/** The ttl of the generated records. */
35public final long ttl;
36
37/** The pattern to use for generating record data. */
38public final String rdataPattern;
39
40/** The origin to append to relative names. */
41public final Name origin;
42
43private long current;
44
45/**
46 * Indicates whether generation is supported for this type.
47 * @throws InvalidTypeException The type is out of range.
48 */
49public static boolean
50supportedType(int type) {
51 Type.check(type);
52 return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME ||
53 type == Type.A || type == Type.AAAA || type == Type.NS);
54}
55
56/**
57 * Creates a specification for generating records, as a $GENERATE
58 * statement in a master file.
59 * @param start The start of the range.
60 * @param end The end of the range.
61 * @param step The step value of the range.
62 * @param namePattern The pattern to use for generating record names.
63 * @param type The type of the generated records. The supported types are
64 * PTR, CNAME, DNAME, A, AAAA, and NS.
65 * @param dclass The class of the generated records.
66 * @param ttl The ttl of the generated records.
67 * @param rdataPattern The pattern to use for generating record data.
68 * @param origin The origin to append to relative names.
69 * @throws IllegalArgumentException The range is invalid.
70 * @throws IllegalArgumentException The type does not support generation.
71 * @throws IllegalArgumentException The dclass is not a valid class.
72 */
73public
74Generator(long start, long end, long step, String namePattern,
75 int type, int dclass, long ttl, String rdataPattern, Name origin)
76{
77 if (start < 0 || end < 0 || start > end || step <= 0)
78 throw new IllegalArgumentException
79 ("invalid range specification");
80 if (!supportedType(type))
81 throw new IllegalArgumentException("unsupported type");
82 DClass.check(dclass);
83
84 this.start = start;
85 this.end = end;
86 this.step = step;
87 this.namePattern = namePattern;
88 this.type = type;
89 this.dclass = dclass;
90 this.ttl = ttl;
91 this.rdataPattern = rdataPattern;
92 this.origin = origin;
93 this.current = start;
94}
95
96private String
97substitute(String spec, long n) throws IOException {
98 boolean escaped = false;
99 byte [] str = spec.getBytes();
100 StringBuffer sb = new StringBuffer();
101
102 for (int i = 0; i < str.length; i++) {
103 char c = (char)(str[i] & 0xFF);
104 if (escaped) {
105 sb.append(c);
106 escaped = false;
107 } else if (c == '\\') {
108 if (i + 1 == str.length)
109 throw new TextParseException
110 ("invalid escape character");
111 escaped = true;
112 } else if (c == '$') {
113 boolean negative = false;
114 long offset = 0;
115 long width = 0;
116 long base = 10;
117 boolean wantUpperCase = false;
118 if (i + 1 < str.length && str[i + 1] == '$') {
119 // '$$' == literal '$' for backwards
120 // compatibility with old versions of BIND.
121 c = (char)(str[++i] & 0xFF);
122 sb.append(c);
123 continue;
124 } else if (i + 1 < str.length && str[i + 1] == '{') {
125 // It's a substitution with modifiers.
126 i++;
127 if (i + 1 < str.length && str[i + 1] == '-') {
128 negative = true;
129 i++;
130 }
131 while (i + 1 < str.length) {
132 c = (char)(str[++i] & 0xFF);
133 if (c == ',' || c == '}')
134 break;
135 if (c < '0' || c > '9')
136 throw new TextParseException(
137 "invalid offset");
138 c -= '0';
139 offset *= 10;
140 offset += c;
141 }
142 if (negative)
143 offset = -offset;
144
145 if (c == ',') {
146 while (i + 1 < str.length) {
147 c = (char)(str[++i] & 0xFF);
148 if (c == ',' || c == '}')
149 break;
150 if (c < '0' || c > '9')
151 throw new
152 TextParseException(
153 "invalid width");
154 c -= '0';
155 width *= 10;
156 width += c;
157 }
158 }
159
160 if (c == ',') {
161 if (i + 1 == str.length)
162 throw new TextParseException(
163 "invalid base");
164 c = (char)(str[++i] & 0xFF);
165 if (c == 'o')
166 base = 8;
167 else if (c == 'x')
168 base = 16;
169 else if (c == 'X') {
170 base = 16;
171 wantUpperCase = true;
172 }
173 else if (c != 'd')
174 throw new TextParseException(
175 "invalid base");
176 }
177
178 if (i + 1 == str.length || str[i + 1] != '}')
179 throw new TextParseException
180 ("invalid modifiers");
181 i++;
182 }
183 long v = n + offset;
184 if (v < 0)
185 throw new TextParseException
186 ("invalid offset expansion");
187 String number;
188 if (base == 8)
189 number = Long.toOctalString(v);
190 else if (base == 16)
191 number = Long.toHexString(v);
192 else
193 number = Long.toString(v);
194 if (wantUpperCase)
195 number = number.toUpperCase();
196 if (width != 0 && width > number.length()) {
197 int zeros = (int)width - number.length();
198 while (zeros-- > 0)
199 sb.append('0');
200 }
201 sb.append(number);
202 } else {
203 sb.append(c);
204 }
205 }
206 return sb.toString();
207}
208
209/**
210 * Constructs and returns the next record in the expansion.
211 * @throws IOException The name or rdata was invalid after substitutions were
212 * performed.
213 */
214public Record
215nextRecord() throws IOException {
216 if (current > end)
217 return null;
218 String namestr = substitute(namePattern, current);
219 Name name = Name.fromString(namestr, origin);
220 String rdata = substitute(rdataPattern, current);
221 current += step;
222 return Record.fromString(name, type, dclass, ttl, rdata, origin);
223}
224
225/**
226 * Constructs and returns all records in the expansion.
227 * @throws IOException The name or rdata of a record was invalid after
228 * substitutions were performed.
229 */
230public Record []
231expand() throws IOException {
232 List list = new ArrayList();
233 for (long i = start; i < end; i += step) {
234 String namestr = substitute(namePattern, current);
235 Name name = Name.fromString(namestr, origin);
236 String rdata = substitute(rdataPattern, current);
237 list.add(Record.fromString(name, type, dclass, ttl,
238 rdata, origin));
239 }
240 return (Record []) list.toArray(new Record[list.size()]);
241}
242
243/**
244 * Converts the generate specification to a string containing the corresponding
245 * $GENERATE statement.
246 */
247public String
248toString() {
249 StringBuffer sb = new StringBuffer();
250 sb.append("$GENERATE ");
251 sb.append(start + "-" + end);
252 if (step > 1)
253 sb.append("/" + step);
254 sb.append(" ");
255 sb.append(namePattern + " ");
256 sb.append(ttl + " ");
257 if (dclass != DClass.IN || !Options.check("noPrintIN"))
258 sb.append(DClass.string(dclass) + " ");
259 sb.append(Type.string(type) + " ");
260 sb.append(rdataPattern + " ");
261 return sb.toString();
262}
263
264}