15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)package org.xbill.DNS;
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import java.io.*;
6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import java.util.*;
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A representation of a $GENERATE statement in a master file.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @author Brian Wellington
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public class Generator {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The start of the range. */
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public long start;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The end of the range. */
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public long end;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The step value of the range. */
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public long step;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The pattern to use for generating record names. */
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final String namePattern;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The type of the generated records. */
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final int type;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The class of the generated records. */
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final int dclass;
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The ttl of the generated records. */
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final long ttl;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The pattern to use for generating record data. */
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final String rdataPattern;
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/** The origin to append to relative names. */
4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)public final Name origin;
4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)private long current;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Indicates whether generation is supported for this type.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @throws InvalidTypeException The type is out of range.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)public static boolean
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)supportedType(int type) {
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	Type.check(type);
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME ||
531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)		type == Type.A || type == Type.AAAA || type == Type.NS);
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Creates a specification for generating records, as a $GENERATE
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * statement in a master file.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param start The start of the range.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param end The end of the range.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param step The step value of the range.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param namePattern The pattern to use for generating record names.
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param type The type of the generated records.  The supported types are
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * PTR, CNAME, DNAME, A, AAAA, and NS.
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param dclass The class of the generated records.
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param ttl The ttl of the generated records.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param rdataPattern The pattern to use for generating record data.
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param origin The origin to append to relative names.
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws IllegalArgumentException The range is invalid.
70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws IllegalArgumentException The type does not support generation.
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws IllegalArgumentException The dclass is not a valid class.
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Generator(long start, long end, long step, String namePattern,
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)	  int type, int dclass, long ttl, String rdataPattern, Name origin)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (start < 0 || end < 0 || start > end || step <= 0)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		throw new IllegalArgumentException
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				("invalid range specification");
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!supportedType(type))
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		throw new IllegalArgumentException("unsupported type");
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	DClass.check(dclass);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	this.start = start;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	this.end = end;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	this.step = step;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	this.namePattern = namePattern;
88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)	this.type = type;
89010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)	this.dclass = dclass;
90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)	this.ttl = ttl;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	this.rdataPattern = rdataPattern;
927d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)	this.origin = origin;
93a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)	this.current = start;
94c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)private String
970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochsubstitute(String spec, long n) throws IOException {
980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch	boolean escaped = false;
990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch	byte [] str = spec.getBytes();
1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu	StringBuffer sb = new StringBuffer();
1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu	for (int i = 0; i < str.length; i++) {
10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)		char c = (char)(str[i] & 0xFF);
10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)		if (escaped) {
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)			sb.append(c);
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)			escaped = false;
1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch		} else if (c == '\\') {
1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch			if (i + 1 == str.length)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				throw new TextParseException
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						("invalid escape character");
111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			escaped = true;
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		} else if (c == '$') {
113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			boolean negative = false;
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			long offset = 0;
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			long width = 0;
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			long base = 10;
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			boolean wantUpperCase = false;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (i + 1 < str.length && str[i + 1] == '$') {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				// '$$' == literal '$' for backwards
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				// compatibility with old versions of BIND.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c = (char)(str[++i] & 0xFF);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				sb.append(c);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				continue;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else if (i + 1 < str.length && str[i + 1] == '{') {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				// It's a substitution with modifiers.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				i++;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (i + 1 < str.length && str[i + 1] == '-') {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					negative = true;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					i++;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				}
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				while (i + 1 < str.length) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c = (char)(str[++i] & 0xFF);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					if (c == ',' || c == '}')
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						break;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					if (c < '0' || c > '9')
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						throw new TextParseException(
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)							"invalid offset");
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c -= '0';
139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch					offset *= 10;
140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch					offset += c;
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch				}
142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch				if (negative)
143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch					offset = -offset;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (c == ',') {
146d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)					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}
265