| // Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) |
| |
| package org.xbill.DNS; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| /** |
| * A representation of a $GENERATE statement in a master file. |
| * |
| * @author Brian Wellington |
| */ |
| |
| public class Generator { |
| |
| /** The start of the range. */ |
| public long start; |
| |
| /** The end of the range. */ |
| public long end; |
| |
| /** The step value of the range. */ |
| public long step; |
| |
| /** The pattern to use for generating record names. */ |
| public final String namePattern; |
| |
| /** The type of the generated records. */ |
| public final int type; |
| |
| /** The class of the generated records. */ |
| public final int dclass; |
| |
| /** The ttl of the generated records. */ |
| public final long ttl; |
| |
| /** The pattern to use for generating record data. */ |
| public final String rdataPattern; |
| |
| /** The origin to append to relative names. */ |
| public final Name origin; |
| |
| private long current; |
| |
| /** |
| * Indicates whether generation is supported for this type. |
| * @throws InvalidTypeException The type is out of range. |
| */ |
| public static boolean |
| supportedType(int type) { |
| Type.check(type); |
| return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME || |
| type == Type.A || type == Type.AAAA || type == Type.NS); |
| } |
| |
| /** |
| * Creates a specification for generating records, as a $GENERATE |
| * statement in a master file. |
| * @param start The start of the range. |
| * @param end The end of the range. |
| * @param step The step value of the range. |
| * @param namePattern The pattern to use for generating record names. |
| * @param type The type of the generated records. The supported types are |
| * PTR, CNAME, DNAME, A, AAAA, and NS. |
| * @param dclass The class of the generated records. |
| * @param ttl The ttl of the generated records. |
| * @param rdataPattern The pattern to use for generating record data. |
| * @param origin The origin to append to relative names. |
| * @throws IllegalArgumentException The range is invalid. |
| * @throws IllegalArgumentException The type does not support generation. |
| * @throws IllegalArgumentException The dclass is not a valid class. |
| */ |
| public |
| Generator(long start, long end, long step, String namePattern, |
| int type, int dclass, long ttl, String rdataPattern, Name origin) |
| { |
| if (start < 0 || end < 0 || start > end || step <= 0) |
| throw new IllegalArgumentException |
| ("invalid range specification"); |
| if (!supportedType(type)) |
| throw new IllegalArgumentException("unsupported type"); |
| DClass.check(dclass); |
| |
| this.start = start; |
| this.end = end; |
| this.step = step; |
| this.namePattern = namePattern; |
| this.type = type; |
| this.dclass = dclass; |
| this.ttl = ttl; |
| this.rdataPattern = rdataPattern; |
| this.origin = origin; |
| this.current = start; |
| } |
| |
| private String |
| substitute(String spec, long n) throws IOException { |
| boolean escaped = false; |
| byte [] str = spec.getBytes(); |
| StringBuffer sb = new StringBuffer(); |
| |
| for (int i = 0; i < str.length; i++) { |
| char c = (char)(str[i] & 0xFF); |
| if (escaped) { |
| sb.append(c); |
| escaped = false; |
| } else if (c == '\\') { |
| if (i + 1 == str.length) |
| throw new TextParseException |
| ("invalid escape character"); |
| escaped = true; |
| } else if (c == '$') { |
| boolean negative = false; |
| long offset = 0; |
| long width = 0; |
| long base = 10; |
| boolean wantUpperCase = false; |
| if (i + 1 < str.length && str[i + 1] == '$') { |
| // '$$' == literal '$' for backwards |
| // compatibility with old versions of BIND. |
| c = (char)(str[++i] & 0xFF); |
| sb.append(c); |
| continue; |
| } else if (i + 1 < str.length && str[i + 1] == '{') { |
| // It's a substitution with modifiers. |
| i++; |
| if (i + 1 < str.length && str[i + 1] == '-') { |
| negative = true; |
| i++; |
| } |
| while (i + 1 < str.length) { |
| c = (char)(str[++i] & 0xFF); |
| if (c == ',' || c == '}') |
| break; |
| if (c < '0' || c > '9') |
| throw new TextParseException( |
| "invalid offset"); |
| c -= '0'; |
| offset *= 10; |
| offset += c; |
| } |
| if (negative) |
| offset = -offset; |
| |
| if (c == ',') { |
| while (i + 1 < str.length) { |
| c = (char)(str[++i] & 0xFF); |
| if (c == ',' || c == '}') |
| break; |
| if (c < '0' || c > '9') |
| throw new |
| TextParseException( |
| "invalid width"); |
| c -= '0'; |
| width *= 10; |
| width += c; |
| } |
| } |
| |
| if (c == ',') { |
| if (i + 1 == str.length) |
| throw new TextParseException( |
| "invalid base"); |
| c = (char)(str[++i] & 0xFF); |
| if (c == 'o') |
| base = 8; |
| else if (c == 'x') |
| base = 16; |
| else if (c == 'X') { |
| base = 16; |
| wantUpperCase = true; |
| } |
| else if (c != 'd') |
| throw new TextParseException( |
| "invalid base"); |
| } |
| |
| if (i + 1 == str.length || str[i + 1] != '}') |
| throw new TextParseException |
| ("invalid modifiers"); |
| i++; |
| } |
| long v = n + offset; |
| if (v < 0) |
| throw new TextParseException |
| ("invalid offset expansion"); |
| String number; |
| if (base == 8) |
| number = Long.toOctalString(v); |
| else if (base == 16) |
| number = Long.toHexString(v); |
| else |
| number = Long.toString(v); |
| if (wantUpperCase) |
| number = number.toUpperCase(); |
| if (width != 0 && width > number.length()) { |
| int zeros = (int)width - number.length(); |
| while (zeros-- > 0) |
| sb.append('0'); |
| } |
| sb.append(number); |
| } else { |
| sb.append(c); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| /** |
| * Constructs and returns the next record in the expansion. |
| * @throws IOException The name or rdata was invalid after substitutions were |
| * performed. |
| */ |
| public Record |
| nextRecord() throws IOException { |
| if (current > end) |
| return null; |
| String namestr = substitute(namePattern, current); |
| Name name = Name.fromString(namestr, origin); |
| String rdata = substitute(rdataPattern, current); |
| current += step; |
| return Record.fromString(name, type, dclass, ttl, rdata, origin); |
| } |
| |
| /** |
| * Constructs and returns all records in the expansion. |
| * @throws IOException The name or rdata of a record was invalid after |
| * substitutions were performed. |
| */ |
| public Record [] |
| expand() throws IOException { |
| List list = new ArrayList(); |
| for (long i = start; i < end; i += step) { |
| String namestr = substitute(namePattern, current); |
| Name name = Name.fromString(namestr, origin); |
| String rdata = substitute(rdataPattern, current); |
| list.add(Record.fromString(name, type, dclass, ttl, |
| rdata, origin)); |
| } |
| return (Record []) list.toArray(new Record[list.size()]); |
| } |
| |
| /** |
| * Converts the generate specification to a string containing the corresponding |
| * $GENERATE statement. |
| */ |
| public String |
| toString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("$GENERATE "); |
| sb.append(start + "-" + end); |
| if (step > 1) |
| sb.append("/" + step); |
| sb.append(" "); |
| sb.append(namePattern + " "); |
| sb.append(ttl + " "); |
| if (dclass != DClass.IN || !Options.check("noPrintIN")) |
| sb.append(DClass.string(dclass) + " "); |
| sb.append(Type.string(type) + " "); |
| sb.append(rdataPattern + " "); |
| return sb.toString(); |
| } |
| |
| } |