• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
2 
3 package org.xbill.DNS;
4 
5 import java.io.*;
6 import java.util.*;
7 
8 /**
9  * A representation of a $GENERATE statement in a master file.
10  *
11  * @author Brian Wellington
12  */
13 
14 public class Generator {
15 
16 /** The start of the range. */
17 public long start;
18 
19 /** The end of the range. */
20 public long end;
21 
22 /** The step value of the range. */
23 public long step;
24 
25 /** The pattern to use for generating record names. */
26 public final String namePattern;
27 
28 /** The type of the generated records. */
29 public final int type;
30 
31 /** The class of the generated records. */
32 public final int dclass;
33 
34 /** The ttl of the generated records. */
35 public final long ttl;
36 
37 /** The pattern to use for generating record data. */
38 public final String rdataPattern;
39 
40 /** The origin to append to relative names. */
41 public final Name origin;
42 
43 private long current;
44 
45 /**
46  * Indicates whether generation is supported for this type.
47  * @throws InvalidTypeException The type is out of range.
48  */
49 public static boolean
supportedType(int type)50 supportedType(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  */
73 public
Generator(long start, long end, long step, String namePattern, int type, int dclass, long ttl, String rdataPattern, Name origin)74 Generator(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 
96 private String
substitute(String spec, long n)97 substitute(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  */
214 public Record
nextRecord()215 nextRecord() 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  */
230 public Record []
expand()231 expand() 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  */
247 public String
toString()248 toString() {
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 }
265