// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) package org.xbill.DNS2.clients; import java.io.IOException; import java.util.ArrayList; import java.util.List; import dorkbox.network.dns.Name; import dorkbox.network.dns.constants.DnsClass; import dorkbox.network.dns.constants.DnsRecordType; import dorkbox.network.dns.exceptions.InvalidTypeException; import dorkbox.network.dns.exceptions.TextParseException; import dorkbox.network.dns.records.DnsRecord; import dorkbox.network.dns.utils.Options; /** * 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; /** * 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"); } DnsClass.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; } /** * Indicates whether generation is supported for this type. * * @throws InvalidTypeException The type is out of range. */ public static boolean supportedType(int type) { DnsRecordType.check(type); return (type == DnsRecordType.PTR || type == DnsRecordType.CNAME || type == DnsRecordType.DNAME || type == DnsRecordType.A || type == DnsRecordType.AAAA || type == DnsRecordType.NS); } /** * Constructs and returns the next record in the expansion. * * @throws IOException The name or rdata was invalid after substitutions were * performed. */ public DnsRecord nextRecord() throws IOException { if (current > end) { return null; } String namestr = substitute(namePattern, current); Name name = Name.Companion.fromString(namestr, origin); String rdata = substitute(rdataPattern, current); current += step; return DnsRecord.fromString(name, type, dclass, ttl, rdata, origin); } private String substitute(String spec, long n) throws IOException { boolean escaped = false; byte[] str = spec.getBytes(); StringBuilder sb = new StringBuilder(); 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 all records in the expansion. * * @throws IOException The name or rdata of a record was invalid after * substitutions were performed. */ public DnsRecord[] expand() throws IOException { List list = new ArrayList(); for (long i = start; i < end; i += step) { String namestr = substitute(namePattern, current); Name name = Name.Companion.fromString(namestr, origin); String rdata = substitute(rdataPattern, current); list.add(DnsRecord.fromString(name, type, dclass, ttl, rdata, origin)); } return (DnsRecord[]) list.toArray(new DnsRecord[list.size()]); } /** * Converts the generate specification to a string containing the corresponding * $GENERATE statement. */ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("$GENERATE "); sb.append(start + "-" + end); if (step > 1) { sb.append("/" + step); } sb.append(" "); sb.append(namePattern + " "); sb.append(ttl + " "); if (dclass != DnsClass.IN || !Options.check("noPrintIN")) { sb.append(DnsClass.string(dclass) + " "); } sb.append(DnsRecordType.string(type) + " "); sb.append(rdataPattern + " "); return sb.toString(); } }