Added xbill derrived DNS - WIP

This commit is contained in:
nathan 2017-11-06 16:55:24 +01:00
parent b17dfce221
commit 2a66a70a4f
120 changed files with 20361 additions and 470 deletions

View File

@ -0,0 +1,85 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns;
import dorkbox.network.dns.records.DnsMessage;
import dorkbox.network.dns.utils.Options;
/**
* DNS Name Compression object.
*
* @author Brian Wellington
* @see DnsMessage
* @see Name
*/
public
class Compression {
private static final int TABLE_SIZE = 17;
private static final int MAX_POINTER = 0x3FFF;
private Entry[] table;
private boolean verbose = Options.check("verbosecompression");
private static
class Entry {
Name name;
int pos;
Entry next;
}
/**
* Creates a new Compression object.
*/
public
Compression() {
table = new Entry[TABLE_SIZE];
}
/**
* Adds a compression entry mapping a name to a position in a message.
*
* @param pos The position at which the name is added.
* @param name The name being added to the message.
*/
public
void add(int pos, Name name) {
if (pos > MAX_POINTER) {
return;
}
int row = (name.hashCode() & 0x7FFFFFFF) % TABLE_SIZE;
Entry entry = new Entry();
entry.name = name;
entry.pos = pos;
entry.next = table[row];
table[row] = entry;
if (verbose) {
System.err.println("Adding " + name + " at " + pos);
}
}
/**
* Retrieves the position of the given name, if it has been previously
* included in the message.
*
* @param name The name to find in the compression table.
*
* @return The position of the name, or -1 if not found.
*/
public
int get(Name name) {
int row = (name.hashCode() & 0x7FFFFFFF) % TABLE_SIZE;
int pos = -1;
for (Entry entry = table[row]; entry != null; entry = entry.next) {
if (entry.name.equals(name)) {
pos = entry.pos;
}
}
if (verbose) {
System.err.println("Looking for " + name + ", found " + pos);
}
return pos;
}
}

View File

@ -0,0 +1,237 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns;
import dorkbox.network.dns.exceptions.WireParseException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* An class for parsing DNS messages.
*
* @author Brian Wellington
*/
public
class DnsInput {
private ByteBuf byteBuf;
private int savedActiveIndex = -1;
private boolean marked = false;
/**
* Creates a new DnsInput
*
* @param input The byte array to read from
*/
public
DnsInput(byte[] input) {
byteBuf = Unpooled.wrappedBuffer(input);
}
/**
* Creates a new DnsInput from the given {@link ByteBuf}
*
* @param byteBuf The ByteBuf
*/
public
DnsInput(ByteBuf byteBuf) {
this.byteBuf = byteBuf;
}
/**
* Returns the current position, for reading only
*/
public
int readIndex() {
return byteBuf.readerIndex();
}
/**
* NOTE: "Active" restricts operations to a specific part of the buffer, defined by the current position + length. Operations that
* extend BEYOND this section are denied by virtue that we set the max readable length in the underlying ByteBuf.
*
* Marks the following bytes in the stream as active, and saves it's state (so it can be restored later)
*
* @param len The number of bytes in the active region.
*
* @throws IllegalArgumentException The number of bytes in the active region
* is longer than the remainder of the input.
*/
public
void setActive(int len) {
savedActiveIndex = byteBuf.writerIndex();
if (len > byteBuf.readableBytes()) {
throw new IllegalArgumentException("cannot set active " + "region past end of input");
}
byteBuf.writerIndex(byteBuf.readerIndex() + len);
}
/**
* Restores the previously set active region.
*/
public
void restoreActive() {
if (savedActiveIndex > -1) {
byteBuf.writerIndex(savedActiveIndex);
savedActiveIndex = -1;
}
}
/**
* Resets the current position of the input stream to the specified index,
* and clears the active region.
*
* @param index The position to continue parsing at.
*
* @throws IllegalArgumentException The index is not within the input.
*/
public
void jump(int index) {
if (index >= byteBuf.capacity()) {
throw new IllegalArgumentException("cannot jump past " + "end of input");
}
byteBuf.readerIndex(index);
restoreActive();
}
/**
* Saves the current state of the input stream. Both the current position and
* the end of the active region are saved.
*
* @throws IllegalArgumentException The index is not within the input.
*/
public
void save() {
marked = true;
byteBuf.markReaderIndex();
}
/**
* Restores the input stream to its state before the call to {@link #save}.
*/
public
void restore() {
if (!marked) {
throw new IllegalStateException("Not marked first");
}
byteBuf.resetReaderIndex();
}
private
void require(int n) throws WireParseException {
if (n > remaining()) {
throw new WireParseException("end of input");
}
}
/**
* Returns the number of bytes that can be read from this stream before
* reaching the end.
*/
public
int remaining() {
return byteBuf.readableBytes();
}
/**
* Reads an unsigned 8 bit value from the stream, as an int.
*
* @return An unsigned 8 bit value.
*
* @throws WireParseException The end of the stream was reached.
*/
public
int readU8() throws WireParseException {
require(1);
return byteBuf.readUnsignedByte();
}
/**
* Reads an unsigned 16 bit value from the stream, as an int.
*
* @return An unsigned 16 bit value.
*
* @throws WireParseException The end of the stream was reached.
*/
public
int readU16() throws WireParseException {
require(2);
return byteBuf.readUnsignedShort();
}
/**
* Reads an unsigned 32 bit value from the stream, as a long.
*
* @return An unsigned 32 bit value.
*
* @throws WireParseException The end of the stream was reached.
*/
public
long readU32() throws WireParseException {
require(4);
return byteBuf.readUnsignedInt();
}
/**
* Reads a byte array of a specified length from the stream into an existing
* array.
*
* @param b The array to read into.
* @param off The offset of the array to start copying data into.
* @param len The number of bytes to copy.
*
* @throws WireParseException The end of the stream was reached.
*/
public
void readByteArray(byte[] b, int off, int len) throws WireParseException {
require(len);
byteBuf.readBytes(b, off, len);
}
/**
* Reads a byte array of a specified length from the stream.
*
* @return The byte array.
*
* @throws WireParseException The end of the stream was reached.
*/
public
byte[] readByteArray(int len) throws WireParseException {
require(len);
byte[] out = new byte[len];
byteBuf.readBytes(out, 0, len);
return out;
}
/**
* Reads a byte array consisting of the remainder of the stream (or the
* active region, if one is set.
*
* @return The byte array.
*/
public
byte[] readByteArray() {
int len = remaining();
byte[] out = new byte[len];
byteBuf.readBytes(out, 0, len);
return out;
}
/**
* Reads a counted string from the stream. A counted string is a one byte
* value indicating string length, followed by bytes of data.
*
* @return A byte array containing the string.
*
* @throws WireParseException The end of the stream was reached.
*/
public
byte[] readCountedString() throws WireParseException {
int len = readU8();
return readByteArray(len);
}
}

View File

@ -0,0 +1,221 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* A class for rendering DNS messages.
*
* @author Brian Wellington
*/
public
class DnsOutput {
private ByteBuf byteBuf;
private boolean marked = false;
/**
* Create a new DnsOutput
*/
public
DnsOutput() {
this(32);
}
/**
* Create a new DnsOutput with a specified size.
*
* @param size The initial size
*/
public
DnsOutput(int size) {
this(Unpooled.buffer(size));
}
/**
* Create a new DnsOutput with a specified ByteBuf.
*
* @param byteBuf The ByteBuf to use
*/
public
DnsOutput(ByteBuf byteBuf) {
this.byteBuf = byteBuf;
}
/**
* Returns the current position.
*/
public
int current() {
return byteBuf.writerIndex();
}
/**
* Resets the current position of the output stream to the specified index.
*
* @param index The new current position.
*
* @throws IllegalArgumentException The index is not within the output.
*/
public
void jump(int index) {
if (index >= byteBuf.writerIndex()) {
// we haven't written data to this point yet, and the contract for jump() is that it can only jump to a PREVIOUSLY written spot
throw new IllegalArgumentException("Unable to jump to invalid position " + index + ". Max is " + byteBuf.writerIndex());
}
byteBuf.writerIndex(index);
}
/**
* Saves the current state of the output stream.
*
* @throws IllegalArgumentException The index is not within the output.
*/
public
void save() {
marked = true;
byteBuf.markWriterIndex();
}
/**
* Restores the input stream to its state before the call to {@link #save}.
*/
public
void restore() {
if (!marked) {
throw new IllegalStateException("Not marked first");
}
byteBuf.resetWriterIndex();
marked = false;
}
/**
* Writes an unsigned 8 bit value to the stream.
*
* @param val The value to be written
*/
public
void writeU8(int val) {
check(val, 8);
byteBuf.ensureWritable(1);
byteBuf.writeByte(val);
}
private
void check(long val, int bits) {
long max = 1;
max <<= bits;
if (val < 0 || val > max) {
throw new IllegalArgumentException(val + " out of range for " + bits + " bit value");
}
}
/**
* Writes an unsigned 16 bit value to the stream.
*
* @param val The value to be written
*/
public
void writeU16(int val) {
check(val, 16);
byteBuf.ensureWritable(2);
byteBuf.writeShort(val);
}
/**
* Writes an unsigned 16 bit value to the specified position in the stream.
*
* @param val The value to be written
* @param where The position to write the value.
*/
public
void writeU16At(int val, int where) {
check(val, 16);
// save and set both the read/write index, otherwise if the read index is > write index, errors happen.
int saved = byteBuf.writerIndex();
int readSaved = byteBuf.readerIndex();
byteBuf.setIndex(where, where);
byteBuf.ensureWritable(2);
byteBuf.writeShort(val);
byteBuf.writerIndex(saved);
byteBuf.readerIndex(readSaved);
}
/**
* Writes an unsigned 32 bit value to the stream.
*
* @param val The value to be written
*/
public
void writeU32(long val) {
check(val, 32);
byteBuf.ensureWritable(4);
byteBuf.writeInt((int) val);
}
/**
* Writes a byte array to the stream.
*
* @param b The array to write.
*/
public
void writeByteArray(byte[] b) {
writeByteArray(b, 0, b.length);
}
/**
* Writes a byte array to the stream.
*
* @param b The array to write.
* @param off The offset of the array to start copying data from.
* @param len The number of bytes to write.
*/
public
void writeByteArray(byte[] b, int off, int len) {
byteBuf.ensureWritable(len);
byteBuf.writeBytes(b, off, len);
}
/**
* Writes a counted string from the stream. A counted string is a one byte
* value indicating string length, followed by bytes of data.
*
* @param s The string to write.
*/
public
void writeCountedString(byte[] s) {
if (s.length > 0xFF) {
throw new IllegalArgumentException("Invalid counted string");
}
byteBuf.ensureWritable(1 + s.length);
byteBuf.writeByte(s.length);
byteBuf.writeBytes(s, 0, s.length);
}
/**
* Returns a byte array containing the current contents of the stream.
*/
public
byte[] toByteArray() {
byte[] out = new byte[byteBuf.writerIndex()];
byteBuf.readBytes(out, 0, out.length);
return out;
}
public
ByteBuf getByteBuf() {
return byteBuf;
}
}

View File

@ -0,0 +1,222 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns;
import java.util.HashMap;
/**
* A utility class for converting between numeric codes and mnemonics
* for those codes. Mnemonics are case insensitive.
*
* @author Brian Wellington
*/
public
class Mnemonic {
private static Integer cachedInts[] = new Integer[64];
/* Strings are case-sensitive. */
public static final int CASE_SENSITIVE = 1;
/* Strings will be stored/searched for in uppercase. */
public static final int CASE_UPPER = 2;
/* Strings will be stored/searched for in lowercase. */
public static final int CASE_LOWER = 3;
private HashMap<String, Integer> strings;
private HashMap<Integer, String> values;
private String description;
private int wordcase;
private String prefix;
private int max;
private boolean numericok;
static {
for (int i = 0; i < cachedInts.length; i++) {
cachedInts[i] = new Integer(i);
}
}
/**
* Creates a new Mnemonic table.
*
* @param description A short description of the mnemonic to use when
* @param wordcase Whether to convert strings into uppercase, lowercase,
* or leave them unchanged.
* throwing exceptions.
*/
public
Mnemonic(String description, int wordcase) {
this.description = description;
this.wordcase = wordcase;
strings = new HashMap();
values = new HashMap();
max = Integer.MAX_VALUE;
}
/**
* Sets the maximum numeric value
*/
public
void setMaximum(int max) {
this.max = max;
}
/**
* Sets the prefix to use when converting to and from values that don't
* have mnemonics.
*/
public
void setPrefix(String prefix) {
this.prefix = sanitize(prefix);
}
/* Converts a String to the correct case. */
private
String sanitize(String str) {
if (wordcase == CASE_UPPER) {
return str.toUpperCase();
}
else if (wordcase == CASE_LOWER) {
return str.toLowerCase();
}
return str;
}
/**
* Sets whether numeric values stored in strings are acceptable.
*/
public
void setNumericAllowed(boolean numeric) {
this.numericok = numeric;
}
/**
* Defines the text representation of a numeric value.
*
* @param val The numeric value
* @param string The text string
*/
public
void add(int val, String string) {
check(val);
Integer value = toInteger(val);
string = sanitize(string);
strings.put(string, value);
values.put(value, string);
}
/**
* Converts an int into a possibly cached Integer.
*/
public static
Integer toInteger(int val) {
if (val >= 0 && val < cachedInts.length) {
return (cachedInts[val]);
}
return new Integer(val);
}
/**
* Checks that a numeric value is within the range [0..max]
*/
public
void check(int val) {
if (val < 0 || val > max) {
throw new IllegalArgumentException(description + " " + val + "is out of range");
}
}
/**
* Defines an additional text representation of a numeric value. This will
* be used by getValue(), but not getText().
*
* @param val The numeric value
* @param string The text string
*/
public
void addAlias(int val, String string) {
check(val);
Integer value = toInteger(val);
string = sanitize(string);
strings.put(string, value);
}
/**
* Copies all mnemonics from one table into another.
*
* @param source The Mnemonic source to add from
*
* @throws IllegalArgumentException The wordcases of the Mnemonics do not
* match.
*/
public
void addAll(Mnemonic source) {
if (wordcase != source.wordcase) {
throw new IllegalArgumentException(source.description + ": wordcases do not match");
}
strings.putAll(source.strings);
values.putAll(source.values);
}
/**
* Gets the text mnemonic corresponding to a numeric value.
*
* @param val The numeric value
*
* @return The corresponding text mnemonic.
*/
public
String getText(int val) {
check(val);
String str = (String) values.get(toInteger(val));
if (str != null) {
return str;
}
str = Integer.toString(val);
if (prefix != null) {
return prefix + str;
}
return str;
}
/**
* Gets the numeric value corresponding to a text mnemonic.
*
* @param str The text mnemonic
*
* @return The corresponding numeric value, or -1 if there is none
*/
public
int getValue(String str) {
str = sanitize(str);
Integer value = (Integer) strings.get(str);
if (value != null) {
return value.intValue();
}
if (prefix != null) {
if (str.startsWith(prefix)) {
int val = parseNumeric(str.substring(prefix.length()));
if (val >= 0) {
return val;
}
}
}
if (numericok) {
return parseNumeric(str);
}
return -1;
}
private
int parseNumeric(String s) {
try {
int val = Integer.parseInt(s);
if (val >= 0 && val <= max) {
return val;
}
} catch (NumberFormatException e) {
}
return -1;
}
}

View File

@ -0,0 +1,989 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns;
import java.io.IOException;
import java.io.Serializable;
import java.text.DecimalFormat;
import dorkbox.network.dns.exceptions.NameTooLongException;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.records.DNAMERecord;
import dorkbox.network.dns.utils.Options;
/**
* A representation of a domain name. It may either be absolute (fully
* qualified) or relative.
*
* @author Brian Wellington
*/
public
class Name implements Comparable, Serializable {
private static final long serialVersionUID = -7257019940971525644L;
private static final int LABEL_NORMAL = 0;
private static final int LABEL_COMPRESSION = 0xC0;
private static final int LABEL_MASK = 0xC0;
/* The name data */
private byte[] name;
/*
* Effectively an 8 byte array, where the low order byte stores the number
* of labels and the 7 higher order bytes store per-label offsets.
*/
private long offsets;
/* Precomputed hashcode. */
private int hashcode;
private static final byte[] emptyLabel = new byte[] {(byte) 0};
private static final byte[] wildLabel = new byte[] {(byte) 1, (byte) '*'};
/**
* The root name
*/
public static final Name root;
/**
* The root name
*/
public static final Name empty;
/**
* The maximum length of a Name
*/
private static final int MAXNAME = 255;
/**
* The maximum length of a label a Name
*/
private static final int MAXLABEL = 63;
/**
* The maximum number of labels in a Name
*/
private static final int MAXLABELS = 128;
/**
* The maximum number of cached offsets
*/
private static final int MAXOFFSETS = 7;
/* Used for printing non-printable characters */
private static final DecimalFormat byteFormat = new DecimalFormat();
/* Used to efficiently convert bytes to lowercase */
private static final byte lowercase[] = new byte[256];
/* Used in wildcard names. */
private static final Name wild;
static {
byteFormat.setMinimumIntegerDigits(3);
for (int i = 0; i < lowercase.length; i++) {
if (i < 'A' || i > 'Z') {
lowercase[i] = (byte) i;
}
else {
lowercase[i] = (byte) (i - 'A' + 'a');
}
}
root = new Name();
root.appendSafe(emptyLabel, 0, 1);
empty = new Name();
empty.name = new byte[0];
wild = new Name();
wild.appendSafe(wildLabel, 0, 1);
}
private
Name() {
}
private
void setoffset(int n, int offset) {
if (n >= MAXOFFSETS) {
return;
}
int shift = 8 * (7 - n);
offsets &= (~(0xFFL << shift));
offsets |= ((long) offset << shift);
}
private
int offset(int n) {
if (n == 0 && getlabels() == 0) {
return 0;
}
if (n < 0 || n >= getlabels()) {
throw new IllegalArgumentException("label out of range");
}
if (n < MAXOFFSETS) {
int shift = 8 * (7 - n);
return ((int) (offsets >>> shift) & 0xFF);
}
else {
int pos = offset(MAXOFFSETS - 1);
for (int i = MAXOFFSETS - 1; i < n; i++) {
pos += (name[pos] + 1);
}
return (pos);
}
}
private
void setlabels(int labels) {
offsets &= ~(0xFF);
offsets |= labels;
}
private static
void copy(Name src, Name dst) {
if (src.offset(0) == 0) {
dst.name = src.name;
dst.offsets = src.offsets;
}
else {
int offset0 = src.offset(0);
int namelen = src.name.length - offset0;
int labels = src.labels();
dst.name = new byte[namelen];
System.arraycopy(src.name, offset0, dst.name, 0, namelen);
for (int i = 0; i < labels && i < MAXOFFSETS; i++) {
dst.setoffset(i, src.offset(i) - offset0);
}
dst.setlabels(labels);
}
}
private
void append(byte[] array, int start, int n) throws NameTooLongException {
int length = (name == null ? 0 : (name.length - offset(0)));
int alength = 0;
for (int i = 0, pos = start; i < n; i++) {
int len = array[pos];
if (len > MAXLABEL) {
throw new IllegalStateException("invalid label");
}
len++;
pos += len;
alength += len;
}
int newlength = length + alength;
if (newlength > MAXNAME) {
throw new NameTooLongException();
}
int labels = getlabels();
int newlabels = labels + n;
if (newlabels > MAXLABELS) {
throw new IllegalStateException("too many labels");
}
byte[] newname = new byte[newlength];
if (length != 0) {
System.arraycopy(name, offset(0), newname, 0, length);
}
System.arraycopy(array, start, newname, length, alength);
name = newname;
for (int i = 0, pos = length; i < n; i++) {
setoffset(labels + i, pos);
pos += (newname[pos] + 1);
}
setlabels(newlabels);
}
private static
TextParseException parseException(String str, String message) {
return new TextParseException("'" + str + "': " + message);
}
private
void appendFromString(String fullName, byte[] array, int start, int n) throws TextParseException {
try {
append(array, start, n);
} catch (NameTooLongException e) {
throw parseException(fullName, "Name too long");
}
}
private
void appendSafe(byte[] array, int start, int n) {
try {
append(array, start, n);
} catch (NameTooLongException e) {
}
}
/**
* Create a new name from a string and an origin. This does not automatically
* make the name absolute; it will be absolute if it has a trailing dot or an
* absolute origin is appended.
*
* @param s The string to be converted
* @param origin If the name is not absolute, the origin to be appended.
*
* @throws TextParseException The name is invalid.
*/
public
Name(String s, Name origin) throws TextParseException {
if (s.equals("")) {
throw parseException(s, "empty name");
}
else if (s.equals("@")) {
if (origin == null) {
copy(empty, this);
}
else {
copy(origin, this);
}
return;
}
else if (s.equals(".")) {
copy(root, this);
return;
}
int labelstart = -1;
int pos = 1;
byte[] label = new byte[MAXLABEL + 1];
boolean escaped = false;
int digits = 0;
int intval = 0;
boolean absolute = false;
for (int i = 0; i < s.length(); i++) {
byte b = (byte) s.charAt(i);
if (escaped) {
if (b >= '0' && b <= '9' && digits < 3) {
digits++;
intval *= 10;
intval += (b - '0');
if (intval > 255) {
throw parseException(s, "bad escape");
}
if (digits < 3) {
continue;
}
b = (byte) intval;
}
else if (digits > 0 && digits < 3) {
throw parseException(s, "bad escape");
}
if (pos > MAXLABEL) {
throw parseException(s, "label too long");
}
labelstart = pos;
label[pos++] = b;
escaped = false;
}
else if (b == '\\') {
escaped = true;
digits = 0;
intval = 0;
}
else if (b == '.') {
if (labelstart == -1) {
throw parseException(s, "invalid empty label");
}
label[0] = (byte) (pos - 1);
appendFromString(s, label, 0, 1);
labelstart = -1;
pos = 1;
}
else {
if (labelstart == -1) {
labelstart = i;
}
if (pos > MAXLABEL) {
throw parseException(s, "label too long");
}
label[pos++] = b;
}
}
if (digits > 0 && digits < 3) {
throw parseException(s, "bad escape");
}
if (escaped) {
throw parseException(s, "bad escape");
}
if (labelstart == -1) {
appendFromString(s, emptyLabel, 0, 1);
absolute = true;
}
else {
label[0] = (byte) (pos - 1);
appendFromString(s, label, 0, 1);
}
if (origin != null && !absolute) {
appendFromString(s, origin.name, origin.offset(0), origin.getlabels());
}
}
/**
* Create a new name from a string. This does not automatically make the name
* absolute; it will be absolute if it has a trailing dot.
*
* @param s The string to be converted
*
* @throws TextParseException The name is invalid.
*/
public
Name(String s) throws TextParseException {
this(s, null);
}
/**
* Create a new name from a string. This does not automatically make the name
* absolute; it will be absolute if it has a trailing dot. This is identical
* to the constructor, except that it will avoid creating new objects in some
* cases.
*
* @param s The string to be converted
*
* @throws TextParseException The name is invalid.
*/
public static
Name fromString(String s) throws TextParseException {
return fromString(s, null);
}
/**
* Create a new name from a string and an origin. This does not automatically
* make the name absolute; it will be absolute if it has a trailing dot or an
* absolute origin is appended. This is identical to the constructor, except
* that it will avoid creating new objects in some cases.
*
* @param s The string to be converted
* @param origin If the name is not absolute, the origin to be appended.
*
* @throws TextParseException The name is invalid.
*/
public static
Name fromString(String s, Name origin) throws TextParseException {
if (s.equals("@") && origin != null) {
return origin;
}
else if (s.equals(".")) {
return (root);
}
return new Name(s, origin);
}
/**
* Create a new name from a constant string. This should only be used when
* the name is known to be good - that is, when it is constant.
*
* @param s The string to be converted
*
* @throws IllegalArgumentException The name is invalid.
*/
public static
Name fromConstantString(String s) {
try {
return fromString(s, null);
} catch (TextParseException e) {
throw new IllegalArgumentException("Invalid name '" + s + "'");
}
}
/**
* Create a new name from DNS a wire format message
*
* @param in A stream containing the DNS message which is currently
* positioned at the start of the name to be read.
*/
public
Name(DnsInput in) throws WireParseException {
int len, pos;
boolean done = false;
byte[] label = new byte[MAXLABEL + 1];
boolean savedState = false;
while (!done) {
len = in.readU8();
switch (len & LABEL_MASK) {
case LABEL_NORMAL:
if (getlabels() >= MAXLABELS) {
throw new WireParseException("too many labels");
}
if (len == 0) {
append(emptyLabel, 0, 1);
done = true;
}
else {
label[0] = (byte) len;
in.readByteArray(label, 1, len);
append(label, 0, 1);
}
break;
case LABEL_COMPRESSION:
pos = in.readU8();
pos += ((len & ~LABEL_MASK) << 8);
if (Options.check("verbosecompression")) {
System.err.println("currently " + in.readIndex() + ", pointer to " + pos);
}
if (pos >= in.readIndex() - 2) {
throw new WireParseException("bad compression");
}
if (!savedState) {
in.save();
savedState = true;
}
in.jump(pos);
if (Options.check("verbosecompression")) {
System.err.println("current name '" + this + "', seeking to " + pos);
}
break;
default:
throw new WireParseException("bad label type");
}
}
if (savedState) {
in.restore();
}
}
/**
* Create a new name from DNS wire format
*
* @param b A byte array containing the wire format of the name.
*/
public
Name(byte[] b) throws IOException {
this(new DnsInput(b));
}
/**
* Create a new name by removing labels from the beginning of an existing Name
*
* @param src An existing Name
* @param n The number of labels to remove from the beginning in the copy
*/
public
Name(Name src, int n) {
int slabels = src.labels();
if (n > slabels) {
throw new IllegalArgumentException("attempted to remove too " + "many labels");
}
name = src.name;
setlabels(slabels - n);
for (int i = 0; i < MAXOFFSETS && i < slabels - n; i++) {
setoffset(i, src.offset(i + n));
}
}
/**
* Creates a new name by concatenating two existing names.
*
* @param prefix The prefix name.
* @param suffix The suffix name.
*
* @return The concatenated name.
*
* @throws NameTooLongException The name is too long.
*/
public static
Name concatenate(Name prefix, Name suffix) throws NameTooLongException {
if (prefix.isAbsolute()) {
return (prefix);
}
Name newname = new Name();
copy(prefix, newname);
newname.append(suffix.name, suffix.offset(0), suffix.getlabels());
return newname;
}
/**
* If this name is a subdomain of origin, return a new name relative to
* origin with the same value. Otherwise, return the existing name.
*
* @param origin The origin to remove.
*
* @return The possibly relativized name.
*/
public
Name relativize(Name origin) {
if (origin == null || !subdomain(origin)) {
return this;
}
Name newname = new Name();
copy(this, newname);
int length = length() - origin.length();
int labels = newname.labels() - origin.labels();
newname.setlabels(labels);
newname.name = new byte[length];
System.arraycopy(name, offset(0), newname.name, 0, length);
return newname;
}
/**
* Generates a new Name with the first n labels replaced by a wildcard
*
* @return The wildcard name
*/
public
Name wild(int n) {
if (n < 1) {
throw new IllegalArgumentException("must replace 1 or more " + "labels");
}
try {
Name newname = new Name();
copy(wild, newname);
newname.append(name, offset(n), getlabels() - n);
return newname;
} catch (NameTooLongException e) {
throw new IllegalStateException("Name.wild: concatenate failed");
}
}
/**
* Returns a canonicalized version of the Name (all lowercase). This may be
* the same name, if the input Name is already canonical.
*/
public
Name canonicalize() {
boolean canonical = true;
for (int i = 0; i < name.length; i++) {
if (lowercase[name[i] & 0xFF] != name[i]) {
canonical = false;
break;
}
}
if (canonical) {
return this;
}
Name newname = new Name();
newname.appendSafe(name, offset(0), getlabels());
for (int i = 0; i < newname.name.length; i++) {
newname.name[i] = lowercase[newname.name[i] & 0xFF];
}
return newname;
}
/**
* Generates a new Name to be used when following a DNAME.
*
* @param dname The DNAME record to follow.
*
* @return The constructed name.
*
* @throws NameTooLongException The resulting name is too long.
*/
public
Name fromDNAME(DNAMERecord dname) throws NameTooLongException {
Name dnameowner = dname.getName();
Name dnametarget = dname.getTarget();
if (!subdomain(dnameowner)) {
return null;
}
int plabels = labels() - dnameowner.labels();
int plength = length() - dnameowner.length();
int pstart = offset(0);
int dlabels = dnametarget.labels();
int dlength = dnametarget.length();
if (plength + dlength > MAXNAME) {
throw new NameTooLongException();
}
Name newname = new Name();
newname.setlabels(plabels + dlabels);
newname.name = new byte[plength + dlength];
System.arraycopy(name, pstart, newname.name, 0, plength);
System.arraycopy(dnametarget.name, 0, newname.name, plength, dlength);
for (int i = 0, pos = 0; i < MAXOFFSETS && i < plabels + dlabels; i++) {
newname.setoffset(i, pos);
pos += (newname.name[pos] + 1);
}
return newname;
}
/**
* Is this name a wildcard?
*/
public
boolean isWild() {
if (labels() == 0) {
return false;
}
return (name[0] == (byte) 1 && name[1] == (byte) '*');
}
/**
* The number of labels in the name.
*/
public
int labels() {
return getlabels();
}
private
int getlabels() {
return (int) (offsets & 0xFF);
}
/**
* Is this name absolute?
*/
public
boolean isAbsolute() {
int nlabels = labels();
if (nlabels == 0) {
return false;
}
return name[offset(nlabels - 1)] == 0;
}
/**
* The length of the name.
*/
public
short length() {
if (getlabels() == 0) {
return 0;
}
return (short) (name.length - offset(0));
}
/**
* Is the current Name a subdomain of the specified name?
*/
public
boolean subdomain(Name domain) {
int labels = labels();
int dlabels = domain.labels();
if (dlabels > labels) {
return false;
}
if (dlabels == labels) {
return equals(domain);
}
return domain.equals(name, offset(labels - dlabels));
}
private
String byteString(byte[] array, int pos) {
StringBuilder sb = new StringBuilder();
int len = array[pos++];
for (int i = pos; i < pos + len; i++) {
int b = array[i] & 0xFF;
if (b <= 0x20 || b >= 0x7f) {
sb.append('\\');
sb.append(byteFormat.format(b));
}
else if (b == '"' || b == '(' || b == ')' || b == '.' || b == ';' || b == '\\' || b == '@' || b == '$') {
sb.append('\\');
sb.append((char) b);
}
else {
sb.append((char) b);
}
}
return sb.toString();
}
/**
* Convert a Name to a String
*
* @param omitFinalDot If true, and the name is absolute, omit the final dot.
*
* @return The representation of this name as a (printable) String.
*/
public
String toString(boolean omitFinalDot) {
int labels = labels();
if (labels == 0) {
return "@";
}
else if (labels == 1 && name[offset(0)] == 0) {
return ".";
}
StringBuilder sb = new StringBuilder();
for (int i = 0, pos = offset(0); i < labels; i++) {
int len = name[pos];
if (len > MAXLABEL) {
throw new IllegalStateException("invalid label");
}
if (len == 0) {
if (!omitFinalDot) {
sb.append('.');
}
break;
}
if (i > 0) {
sb.append('.');
}
sb.append(byteString(name, pos));
pos += (1 + len);
}
return sb.toString();
}
/**
* Retrieve the nth label of a Name. This makes a copy of the label; changing
* this does not change the Name.
*
* @param n The label to be retrieved. The first label is 0.
*/
public
byte[] getLabel(int n) {
int pos = offset(n);
byte len = (byte) (name[pos] + 1);
byte[] label = new byte[len];
System.arraycopy(name, pos, label, 0, len);
return label;
}
/**
* Convert the nth label in a Name to a String
*
* @param n The label to be converted to a (printable) String. The first
* label is 0.
*/
public
String getLabelString(int n) {
int pos = offset(n);
return byteString(name, pos);
}
/**
* Emit a Name in DNS wire format
*
* @param out The output stream containing the DNS message.
* @param c The compression context, or null of no compression is desired.
*
* @throws IllegalArgumentException The name is not absolute.
*/
public
void toWire(DnsOutput out, Compression c) {
if (!isAbsolute()) {
throw new IllegalArgumentException("toWire() called on " + "non-absolute name");
}
int labels = labels();
for (int i = 0; i < labels - 1; i++) {
Name tname;
if (i == 0) {
tname = this;
}
else {
tname = new Name(this, i);
}
int pos = -1;
if (c != null) {
pos = c.get(tname);
}
if (pos >= 0) {
pos |= (LABEL_MASK << 8);
out.writeU16(pos);
return;
}
else {
if (c != null) {
c.add(out.current(), tname);
}
int off = offset(i);
out.writeByteArray(name, off, name[off] + 1);
}
}
out.writeU8(0);
}
/**
* Emit a Name in DNS wire format
*
* @throws IllegalArgumentException The name is not absolute.
*/
public
byte[] toWire() {
DnsOutput out = new DnsOutput();
toWire(out, null);
return out.toByteArray();
}
/**
* Emit a Name in canonical DNS wire format (all lowercase)
*
* @param out The output stream to which the message is written.
*/
public
void toWireCanonical(DnsOutput out) {
byte[] b = toWireCanonical();
out.writeByteArray(b);
}
/**
* Emit a Name in canonical DNS wire format (all lowercase)
*
* @return The canonical form of the name.
*/
public
byte[] toWireCanonical() {
int labels = labels();
if (labels == 0) {
return (new byte[0]);
}
byte[] b = new byte[name.length - offset(0)];
for (int i = 0, spos = offset(0), dpos = 0; i < labels; i++) {
int len = name[spos];
if (len > MAXLABEL) {
throw new IllegalStateException("invalid label");
}
b[dpos++] = name[spos++];
for (int j = 0; j < len; j++) {
b[dpos++] = lowercase[(name[spos++] & 0xFF)];
}
}
return b;
}
/**
* Emit a Name in DNS wire format
*
* @param out The output stream containing the DNS message.
* @param c The compression context, or null of no compression is desired.
* @param canonical If true, emit the name in canonicalized form
* (all lowercase).
*
* @throws IllegalArgumentException The name is not absolute.
*/
public
void toWire(DnsOutput out, Compression c, boolean canonical) {
if (canonical) {
toWireCanonical(out);
}
else {
toWire(out, c);
}
}
private
boolean equals(byte[] b, int bpos) {
int labels = labels();
for (int i = 0, pos = offset(0); i < labels; i++) {
if (name[pos] != b[bpos]) {
return false;
}
int len = name[pos++];
bpos++;
if (len > MAXLABEL) {
throw new IllegalStateException("invalid label");
}
for (int j = 0; j < len; j++) {
if (lowercase[(name[pos++] & 0xFF)] != lowercase[(b[bpos++] & 0xFF)]) {
return false;
}
}
}
return true;
}
/**
* Computes a hashcode based on the value
*/
public
int hashCode() {
if (hashcode != 0) {
return (hashcode);
}
int code = 0;
for (int i = offset(0); i < name.length; i++) {
code += ((code << 3) + lowercase[(name[i] & 0xFF)]);
}
hashcode = code;
return hashcode;
}
/**
* Are these two Names equivalent?
*/
public
boolean equals(Object arg) {
if (arg == this) {
return true;
}
if (arg == null || !(arg instanceof Name)) {
return false;
}
Name d = (Name) arg;
if (d.hashcode == 0) {
d.hashCode();
}
if (hashcode == 0) {
hashCode();
}
if (d.hashcode != hashcode) {
return false;
}
if (d.labels() != labels()) {
return false;
}
return equals(d.name, d.offset(0));
}
/**
* Convert a Name to a String
*
* @return The representation of this name as a (printable) String.
*/
public
String toString() {
return toString(false);
}
/**
* Compares this Name to another Object.
*
* @param o The Object to be compared.
*
* @return The value 0 if the argument is a name equivalent to this name;
* a value less than 0 if the argument is less than this name in the canonical
* ordering, and a value greater than 0 if the argument is greater than this
* name in the canonical ordering.
*
* @throws ClassCastException if the argument is not a Name.
*/
@Override
public
int compareTo(Object o) {
Name arg = (Name) o;
if (this == arg) {
return (0);
}
int labels = labels();
int alabels = arg.labels();
int compares = labels > alabels ? alabels : labels;
for (int i = 1; i <= compares; i++) {
int start = offset(labels - i);
int astart = arg.offset(alabels - i);
int length = name[start];
int alength = arg.name[astart];
for (int j = 0; j < length && j < alength; j++) {
int n = lowercase[(name[j + start + 1]) & 0xFF] - lowercase[(arg.name[j + astart + 1]) & 0xFF];
if (n != 0) {
return (n);
}
}
if (length != alength) {
return (length - alength);
}
}
return (labels - alabels);
}
}

View File

@ -0,0 +1,132 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.constants;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.exceptions.InvalidDClassException;
/**
* Constants and functions relating to DNS classes. This is called DnsClass to avoid confusion with Class.
*
* @author Brian Wellington
*/
public final
class DnsClass {
/**
* Internet DNS resource record class: {@code IN}
*/
public static final int IN = 1;
/**
* Computer Science Network network DNS resource record class: {@code CSNET}. It was never installed as a top-level domain
* in the Domain Name System, but parsed in the message routing logic of mail transport agents (MTA). It was introduced in 1985.
*/
public static final int CS = 2;
/**
* Computer Science Network network DNS resource record class: {@code CSNET}. It was never installed as a top-level domain
* in the Domain Name System, but parsed in the message routing logic of mail transport agents (MTA). It was introduced in 1985.
*/
public static final int CSNET = 2;
/**
* Chaos network DNS resource record class: {@code CH} (MIT)
*/
public static final int CH = 3;
/**
* Chaos network DNS resource record class: {@code CHAOS} (MIT, alternate name)
*/
public static final int CHAOS = 3;
/**
* Hesiod DNS resource record class: {@code HS} (MIT)
*/
public static final int HS = 4;
/**
* Hesiod DNS resource record class: {@code HESIOD} (MIT, alternate name)
*/
public static final int HESIOD = 4;
/**
* Special value used in dynamic update messages
*/
public static final int NONE = 254;
/**
* Matches any class
*/
public static final int ANY = 255;
private static Mnemonic classes = new DClassMnemonic();
private static
class DClassMnemonic extends Mnemonic {
DClassMnemonic() {
super("DnsClass", CASE_UPPER);
setPrefix("CLASS");
}
@Override
public
void check(int val) {
DnsClass.check(val);
}
}
static {
classes.add(IN, "IN");
classes.add(CS, "CS");
classes.addAlias(CSNET, "CSNET");
classes.add(CH, "CH");
classes.addAlias(CH, "CHAOS");
classes.add(HS, "HS");
classes.addAlias(HS, "HESIOD");
classes.add(NONE, "NONE");
classes.add(ANY, "ANY");
}
private
DnsClass() {}
/**
* Checks that a numeric DnsClass is valid.
*
* @throws InvalidDClassException The class is out of range.
*/
public static
void check(int i) {
if (i < 0 || i > 0xFFFF) {
throw new InvalidDClassException(i);
}
}
/**
* Converts a numeric DnsClass into a String
*
* @return The canonical string representation of the class
*
* @throws InvalidDClassException The class is out of range.
*/
public static
String string(int i) {
return classes.getText(i);
}
/**
* Converts a String representation of a DnsClass into its numeric value
*
* @return The class code, or -1 on error.
*/
public static
int value(String s) {
return classes.getValue(s);
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.constants;
import dorkbox.network.dns.Mnemonic;
/**
* Constants and functions relating to DNS opcodes
*
* @author Brian Wellington
*/
public final
class DnsOpCode {
/**
* A standard query
*/
public static final int QUERY = 0;
/**
* An inverse query (deprecated)
*/
public static final int IQUERY = 1;
/**
* A server status request (not used)
*/
public static final int STATUS = 2;
/**
* A message from a primary to a secondary server to initiate a zone transfer
*/
public static final int NOTIFY = 4;
/**
* A dynamic update message
*/
public static final int UPDATE = 5;
private static Mnemonic opcodes = new Mnemonic("DNS DnsOpCode", Mnemonic.CASE_UPPER);
static {
opcodes.setMaximum(0xF);
opcodes.setPrefix("RESERVED");
opcodes.setNumericAllowed(true);
opcodes.add(QUERY, "QUERY");
opcodes.add(IQUERY, "IQUERY");
opcodes.add(STATUS, "STATUS");
opcodes.add(NOTIFY, "NOTIFY");
opcodes.add(UPDATE, "UPDATE");
}
private
DnsOpCode() {}
/**
* Converts a numeric DnsOpCode into a String
*/
public static
String string(int i) {
return opcodes.getText(i);
}
/**
* Converts a String representation of an DnsOpCode into its numeric value
*/
public static
int value(String s) {
return opcodes.getValue(s);
}
}

View File

@ -0,0 +1,565 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.constants;
import java.util.HashMap;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.exceptions.InvalidTypeException;
import dorkbox.network.dns.records.DnsRecord;
import dorkbox.network.dns.records.DnsTypeProtoAssignment;
/**
* Constants and functions relating to DNS Types
*
* @author Brian Wellington
*/
public final
class DnsRecordType {
/**
* Address record RFC 1035 Returns a 32-bit IPv4 address, most commonly used
* to map hostnames to an IP address of the host, but also used for DNSBLs,
* storing subnet masks in RFC 1101, etc.
*/
public static final int A = 1;
/**
* Name server record RFC 1035 Delegates a DNS zone to use the given
* authoritative name servers
*/
public static final int NS = 2;
/**
* Mail destination. MD specifies the final destination to which a message addressed to a given domain name should be delivered
* (Obsolete, as it's behavior has been replaced by MX)
*/
@Deprecated
public static final int MD = 3;
/**
* Mail forwarder. MF specifies a host that would forward mail on to the eventual destination, should that destination be unreachable.
* (Obsolete, as it's behavior has been replaced by MX)
*/
@Deprecated
public static final int MF = 4;
/**
* Canonical name (alias) record RFC 1035 Alias of one name to another: the DNS
* lookup will continue by retrying the lookup with the new name.
*/
public static final int CNAME = 5;
/**
* Start of [a zone of] authority record RFC 1035 and RFC 2308 Specifies
* authoritative information about a DNS zone, including the primary name
* server, the email of the domain administrator, the domain serial number,
* and several timers relating to refreshing the zone.
*/
public static final int SOA = 6;
/**
* Mailbox domain name RFC 1035. EXPERIMENTAL. A <domain-name> which specifies a host which has the
* specified mailbox.
*/
public static final int MB = 7;
/**
* Mail group member RFC 1035. EXPERIMENTAL. A <domain-name> which specifies a mailbox which is a
* member of the mail group specified by the domain name. MG records cause no additional section processing.
*/
public static final int MG = 8;
/**
* Mail rename name RFC 1035. EXPERIMENTAL. A <domain-name> which specifies a mailbox which is the
* proper rename of the specified mailbox.
*/
public static final int MR = 9;
/**
* Null record RFC 1035. EXPERIMENTAL. Anything at all may be in the RDATA field so long as it is 65535 octets
* or less
*/
public static final int NULL = 10;
/**
* The WKS record RFC 1035 is used to describe the well known services supported by
* a particular protocol on a particular internet address.
*/
public static final int WKS = 11;
/**
* Pointer record RFC 1035 Pointer to a canonical name. Unlike a CNAME, DNS
* processing does NOT proceed, just the name is returned. The most common
* use is for implementing reverse DNS lookups, but other uses include such
* things as DNS-SD.
*/
public static final int PTR = 12;
/**
* Host information HINFO RFC 1035. records are used to acquire general information about a host. The
* main use is for protocols such as FTP that can use special procedures when talking between machines
* or operating systems of the same type.
*/
public static final int HINFO = 13;
/**
* Mailbox or mail list information RFC 1035. EXPERIMENTAL. MINFO records cause no additional section processing. Although these
* records can be associated with a simple mailbox, they are usually used with a mailing list.
*/
public static final int MINFO = 14;
/**
* Mail exchange (routing) record RFC 1035 Maps a domain name to a list of message transfer agents for that domain.
*/
public static final int MX = 15;
/**
* Text record RFC 1035 Originally for arbitrary human-readable text in a
* DNS record. Since the early 1990s, however, this record more often
* carries machine-readable data, such as specified by RFC 1464,
* opportunistic encryption, Sender Policy Framework, DKIM, DMARC DNS-SD,
* etc.
*/
public static final int TXT = 16;
/**
* Responsible person record RFC 1183 Information about the responsible
* person(s) for the domain. Usually an email address with the @ replaced by
* a .
*/
public static final int RP = 17;
/**
* AFS cell database record RFC 1183 Location of database servers of an AFS cell.
* This record is commonly used by AFS clients to contact AFS cells outside
* their local domain. A subtype of this record is used by the obsolete
* DCE/DFS file system.
*/
public static final int AFSDB = 18;
/**
* X.25 calling address
*/
public static final int X25 = 19;
/**
* ISDN calling address
*/
public static final int ISDN = 20;
/**
* Router
*/
public static final int RT = 21;
/**
* NSAP address
*/
public static final int NSAP = 22;
/**
* Reverse NSAP address (deprecated)
*/
@Deprecated
public static final int NSAP_PTR = 23;
/**
* Signature record RFC 2535 Signature record used in SIG(0) (RFC 2931) and
* TKEY (RFC 2930). RFC 3755 designated RRSIG as the replacement for SIG for
* use within DNSSEC.
*/
public static final int SIG = 24;
/**
* key record RFC 2535 and RFC 2930 Used only for SIG(0) (RFC 2931) and TKEY
* (RFC 2930). RFC 3445 eliminated their use for application keys and
* limited their use to DNSSEC. RFC 3755 designates DNSKEY as the
* replacement within DNSSEC. RFC 4025 designates IPSECKEY as the
* replacement for use with IPsec.
*/
public static final int KEY = 25;
/**
* X.400 mail mapping
*/
public static final int PX = 26;
/**
* Geographical position (withdrawn)
*/
@Deprecated
public static final int GPOS = 27;
/**
* IPv6 address record RFC 3596 Returns a 128-bit IPv6 address, most
* commonly used to map hostnames to an IP address of the host.
*/
public static final int AAAA = 28;
/**
* Location record RFC 1876 Specifies a geographical location associated
* with a domain name.
*/
public static final int LOC = 29;
/**
* Next valid name in zone
*/
public static final int NXT = 30;
/**
* Endpoint identifier
*/
public static final int EID = 31;
/**
* Nimrod locator
*/
public static final int NIMLOC = 32;
/**
* Service selection locator RFC 2782 Generalized service location record, used for
* newer protocols instead of creating protocol-specific records such as MX.
*/
public static final int SRV = 33;
/**
* ATM address
*/
public static final int ATMA = 34;
/**
* Naming Authority Pointer record RFC 3403 Allows regular expression based
* rewriting of domain names which can then be used as URIs, further domain
* names to lookups, etc.
*/
public static final int NAPTR = 35;
/**
* Key eXchanger record RFC 2230 Used with some cryptographic systems (not
* including DNSSEC) to identify a key management agent for the associated
* domain-name. Note that this has nothing to do with DNS Security. It is
* Informational status, rather than being on the IETF standards-track. It
* has always had limited deployment, but is still in use.
*/
public static final int KX = 36;
/**
* Certificate record RFC 4398 Stores PKIX, SPKI, PGP, etc.
*/
public static final int CERT = 37;
/**
* IPv6 address (experimental)
*/
public static final int A6 = 38;
/**
* Delegation name record RFC 2672 DNAME creates an alias for a name and all
* its subnames, unlike CNAME, which aliases only the exact name in its
* label. Like the CNAME record, the DNS lookup will continue by retrying
* the lookup with the new name. This is also known as Non-terminal name redirection
*/
public static final int DNAME = 39;
/**
* Options - contains EDNS metadata. Option record RFC 2671 This is a pseudo DNS
* record type needed to support EDNS.
*/
public static final int OPT = 41;
/**
* Address Prefix List record RFC 3123 Specify lists of address ranges, e.g.
* in CIDR format, for various address families. Experimental.
*/
public static final int APL = 42;
/**
* Delegation signer record RFC 4034 The record used to identify the DNSSEC
* signing key of a delegated zone.
*/
public static final int DS = 43;
/**
* SSH Public Key Fingerprint record RFC 4255 Resource record for publishing
* SSH public host key fingerprints in the DNS System, in order to aid in
* verifying the authenticity of the host. RFC 6594 defines ECC SSH keys and
* SHA-256 hashes. See the IANA SSHFP RR parameters registry for details.
*/
public static final int SSHFP = 44;
/**
* IPsec Key record RFC 4025 Key record that can be used with IPsec.
*/
public static final int IPSECKEY = 45;
/**
* Resource Record Signature. DNSSEC signature record RFC 4034 Signature for a DNSSEC-secured record
* set. Uses the same format as the SIG record.
*/
public static final int RRSIG = 46;
/**
* Next Secure Name. Next-Secure record RFC 4034 Part of DNSSEC, used to prove a name does not
* exist. Uses the same format as the (obsolete) NXT record.
*/
public static final int NSEC = 47;
/**
* DNSSEC Key record RFC 4034 The key record used in DNSSEC. Uses the same
* format as the KEY record.
*/
public static final int DNSKEY = 48;
/**
* Dynamic Host Configuration Protocol (DHCP) ID. DHCP identifier record RFC 4701
* Used in conjunction with the FQDN option to DHCP.
*/
public static final int DHCID = 49;
/**
* Next SECure, 3rd edition, RFC 5155. An extension to DNSSEC that allows proof
* of nonexistence for a name without permitting zonewalking.
*/
public static final int NSEC3 = 50;
/**
* NSEC3 parameters record RFC 5155 Parameter record for use with NSEC3.
*/
public static final int NSEC3PARAM = 51;
/**
* Transport Layer Security Authentication, draft-ietf-dane-protocol-23.
* TLSA certificate association record RFC 6698 A record for DNS-based
* Authentication of Named Entities (DANE). RFC 6698 defines The TLSA DNS
* resource record is used to associate a TLS server certificate or public
* key with the domain name where the record is found, thus forming a 'TLSA
* certificate association'.
*/
public static final int TLSA = 52;
/**
* S/MIME cert association, draft-ietf-dane-smime
*/
public static final int SMIMEA = 53;
/**
* Host Identity Protocol record RFC 5205 Method of separating the end-point
* identifier and locator roles of IP addresses.
*/
public static final int HIP = 55;
/**
* OpenPGP Key, RFC 7929
*/
public static final int OPENPGPKEY = 61;
/**
* Sender Policy Framework (experimental) record RFC 4408 Specified as part of the SPF
* protocol as an alternative to of storing SPF data in TXT records. Uses
* the same format as the earlier TXT record.
*/
public static final int SPF = 99;
/**
* Transaction key - used to compute a shared secret or exchange a key.
* Secret key record RFC 2930 A method of providing keying material to be
* used with TSIG that is encrypted under the public key in an accompanying
* KEY RR..
*/
public static final int TKEY = 249;
/**
* Transaction Signature record RFC 2845 Can be used to authenticate dynamic
* updates as coming from an approved client, or to authenticate responses
* as coming from an approved recursive name server similar to DNSSEC.
*/
public static final int TSIG = 250;
/**
* Incremental Zone Transfer record RFC 1996 Requests a zone transfer of the
* given zone but only differences from a previous serial number. This
* request may be ignored and a full (AXFR) sent in response if the
* authoritative server is unable to fulfill the request due to
* configuration or lack of required deltas.
*/
public static final int IXFR = 251;
/**
* Authoritative Zone Transfer record RFC 1035 Transfer entire zone file
* from the master name server to secondary name servers.
*/
public static final int AXFR = 252;
/**
* Transfer mailbox records
*/
public static final int MAILB = 253;
/**
* Transfer mail agent records
*/
public static final int MAILA = 254;
/**
* Matches any type
*
* All cached records RFC 1035 Returns all records of all types known to the
* name server. If the name server does not have any information on the
* name, the request will be forwarded on. The records returned may not be
* complete. For example, if there is both an A and an MX for a name, but
* the name server has only the A record cached, only the A record will be
* returned. Sometimes referred to as ANY, for example in Windows nslookup
* and Wireshark.
*/
public static final int ANY = 255;
/**
* URI
*
* @see <a href="http://tools.ietf.org/html/draft-faltstrom-uri-14">draft-faltstrom-uri-14</a>
*/
public static final int URI = 256;
/**
* Certification Authority Authorization, RFC 6844. CA pinning,
* constraining acceptable CAs for a host/domain.
*/
public static final int CAA = 257;
/**
* DNSSEC Trust Authorities record N/A Part of a deployment proposal for
* DNSSEC without a signed DNS root. See the IANA database and Weiler Spec
* for details. Uses the same format as the DS record.
*/
public static final int TA = 32768;
/**
* DNSSEC Lookaside Validation, RFC 4431. For publishing DNSSEC trust
* anchors outside of the DNS delegation chain. Uses the same format as the
* DS record. RFC 5074 describes a way of using these records.
*/
public static final int DLV = 32769;
private static TypeMnemonic types = new TypeMnemonic();
public static
class TypeMnemonic extends Mnemonic {
private HashMap objects;
public
TypeMnemonic() {
super("DnsRecordType", CASE_UPPER);
setPrefix("TYPE");
objects = new HashMap();
}
public
void add(int val, String str, DnsRecord proto) {
super.add(val, str);
objects.put(Mnemonic.toInteger(val), proto);
}
public
<T extends DnsRecord> T getProto(int val) {
check(val);
return (T) objects.get(toInteger(val));
}
@Override
public
void check(int val) {
DnsRecordType.check(val);
}
}
static {
// this is so we don't have to make each type constructor public
DnsTypeProtoAssignment.assign(types);
}
private
DnsRecordType() {
}
/**
* Checks that a numeric DnsRecordType is valid.
*
* @throws InvalidTypeException The type is out of range.
*/
public static
void check(int val) {
if (val < 0 || val > 0xFFFF) {
throw new InvalidTypeException(val);
}
}
/**
* Converts a numeric DnsRecordType into a String
*
* @param val The type value.
*
* @return The canonical string representation of the type
*
* @throws InvalidTypeException The type is out of range.
*/
public static
String string(int val) {
return types.getText(val);
}
/**
* Converts a String representation of an DnsRecordType into its numeric value
*
* @return The type code, or -1 on error.
*/
public static
int value(String s) {
return value(s, false);
}
/**
* Converts a String representation of an DnsRecordType into its numeric value.
*
* @param s The string representation of the type
* @param numberok Whether a number will be accepted or not.
*
* @return The type code, or -1 on error.
*/
public static
int value(String s, boolean numberok) {
int val = types.getValue(s);
if (val == -1 && numberok) {
val = types.getValue("TYPE" + s);
}
return val;
}
public static
<T extends DnsRecord> T getProto(int val) {
return types.getProto(val);
}
/**
* Is this type valid for a record (a non-meta type)?
*/
public static
boolean isRR(int type) {
switch (type) {
case OPT:
case TKEY:
case TSIG:
case IXFR:
case AXFR:
case MAILB:
case MAILA:
case ANY:
return false;
default:
return true;
}
}
}

View File

@ -0,0 +1,180 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.constants;
import dorkbox.network.dns.Mnemonic;
/**
* Constants and functions relating to DNS rcodes (error values)
*
* @author Brian Wellington
*/
public final
class DnsResponseCode {
private static Mnemonic rcodes = new Mnemonic("DNS DnsResponseCode", Mnemonic.CASE_UPPER);
private static Mnemonic tsigrcodes = new Mnemonic("TSIG rcode", Mnemonic.CASE_UPPER);
/**
* No error
*/
public static final int NOERROR = 0;
/**
* Format error
*/
public static final int FORMERR = 1;
/**
* Server failure
*/
public static final int SERVFAIL = 2;
/**
* The name does not exist
*/
public static final int NXDOMAIN = 3;
/**
* The operation requested is not implemented
*/
public static final int NOTIMP = 4;
/**
* Deprecated synonym for NOTIMP.
*/
public static final int NOTIMPL = 4;
/**
* The operation was refused by the server
*/
public static final int REFUSED = 5;
/**
* The name exists
*/
public static final int YXDOMAIN = 6;
/**
* The RRset (name, type) exists
*/
public static final int YXRRSET = 7;
/**
* The RRset (name, type) does not exist
*/
public static final int NXRRSET = 8;
/**
* The requestor is not authorized to perform this operation
*/
public static final int NOTAUTH = 9;
/**
* The zone specified is not a zone
*/
public static final int NOTZONE = 10;
/* EDNS extended rcodes */
/**
* Unsupported EDNS level
*/
public static final int BADVERS = 16;
/* TSIG/TKEY only rcodes */
/**
* The signature is invalid (TSIG/TKEY extended error)
*/
public static final int BADSIG = 16;
/**
* The key is invalid (TSIG/TKEY extended error)
*/
public static final int BADKEY = 17;
/**
* The time is out of range (TSIG/TKEY extended error)
*/
public static final int BADTIME = 18;
/**
* The mode is invalid (TKEY extended error)
*/
public static final int BADMODE = 19;
/**
* The 'BADNAME' DNS RCODE (20), as defined in <a href="https://tools.ietf.org/html/rfc2930">RFC2930</a>.
*/
public static final int BADNAME = 20;
/**
* The 'BADALG' DNS RCODE (21), as defined in <a href="https://tools.ietf.org/html/rfc2930">RFC2930</a>.
*/
public static final int BADALG = 21;
static {
rcodes.setMaximum(0xFFF);
rcodes.setPrefix("RESERVED");
rcodes.setNumericAllowed(true);
rcodes.add(NOERROR, "NOERROR");
rcodes.add(FORMERR, "FORMERR");
rcodes.add(SERVFAIL, "SERVFAIL");
rcodes.add(NXDOMAIN, "NXDOMAIN");
rcodes.add(NOTIMP, "NOTIMP");
rcodes.addAlias(NOTIMP, "NOTIMPL");
rcodes.add(REFUSED, "REFUSED");
rcodes.add(YXDOMAIN, "YXDOMAIN");
rcodes.add(YXRRSET, "YXRRSET");
rcodes.add(NXRRSET, "NXRRSET");
rcodes.add(NOTAUTH, "NOTAUTH");
rcodes.add(NOTZONE, "NOTZONE");
rcodes.add(BADVERS, "BADVERS");
tsigrcodes.setMaximum(0xFFFF);
tsigrcodes.setPrefix("RESERVED");
tsigrcodes.setNumericAllowed(true);
tsigrcodes.addAll(rcodes);
tsigrcodes.add(BADSIG, "BADSIG");
tsigrcodes.add(BADKEY, "BADKEY");
tsigrcodes.add(BADTIME, "BADTIME");
tsigrcodes.add(BADMODE, "BADMODE");
tsigrcodes.add(BADNAME, "BADNAME");
tsigrcodes.add(BADALG, "BADALG");
}
private
DnsResponseCode() {}
/**
* Converts a numeric DnsResponseCode into a String
*/
public static
String string(int i) {
return rcodes.getText(i);
}
/**
* Converts a numeric TSIG extended DnsResponseCode into a String
*/
public static
String TSIGstring(int i) {
return tsigrcodes.getText(i);
}
/**
* Converts a String representation of an DnsResponseCode into its numeric value
*/
public static
int value(String s) {
return rcodes.getValue(s);
}
}

View File

@ -0,0 +1,115 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.constants;
import dorkbox.network.dns.Mnemonic;
/**
* Constants and functions relating to DNS message sections
*
* @author Brian Wellington
*/
public final
class DnsSection {
public static final int TOTAL_SECTION_COUNT = 4;
/**
* The question (first) section
*/
public static final int QUESTION = 0;
/**
* The answer (second) section
*/
public static final int ANSWER = 1;
/**
* The authority (third) section
*/
public static final int AUTHORITY = 2;
/**
* The additional (fourth) section
*/
public static final int ADDITIONAL = 3;
/* Aliases for dynamic update */
/**
* The zone (first) section of a dynamic update message
*/
public static final int ZONE = 0;
/**
* The prerequisite (second) section of a dynamic update message
*/
public static final int PREREQ = 1;
/**
* The update (third) section of a dynamic update message
*/
public static final int UPDATE = 2;
private static Mnemonic sections = new Mnemonic("DnsMessage DnsSection", Mnemonic.CASE_LOWER);
private static String[] longSections = new String[4];
private static String[] updateSections = new String[4];
static {
sections.setMaximum(3);
sections.setNumericAllowed(true);
sections.add(QUESTION, "qd");
sections.add(ANSWER, "an");
sections.add(AUTHORITY, "au");
sections.add(ADDITIONAL, "ad");
longSections[QUESTION] = "QUESTIONS";
longSections[ANSWER] = "ANSWERS";
longSections[AUTHORITY] = "AUTHORITY RECORDS";
longSections[ADDITIONAL] = "ADDITIONAL RECORDS";
updateSections[ZONE] = "ZONE";
updateSections[PREREQ] = "PREREQUISITES";
updateSections[UPDATE] = "UPDATE RECORDS";
updateSections[ADDITIONAL] = "ADDITIONAL RECORDS";
}
private
DnsSection() {}
/**
* Converts a numeric DnsSection into an abbreviation String
*/
public static
String string(int i) {
return sections.getText(i);
}
/**
* Converts a numeric DnsSection into a full description String
*/
public static
String longString(int i) {
sections.check(i);
return longSections[i];
}
/**
* Converts a numeric DnsSection into a full description String for an update
* DnsMessage.
*/
public static
String updString(int i) {
sections.check(i);
return updateSections[i];
}
/**
* Converts a String representation of a DnsSection into its numeric value
*/
public static
int value(String s) {
return sections.getValue(s);
}
}

View File

@ -0,0 +1,105 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.constants;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.records.ExtendedFlags;
/**
* Constants and functions relating to flags in the DNS header.
*
* @author Brian Wellington
*/
public final
class Flags {
private static Mnemonic flags = new Mnemonic("DNS Header Flag", Mnemonic.CASE_LOWER);
/**
* query/response
*/
public static final byte QR = 0;
/**
* authoritative answer
*/
public static final byte AA = 5;
/**
* truncated
*/
public static final byte TC = 6;
/**
* recursion desired
*/
public static final byte RD = 7;
/**
* recursion available
*/
public static final byte RA = 8;
/**
* authenticated data
*/
public static final byte AD = 10;
/**
* (security) checking disabled
*/
public static final byte CD = 11;
/**
* dnssec ok (extended)
*/
public static final int DO = ExtendedFlags.DO;
static {
flags.setMaximum(0xF);
flags.setPrefix("FLAG");
flags.setNumericAllowed(true);
flags.add(QR, "qr");
flags.add(AA, "aa");
flags.add(TC, "tc");
flags.add(RD, "rd");
flags.add(RA, "ra");
flags.add(AD, "ad");
flags.add(CD, "cd");
}
private
Flags() {}
/**
* Converts a numeric Flag into a String
*/
public static
String string(int i) {
return flags.getText(i);
}
/**
* Converts a String representation of an Flag into its numeric value
*/
public static
int value(String s) {
return flags.getValue(s);
}
/**
* Indicates if a bit in the flags field is a flag or not. If it's part of
* the rcode or opcode, it's not.
*/
public static
boolean isFlag(int index) {
flags.check(index);
if ((index >= 1 && index <= 4) || (index >= 12)) {
return false;
}
return true;
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.decoder;
import dorkbox.network.dns.record.MailExchangerRecord;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.dns.DnsRecord;
import io.netty.resolver.dns.DnsNameResolverAccess;
public
class MailExchangerDecoder implements RecordDecoder<MailExchangerRecord> {
@Override
public
MailExchangerRecord decode(final DnsRecord record, final ByteBuf response) {
int priority = response.readUnsignedShort();
String name = DnsNameResolverAccess.decodeDomainName(response);
return new MailExchangerRecord(priority, name);
}
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.decoder;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.dns.DnsRecord;
public
interface RecordDecoder<T> {
T decode(final DnsRecord record, final ByteBuf response) throws DecoderException;
}

View File

@ -1,36 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.decoder;
import dorkbox.network.dns.record.ServiceRecord;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.dns.DnsRecord;
import io.netty.resolver.dns.DnsNameResolverAccess;
public
class ServiceDecoder implements RecordDecoder<ServiceRecord> {
@Override
public
ServiceRecord decode(final DnsRecord record, final ByteBuf response) {
int priority = response.readShort();
int weight = response.readShort();
int port = response.readUnsignedShort();
String target = DnsNameResolverAccess.decodeDomainName(response);
return new ServiceRecord(record.name(), priority, weight, port, target);
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.decoder;
import dorkbox.network.dns.record.StartOfAuthorityRecord;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.dns.DnsRecord;
import io.netty.resolver.dns.DnsNameResolverAccess;
public
class StartOfAuthorityDecoder implements RecordDecoder<StartOfAuthorityRecord> {
@Override
public
StartOfAuthorityRecord decode(final DnsRecord record, final ByteBuf response) {
String primaryName = DnsNameResolverAccess.decodeDomainName(response);
String personName = DnsNameResolverAccess.decodeDomainName(response);
long serial = response.readUnsignedInt();
int refresh = response.readInt();
int retry = response.readInt();
int expire = response.readInt();
long minimum = response.readUnsignedInt();
return new StartOfAuthorityRecord(primaryName, personName, serial, refresh, retry, expire, minimum);
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.decoder;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.dns.DnsRecord;
import io.netty.util.CharsetUtil;
import java.util.ArrayList;
import java.util.List;
public
class TextDecoder implements RecordDecoder<List<String>> {
@Override
public
List<String> decode(final DnsRecord record, final ByteBuf response) {
List<String> list = new ArrayList<String>();
int index = response.readerIndex();
while (index < response.writerIndex()) {
int len = response.getUnsignedByte(index++);
list.add(response.toString(index, len, CharsetUtil.UTF_8));
index += len;
}
return list;
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
/**
* An exception thrown when an invalid dclass code is specified.
*
* @author Brian Wellington
*/
public
class InvalidDClassException extends IllegalArgumentException {
public
InvalidDClassException(int dclass) {
super("Invalid DNS class: " + dclass);
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
/**
* An exception thrown when an invalid TTL is specified.
*
* @author Brian Wellington
*/
public
class InvalidTTLException extends IllegalArgumentException {
public
InvalidTTLException(long ttl) {
super("Invalid DNS TTL: " + ttl);
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
/**
* An exception thrown when an invalid type code is specified.
*
* @author Brian Wellington
*/
public
class InvalidTypeException extends IllegalArgumentException {
public
InvalidTypeException(int type) {
super("Invalid DNS type: " + type);
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
/**
* An exception thrown when a name is longer than the maximum length of a DNS
* name.
*
* @author Brian Wellington
*/
public
class NameTooLongException extends WireParseException {
public
NameTooLongException() {
super();
}
public
NameTooLongException(String s) {
super(s);
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
import dorkbox.network.dns.Name;
/**
* An exception thrown when a relative name is passed as an argument to
* a method requiring an absolute name.
*
* @author Brian Wellington
*/
public
class RelativeNameException extends IllegalArgumentException {
public
RelativeNameException(Name name) {
super("'" + name + "' is not an absolute name");
}
public
RelativeNameException(String s) {
super(s);
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
import java.io.IOException;
/**
* An exception thrown when unable to parse text.
*
* @author Brian Wellington
*/
public
class TextParseException extends IOException {
public
TextParseException() {
super();
}
public
TextParseException(String s) {
super(s);
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
import java.io.IOException;
/**
* An exception thrown when a DNS message is invalid.
*
* @author Brian Wellington
*/
public
class WireParseException extends IOException {
public
WireParseException() {
super();
}
public
WireParseException(String s) {
super(s);
}
public
WireParseException(String s, Throwable cause) {
super(s);
initCause(cause);
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.exceptions;
/**
* An exception thrown when a zone transfer fails.
*
* @author Brian Wellington
*/
public
class ZoneTransferException extends Exception {
public
ZoneTransferException() {
super();
}
public
ZoneTransferException(String s) {
super(s);
}
}

View File

@ -0,0 +1,17 @@
package dorkbox.network.dns.handlers;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.DatagramChannel;
public
class DnsHandler extends ChannelInitializer<DatagramChannel> {
@Override
protected
void initChannel(DatagramChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DnsMessageEncoder());
pipeline.addLast(new DnsMessageDecoder());
// pipeline.addLast(new DNSMessageDecoder());
}
}

View File

@ -0,0 +1,46 @@
package dorkbox.network.dns.handlers;
import java.util.List;
import org.handwerkszeug.dns.DNSMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
public
class DnsMessageDecoder extends MessageToMessageDecoder<DatagramPacket> {
@Override
public
void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
// Channel channel = context.channel();
System.err.println("POW! ");
cause.printStackTrace();
// this.logger.error("Unexpected exception while trying to send/receive data on Client remote (network) channel. ({})" +
// System.getProperty("line.separator"), channel.remoteAddress(), cause);
// if (channel.isOpen()) {
// channel.close();
// }
}
@Override
protected
void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
System.err.println("READING MESSAGE");
final ByteBuf buf = packet.content();
boolean success = false;
try {
DNSMessage dnsMessage = new DNSMessage(buf);
out.add(dnsMessage);
success = true;
} finally {
if (!success) {
buf.release();
}
}
}
}

View File

@ -0,0 +1,76 @@
package dorkbox.network.dns.handlers;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.records.DnsMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* An encoder which serializes a Java object into a {@link ByteBuf}.
* <p>
* Please note that the serialized form this encoder produces is not
* compatible with the standard {@link ObjectInputStream}. Please use
* {@link ObjectDecoder} or {@link ObjectDecoderInputStream} to ensure the
* interoperability with this encoder.
*/
@ChannelHandler.Sharable
public
class DnsMessageEncoder extends MessageToByteEncoder<DnsMessage> {
// public
// class ObjectEncoder extends MessageToByteEncoder<Serializable> {
// private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
// @Override
// protected
// void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
// int startIdx = out.writerIndex();
//
// ByteBufOutputStream bout = new ByteBufOutputStream(out);
// ObjectOutputStream oout = null;
// try {
// bout.write(LENGTH_PLACEHOLDER);
// oout = new CompactObjectOutputStream(bout);
// oout.writeObject(msg);
// oout.flush();
// } finally {
// if (oout != null) {
// oout.close();
// }
// else {
// bout.close();
// }
// }
//
// int endIdx = out.writerIndex();
//
// out.setInt(startIdx, endIdx - startIdx - 4);
// }
@Override
protected
void encode(final ChannelHandlerContext ctx, final DnsMessage msg, final ByteBuf out) throws Exception {
System.err.println("WRITING MESSAGE");
final DnsOutput outd = new DnsOutput(out);
msg.toWire(outd);
}
@Override
public
void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
// Channel channel = context.channel();
System.err.println("POW! ");
cause.printStackTrace();
// this.logger.error("Unexpected exception while trying to send/receive data on Client remote (network) channel. ({})" +
// System.getProperty("line.separator"), channel.remoteAddress(), cause);
// if (channel.isOpen()) {
// channel.close();
// }
}
}

View File

@ -0,0 +1,56 @@
package dorkbox.network.dns.handlers;
import org.handwerkszeug.dns.server.DNSMessageDecoder;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
/**
*
*/
public
class DnsServerHandler extends ChannelInboundHandlerAdapter {
protected DNSMessageDecoder decoder = new DNSMessageDecoder();
public
DnsServerHandler(final String threadName) {
}
@Override
public final
void channelRegistered(final ChannelHandlerContext context) throws Exception {
boolean success = false;
try {
initChannel(context.channel());
context.fireChannelRegistered();
success = true;
} catch (Throwable t) {
// this.logger.error("Failed to initialize a channel. Closing: {}", context.channel(), t);
} finally {
if (!success) {
context.close();
}
}
}
/**
* STEP 1: Channel is first created
*/
protected
void initChannel(final Channel channel) {
ChannelPipeline pipeline = channel.pipeline();
///////////////////////
// DECODE (or upstream)
///////////////////////
pipeline.addLast("decoder", this.decoder);
// ENCODE (or downstream)
/////////////////////////
pipeline.addLast("fowarder", new ForwardingHandler());
// pipeline.addLast("fowarder", new ForwardingHandler(this.config, this.clientChannelFactory));
}
}

View File

@ -0,0 +1,156 @@
package dorkbox.network.dns.handlers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOutboundHandlerAdapter;
public class ForwardingHandler extends ChannelOutboundHandlerAdapter {
static final Logger LOG = LoggerFactory.getLogger(ForwardingHandler.class);
// protected ServerConfiguration config;
// protected ChannelFactory clientChannelFactory;
// public
// ForwardingHandler(ServerConfiguration config,
// ChannelFactory clientChannelFactory) {
// this.config = config;
// this.clientChannelFactory = clientChannelFactory;
// }
@Override
public
void read(final ChannelHandlerContext ctx) throws Exception {
super.read(ctx);
}
// @Override
// public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e)
// throws Exception {
// final DNSMessage original = DNSMessage.class.cast(e.getMessage());
//
// ClientBootstrap cb = new ClientBootstrap(this.clientChannelFactory);
// cb.setOption("broadcast", "false");
// cb.setPipelineFactory(new ChannelPipelineFactory() {
// @Override
// public ChannelPipeline getPipeline() throws Exception {
// return Channels.pipeline(new ClientHanler(original, e
// .getChannel(), e.getRemoteAddress()));
// }
// });
//
// List<SocketAddress> newlist = new ArrayList<SocketAddress>(
// this.config.getForwarders());
// sendRequest(e, original, cb, newlist);
// }
//
// protected void sendRequest(final MessageEvent e, final DNSMessage original, final ClientBootstrap bootstrap, final List<SocketAddress> forwarders) {
// if (0 < forwarders.size()) {
// SocketAddress sa = forwarders.remove(0);
// LOG.debug("send to {}", sa);
//
// ChannelFuture f = bootstrap.connect(sa);
// ChannelBuffer newone = ChannelBuffers.buffer(512);
// DNSMessage msg = new DNSMessage(original);
// msg.write(newone);
// newone.resetReaderIndex();
// final Channel c = f.getChannel();
//
// if (LOG.isDebugEnabled()) {
// LOG.debug(
// "STATUS : [isOpen/isConnected/isWritable {}] {} {}",
// new Object[] {
// new boolean[] { c.isOpen(), c.isConnected(),
// c.isWritable() }, c.getRemoteAddress(),
// c.getClass() });
// }
//
// c.write(newone, sa).addListener(new ChannelFutureListener() {
// @Override
// public void operationComplete(ChannelFuture future)
// throws Exception {
// LOG.debug("request complete isSuccess : {}",
// future.isSuccess());
// if (future.isSuccess() == false) {
// if (0 < forwarders.size()) {
// sendRequest(e, original, bootstrap, forwarders);
// } else {
// original.header().rcode(RCode.ServFail);
// ChannelBuffer buffer = ChannelBuffers.buffer(512);
// original.write(buffer);
// // close inbound channel
// e.getChannel().write(buffer)
// .addListener(ChannelFutureListener.CLOSE);
// }
// }
// }
// });
//
// // f.awaitUninterruptibly(30, TimeUnit.SECONDS);
// }
// }
@Override
public
void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
LOG.error("ForwardingHandler#exceptionCaught");
LOG.error(cause.getMessage(), cause);
}
protected class ClientHanler extends ChannelInboundHandlerAdapter {
// protected DNSMessage original;
//
// protected Channel originalChannel;
//
// protected SocketAddress originalAddress;
//
// public ClientHanler(DNSMessage msg, Channel c, SocketAddress sa) {
// this.original = msg;
// this.originalChannel = c;
// this.originalAddress = sa;
// }
//
// @Override
// public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
// LOG.debug("ClientHanler#messageReceived");
// ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
// DNSMessage msg = new DNSMessage(buffer);
// msg.header().id(this.original.header().id());
// ChannelBuffer newone = ChannelBuffers.buffer(buffer.capacity());
// msg.write(newone);
// newone.resetReaderIndex();
// this.originalChannel.write(newone, this.originalAddress)
// .addListener(new ChannelFutureListener() {
// @Override
// public void operationComplete(ChannelFuture future)
// throws Exception {
// e.getChannel().close();
// }
// });
// }
//
@Override
public
void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
LOG.error("ClientHanler#exceptionCaught");
LOG.error(cause.getMessage(), cause);
// e.getFuture()
// .setFailure(t);
ctx.channel().close();
}
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.record;
/**
* Represents an MX record.
*/
public
class MailExchangerRecord {
private final int priority;
private final String name;
/**
* @param priority lower is more preferred
* @param name e-mail address in the format admin.example.com, which
* represents admin@example.com
*/
public
MailExchangerRecord(int priority, String name) {
this.priority = priority;
this.name = name;
}
public
int priority() {
return this.priority;
}
/**
* Returns the MX (an e-mail address) in the format
* admin.example.com, which represents admin@example.com.
*/
public
String name() {
return this.name;
}
}

View File

@ -1,100 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.record;
/**
* Represents an SRV record.
* <p/>
* This is the location (or hostname and port), of servers for specified services. For example, a service "http"
* may be running on the server "example.com" on port 80.
*/
public
class ServiceRecord {
private final int priority;
private final int weight;
private final int port;
private final String name;
private final String protocol;
private final String service;
private final String target;
/**
* @param fullPath the name first read in the SRV record which contains the
* service, the protocol, and the name of the server being
* queried. The format is {@code _service._protocol.hostname}, or
* for example {@code _http._tcp.example.com}
* @param priority relative priority of this service, range 0-65535 (lower is
* higher priority)
* @param weight determines how often multiple services will be used in the
* event they have the same priority (greater weight means
* service is used more often)
* @param port the port for the service
* @param target the name of the host for the service
*/
public
ServiceRecord(String fullPath, int priority, int weight, int port, String target) {
String[] parts = fullPath.split("\\.", 3);
this.service = parts[0];
this.protocol = parts[1];
this.name = parts[2];
this.priority = priority;
this.weight = weight;
this.port = port;
this.target = target;
}
public
int priority() {
return this.priority;
}
public
int weight() {
return this.weight;
}
public
int port() {
return this.port;
}
public
String name() {
return this.name;
}
public
String protocol() {
return this.protocol;
}
/**
* @return the service's name (i.e. "_http").
*/
public
String service() {
return this.service;
}
/**
* @return he name of the host for the service.
*/
public
String target() {
return this.target;
}
}

View File

@ -1,129 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package dorkbox.network.dns.record;
/**
* Represents an SOA record.
* <p/>
* There can only be one SOA record per zone.
*/
public
class StartOfAuthorityRecord {
private final String primaryNameServer;
private final String responsiblePerson;
private final long serial;
private final int refreshTime;
private final int retryTime;
private final int expireTime;
private final long minimumTtl;
/**
* @param primaryNameServer any name server that will respond authoritatively for the domain
* @param responsiblePerson e-mail address of person responsible for this zone
* @param serial a serial number that must be incremented when changes are
* made. Recommended format is YYYYMMDDnn. For example, if the
* primary name server is changed on June 19, 2013, then the
* serial would be 2013061901. If it is changed again on the same
* day it would be 2013061902
* @param refreshTime number of seconds a secondary name server waits, after getting
* a copy of the zone, before it checks the zone again for
* changes
* @param retryTime number of seconds to wait after a failed refresh attempt
* before another attempt to refresh is made
* @param expireTime number of seconds secondary name server can hold information
* before it is considered not authoritative
* @param minimumTtl number of seconds that records in the zone are valid for (if a
* record has a higher TTL, it overrides this value which is just
* a minimum)
*/
public
StartOfAuthorityRecord(String primaryNameServer,
String responsiblePerson,
long serial,
int refreshTime,
int retryTime,
int expireTime,
long minimumTtl) {
this.primaryNameServer = primaryNameServer;
this.responsiblePerson = responsiblePerson;
this.serial = serial;
this.refreshTime = refreshTime;
this.retryTime = retryTime;
this.expireTime = expireTime;
this.minimumTtl = minimumTtl;
}
/**
* Returns the primary name server.
*/
public
String primaryNameServer() {
return this.primaryNameServer;
}
/**
* Returns the responsible person's e-mail.
*/
public
String responsiblePerson() {
return this.responsiblePerson;
}
/**
* Returns the zone's serial number, usually in format YYYYMMDDnn.
*/
public
long serial() {
return this.serial;
}
/**
* Returns time between refreshes for secondary name servers.
*/
public
int refreshTime() {
return this.refreshTime;
}
/**
* Returns time between retries for failed refresh attempts.
*/
public
int retryTime() {
return this.retryTime;
}
/**
* Returns time before information stored in secondary name servers becomes
* non authoritative.
*/
public
int expireTime() {
return this.expireTime;
}
/**
* Returns the minimum TTL for records in the zone (if the record has a
* higher TTL, that value should be used as the TTL).
*/
public
long minimumTtl() {
return this.minimumTtl;
}
}

View File

@ -0,0 +1,147 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Address;
import dorkbox.network.dns.utils.Tokenizer;
/**
* A6 Record - maps a domain name to an IPv6 address (experimental)
*
* @author Brian Wellington
*/
public
class A6Record extends DnsRecord {
private static final long serialVersionUID = -8815026887337346789L;
private int prefixBits;
private InetAddress suffix;
private Name prefix;
A6Record() {}
@Override
DnsRecord getObject() {
return new A6Record();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
prefixBits = in.readU8();
int suffixbits = 128 - prefixBits;
int suffixbytes = (suffixbits + 7) / 8;
if (prefixBits < 128) {
byte[] bytes = new byte[16];
in.readByteArray(bytes, 16 - suffixbytes, suffixbytes);
suffix = InetAddress.getByAddress(bytes);
}
if (prefixBits > 0) {
prefix = new Name(in);
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(prefixBits);
if (suffix != null) {
int suffixbits = 128 - prefixBits;
int suffixbytes = (suffixbits + 7) / 8;
byte[] data = suffix.getAddress();
out.writeByteArray(data, 16 - suffixbytes, suffixbytes);
}
if (prefix != null) {
prefix.toWire(out, null, canonical);
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(prefixBits);
if (suffix != null) {
sb.append(" ");
sb.append(suffix.getHostAddress());
}
if (prefix != null) {
sb.append(" ");
sb.append(prefix);
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
prefixBits = st.getUInt8();
if (prefixBits > 128) {
throw st.exception("prefix bits must be [0..128]");
}
else if (prefixBits < 128) {
String s = st.getString();
try {
suffix = Address.getByAddress(s, Address.IPv6);
} catch (UnknownHostException e) {
throw st.exception("invalid IPv6 address: " + s);
}
}
if (prefixBits > 0) {
prefix = st.getName(origin);
}
}
/**
* Creates an A6 Record from the given data
*
* @param prefixBits The number of bits in the address prefix
* @param suffix The address suffix
* @param prefix The name of the prefix
*/
public
A6Record(Name name, int dclass, long ttl, int prefixBits, InetAddress suffix, Name prefix) {
super(name, DnsRecordType.A6, dclass, ttl);
this.prefixBits = checkU8("prefixBits", prefixBits);
if (suffix != null && Address.familyOf(suffix) != Address.IPv6) {
throw new IllegalArgumentException("invalid IPv6 address");
}
this.suffix = suffix;
if (prefix != null) {
this.prefix = checkName("prefix", prefix);
}
}
/**
* Returns the number of bits in the prefix
*/
public
int getPrefixBits() {
return prefixBits;
}
/**
* Returns the address suffix
*/
public
InetAddress getSuffix() {
return suffix;
}
/**
* Returns the address prefix
*/
public
Name getPrefix() {
return prefix;
}
}

View File

@ -0,0 +1,108 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Address;
import dorkbox.network.dns.utils.Tokenizer;
/**
* IPv6 Address Record - maps a domain name to an IPv6 address
*
* @author Brian Wellington
*/
public
class AAAARecord extends DnsRecord {
private static final long serialVersionUID = -4588601512069748050L;
private byte[] address;
AAAARecord() {}
@Override
DnsRecord getObject() {
return new AAAARecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
address = in.readByteArray(16);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeByteArray(address);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
InetAddress addr;
try {
addr = InetAddress.getByAddress(null, address);
} catch (UnknownHostException e) {
return;
}
if (addr.getAddress().length == 4) {
// Deal with Java's broken handling of mapped IPv4 addresses.
sb.append("0:0:0:0:0:ffff:");
int high = ((address[12] & 0xFF) << 8) + (address[13] & 0xFF);
int low = ((address[14] & 0xFF) << 8) + (address[15] & 0xFF);
sb.append(Integer.toHexString(high));
sb.append(':');
sb.append(Integer.toHexString(low));
return;
}
sb.append(addr.getHostAddress());
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
address = st.getAddressBytes(Address.IPv6);
}
/**
* Creates an AAAA Record from the given data
*
* @param address The address suffix
*/
public
AAAARecord(Name name, int dclass, long ttl, InetAddress address) {
super(name, DnsRecordType.AAAA, dclass, ttl);
if (Address.familyOf(address) != Address.IPv6) {
throw new IllegalArgumentException("invalid IPv6 address");
}
this.address = address.getAddress();
}
/**
* Returns the address
*/
public
InetAddress getAddress() {
try {
if (name == null) {
return InetAddress.getByAddress(address);
}
else {
return InetAddress.getByAddress(name.toString(true), address);
}
} catch (UnknownHostException e) {
return null;
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* AFS Data Base Record - maps a domain name to the name of an AFS cell
* database server.
*
* @author Brian Wellington
*/
public
class AFSDBRecord extends U16NameBase {
private static final long serialVersionUID = 3034379930729102437L;
AFSDBRecord() {}
@Override
DnsRecord getObject() {
return new AFSDBRecord();
}
/**
* Creates an AFSDB Record from the given data.
*
* @param subtype Indicates the type of service provided by the host.
* @param host The host providing the service.
*/
public
AFSDBRecord(Name name, int dclass, long ttl, int subtype, Name host) {
super(name, DnsRecordType.AFSDB, dclass, ttl, subtype, "subtype", host, "host");
}
/**
* Gets the subtype indicating the service provided by the host.
*/
public
int getSubtype() {
return getU16Field();
}
/**
* Gets the host providing service for the domain.
*/
public
Name getHost() {
return getNameField();
}
}

View File

@ -0,0 +1,305 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Address;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* APL - Address Prefix List. See RFC 3123.
*
* @author Brian Wellington
*/
/*
* Note: this currently uses the same constants as the Address class;
* this could change if more constants are defined for APL records.
*/
public
class APLRecord extends DnsRecord {
private static final long serialVersionUID = -1348173791712935864L;
private List elements;
public static
class Element {
public final int family;
public final boolean negative;
public final int prefixLength;
public final Object address;
/**
* Creates an APL element corresponding to an IPv4 or IPv6 prefix.
*
* @param negative Indicates if this prefix is a negation.
* @param address The IPv4 or IPv6 address.
* @param prefixLength The length of this prefix, in bits.
*
* @throws IllegalArgumentException The prefix length is invalid.
*/
public
Element(boolean negative, InetAddress address, int prefixLength) {
this(Address.familyOf(address), negative, address, prefixLength);
}
private
Element(int family, boolean negative, Object address, int prefixLength) {
this.family = family;
this.negative = negative;
this.address = address;
this.prefixLength = prefixLength;
if (!validatePrefixLength(family, prefixLength)) {
throw new IllegalArgumentException("invalid prefix " + "length");
}
}
public
int hashCode() {
return address.hashCode() + prefixLength + (negative ? 1 : 0);
}
public
boolean equals(Object arg) {
if (arg == null || !(arg instanceof Element)) {
return false;
}
Element elt = (Element) arg;
return (family == elt.family && negative == elt.negative && prefixLength == elt.prefixLength && address.equals(elt.address));
}
public
String toString() {
StringBuilder sb = new StringBuilder();
if (negative) {
sb.append("!");
}
sb.append(family);
sb.append(":");
if (family == Address.IPv4 || family == Address.IPv6) {
sb.append(((InetAddress) address).getHostAddress());
}
else {
sb.append(base16.toString((byte[]) address));
}
sb.append("/");
sb.append(prefixLength);
return sb.toString();
}
}
APLRecord() {}
@Override
DnsRecord getObject() {
return new APLRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
elements = new ArrayList(1);
while (in.remaining() != 0) {
int family = in.readU16();
int prefix = in.readU8();
int length = in.readU8();
boolean negative = (length & 0x80) != 0;
length &= ~0x80;
byte[] data = in.readByteArray(length);
Element element;
if (!validatePrefixLength(family, prefix)) {
throw new WireParseException("invalid prefix length");
}
if (family == Address.IPv4 || family == Address.IPv6) {
data = parseAddress(data, Address.addressLength(family));
InetAddress addr = InetAddress.getByAddress(data);
element = new Element(negative, addr, prefix);
}
else {
element = new Element(family, negative, data, prefix);
}
elements.add(element);
}
}
private static
boolean validatePrefixLength(int family, int prefixLength) {
if (prefixLength < 0 || prefixLength >= 256) {
return false;
}
if ((family == Address.IPv4 && prefixLength > 32) || (family == Address.IPv6 && prefixLength > 128)) {
return false;
}
return true;
}
private static
byte[] parseAddress(byte[] in, int length) throws WireParseException {
if (in.length > length) {
throw new WireParseException("invalid address length");
}
if (in.length == length) {
return in;
}
byte[] out = new byte[length];
System.arraycopy(in, 0, out, 0, in.length);
return out;
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Element element = (Element) it.next();
int length = 0;
byte[] data;
if (element.family == Address.IPv4 || element.family == Address.IPv6) {
InetAddress addr = (InetAddress) element.address;
data = addr.getAddress();
length = addressLength(data);
}
else {
data = (byte[]) element.address;
length = data.length;
}
int wlength = length;
if (element.negative) {
wlength |= 0x80;
}
out.writeU16(element.family);
out.writeU8(element.prefixLength);
out.writeU8(wlength);
out.writeByteArray(data, 0, length);
}
}
@Override
void rrToString(StringBuilder sb) {
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Element element = (Element) it.next();
sb.append(element);
if (it.hasNext()) {
sb.append(" ");
}
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
elements = new ArrayList(1);
while (true) {
Tokenizer.Token t = st.get();
if (!t.isString()) {
break;
}
boolean negative = false;
int family = 0;
int prefix = 0;
String s = t.value;
int start = 0;
if (s.startsWith("!")) {
negative = true;
start = 1;
}
int colon = s.indexOf(':', start);
if (colon < 0) {
throw st.exception("invalid address prefix element");
}
int slash = s.indexOf('/', colon);
if (slash < 0) {
throw st.exception("invalid address prefix element");
}
String familyString = s.substring(start, colon);
String addressString = s.substring(colon + 1, slash);
String prefixString = s.substring(slash + 1);
try {
family = Integer.parseInt(familyString);
} catch (NumberFormatException e) {
throw st.exception("invalid family");
}
if (family != Address.IPv4 && family != Address.IPv6) {
throw st.exception("unknown family");
}
try {
prefix = Integer.parseInt(prefixString);
} catch (NumberFormatException e) {
throw st.exception("invalid prefix length");
}
if (!validatePrefixLength(family, prefix)) {
throw st.exception("invalid prefix length");
}
byte[] bytes = Address.toByteArray(addressString, family);
if (bytes == null) {
throw st.exception("invalid IP address " + addressString);
}
InetAddress address = InetAddress.getByAddress(bytes);
elements.add(new Element(negative, address, prefix));
}
st.unget();
}
private static
int addressLength(byte[] addr) {
for (int i = addr.length - 1; i >= 0; i--) {
if (addr[i] != 0) {
return i + 1;
}
}
return 0;
}
/**
* Creates an APL Record from the given data.
*
* @param elements The list of APL elements.
*/
public
APLRecord(Name name, int dclass, long ttl, List elements) {
super(name, DnsRecordType.APL, dclass, ttl);
this.elements = new ArrayList(elements.size());
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Object o = it.next();
if (!(o instanceof Element)) {
throw new IllegalArgumentException("illegal element");
}
Element element = (Element) o;
if (element.family != Address.IPv4 && element.family != Address.IPv6) {
throw new IllegalArgumentException("unknown family");
}
this.elements.add(element);
}
}
/**
* Returns the list of APL elements.
*/
public
List getElements() {
return elements;
}
}

View File

@ -0,0 +1,106 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Address;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Address Record - maps a domain name to an Internet address
*
* @author Brian Wellington
*/
public
class ARecord extends DnsRecord {
private static final long serialVersionUID = -2172609200849142323L;
private int addr;
ARecord() {}
@Override
DnsRecord getObject() {
return new ARecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
addr = fromArray(in.readByteArray(4));
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU32(((long) addr) & 0xFFFFFFFFL);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(Address.toDottedQuad(toArray(addr)));
}
private static
byte[] toArray(int addr) {
byte[] bytes = new byte[4];
bytes[0] = (byte) ((addr >>> 24) & 0xFF);
bytes[1] = (byte) ((addr >>> 16) & 0xFF);
bytes[2] = (byte) ((addr >>> 8) & 0xFF);
bytes[3] = (byte) (addr & 0xFF);
return bytes;
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
addr = fromArray(st.getAddressBytes(Address.IPv4));
}
private static
int fromArray(byte[] array) {
return (((array[0] & 0xFF) << 24) | ((array[1] & 0xFF) << 16) | ((array[2] & 0xFF) << 8) | (array[3] & 0xFF));
}
/**
* Creates an A Record from the given data
*
* @param address The address that the name refers to
*/
public
ARecord(Name name, int dclass, long ttl, InetAddress address) {
super(name, DnsRecordType.A, dclass, ttl);
if (Address.familyOf(address) != Address.IPv4) {
throw new IllegalArgumentException("invalid IPv4 address");
}
addr = fromArray(address.getAddress());
}
/**
* Returns the Internet address
*/
public
InetAddress getAddress() {
try {
if (name == null) {
return InetAddress.getByAddress(toArray(addr));
}
else {
return InetAddress.getByAddress(name.toString(true), toArray(addr));
}
} catch (UnknownHostException e) {
return null;
}
}
}

View File

@ -0,0 +1,122 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Certification Authority Authorization
*
* @author Brian Wellington
*/
public
class CAARecord extends DnsRecord {
private static final long serialVersionUID = 8544304287274216443L;
private int flags;
private byte[] tag;
private byte[] value;
public static
class Flags {
public static final int IssuerCritical = 128;
private
Flags() {}
}
CAARecord() {}
@Override
DnsRecord getObject() {
return new CAARecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
flags = in.readU8();
tag = in.readCountedString();
value = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(flags);
out.writeCountedString(tag);
out.writeByteArray(value);
}
@Override
void rrToString(StringBuilder sb) {
sb.append(flags);
sb.append(" ");
sb.append(byteArrayToString(tag, false));
sb.append(" ");
sb.append(byteArrayToString(value, true));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
flags = st.getUInt8();
try {
tag = byteArrayFromString(st.getString());
value = byteArrayFromString(st.getString());
} catch (TextParseException e) {
throw st.exception(e.getMessage());
}
}
/**
* Creates an CAA Record from the given data.
*
* @param flags The flags.
* @param tag The tag.
* @param value The value.
*/
public
CAARecord(Name name, int dclass, long ttl, int flags, String tag, String value) {
super(name, DnsRecordType.CAA, dclass, ttl);
this.flags = checkU8("flags", flags);
try {
this.tag = byteArrayFromString(tag);
this.value = byteArrayFromString(value);
} catch (TextParseException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
/**
* Returns the flags.
*/
public
int getFlags() {
return flags;
}
/**
* Returns the tag.
*/
public
String getTag() {
return byteArrayToString(tag, false);
}
/**
* Returns the value
*/
public
String getValue() {
return byteArrayToString(value, false);
}
}

View File

@ -0,0 +1,257 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Options;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.Base64Fast;
import dorkbox.util.OS;
/**
* Certificate Record - Stores a certificate associated with a name. The
* certificate might also be associated with a KEYRecord.
*
* @author Brian Wellington
* @see KEYRecord
*/
public
class CERTRecord extends DnsRecord {
/**
* PKIX (X.509v3)
*/
public static final int PKIX = CertificateType.PKIX;
/**
* Simple Public Key Infrastructure
*/
public static final int SPKI = CertificateType.SPKI;
/**
* Pretty Good Privacy
*/
public static final int PGP = CertificateType.PGP;
/**
* Certificate format defined by URI
*/
public static final int URI = CertificateType.URI;
/**
* Certificate format defined by IOD
*/
public static final int OID = CertificateType.OID;
private static final long serialVersionUID = 4763014646517016835L;
private int certType, keyTag;
private int alg;
private byte[] cert;
public static
class CertificateType {
/**
* PKIX (X.509v3)
*/
public static final int PKIX = 1;
/**
* Simple Public Key Infrastructure
*/
public static final int SPKI = 2;
/**
* Pretty Good Privacy
*/
public static final int PGP = 3;
/**
* URL of an X.509 data object
*/
public static final int IPKIX = 4;
/**
* URL of an SPKI certificate
*/
public static final int ISPKI = 5;
/**
* Fingerprint and URL of an OpenPGP packet
*/
public static final int IPGP = 6;
/**
* Attribute Certificate
*/
public static final int ACPKIX = 7;
/**
* URL of an Attribute Certificate
*/
public static final int IACPKIX = 8;
/**
* Certificate format defined by URI
*/
public static final int URI = 253;
/**
* Certificate format defined by OID
*/
public static final int OID = 254;
private static Mnemonic types = new Mnemonic("Certificate type", Mnemonic.CASE_UPPER);
/**
* Certificate type identifiers. See RFC 4398 for more detail.
*/
private
CertificateType() {}
static {
types.setMaximum(0xFFFF);
types.setNumericAllowed(true);
types.add(PKIX, "PKIX");
types.add(SPKI, "SPKI");
types.add(PGP, "PGP");
types.add(PKIX, "IPKIX");
types.add(SPKI, "ISPKI");
types.add(PGP, "IPGP");
types.add(PGP, "ACPKIX");
types.add(PGP, "IACPKIX");
types.add(URI, "URI");
types.add(OID, "OID");
}
/**
* Converts a certificate type into its textual representation
*/
public static
String string(int type) {
return types.getText(type);
}
/**
* Converts a textual representation of an certificate type into its
* numeric code. Integers in the range 0..65535 are also accepted.
*
* @param s The textual representation of the algorithm
*
* @return The algorithm code, or -1 on error.
*/
public static
int value(String s) {
return types.getValue(s);
}
}
CERTRecord() {}
@Override
DnsRecord getObject() {
return new CERTRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
certType = in.readU16();
keyTag = in.readU16();
alg = in.readU8();
cert = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(certType);
out.writeU16(keyTag);
out.writeU8(alg);
out.writeByteArray(cert);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(certType);
sb.append(" ");
sb.append(keyTag);
sb.append(" ");
sb.append(alg);
if (cert != null) {
if (Options.check("multiline")) {
sb.append(" (");
sb.append(OS.LINE_SEPARATOR);
sb.append(Base64Fast.formatString(Base64Fast.encode2(cert), 64, "\t", true));
}
else {
sb.append(" ");
sb.append(Base64Fast.encode2(cert));
}
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
String certTypeString = st.getString();
certType = CertificateType.value(certTypeString);
if (certType < 0) {
throw st.exception("Invalid certificate type: " + certTypeString);
}
keyTag = st.getUInt16();
String algString = st.getString();
alg = DNSSEC.Algorithm.value(algString);
if (alg < 0) {
throw st.exception("Invalid algorithm: " + algString);
}
cert = st.getBase64();
}
/**
* Creates a CERT Record from the given data
*
* @param certType The type of certificate (see constants)
* @param keyTag The ID of the associated KEYRecord, if present
* @param alg The algorithm of the associated KEYRecord, if present
* @param cert Binary data representing the certificate
*/
public
CERTRecord(Name name, int dclass, long ttl, int certType, int keyTag, int alg, byte[] cert) {
super(name, DnsRecordType.CERT, dclass, ttl);
this.certType = checkU16("certType", certType);
this.keyTag = checkU16("keyTag", keyTag);
this.alg = checkU8("alg", alg);
this.cert = cert;
}
/**
* Returns the type of certificate
*/
public
int getCertType() {
return certType;
}
/**
* Returns the ID of the associated KEYRecord, if present
*/
public
int getKeyTag() {
return keyTag;
}
/**
* Returns the algorithm of the associated KEYRecord, if present
*/
public
int getAlgorithm() {
return alg;
}
/**
* Returns the binary representation of the certificate
*/
public
byte[] getCert() {
return cert;
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* CNAME Record - maps an alias to its real name
*
* @author Brian Wellington
*/
public
class CNAMERecord extends SingleCompressedNameBase {
private static final long serialVersionUID = -4020373886892538580L;
CNAMERecord() {}
@Override
DnsRecord getObject() {
return new CNAMERecord();
}
/**
* Creates a new CNAMERecord with the given data
*
* @param alias The name to which the CNAME alias points
*/
public
CNAMERecord(Name name, int dclass, long ttl, Name alias) {
super(name, DnsRecordType.CNAME, dclass, ttl, alias, "alias");
}
/**
* Gets the target of the CNAME Record
*/
public
Name getTarget() {
return getSingleName();
}
/**
* Gets the alias specified by the CNAME Record
*/
public
Name getAlias() {
return getSingleName();
}
}

View File

@ -0,0 +1,187 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.net.InetAddress;
import java.net.UnknownHostException;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Address;
/**
* The Client Subnet EDNS Option, defined in
* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-00
* ("Client subnet in DNS requests").
* <p>
* The option is used to convey information about the IP address of the
* originating client, so that an authoritative server can make decisions
* based on this address, rather than the address of the intermediate
* caching name server.
* <p>
* The option is transmitted as part of an OPTRecord in the additional section
* of a DNS message, as defined by RFC 2671 (EDNS0).
* <p>
* The wire format of the option contains a 2-byte length field (1 for IPv4, 2
* for IPv6), a 1-byte source netmask, a 1-byte scope netmask, and an address
* truncated to the source netmask length (where the final octet is padded with
* bits set to 0)
*
* @author Brian Wellington
* @author Ming Zhou &lt;mizhou@bnivideo.com&gt;, Beaumaris Networks
* @see OPTRecord
*/
public
class ClientSubnetOption extends EDNSOption {
private static final long serialVersionUID = -3868158449890266347L;
private int family;
private int sourceNetmask;
private int scopeNetmask;
private InetAddress address;
ClientSubnetOption() {
super(EDNSOption.Code.CLIENT_SUBNET);
}
/**
* Construct a Client Subnet option with scope netmask set to 0.
*
* @param sourceNetmask The length of the netmask pertaining to the query.
* In replies, it mirrors the same value as in the requests.
* @param address The address of the client.
*
* @see ClientSubnetOption
*/
public
ClientSubnetOption(int sourceNetmask, InetAddress address) {
this(sourceNetmask, 0, address);
}
/**
* Construct a Client Subnet option. Note that the number of significant bits
* in the address must not be greater than the supplied source netmask. There
* may also be issues related to Java's handling of mapped addresses
*
* @param sourceNetmask The length of the netmask pertaining to the query.
* In replies, it mirrors the same value as in the requests.
* @param scopeNetmask The length of the netmask pertaining to the reply.
* In requests, it MUST be set to 0. In responses, this may or may not match
* the source netmask.
* @param address The address of the client.
*/
public
ClientSubnetOption(int sourceNetmask, int scopeNetmask, InetAddress address) {
super(EDNSOption.Code.CLIENT_SUBNET);
this.family = Address.familyOf(address);
this.sourceNetmask = checkMaskLength("source netmask", this.family, sourceNetmask);
this.scopeNetmask = checkMaskLength("scope netmask", this.family, scopeNetmask);
this.address = Address.truncate(address, sourceNetmask);
if (!address.equals(this.address)) {
throw new IllegalArgumentException("source netmask is not " + "valid for address");
}
}
private static
int checkMaskLength(String field, int family, int val) {
int max = Address.addressLength(family) * 8;
if (val < 0 || val > max) {
throw new IllegalArgumentException("\"" + field + "\" " + val + " must be in the range " + "[0.." + max + "]");
}
return val;
}
/**
* Returns the family of the network address. This will be either IPv4 (1)
* or IPv6 (2).
*/
public
int getFamily() {
return family;
}
/**
* Returns the source netmask.
*/
public
int getSourceNetmask() {
return sourceNetmask;
}
/**
* Returns the scope netmask.
*/
public
int getScopeNetmask() {
return scopeNetmask;
}
/**
* Returns the IP address of the client.
*/
public
InetAddress getAddress() {
return address;
}
@Override
void optionFromWire(DnsInput in) throws WireParseException {
family = in.readU16();
if (family != Address.IPv4 && family != Address.IPv6) {
throw new WireParseException("unknown address family");
}
sourceNetmask = in.readU8();
if (sourceNetmask > Address.addressLength(family) * 8) {
throw new WireParseException("invalid source netmask");
}
scopeNetmask = in.readU8();
if (scopeNetmask > Address.addressLength(family) * 8) {
throw new WireParseException("invalid scope netmask");
}
// Read the truncated address
byte[] addr = in.readByteArray();
if (addr.length != (sourceNetmask + 7) / 8) {
throw new WireParseException("invalid address");
}
// Convert it to a full length address.
byte[] fulladdr = new byte[Address.addressLength(family)];
System.arraycopy(addr, 0, fulladdr, 0, addr.length);
try {
address = InetAddress.getByAddress(fulladdr);
} catch (UnknownHostException e) {
throw new WireParseException("invalid address", e);
}
InetAddress tmp = Address.truncate(address, sourceNetmask);
if (!tmp.equals(address)) {
throw new WireParseException("invalid padding");
}
}
@Override
void optionToWire(DnsOutput out) {
out.writeU16(family);
out.writeU8(sourceNetmask);
out.writeU8(scopeNetmask);
out.writeByteArray(address.getAddress(), 0, (sourceNetmask + 7) / 8);
}
@Override
String optionToString() {
StringBuilder sb = new StringBuilder();
sb.append(address.getHostAddress());
sb.append("/");
sb.append(sourceNetmask);
sb.append(", scope netmask ");
sb.append(scopeNetmask);
return sb.toString();
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) 2008 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.Base64Fast;
/**
* DHCID - Dynamic Host Configuration Protocol (DHCP) ID (RFC 4701)
*
* @author Brian Wellington
*/
public
class DHCIDRecord extends DnsRecord {
private static final long serialVersionUID = -8214820200808997707L;
private byte[] data;
DHCIDRecord() {}
@Override
DnsRecord getObject() {
return new DHCIDRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
data = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeByteArray(data);
}
@Override
void rrToString(StringBuilder sb) {
sb.append(Base64Fast.encode2(data));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
data = st.getBase64();
}
/**
* Creates an DHCID Record from the given data
*
* @param data The binary data, which is opaque to DNS.
*/
public
DHCIDRecord(Name name, int dclass, long ttl, byte[] data) {
super(name, DnsRecordType.DHCID, dclass, ttl);
this.data = data;
}
/**
* Returns the binary data.
*/
public
byte[] getData() {
return data;
}
}

View File

@ -0,0 +1,136 @@
// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* DLV - contains a Delegation Lookaside Validation record, which acts
* as the equivalent of a DS record in a lookaside zone.
*
* @author David Blacka
* @author Brian Wellington
* @see DNSSEC
* @see DSRecord
*/
public
class DLVRecord extends DnsRecord {
public static final int SHA1_DIGEST_ID = DSRecord.Digest.SHA1;
public static final int SHA256_DIGEST_ID = DSRecord.Digest.SHA1;
private static final long serialVersionUID = 1960742375677534148L;
private int footprint;
private int alg;
private int digestid;
private byte[] digest;
DLVRecord() {}
@Override
DnsRecord getObject() {
return new DLVRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
footprint = in.readU16();
alg = in.readU8();
digestid = in.readU8();
digest = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(footprint);
out.writeU8(alg);
out.writeU8(digestid);
if (digest != null) {
out.writeByteArray(digest);
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(footprint);
sb.append(" ");
sb.append(alg);
sb.append(" ");
sb.append(digestid);
if (digest != null) {
sb.append(" ");
sb.append(base16.toString(digest));
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
footprint = st.getUInt16();
alg = st.getUInt8();
digestid = st.getUInt8();
digest = st.getHex();
}
/**
* Creates a DLV Record from the given data
*
* @param footprint The original KEY record's footprint (keyid).
* @param alg The original key algorithm.
* @param digestid The digest id code.
* @param digest A hash of the original key.
*/
public
DLVRecord(Name name, int dclass, long ttl, int footprint, int alg, int digestid, byte[] digest) {
super(name, DnsRecordType.DLV, dclass, ttl);
this.footprint = checkU16("footprint", footprint);
this.alg = checkU8("alg", alg);
this.digestid = checkU8("digestid", digestid);
this.digest = digest;
}
/**
* Returns the key's algorithm.
*/
public
int getAlgorithm() {
return alg;
}
/**
* Returns the key's Digest ID.
*/
public
int getDigestID() {
return digestid;
}
/**
* Returns the binary hash of the key.
*/
public
byte[] getDigest() {
return digest;
}
/**
* Returns the key's footprint.
*/
public
int getFootprint() {
return footprint;
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* DNAME Record - maps a nonterminal alias (subtree) to a different domain
*
* @author Brian Wellington
*/
public
class DNAMERecord extends SingleNameBase {
private static final long serialVersionUID = 2670767677200844154L;
DNAMERecord() {}
@Override
DnsRecord getObject() {
return new DNAMERecord();
}
/**
* Creates a new DNAMERecord with the given data
*
* @param alias The name to which the DNAME alias points
*/
public
DNAMERecord(Name name, int dclass, long ttl, Name alias) {
super(name, DnsRecordType.DNAME, dclass, ttl, alias, "alias");
}
/**
* Gets the target of the DNAME Record
*/
public
Name getTarget() {
return getSingleName();
}
/**
* Gets the alias specified by the DNAME Record
*/
public
Name getAlias() {
return getSingleName();
}
}

View File

@ -0,0 +1,107 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.security.PublicKey;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Key - contains a cryptographic public key for use by DNS.
* The data can be converted to objects implementing
* java.security.interfaces.PublicKey
*
* @author Brian Wellington
* @see DNSSEC
*/
public
class DNSKEYRecord extends KEYBase {
private static final long serialVersionUID = -8679800040426675002L;
public static
class Protocol {
/**
* Key will be used for DNSSEC
*/
public static final int DNSSEC = 3;
private
Protocol() {}
}
public static
class Flags {
/**
* Key is a zone key
*/
public static final int ZONE_KEY = 0x100;
/**
* Key is a secure entry point key
*/
public static final int SEP_KEY = 0x1;
/**
* Key has been revoked
*/
public static final int REVOKE = 0x80;
private
Flags() {}
}
DNSKEYRecord() {}
@Override
DnsRecord getObject() {
return new DNSKEYRecord();
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
flags = st.getUInt16();
proto = st.getUInt8();
String algString = st.getString();
alg = DNSSEC.Algorithm.value(algString);
if (alg < 0) {
throw st.exception("Invalid algorithm: " + algString);
}
key = st.getBase64();
}
/**
* Creates a DNSKEY Record from the given data
*
* @param flags Flags describing the key's properties
* @param proto The protocol that the key was created for
* @param alg The key's algorithm
* @param key Binary representation of the key
*/
public
DNSKEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg, byte[] key) {
super(name, DnsRecordType.DNSKEY, dclass, ttl, flags, proto, alg, key);
}
/**
* Creates a DNSKEY Record from the given data
*
* @param flags Flags describing the key's properties
* @param proto The protocol that the key was created for
* @param alg The key's algorithm
* @param key The key as a PublicKey
*
* @throws DNSSEC.DNSSECException The PublicKey could not be converted into DNS
* format.
*/
public
DNSKEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg, PublicKey key) throws DNSSEC.DNSSECException {
super(name, DnsRecordType.DNSKEY, dclass, ttl, flags, proto, alg, DNSSEC.fromPublicKey(key, alg));
publicKey = key;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,170 @@
// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* DS - contains a Delegation Signer record, which acts as a
* placeholder for KEY records in the parent zone.
*
* @author David Blacka
* @author Brian Wellington
* @see DNSSEC
*/
public
class DSRecord extends DnsRecord {
public static final int SHA1_DIGEST_ID = Digest.SHA1;
public static final int SHA256_DIGEST_ID = Digest.SHA256;
public static final int GOST3411_DIGEST_ID = Digest.GOST3411;
public static final int SHA384_DIGEST_ID = Digest.SHA384;
private static final long serialVersionUID = -9001819329700081493L;
private int footprint;
private int alg;
private int digestid;
private byte[] digest;
public static
class Digest {
/**
* SHA-1
*/
public static final int SHA1 = 1;
/**
* SHA-256
*/
public static final int SHA256 = 2;
/**
* GOST R 34.11-94
*/
public static final int GOST3411 = 3;
/**
* SHA-384
*/
public static final int SHA384 = 4;
private
Digest() {}
}
DSRecord() {}
@Override
DnsRecord getObject() {
return new DSRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
footprint = in.readU16();
alg = in.readU8();
digestid = in.readU8();
digest = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(footprint);
out.writeU8(alg);
out.writeU8(digestid);
if (digest != null) {
out.writeByteArray(digest);
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(footprint);
sb.append(" ");
sb.append(alg);
sb.append(" ");
sb.append(digestid);
if (digest != null) {
sb.append(" ");
sb.append(base16.toString(digest));
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
footprint = st.getUInt16();
alg = st.getUInt8();
digestid = st.getUInt8();
digest = st.getHex();
}
/**
* Creates a DS Record from the given data
*
* @param digestid The digest id code.
* @param key The key to digest
*/
public
DSRecord(Name name, int dclass, long ttl, int digestid, DNSKEYRecord key) {
this(name, dclass, ttl, key.getFootprint(), key.getAlgorithm(), digestid, DNSSEC.generateDSDigest(key, digestid));
}
/**
* Creates a DS Record from the given data
*
* @param footprint The original KEY record's footprint (keyid).
* @param alg The original key algorithm.
* @param digestid The digest id code.
* @param digest A hash of the original key.
*/
public
DSRecord(Name name, int dclass, long ttl, int footprint, int alg, int digestid, byte[] digest) {
super(name, DnsRecordType.DS, dclass, ttl);
this.footprint = checkU16("footprint", footprint);
this.alg = checkU8("alg", alg);
this.digestid = checkU8("digestid", digestid);
this.digest = digest;
}
/**
* Returns the key's algorithm.
*/
public
int getAlgorithm() {
return alg;
}
/**
* Returns the key's Digest ID.
*/
public
int getDigestID() {
return digestid;
}
/**
* Returns the binary hash of the key.
*/
public
byte[] getDigest() {
return digest;
}
/**
* Returns the key's footprint.
*/
public
int getFootprint() {
return footprint;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,823 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.Arrays;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsClass;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.exceptions.RelativeNameException;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Options;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* A generic DNS resource record. The specific record types extend this class.
* A record contains a name, type, class, ttl, and rdata.
*
* @author Brian Wellington
*/
public abstract
class DnsRecord implements Cloneable, Comparable, Serializable {
private static final long serialVersionUID = 2694906050116005466L;
protected Name name;
protected int type, dclass;
protected long ttl;
private static final DecimalFormat byteFormat = new DecimalFormat();
static {
byteFormat.setMinimumIntegerDigits(3);
}
protected
DnsRecord() {}
DnsRecord(Name name, int type, int dclass, long ttl) {
if (!name.isAbsolute()) {
throw new RelativeNameException(name);
}
DnsRecordType.check(type);
DnsClass.check(dclass);
TTL.check(ttl);
this.name = name;
this.type = type;
this.dclass = dclass;
this.ttl = ttl;
}
/**
* Creates a new record, with the given parameters.
*
* @param name The owner name of the record.
* @param type The record's type.
* @param dclass The record's class.
* @param ttl The record's time to live.
* @param data The complete rdata of the record, in uncompressed DNS wire
* format.
*/
public static
DnsRecord newRecord(Name name, int type, int dclass, long ttl, byte[] data) {
return newRecord(name, type, dclass, ttl, data.length, data);
}
/**
* Creates a new record, with the given parameters.
*
* @param name The owner name of the record.
* @param type The record's type.
* @param dclass The record's class.
* @param ttl The record's time to live.
* @param length The length of the record's data.
* @param data The rdata of the record, in uncompressed DNS wire format. Only
* the first length bytes are used.
*/
public static
DnsRecord newRecord(Name name, int type, int dclass, long ttl, int length, byte[] data) {
if (!name.isAbsolute()) {
throw new RelativeNameException(name);
}
DnsRecordType.check(type);
DnsClass.check(dclass);
TTL.check(ttl);
DnsInput in;
if (data != null) {
in = new DnsInput(data);
}
else {
in = null;
}
try {
return newRecord(name, type, dclass, ttl, length, in);
} catch (IOException e) {
return null;
}
}
private static
DnsRecord newRecord(Name name, int type, int dclass, long ttl, int length, DnsInput in) throws IOException {
DnsRecord rec;
rec = getEmptyRecord(name, type, dclass, ttl, in != null);
if (in != null) {
if (in.remaining() < length) {
throw new WireParseException("truncated record");
}
in.setActive(length);
rec.rrFromWire(in);
int remaining = in.remaining();
in.restoreActive();
if (remaining > 0) {
throw new WireParseException("invalid record length");
}
}
return rec;
}
private static
DnsRecord getEmptyRecord(Name name, int type, int dclass, long ttl, boolean hasData) {
DnsRecord proto, rec;
if (hasData) {
proto = DnsRecordType.getProto(type);
if (proto != null) {
rec = proto.getObject();
}
else {
rec = new UNKRecord();
}
}
else {
rec = new EmptyRecord();
}
rec.name = name;
rec.type = type;
rec.dclass = dclass;
rec.ttl = ttl;
return rec;
}
/**
* Creates an empty record of the correct type; must be overriden
*/
abstract
DnsRecord getObject();
/**
* Converts the type-specific RR to wire format - must be overriden
*/
abstract
void rrFromWire(DnsInput in) throws IOException;
/**
* Creates a new empty record, with the given parameters.
*
* @param name The owner name of the record.
* @param type The record's type.
* @param dclass The record's class.
* @param ttl The record's time to live.
*
* @return An object of a subclass of Record
*/
public static
DnsRecord newRecord(Name name, int type, int dclass, long ttl) {
if (!name.isAbsolute()) {
throw new RelativeNameException(name);
}
DnsRecordType.check(type);
DnsClass.check(dclass);
TTL.check(ttl);
return getEmptyRecord(name, type, dclass, ttl, false);
}
/**
* Creates a new empty record, with the given parameters. This method is
* designed to create records that will be added to the QUERY section
* of a message.
*
* @param name The owner name of the record.
* @param type The record's type.
* @param dclass The record's class.
*
* @return An object of a subclass of Record
*/
public static
DnsRecord newRecord(Name name, int type, int dclass) {
return newRecord(name, type, dclass, 0);
}
static
DnsRecord fromWire(DnsInput in, int section, boolean isUpdate) throws IOException {
int type, dclass;
long ttl;
int length;
Name name;
DnsRecord rec;
name = new Name(in);
type = in.readU16();
dclass = in.readU16();
if (section == DnsSection.QUESTION) {
return newRecord(name, type, dclass);
}
ttl = in.readU32();
length = in.readU16();
if (length == 0 && isUpdate && (section == DnsSection.PREREQ || section == DnsSection.UPDATE)) {
return newRecord(name, type, dclass, ttl);
}
rec = newRecord(name, type, dclass, ttl, length, in);
return rec;
}
static
DnsRecord fromWire(DnsInput in, int section) throws IOException {
return fromWire(in, section, false);
}
/**
* Builds a Record from DNS uncompressed wire format.
*/
public static
DnsRecord fromWire(byte[] b, int section) throws IOException {
return fromWire(new DnsInput(b), section, false);
}
/**
* Converts a Record into DNS uncompressed wire format.
*/
public
byte[] toWire(int section) {
DnsOutput out = new DnsOutput();
toWire(out, section, null);
return out.toByteArray();
}
void toWire(DnsOutput out, int section, Compression c) {
name.toWire(out, c);
out.writeU16(type);
out.writeU16(dclass);
if (section == DnsSection.QUESTION) {
return;
}
out.writeU32(ttl);
int lengthPosition = out.current();
out.writeU16(0); /* until we know better */
rrToWire(out, c, false);
int rrlength = out.current() - lengthPosition - 2;
out.writeU16At(rrlength, lengthPosition);
}
/**
* Converts the type-specific RR to wire format - must be overriden
*/
abstract
void rrToWire(DnsOutput out, Compression c, boolean canonical);
/**
* Converts a Record into canonical DNS uncompressed wire format (all names are
* converted to lowercase).
*/
public
byte[] toWireCanonical() {
return toWireCanonical(false);
}
/*
* Converts a Record into canonical DNS uncompressed wire format (all names are
* converted to lowercase), optionally ignoring the TTL.
*/
private
byte[] toWireCanonical(boolean noTTL) {
DnsOutput out = new DnsOutput();
toWireCanonical(out, noTTL);
return out.toByteArray();
}
private
void toWireCanonical(DnsOutput out, boolean noTTL) {
name.toWireCanonical(out);
out.writeU16(type);
out.writeU16(dclass);
if (noTTL) {
out.writeU32(0);
}
else {
out.writeU32(ttl);
}
int lengthPosition = out.current();
out.writeU16(0); /* until we know better */
rrToWire(out, null, true);
int rrlength = out.current() - lengthPosition - 2;
out.writeU16At(rrlength, lengthPosition);
}
/**
* Converts the rdata portion of a Record into a String representation
*/
public
void rdataToString(StringBuilder sb) {
rrToString(sb);
}
/**
* Converts the type-specific RR to text format - must be overriden
*/
abstract
void rrToString(StringBuilder sb);
/**
* Converts the text format of an RR to the internal format - must be overriden
*/
abstract
void rdataFromString(Tokenizer st, Name origin) throws IOException;
/**
* Converts a String into a byte array.
*/
protected static
byte[] byteArrayFromString(String s) throws TextParseException {
byte[] array = s.getBytes();
boolean escaped = false;
boolean hasEscapes = false;
for (int i = 0; i < array.length; i++) {
if (array[i] == '\\') {
hasEscapes = true;
break;
}
}
if (!hasEscapes) {
if (array.length > 255) {
throw new TextParseException("text string too long");
}
return array;
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
int digits = 0;
int intval = 0;
for (int i = 0; i < array.length; i++) {
byte b = array[i];
if (escaped) {
if (b >= '0' && b <= '9' && digits < 3) {
digits++;
intval *= 10;
intval += (b - '0');
if (intval > 255) {
throw new TextParseException("bad escape");
}
if (digits < 3) {
continue;
}
b = (byte) intval;
}
else if (digits > 0 && digits < 3) {
throw new TextParseException("bad escape");
}
os.write(b);
escaped = false;
}
else if (array[i] == '\\') {
escaped = true;
digits = 0;
intval = 0;
}
else {
os.write(array[i]);
}
}
if (digits > 0 && digits < 3) {
throw new TextParseException("bad escape");
}
array = os.toByteArray();
if (array.length > 255) {
throw new TextParseException("text string too long");
}
return os.toByteArray();
}
/**
* Converts a byte array into a String.
*/
protected static
String byteArrayToString(byte[] array, boolean quote) {
StringBuilder sb = new StringBuilder();
if (quote) {
sb.append('"');
}
for (int i = 0; i < array.length; i++) {
int b = array[i] & 0xFF;
if (b < 0x20 || b >= 0x7f) {
sb.append('\\');
sb.append(byteFormat.format(b));
}
else if (b == '"' || b == '\\') {
sb.append('\\');
sb.append((char) b);
}
else {
sb.append((char) b);
}
}
if (quote) {
sb.append('"');
}
return sb.toString();
}
/**
* Converts a byte array into the unknown RR format.
*/
protected static
String unknownToString(byte[] data) {
StringBuilder sb = new StringBuilder();
sb.append("\\# ");
sb.append(data.length);
sb.append(" ");
sb.append(base16.toString(data));
return sb.toString();
}
/**
* Builds a new Record from its textual representation
*
* @param name The owner name of the record.
* @param type The record's type.
* @param dclass The record's class.
* @param ttl The record's time to live.
* @param st A tokenizer containing the textual representation of the rdata.
* @param origin The default origin to be appended to relative domain names.
*
* @return The new record
*
* @throws IOException The text format was invalid.
*/
public static
DnsRecord fromString(Name name, int type, int dclass, long ttl, Tokenizer st, Name origin) throws IOException {
DnsRecord rec;
if (!name.isAbsolute()) {
throw new RelativeNameException(name);
}
DnsRecordType.check(type);
DnsClass.check(dclass);
TTL.check(ttl);
Tokenizer.Token t = st.get();
if (t.type == Tokenizer.IDENTIFIER && t.value.equals("\\#")) {
int length = st.getUInt16();
byte[] data = st.getHex();
if (data == null) {
data = new byte[0];
}
if (length != data.length) {
throw st.exception("invalid unknown RR encoding: " + "length mismatch");
}
DnsInput in = new DnsInput(data);
return newRecord(name, type, dclass, ttl, length, in);
}
st.unget();
rec = getEmptyRecord(name, type, dclass, ttl, true);
rec.rdataFromString(st, origin);
t = st.get();
if (t.type != Tokenizer.EOL && t.type != Tokenizer.EOF) {
throw st.exception("unexpected tokens at end of record");
}
return rec;
}
/**
* Builds a new Record from its textual representation
*
* @param name The owner name of the record.
* @param type The record's type.
* @param dclass The record's class.
* @param ttl The record's time to live.
* @param s The textual representation of the rdata.
* @param origin The default origin to be appended to relative domain names.
*
* @return The new record
*
* @throws IOException The text format was invalid.
*/
public static
DnsRecord fromString(Name name, int type, int dclass, long ttl, String s, Name origin) throws IOException {
return fromString(name, type, dclass, ttl, new Tokenizer(s), origin);
}
/**
* Returns the record's name
*
* @see Name
*/
public
Name getName() {
return name;
}
/**
* Returns the record's type
*
* @see DnsRecordType
*/
public
int getType() {
return type;
}
/**
* Returns the record's class
*/
public
int getDClass() {
return dclass;
}
/**
* Returns the record's TTL
*/
public
long getTTL() {
return ttl;
}
/* Sets the TTL to the specified value. This is intentionally not public. EDIT: public now so we can change it if we want to */
public
void setTTL(long ttl) {
this.ttl = ttl;
}
/**
* Determines if two Records could be part of the same RRset.
* This compares the name, type, and class of the Records; the ttl and
* rdata are not compared.
*/
public
boolean sameRRset(DnsRecord rec) {
return (getRRsetType() == rec.getRRsetType() && dclass == rec.dclass && name.equals(rec.name));
}
/**
* Returns the type of RRset that this record would belong to. For all types
* except RRSIG, this is equivalent to getType().
*
* @return The type of record, if not RRSIG. If the type is RRSIG,
* the type covered is returned.
*
* @see DnsRecordType
* @see RRset
* @see SIGRecord
*/
public
int getRRsetType() {
if (type == DnsRecordType.RRSIG) {
RRSIGRecord sig = (RRSIGRecord) this;
return sig.getTypeCovered();
}
return type;
}
/**
* Generates a hash code based on the Record's data.
*/
public
int hashCode() {
byte[] array = toWireCanonical(true);
int code = 0;
for (int i = 0; i < array.length; i++) {
code += ((code << 3) + (array[i] & 0xFF));
}
return code;
}
/**
* Determines if two Records are identical. This compares the name, type,
* class, and rdata (with names canonicalized). The TTLs are not compared.
*
* @param arg The record to compare to
*
* @return true if the records are equal, false otherwise.
*/
public
boolean equals(Object arg) {
if (arg == null || !(arg instanceof DnsRecord)) {
return false;
}
DnsRecord r = (DnsRecord) arg;
if (type != r.type || dclass != r.dclass || !name.equals(r.name)) {
return false;
}
byte[] array1 = rdataToWireCanonical();
byte[] array2 = r.rdataToWireCanonical();
return Arrays.equals(array1, array2);
}
/**
* Converts the rdata in a Record into canonical DNS uncompressed wire format
* (all names are converted to lowercase).
*/
public
byte[] rdataToWireCanonical() {
DnsOutput out = new DnsOutput();
rrToWire(out, null, true);
return out.toByteArray();
}
/**
* Converts a Record into a String representation
*/
@Override
public final
String toString() {
StringBuilder sb = new StringBuilder();
toString(sb);
return sb.toString();
}
/**
* Converts a Record into a String representation in a StringBuilder
*/
public
void toString(StringBuilder sb) {
sb.append(name);
if (sb.length() < 8) {
sb.append("\t");
}
if (sb.length() < 16) {
sb.append("\t");
}
sb.append("\t");
if (Options.check("BINDTTL")) {
sb.append(TTL.format(ttl));
}
else {
sb.append(ttl);
}
sb.append("\t");
if (dclass != DnsClass.IN || !Options.check("noPrintIN")) {
sb.append(DnsClass.string(dclass));
sb.append("\t");
}
sb.append(DnsRecordType.string(type));
sb.append("\t");
int length = sb.length();
rrToString(sb);
if (length == sb.length()) {
// delete the /t since we had no record data
sb.deleteCharAt(length-1);
}
}
/**
* Creates a new record identical to the current record, but with a different
* name. This is most useful for replacing the name of a wildcard record.
*/
public
DnsRecord withName(Name name) {
if (!name.isAbsolute()) {
throw new RelativeNameException(name);
}
DnsRecord rec = cloneRecord();
rec.name = name;
return rec;
}
DnsRecord cloneRecord() {
try {
return (DnsRecord) clone();
} catch (CloneNotSupportedException e) {
throw new IllegalStateException();
}
}
/**
* Creates a new record identical to the current record, but with a different
* class and ttl. This is most useful for dynamic update.
*/
DnsRecord withDClass(int dclass, long ttl) {
DnsRecord rec = cloneRecord();
rec.dclass = dclass;
rec.ttl = ttl;
return rec;
}
/**
* Compares this Record to another Object.
*
* @param o The Object to be compared.
*
* @return The value 0 if the argument is a record equivalent to this record;
* a value less than 0 if the argument is less than this record in the
* canonical ordering, and a value greater than 0 if the argument is greater
* than this record in the canonical ordering. The canonical ordering
* is defined to compare by name, class, type, and rdata.
*
* @throws ClassCastException if the argument is not a Record.
*/
@Override
public
int compareTo(Object o) {
DnsRecord arg = (DnsRecord) o;
if (this == arg) {
return (0);
}
int n = name.compareTo(arg.name);
if (n != 0) {
return (n);
}
n = dclass - arg.dclass;
if (n != 0) {
return (n);
}
n = type - arg.type;
if (n != 0) {
return (n);
}
byte[] rdata1 = rdataToWireCanonical();
byte[] rdata2 = arg.rdataToWireCanonical();
for (int i = 0; i < rdata1.length && i < rdata2.length; i++) {
n = (rdata1[i] & 0xFF) - (rdata2[i] & 0xFF);
if (n != 0) {
return (n);
}
}
return (rdata1.length - rdata2.length);
}
/**
* Returns the name for which additional data processing should be done
* for this record. This can be used both for building responses and
* parsing responses.
*
* @return The name to used for additional data processing, or null if this
* record type does not require additional data processing.
*/
public
Name getAdditionalName() {
return null;
}
/* Checks that an int contains an unsigned 8 bit value */
static
int checkU8(String field, int val) {
if (val < 0 || val > 0xFF) {
throw new IllegalArgumentException("\"" + field + "\" " + val + " must be an unsigned 8 " + "bit value");
}
return val;
}
/* Checks that an int contains an unsigned 16 bit value */
static
int checkU16(String field, int val) {
if (val < 0 || val > 0xFFFF) {
throw new IllegalArgumentException("\"" + field + "\" " + val + " must be an unsigned 16 " + "bit value");
}
return val;
}
/* Checks that a long contains an unsigned 32 bit value */
static
long checkU32(String field, long val) {
if (val < 0 || val > 0xFFFFFFFFL) {
throw new IllegalArgumentException("\"" + field + "\" " + val + " must be an unsigned 32 " + "bit value");
}
return val;
}
/* Checks that a name is absolute */
static
Name checkName(String field, Name name) {
if (!name.isAbsolute()) {
throw new RelativeNameException(name);
}
return name;
}
static
byte[] checkByteArrayLength(String field, byte[] array, int maxLength) {
if (array.length > 0xFFFF) {
throw new IllegalArgumentException("\"" + field + "\" array " + "must have no more than " + maxLength + " elements");
}
byte[] out = new byte[array.length];
System.arraycopy(array, 0, out, 0, array.length);
return out;
}
}

View File

@ -0,0 +1,78 @@
package dorkbox.network.dns.records;
import dorkbox.network.dns.constants.DnsRecordType;
public
class DnsTypeProtoAssignment {
// this is so we don't have to make each type constructor public
public static
void assign(final DnsRecordType.TypeMnemonic types) {
types.add(DnsRecordType.A, "A", new ARecord());
types.add(DnsRecordType.NS, "NS", new NSRecord());
types.add(DnsRecordType.MD, "MD", new MDRecord());
types.add(DnsRecordType.MF, "MF", new MFRecord());
types.add(DnsRecordType.CNAME, "CNAME", new CNAMERecord());
types.add(DnsRecordType.SOA, "SOA", new SOARecord());
types.add(DnsRecordType.MB, "MB", new MBRecord());
types.add(DnsRecordType.MG, "MG", new MGRecord());
types.add(DnsRecordType.MR, "MR", new MRRecord());
types.add(DnsRecordType.NULL, "NULL", new NULLRecord());
types.add(DnsRecordType.WKS, "WKS", new WKSRecord());
types.add(DnsRecordType.PTR, "PTR", new PTRRecord());
types.add(DnsRecordType.HINFO, "HINFO", new HINFORecord());
types.add(DnsRecordType.MINFO, "MINFO", new MINFORecord());
types.add(DnsRecordType.MX, "MX", new MXRecord());
types.add(DnsRecordType.TXT, "TXT", new TXTRecord());
types.add(DnsRecordType.RP, "RP", new RPRecord());
types.add(DnsRecordType.AFSDB, "AFSDB", new AFSDBRecord());
types.add(DnsRecordType.X25, "X25", new X25Record());
types.add(DnsRecordType.ISDN, "ISDN", new ISDNRecord());
types.add(DnsRecordType.RT, "RT", new RTRecord());
types.add(DnsRecordType.NSAP, "NSAP", new NSAPRecord());
types.add(DnsRecordType.NSAP_PTR, "NSAP-PTR", new NSAP_PTRRecord());
types.add(DnsRecordType.SIG, "SIG", new SIGRecord());
types.add(DnsRecordType.KEY, "KEY", new KEYRecord());
types.add(DnsRecordType.PX, "PX", new PXRecord());
types.add(DnsRecordType.GPOS, "GPOS", new GPOSRecord());
types.add(DnsRecordType.AAAA, "AAAA", new AAAARecord());
types.add(DnsRecordType.LOC, "LOC", new LOCRecord());
types.add(DnsRecordType.NXT, "NXT", new NXTRecord());
types.add(DnsRecordType.EID, "EID");
types.add(DnsRecordType.NIMLOC, "NIMLOC");
types.add(DnsRecordType.SRV, "SRV", new SRVRecord());
types.add(DnsRecordType.ATMA, "ATMA");
types.add(DnsRecordType.NAPTR, "NAPTR", new NAPTRRecord());
types.add(DnsRecordType.KX, "KX", new KXRecord());
types.add(DnsRecordType.CERT, "CERT", new CERTRecord());
types.add(DnsRecordType.A6, "A6", new A6Record());
types.add(DnsRecordType.DNAME, "DNAME", new DNAMERecord());
types.add(DnsRecordType.OPT, "OPT", new OPTRecord());
types.add(DnsRecordType.APL, "APL", new APLRecord());
types.add(DnsRecordType.DS, "DS", new DSRecord());
types.add(DnsRecordType.SSHFP, "SSHFP", new SSHFPRecord());
types.add(DnsRecordType.IPSECKEY, "IPSECKEY", new IPSECKEYRecord());
types.add(DnsRecordType.RRSIG, "RRSIG", new RRSIGRecord());
types.add(DnsRecordType.NSEC, "NSEC", new NSECRecord());
types.add(DnsRecordType.DNSKEY, "DNSKEY", new DNSKEYRecord());
types.add(DnsRecordType.DHCID, "DHCID", new DHCIDRecord());
types.add(DnsRecordType.NSEC3, "NSEC3", new NSEC3Record());
types.add(DnsRecordType.NSEC3PARAM, "NSEC3PARAM", new NSEC3PARAMRecord());
types.add(DnsRecordType.TLSA, "TLSA", new TLSARecord());
types.add(DnsRecordType.SMIMEA, "SMIMEA", new SMIMEARecord());
types.add(DnsRecordType.SMIMEA, "HIP");
types.add(DnsRecordType.OPENPGPKEY, "OPENPGPKEY", new OPENPGPKEYRecord());
types.add(DnsRecordType.SPF, "SPF", new SPFRecord());
types.add(DnsRecordType.TKEY, "TKEY", new TKEYRecord());
types.add(DnsRecordType.TSIG, "TSIG", new TSIGRecord());
types.add(DnsRecordType.IXFR, "IXFR");
types.add(DnsRecordType.AXFR, "AXFR");
types.add(DnsRecordType.MAILB, "MAILB");
types.add(DnsRecordType.MAILA, "MAILA");
types.add(DnsRecordType.ANY, "ANY");
types.add(DnsRecordType.URI, "URI", new URIRecord());
types.add(DnsRecordType.CAA, "CAA", new CAARecord());
types.add(DnsRecordType.TA, "TA");
types.add(DnsRecordType.DLV, "DLV", new DLVRecord());
}
}

View File

@ -0,0 +1,236 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.util.Arrays;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.exceptions.WireParseException;
/**
* DNS extension options, as described in RFC 2671. The rdata of an OPT record
* is defined as a list of options; this represents a single option.
*
* @author Brian Wellington
* @author Ming Zhou &lt;mizhou@bnivideo.com&gt;, Beaumaris Networks
*/
public abstract
class EDNSOption {
private final int code;
public static
class Code {
/**
* Name Server Identifier, RFC 5001
*/
public final static int NSID = 3;
/**
* Client Subnet, defined in draft-vandergaast-edns-client-subnet-02
*/
public final static int CLIENT_SUBNET = 8;
private static Mnemonic codes = new Mnemonic("EDNS Option Codes", Mnemonic.CASE_UPPER);
private
Code() {}
static {
codes.setMaximum(0xFFFF);
codes.setPrefix("CODE");
codes.setNumericAllowed(true);
codes.add(NSID, "NSID");
codes.add(CLIENT_SUBNET, "CLIENT_SUBNET");
}
/**
* Converts an EDNS Option Code into its textual representation
*/
public static
String string(int code) {
return codes.getText(code);
}
/**
* Converts a textual representation of an EDNS Option Code into its
* numeric value.
*
* @param s The textual representation of the option code
*
* @return The option code, or -1 on error.
*/
public static
int value(String s) {
return codes.getValue(s);
}
}
/**
* Creates an option with the given option code and data.
*/
public
EDNSOption(int code) {
this.code = DnsRecord.checkU16("code", code);
}
/**
* Returns the EDNS Option's code.
*
* @return the option code
*/
public
int getCode() {
return code;
}
/**
* Converts the wire format of an EDNS Option (including code and length) into
* the type-specific format.
*
* @return The option, in wire format.
*/
public static
EDNSOption fromWire(byte[] b) throws IOException {
return fromWire(new DnsInput(b));
}
/**
* Converts the wire format of an EDNS Option (including code and length) into
* the type-specific format.
*
* @param in The input stream.
*/
static
EDNSOption fromWire(DnsInput in) throws IOException {
int code, length;
code = in.readU16();
length = in.readU16();
if (in.remaining() < length) {
throw new WireParseException("truncated option");
}
in.setActive(length);
EDNSOption option;
switch (code) {
case Code.NSID:
option = new NSIDOption();
break;
case Code.CLIENT_SUBNET:
option = new ClientSubnetOption();
break;
default:
option = new GenericEDNSOption(code);
break;
}
option.optionFromWire(in);
in.restoreActive();
return option;
}
/**
* Converts the wire format of an EDNS Option (the option data only) into the
* type-specific format.
*
* @param in The input Stream.
*/
abstract
void optionFromWire(DnsInput in) throws IOException;
/**
* Converts an EDNS Option (including code and length) into wire format.
*
* @return The option, in wire format.
*/
public
byte[] toWire() throws IOException {
DnsOutput out = new DnsOutput();
toWire(out);
return out.toByteArray();
}
/**
* Converts an EDNS Option (including code and length) into wire format.
*
* @param out The output stream.
*/
void toWire(DnsOutput out) {
out.writeU16(code);
int lengthPosition = out.current();
out.writeU16(0); /* until we know better */
optionToWire(out);
int length = out.current() - lengthPosition - 2;
out.writeU16At(length, lengthPosition);
}
/**
* Converts an EDNS Option (the type-specific option data only) into wire format.
*
* @param out The output stream.
*/
abstract
void optionToWire(DnsOutput out);
/**
* Generates a hash code based on the EDNS Option's data.
*/
public
int hashCode() {
byte[] array = getData();
int hashval = 0;
for (int i = 0; i < array.length; i++) {
hashval += ((hashval << 3) + (array[i] & 0xFF));
}
return hashval;
}
/**
* Determines if two EDNS Options are identical.
*
* @param arg The option to compare to
*
* @return true if the options are equal, false otherwise.
*/
public
boolean equals(Object arg) {
if (arg == null || !(arg instanceof EDNSOption)) {
return false;
}
EDNSOption opt = (EDNSOption) arg;
if (code != opt.code) {
return false;
}
return Arrays.equals(getData(), opt.getData());
}
public
String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(EDNSOption.Code.string(code));
sb.append(": ");
sb.append(optionToString());
sb.append("}");
return sb.toString();
}
abstract
String optionToString();
/**
* Returns the EDNS Option's data, as a byte array.
*
* @return the option data
*/
byte[] getData() {
DnsOutput out = new DnsOutput();
optionToWire(out);
return out.toByteArray();
}
}

View File

@ -0,0 +1,47 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.utils.Tokenizer;
/**
* A class implementing Records with no data; that is, records used in
* the question section of messages and meta-records in dynamic update.
*
* @author Brian Wellington
*/
class EmptyRecord extends DnsRecord {
private static final long serialVersionUID = 3601852050646429582L;
EmptyRecord() {}
@Override
DnsRecord getObject() {
return new EmptyRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
}
@Override
void rrToString(StringBuilder sb) {
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Mnemonic;
/**
* Constants and functions relating to EDNS flags.
*
* @author Brian Wellington
*/
public final
class ExtendedFlags {
private static Mnemonic extflags = new Mnemonic("EDNS Flag", Mnemonic.CASE_LOWER);
/**
* dnssec ok
*/
public static final int DO = 0x8000;
static {
extflags.setMaximum(0xFFFF);
extflags.setPrefix("FLAG");
extflags.setNumericAllowed(true);
extflags.add(DO, "do");
}
private
ExtendedFlags() {}
/**
* Converts a numeric extended flag into a String
*/
public static
String string(int i) {
return extflags.getText(i);
}
/**
* Converts a textual representation of an extended flag into its numeric
* value
*/
public static
int value(String s) {
return extflags.getValue(s);
}
}

View File

@ -0,0 +1,191 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Geographical Location - describes the physical location of a host.
*
* @author Brian Wellington
*/
public
class GPOSRecord extends DnsRecord {
private static final long serialVersionUID = -6349714958085750705L;
private byte[] latitude, longitude, altitude;
GPOSRecord() {}
@Override
DnsRecord getObject() {
return new GPOSRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
longitude = in.readCountedString();
latitude = in.readCountedString();
altitude = in.readCountedString();
try {
validate(getLongitude(), getLatitude());
} catch (IllegalArgumentException e) {
throw new WireParseException(e.getMessage());
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeCountedString(longitude);
out.writeCountedString(latitude);
out.writeCountedString(altitude);
}
/**
* Convert to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(byteArrayToString(longitude, true));
sb.append(" ");
sb.append(byteArrayToString(latitude, true));
sb.append(" ");
sb.append(byteArrayToString(altitude, true));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
try {
longitude = byteArrayFromString(st.getString());
latitude = byteArrayFromString(st.getString());
altitude = byteArrayFromString(st.getString());
} catch (TextParseException e) {
throw st.exception(e.getMessage());
}
try {
validate(getLongitude(), getLatitude());
} catch (IllegalArgumentException e) {
throw new WireParseException(e.getMessage());
}
}
/**
* Creates an GPOS Record from the given data
*
* @param longitude The longitude component of the location.
* @param latitude The latitude component of the location.
* @param altitude The altitude component of the location (in meters above sea
* level).
*/
public
GPOSRecord(Name name, int dclass, long ttl, double longitude, double latitude, double altitude) {
super(name, DnsRecordType.GPOS, dclass, ttl);
validate(longitude, latitude);
this.longitude = Double.toString(longitude)
.getBytes();
this.latitude = Double.toString(latitude)
.getBytes();
this.altitude = Double.toString(altitude)
.getBytes();
}
private
void validate(double longitude, double latitude) throws IllegalArgumentException {
if (longitude < -90.0 || longitude > 90.0) {
throw new IllegalArgumentException("illegal longitude " + longitude);
}
if (latitude < -180.0 || latitude > 180.0) {
throw new IllegalArgumentException("illegal latitude " + latitude);
}
}
/**
* Creates an GPOS Record from the given data
*
* @param longitude The longitude component of the location.
* @param latitude The latitude component of the location.
* @param altitude The altitude component of the location (in meters above sea
* level).
*/
public
GPOSRecord(Name name, int dclass, long ttl, String longitude, String latitude, String altitude) {
super(name, DnsRecordType.GPOS, dclass, ttl);
try {
this.longitude = byteArrayFromString(longitude);
this.latitude = byteArrayFromString(latitude);
validate(getLongitude(), getLatitude());
this.altitude = byteArrayFromString(altitude);
} catch (TextParseException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
/**
* Returns the longitude as a double
*
* @throws NumberFormatException The string does not contain a valid numeric
* value.
*/
public
double getLongitude() {
return Double.parseDouble(getLongitudeString());
}
/**
* Returns the longitude as a string
*/
public
String getLongitudeString() {
return byteArrayToString(longitude, false);
}
/**
* Returns the latitude as a double
*
* @throws NumberFormatException The string does not contain a valid numeric
* value.
*/
public
double getLatitude() {
return Double.parseDouble(getLatitudeString());
}
/**
* Returns the latitude as a string
*/
public
String getLatitudeString() {
return byteArrayToString(latitude, false);
}
/**
* Returns the altitude as a double
*
* @throws NumberFormatException The string does not contain a valid numeric
* value.
*/
public
double getAltitude() {
return Double.parseDouble(getAltitudeString());
}
/**
* Returns the altitude as a string
*/
public
String getAltitudeString() {
return byteArrayToString(altitude, false);
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.utils.base16;
/**
* An EDNSOption with no internal structure.
*
* @author Ming Zhou &lt;mizhou@bnivideo.com&gt;, Beaumaris Networks
* @author Brian Wellington
*/
public
class GenericEDNSOption extends EDNSOption {
private byte[] data;
GenericEDNSOption(int code) {
super(code);
}
/**
* Construct a generic EDNS option.
*
* @param data The contents of the option.
*/
public
GenericEDNSOption(int code, byte[] data) {
super(code);
this.data = DnsRecord.checkByteArrayLength("option data", data, 0xFFFF);
}
@Override
void optionFromWire(DnsInput in) throws IOException {
data = in.readByteArray();
}
@Override
void optionToWire(DnsOutput out) {
out.writeByteArray(data);
}
@Override
String optionToString() {
return "<" + base16.toString(data) + ">";
}
}

View File

@ -0,0 +1,102 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Host Information - describes the CPU and OS of a host
*
* @author Brian Wellington
*/
public
class HINFORecord extends DnsRecord {
private static final long serialVersionUID = -4732870630947452112L;
private byte[] cpu, os;
HINFORecord() {}
@Override
DnsRecord getObject() {
return new HINFORecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
cpu = in.readCountedString();
os = in.readCountedString();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeCountedString(cpu);
out.writeCountedString(os);
}
/**
* Converts to a string
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(byteArrayToString(cpu, true));
sb.append(" ");
sb.append(byteArrayToString(os, true));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
try {
cpu = byteArrayFromString(st.getString());
os = byteArrayFromString(st.getString());
} catch (TextParseException e) {
throw st.exception(e.getMessage());
}
}
/**
* Creates an HINFO Record from the given data
*
* @param cpu A string describing the host's CPU
* @param os A string describing the host's OS
*
* @throws IllegalArgumentException One of the strings has invalid escapes
*/
public
HINFORecord(Name name, int dclass, long ttl, String cpu, String os) {
super(name, DnsRecordType.HINFO, dclass, ttl);
try {
this.cpu = byteArrayFromString(cpu);
this.os = byteArrayFromString(os);
} catch (TextParseException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
/**
* Returns the host's CPU
*/
public
String getCPU() {
return byteArrayToString(cpu, false);
}
/**
* Returns the host's OS
*/
public
String getOS() {
return byteArrayToString(os, false);
}
}

View File

@ -0,0 +1,343 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.constants.DnsOpCode;
import dorkbox.network.dns.constants.DnsResponseCode;
import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.constants.Flags;
import dorkbox.util.FastThreadLocal;
import dorkbox.util.MersenneTwisterFast;
import dorkbox.util.OS;
/**
* A DNS message header
*
* @author Brian Wellington
* @see DnsMessage
*/
public
class Header implements Cloneable {
private int id;
private int flags;
private int[] counts;
private static final
FastThreadLocal<MersenneTwisterFast> random = new FastThreadLocal<MersenneTwisterFast>() {
@Override
public
MersenneTwisterFast initialValue() {
return new MersenneTwisterFast();
}
};
/**
* The length of a DNS Header in wire format.
*/
public static final int LENGTH = 12;
/**
* Create a new empty header with a random message id
*/
public
Header() {
init();
}
private
void init() {
counts = new int[4];
flags = 0;
id = -1;
}
/**
* Creates a new Header from its DNS wire format representation
*
* @param b A byte array containing the DNS Header.
*/
public
Header(byte[] b) throws IOException {
this(new DnsInput(b));
}
/**
* Parses a Header from a stream containing DNS wire format.
*/
Header(DnsInput in) throws IOException {
this(in.readU16());
flags = in.readU16();
for (int i = 0; i < counts.length; i++) {
counts[i] = in.readU16();
}
}
/**
* Create a new empty header.
*
* @param id The message id
*/
public
Header(int id) {
init();
setID(id);
}
public
byte[] toWire() {
DnsOutput out = new DnsOutput();
toWire(out);
return out.toByteArray();
}
void toWire(DnsOutput out) {
out.writeU16(getID());
out.writeU16(flags);
for (int i = 0; i < counts.length; i++) {
out.writeU16(counts[i]);
}
}
/**
* Retrieves the message ID
*/
public
int getID() {
if (id >= 0) {
return id;
}
synchronized (this) {
if (id < 0) {
id = random.get().nextInt(0xffff);
}
return id;
}
}
/**
* Sets the message ID
*/
public
void setID(int id) {
if (id < 0 || id > 0xffff) {
throw new IllegalArgumentException("DNS message ID " + id + " is out of range");
}
this.id = id;
}
/**
* Sets a flag to the supplied value
*
* @see Flags
*/
public
void setFlag(int bit) {
checkFlag(bit);
flags = setFlag(flags, bit, true);
}
static private
void checkFlag(int bit) {
if (!validFlag(bit)) {
throw new IllegalArgumentException("invalid flag bit " + bit);
}
}
static private
boolean validFlag(int bit) {
return (bit >= 0 && bit <= 0xF && Flags.isFlag(bit));
}
static
int setFlag(int flags, int bit, boolean value) {
checkFlag(bit);
// bits are indexed from left to right
if (value) {
return flags |= (1 << (15 - bit));
}
else {
return flags &= ~(1 << (15 - bit));
}
}
/**
* Sets a flag to the supplied value
*
* @see Flags
*/
public
void unsetFlag(int bit) {
checkFlag(bit);
flags = setFlag(flags, bit, false);
}
boolean[] getFlags() {
boolean[] array = new boolean[16];
for (int i = 0; i < array.length; i++) {
if (validFlag(i)) {
array[i] = getFlag(i);
}
}
return array;
}
/**
* Retrieves a flag
*
* @see Flags
*/
public
boolean getFlag(int bit) {
checkFlag(bit);
// bits are indexed from left to right
return (flags & (1 << (15 - bit))) != 0;
}
void setCount(int field, int value) {
if (value < 0 || value > 0xFFFF) {
throw new IllegalArgumentException("DNS section count " + value + " is out of range");
}
counts[field] = value;
}
void incCount(int field) {
if (counts[field] == 0xFFFF) {
throw new IllegalStateException("DNS section count cannot " + "be incremented");
}
counts[field]++;
}
void decCount(int field) {
if (counts[field] == 0) {
throw new IllegalStateException("DNS section count cannot " + "be decremented");
}
counts[field]--;
}
int getFlagsByte() {
return flags;
}
/* Creates a new Header identical to the current one */
@Override
public
Object clone() {
Header h = new Header();
h.id = id;
h.flags = flags;
System.arraycopy(counts, 0, h.counts, 0, counts.length);
return h;
}
/**
* Converts the header into a String
*/
public
String toString() {
return toStringWithRcode(getRcode());
}
/**
* Retrieves the mesasge's rcode
*
* @see DnsResponseCode
*/
public
int getRcode() {
return flags & 0xF;
}
/**
* Sets the message's rcode
*
* @see DnsResponseCode
*/
public
void setRcode(int value) {
if (value < 0 || value > 0xF) {
throw new IllegalArgumentException("DNS DnsResponseCode " + value + " is out of range");
}
flags &= ~0xF;
flags |= value;
}
String toStringWithRcode(int newrcode) {
StringBuilder sb = new StringBuilder();
sb.append(";; ->>HEADER<<- ");
sb.append("opcode: " + DnsOpCode.string(getOpcode()));
sb.append(", status: " + DnsResponseCode.string(newrcode));
sb.append(", id: " + getID());
sb.append(OS.LINE_SEPARATOR);
sb.append(";; flags: ")
.append(printFlags());
sb.append("; ");
for (int i = 0; i < 4; i++) {
sb.append(DnsSection.string(i))
.append(": ")
.append(getCount(i))
.append(" ");
}
return sb.toString();
}
/**
* Retrieves the mesasge's opcode
*
* @see DnsOpCode
*/
public
int getOpcode() {
return (flags >> 11) & 0xF;
}
/**
* Sets the message's opcode
*
* @see DnsOpCode
*/
public
void setOpcode(int value) {
if (value < 0 || value > 0xF) {
throw new IllegalArgumentException("DNS DnsOpCode " + value + "is out of range");
}
flags &= 0x87FF;
flags |= (value << 11);
}
/**
* Retrieves the record count for the given section
*
* @see DnsSection
*/
public
int getCount(int field) {
return counts[field];
}
/**
* Converts the header's flags into a String
*/
public
String printFlags() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++) {
if (validFlag(i) && getFlag(i)) {
sb.append(Flags.string(i));
sb.append(" ");
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,253 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Address;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.Base64Fast;
/**
* IPsec Keying Material (RFC 4025)
*
* @author Brian Wellington
*/
public
class IPSECKEYRecord extends DnsRecord {
private static final long serialVersionUID = 3050449702765909687L;
private int precedence;
private int gatewayType;
private int algorithmType;
private Object gateway;
private byte[] key;
public static
class Algorithm {
public static final int DSA = 1;
public static final int RSA = 2;
private
Algorithm() {}
}
public static
class Gateway {
public static final int None = 0;
public static final int IPv4 = 1;
public static final int IPv6 = 2;
public static final int Name = 3;
private
Gateway() {}
}
IPSECKEYRecord() {}
@Override
DnsRecord getObject() {
return new IPSECKEYRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
precedence = in.readU8();
gatewayType = in.readU8();
algorithmType = in.readU8();
switch (gatewayType) {
case Gateway.None:
gateway = null;
break;
case Gateway.IPv4:
gateway = InetAddress.getByAddress(in.readByteArray(4));
break;
case Gateway.IPv6:
gateway = InetAddress.getByAddress(in.readByteArray(16));
break;
case Gateway.Name:
gateway = new Name(in);
break;
default:
throw new WireParseException("invalid gateway type");
}
if (in.remaining() > 0) {
key = in.readByteArray();
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(precedence);
out.writeU8(gatewayType);
out.writeU8(algorithmType);
switch (gatewayType) {
case Gateway.None:
break;
case Gateway.IPv4:
case Gateway.IPv6:
InetAddress gatewayAddr = (InetAddress) gateway;
out.writeByteArray(gatewayAddr.getAddress());
break;
case Gateway.Name:
Name gatewayName = (Name) gateway;
gatewayName.toWire(out, null, canonical);
break;
}
if (key != null) {
out.writeByteArray(key);
}
}
@Override
void rrToString(StringBuilder sb) {
sb.append(precedence);
sb.append(" ");
sb.append(gatewayType);
sb.append(" ");
sb.append(algorithmType);
sb.append(" ");
switch (gatewayType) {
case Gateway.None:
sb.append(".");
break;
case Gateway.IPv4:
case Gateway.IPv6:
InetAddress gatewayAddr = (InetAddress) gateway;
sb.append(gatewayAddr.getHostAddress());
break;
case Gateway.Name:
sb.append(gateway);
break;
}
if (key != null) {
sb.append(" ");
sb.append(Base64Fast.encode2(key));
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
precedence = st.getUInt8();
gatewayType = st.getUInt8();
algorithmType = st.getUInt8();
switch (gatewayType) {
case Gateway.None:
String s = st.getString();
if (!s.equals(".")) {
throw new TextParseException("invalid gateway format");
}
gateway = null;
break;
case Gateway.IPv4:
gateway = st.getAddress(Address.IPv4);
break;
case Gateway.IPv6:
gateway = st.getAddress(Address.IPv6);
break;
case Gateway.Name:
gateway = st.getName(origin);
break;
default:
throw new WireParseException("invalid gateway type");
}
key = st.getBase64(false);
}
/**
* Creates an IPSECKEY Record from the given data.
*
* @param precedence The record's precedence.
* @param gatewayType The record's gateway type.
* @param algorithmType The record's algorithm type.
* @param gateway The record's gateway.
* @param key The record's public key.
*/
public
IPSECKEYRecord(Name name, int dclass, long ttl, int precedence, int gatewayType, int algorithmType, Object gateway, byte[] key) {
super(name, DnsRecordType.IPSECKEY, dclass, ttl);
this.precedence = checkU8("precedence", precedence);
this.gatewayType = checkU8("gatewayType", gatewayType);
this.algorithmType = checkU8("algorithmType", algorithmType);
switch (gatewayType) {
case Gateway.None:
this.gateway = null;
break;
case Gateway.IPv4:
if (!(gateway instanceof InetAddress)) {
throw new IllegalArgumentException("\"gateway\" " + "must be an IPv4 " + "address");
}
this.gateway = gateway;
break;
case Gateway.IPv6:
if (!(gateway instanceof Inet6Address)) {
throw new IllegalArgumentException("\"gateway\" " + "must be an IPv6 " + "address");
}
this.gateway = gateway;
break;
case Gateway.Name:
if (!(gateway instanceof Name)) {
throw new IllegalArgumentException("\"gateway\" " + "must be a DNS " + "name");
}
this.gateway = checkName("gateway", (Name) gateway);
break;
default:
throw new IllegalArgumentException("\"gatewayType\" " + "must be between 0 and 3");
}
this.key = key;
}
/**
* Returns the record's precedence.
*/
public
int getPrecedence() {
return precedence;
}
/**
* Returns the record's gateway type.
*/
public
int getGatewayType() {
return gatewayType;
}
/**
* Returns the record's algorithm type.
*/
public
int getAlgorithmType() {
return algorithmType;
}
/**
* Returns the record's gateway.
*/
public
Object getGateway() {
return gateway;
}
/**
* Returns the record's public key
*/
public
byte[] getKey() {
return key;
}
}

View File

@ -0,0 +1,118 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.utils.Tokenizer;
/**
* ISDN - identifies the ISDN number and subaddress associated with a name.
*
* @author Brian Wellington
*/
public
class ISDNRecord extends DnsRecord {
private static final long serialVersionUID = -8730801385178968798L;
private byte[] address;
private byte[] subAddress;
ISDNRecord() {}
@Override
DnsRecord getObject() {
return new ISDNRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
address = in.readCountedString();
if (in.remaining() > 0) {
subAddress = in.readCountedString();
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeCountedString(address);
if (subAddress != null) {
out.writeCountedString(subAddress);
}
}
@Override
void rrToString(StringBuilder sb) {
sb.append(byteArrayToString(address, true));
if (subAddress != null) {
sb.append(" ");
sb.append(byteArrayToString(subAddress, true));
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
try {
address = byteArrayFromString(st.getString());
Tokenizer.Token t = st.get();
if (t.isString()) {
subAddress = byteArrayFromString(t.value);
}
else {
st.unget();
}
} catch (TextParseException e) {
throw st.exception(e.getMessage());
}
}
/**
* Creates an ISDN Record from the given data
*
* @param address The ISDN number associated with the domain.
* @param subAddress The subaddress, if any.
*
* @throws IllegalArgumentException One of the strings is invalid.
*/
public
ISDNRecord(Name name, int dclass, long ttl, String address, String subAddress) {
super(name, DnsRecordType.ISDN, dclass, ttl);
try {
this.address = byteArrayFromString(address);
if (subAddress != null) {
this.subAddress = byteArrayFromString(subAddress);
}
} catch (TextParseException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
/**
* Returns the ISDN number associated with the domain.
*/
public
String getAddress() {
return byteArrayToString(address, false);
}
/**
* Returns the ISDN subaddress, or null if there is none.
*/
public
String getSubAddress() {
if (subAddress == null) {
return null;
}
return byteArrayToString(subAddress, false);
}
}

View File

@ -0,0 +1,175 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.security.PublicKey;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.utils.Options;
import dorkbox.util.Base64Fast;
import dorkbox.util.OS;
/**
* The base class for KEY/DNSKEY records, which have identical formats
*
* @author Brian Wellington
*/
abstract
class KEYBase extends DnsRecord {
private static final long serialVersionUID = 3469321722693285454L;
protected int flags, proto, alg;
protected byte[] key;
protected int footprint = -1;
protected PublicKey publicKey = null;
protected
KEYBase() {}
public
KEYBase(Name name, int type, int dclass, long ttl, int flags, int proto, int alg, byte[] key) {
super(name, type, dclass, ttl);
this.flags = checkU16("flags", flags);
this.proto = checkU8("proto", proto);
this.alg = checkU8("alg", alg);
this.key = key;
}
@Override
void rrFromWire(DnsInput in) throws IOException {
flags = in.readU16();
proto = in.readU8();
alg = in.readU8();
if (in.remaining() > 0) {
key = in.readByteArray();
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(flags);
out.writeU8(proto);
out.writeU8(alg);
if (key != null) {
out.writeByteArray(key);
}
}
/**
* Converts the DNSKEY/KEY Record to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(flags);
sb.append(" ");
sb.append(proto);
sb.append(" ");
sb.append(alg);
if (key != null) {
if (Options.check("multiline")) {
sb.append(" (")
.append(OS.LINE_SEPARATOR);
sb.append(Base64Fast.formatString(Base64Fast.encode2(key), 64, "\t", true));
sb.append(" ; key_tag = ");
sb.append(getFootprint());
}
else {
sb.append(" ");
sb.append(Base64Fast.encode2(key));
}
}
}
/**
* Returns the key's footprint (after computing it)
*/
public
int getFootprint() {
if (footprint >= 0) {
return footprint;
}
int foot = 0;
DnsOutput out = new DnsOutput();
rrToWire(out, null, false);
byte[] rdata = out.toByteArray();
if (alg == DNSSEC.Algorithm.RSAMD5) {
int d1 = rdata[rdata.length - 3] & 0xFF;
int d2 = rdata[rdata.length - 2] & 0xFF;
foot = (d1 << 8) + d2;
}
else {
int i;
for (i = 0; i < rdata.length - 1; i += 2) {
int d1 = rdata[i] & 0xFF;
int d2 = rdata[i + 1] & 0xFF;
foot += ((d1 << 8) + d2);
}
if (i < rdata.length) {
int d1 = rdata[i] & 0xFF;
foot += (d1 << 8);
}
foot += ((foot >> 16) & 0xFFFF);
}
footprint = (foot & 0xFFFF);
return footprint;
}
/**
* Returns the flags describing the key's properties
*/
public
int getFlags() {
return flags;
}
/**
* Returns the protocol that the key was created for
*/
public
int getProtocol() {
return proto;
}
/**
* Returns the key's algorithm
*/
public
int getAlgorithm() {
return alg;
}
/**
* Returns the binary data representing the key
*/
public
byte[] getKey() {
return key;
}
/**
* Returns a PublicKey corresponding to the data in this key.
*
* @throws DNSSEC.DNSSECException The key could not be converted.
*/
public
PublicKey getPublicKey() throws DNSSEC.DNSSECException {
if (publicKey != null) {
return publicKey;
}
publicKey = DNSSEC.toPublicKey(this);
return publicKey;
}
}

View File

@ -0,0 +1,421 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.security.PublicKey;
import java.util.StringTokenizer;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Key - contains a cryptographic public key. The data can be converted
* to objects implementing java.security.interfaces.PublicKey
*
* @author Brian Wellington
* @see DNSSEC
*/
public
class KEYRecord extends KEYBase {
private static final long serialVersionUID = 6385613447571488906L;
/**
* This key cannot be used for confidentiality (encryption)
*/
public static final int FLAG_NOCONF = Flags.NOCONF;
/**
* This key cannot be used for authentication
*/
public static final int FLAG_NOAUTH = Flags.NOAUTH;
/* flags */
/**
* This key cannot be used for authentication or confidentiality
*/
public static final int FLAG_NOKEY = Flags.NOKEY;
/**
* A zone key
*/
public static final int OWNER_ZONE = Flags.ZONE;
/**
* A host/end entity key
*/
public static final int OWNER_HOST = Flags.HOST;
/**
* A user key
*/
public static final int OWNER_USER = Flags.USER;
/**
* Key was created for use with transaction level security
*/
public static final int PROTOCOL_TLS = Protocol.TLS;
/**
* Key was created for use with email
*/
public static final int PROTOCOL_EMAIL = Protocol.EMAIL;
/* protocols */
/**
* Key was created for use with DNSSEC
*/
public static final int PROTOCOL_DNSSEC = Protocol.DNSSEC;
/**
* Key was created for use with IPSEC
*/
public static final int PROTOCOL_IPSEC = Protocol.IPSEC;
/**
* Key was created for use with any protocol
*/
public static final int PROTOCOL_ANY = Protocol.ANY;
public static
class Protocol {
/**
* No defined protocol.
*/
public static final int NONE = 0;
/**
* Transaction Level Security
*/
public static final int TLS = 1;
/**
* Email
*/
public static final int EMAIL = 2;
/**
* DNSSEC
*/
public static final int DNSSEC = 3;
/**
* IPSEC Control
*/
public static final int IPSEC = 4;
/**
* Any protocol
*/
public static final int ANY = 255;
private static Mnemonic protocols = new Mnemonic("KEY protocol", Mnemonic.CASE_UPPER);
/**
* KEY protocol identifiers.
*/
private
Protocol() {}
static {
protocols.setMaximum(0xFF);
protocols.setNumericAllowed(true);
protocols.add(NONE, "NONE");
protocols.add(TLS, "TLS");
protocols.add(EMAIL, "EMAIL");
protocols.add(DNSSEC, "DNSSEC");
protocols.add(IPSEC, "IPSEC");
protocols.add(ANY, "ANY");
}
/**
* Converts an KEY protocol value into its textual representation
*/
public static
String string(int type) {
return protocols.getText(type);
}
/**
* Converts a textual representation of a KEY protocol into its
* numeric code. Integers in the range 0..255 are also accepted.
*
* @param s The textual representation of the protocol
*
* @return The protocol code, or -1 on error.
*/
public static
int value(String s) {
return protocols.getValue(s);
}
}
public static
class Flags {
/**
* KEY cannot be used for confidentiality
*/
public static final int NOCONF = 0x4000;
/**
* KEY cannot be used for authentication
*/
public static final int NOAUTH = 0x8000;
/**
* No key present
*/
public static final int NOKEY = 0xC000;
/**
* Bitmask of the use fields
*/
public static final int USE_MASK = 0xC000;
/**
* Flag 2 (unused)
*/
public static final int FLAG2 = 0x2000;
/**
* Flags extension
*/
public static final int EXTEND = 0x1000;
/**
* Flag 4 (unused)
*/
public static final int FLAG4 = 0x0800;
/**
* Flag 5 (unused)
*/
public static final int FLAG5 = 0x0400;
/**
* Key is owned by a user.
*/
public static final int USER = 0x0000;
/**
* Key is owned by a zone.
*/
public static final int ZONE = 0x0100;
/**
* Key is owned by a host.
*/
public static final int HOST = 0x0200;
/**
* Key owner type 3 (reserved).
*/
public static final int NTYP3 = 0x0300;
/**
* Key owner bitmask.
*/
public static final int OWNER_MASK = 0x0300;
/**
* Flag 8 (unused)
*/
public static final int FLAG8 = 0x0080;
/**
* Flag 9 (unused)
*/
public static final int FLAG9 = 0x0040;
/**
* Flag 10 (unused)
*/
public static final int FLAG10 = 0x0020;
/**
* Flag 11 (unused)
*/
public static final int FLAG11 = 0x0010;
/**
* Signatory value 0
*/
public static final int SIG0 = 0;
/**
* Signatory value 1
*/
public static final int SIG1 = 1;
/**
* Signatory value 2
*/
public static final int SIG2 = 2;
/**
* Signatory value 3
*/
public static final int SIG3 = 3;
/**
* Signatory value 4
*/
public static final int SIG4 = 4;
/**
* Signatory value 5
*/
public static final int SIG5 = 5;
/**
* Signatory value 6
*/
public static final int SIG6 = 6;
/**
* Signatory value 7
*/
public static final int SIG7 = 7;
/**
* Signatory value 8
*/
public static final int SIG8 = 8;
/**
* Signatory value 9
*/
public static final int SIG9 = 9;
/**
* Signatory value 10
*/
public static final int SIG10 = 10;
/**
* Signatory value 11
*/
public static final int SIG11 = 11;
/**
* Signatory value 12
*/
public static final int SIG12 = 12;
/**
* Signatory value 13
*/
public static final int SIG13 = 13;
/**
* Signatory value 14
*/
public static final int SIG14 = 14;
/**
* Signatory value 15
*/
public static final int SIG15 = 15;
private static Mnemonic flags = new Mnemonic("KEY flags", Mnemonic.CASE_UPPER);
/**
* KEY flags identifiers.
*/
private
Flags() {}
static {
flags.setMaximum(0xFFFF);
flags.setNumericAllowed(false);
flags.add(NOCONF, "NOCONF");
flags.add(NOAUTH, "NOAUTH");
flags.add(NOKEY, "NOKEY");
flags.add(FLAG2, "FLAG2");
flags.add(EXTEND, "EXTEND");
flags.add(FLAG4, "FLAG4");
flags.add(FLAG5, "FLAG5");
flags.add(USER, "USER");
flags.add(ZONE, "ZONE");
flags.add(HOST, "HOST");
flags.add(NTYP3, "NTYP3");
flags.add(FLAG8, "FLAG8");
flags.add(FLAG9, "FLAG9");
flags.add(FLAG10, "FLAG10");
flags.add(FLAG11, "FLAG11");
flags.add(SIG0, "SIG0");
flags.add(SIG1, "SIG1");
flags.add(SIG2, "SIG2");
flags.add(SIG3, "SIG3");
flags.add(SIG4, "SIG4");
flags.add(SIG5, "SIG5");
flags.add(SIG6, "SIG6");
flags.add(SIG7, "SIG7");
flags.add(SIG8, "SIG8");
flags.add(SIG9, "SIG9");
flags.add(SIG10, "SIG10");
flags.add(SIG11, "SIG11");
flags.add(SIG12, "SIG12");
flags.add(SIG13, "SIG13");
flags.add(SIG14, "SIG14");
flags.add(SIG15, "SIG15");
}
/**
* Converts a textual representation of KEY flags into its
* numeric code. Integers in the range 0..65535 are also accepted.
*
* @param s The textual representation of the protocol
*
* @return The protocol code, or -1 on error.
*/
public static
int value(String s) {
int value;
try {
value = Integer.parseInt(s);
if (value >= 0 && value <= 0xFFFF) {
return value;
}
return -1;
} catch (NumberFormatException e) {
}
StringTokenizer st = new StringTokenizer(s, "|");
value = 0;
while (st.hasMoreTokens()) {
int val = flags.getValue(st.nextToken());
if (val < 0) {
return -1;
}
value |= val;
}
return value;
}
}
KEYRecord() {}
@Override
DnsRecord getObject() {
return new KEYRecord();
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
String flagString = st.getIdentifier();
flags = Flags.value(flagString);
if (flags < 0) {
throw st.exception("Invalid flags: " + flagString);
}
String protoString = st.getIdentifier();
proto = Protocol.value(protoString);
if (proto < 0) {
throw st.exception("Invalid protocol: " + protoString);
}
String algString = st.getIdentifier();
alg = DNSSEC.Algorithm.value(algString);
if (alg < 0) {
throw st.exception("Invalid algorithm: " + algString);
}
/* If this is a null KEY, there's no key data */
if ((flags & Flags.USE_MASK) == Flags.NOKEY) {
key = null;
}
else {
key = st.getBase64();
}
}
/**
* Creates a KEY Record from the given data
*
* @param flags Flags describing the key's properties
* @param proto The protocol that the key was created for
* @param alg The key's algorithm
* @param key Binary data representing the key
*/
public
KEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg, byte[] key) {
super(name, DnsRecordType.KEY, dclass, ttl, flags, proto, alg, key);
}
/**
* Creates a KEY Record from the given data
*
* @param flags Flags describing the key's properties
* @param proto The protocol that the key was created for
* @param alg The key's algorithm
* @param key The key as a PublicKey
*
* @throws DNSSEC.DNSSECException The PublicKey could not be converted into DNS
* format.
*/
public
KEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg, PublicKey key) throws DNSSEC.DNSSECException {
super(name, DnsRecordType.KEY, dclass, ttl, flags, proto, alg, DNSSEC.fromPublicKey(key, alg));
publicKey = key;
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Key Exchange - delegation of authority
*
* @author Brian Wellington
*/
public
class KXRecord extends U16NameBase {
private static final long serialVersionUID = 7448568832769757809L;
KXRecord() {}
@Override
DnsRecord getObject() {
return new KXRecord();
}
@Override
public
Name getAdditionalName() {
return getNameField();
}
/**
* Creates a KX Record from the given data
*
* @param preference The preference of this KX. Records with lower priority
* are preferred.
* @param target The host that authority is delegated to
*/
public
KXRecord(Name name, int dclass, long ttl, int preference, Name target) {
super(name, DnsRecordType.KX, dclass, ttl, preference, "preference", target, "target");
}
/**
* Returns the target of the KX record
*/
public
Name getTarget() {
return getNameField();
}
/**
* Returns the preference of this KX record
*/
public
int getPreference() {
return getU16Field();
}
}

View File

@ -0,0 +1,349 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Location - describes the physical location of hosts, networks, subnets.
*
* @author Brian Wellington
*/
public
class LOCRecord extends DnsRecord {
private static final long serialVersionUID = 9058224788126750409L;
private static NumberFormat w2, w3;
private long size, hPrecision, vPrecision;
private long latitude, longitude, altitude;
static {
w2 = new DecimalFormat();
w2.setMinimumIntegerDigits(2);
w3 = new DecimalFormat();
w3.setMinimumIntegerDigits(3);
}
LOCRecord() {}
@Override
DnsRecord getObject() {
return new LOCRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
int version;
version = in.readU8();
if (version != 0) {
throw new WireParseException("Invalid LOC version");
}
size = parseLOCformat(in.readU8());
hPrecision = parseLOCformat(in.readU8());
vPrecision = parseLOCformat(in.readU8());
latitude = in.readU32();
longitude = in.readU32();
altitude = in.readU32();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(0); /* version */
out.writeU8(toLOCformat(size));
out.writeU8(toLOCformat(hPrecision));
out.writeU8(toLOCformat(vPrecision));
out.writeU32(latitude);
out.writeU32(longitude);
out.writeU32(altitude);
}
/**
* Convert to a String
*/
@Override
void rrToString(StringBuilder sb) {
/* Latitude */
sb.append(positionToString(latitude, 'N', 'S'));
sb.append(" ");
/* Latitude */
sb.append(positionToString(longitude, 'E', 'W'));
sb.append(" ");
/* Altitude */
renderFixedPoint(sb, w2, altitude - 10000000, 100);
sb.append("m ");
/* Size */
renderFixedPoint(sb, w2, size, 100);
sb.append("m ");
/* Horizontal precision */
renderFixedPoint(sb, w2, hPrecision, 100);
sb.append("m ");
/* Vertical precision */
renderFixedPoint(sb, w2, vPrecision, 100);
sb.append("m");
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
latitude = parsePosition(st, "latitude");
longitude = parsePosition(st, "longitude");
altitude = parseDouble(st, "altitude", true, -10000000, 4284967295L, 0) + 10000000;
size = parseDouble(st, "size", false, 0, 9000000000L, 100);
hPrecision = parseDouble(st, "horizontal precision", false, 0, 9000000000L, 1000000);
vPrecision = parseDouble(st, "vertical precision", false, 0, 9000000000L, 1000);
}
private
long parsePosition(Tokenizer st, String type) throws IOException {
boolean isLatitude = type.equals("latitude");
int deg = 0, min = 0;
double sec = 0;
long value;
String s;
deg = st.getUInt16();
if (deg > 180 || (deg > 90 && isLatitude)) {
throw st.exception("Invalid LOC " + type + " degrees");
}
s = st.getString();
try {
min = Integer.parseInt(s);
if (min < 0 || min > 59) {
throw st.exception("Invalid LOC " + type + " minutes");
}
s = st.getString();
sec = parseFixedPoint(s);
if (sec < 0 || sec >= 60) {
throw st.exception("Invalid LOC " + type + " seconds");
}
s = st.getString();
} catch (NumberFormatException e) {
}
if (s.length() != 1) {
throw st.exception("Invalid LOC " + type);
}
value = (long) (1000 * (sec + 60L * (min + 60L * deg)));
char c = Character.toUpperCase(s.charAt(0));
if ((isLatitude && c == 'S') || (!isLatitude && c == 'W')) {
value = -value;
}
else if ((isLatitude && c != 'N') || (!isLatitude && c != 'E')) {
throw st.exception("Invalid LOC " + type);
}
value += (1L << 31);
return value;
}
private
double parseFixedPoint(String s) {
if (s.matches("^-?\\d+$")) {
return Integer.parseInt(s);
}
else if (s.matches("^-?\\d+\\.\\d*$")) {
String[] parts = s.split("\\.");
double value = Integer.parseInt(parts[0]);
double fraction = Integer.parseInt(parts[1]);
if (value < 0) {
fraction *= -1;
}
int digits = parts[1].length();
return value + (fraction / Math.pow(10, digits));
}
else {
throw new NumberFormatException();
}
}
private
long parseDouble(Tokenizer st, String type, boolean required, long min, long max, long defaultValue) throws IOException {
Tokenizer.Token token = st.get();
if (token.isEOL()) {
if (required) {
throw st.exception("Invalid LOC " + type);
}
st.unget();
return defaultValue;
}
String s = token.value;
if (s.length() > 1 && s.charAt(s.length() - 1) == 'm') {
s = s.substring(0, s.length() - 1);
}
try {
long value = (long) (100 * parseFixedPoint(s));
if (value < min || value > max) {
throw st.exception("Invalid LOC " + type);
}
return value;
} catch (NumberFormatException e) {
throw st.exception("Invalid LOC " + type);
}
}
private
String positionToString(long value, char pos, char neg) {
StringBuilder sb = new StringBuilder();
char direction;
long temp = value - (1L << 31);
if (temp < 0) {
temp = -temp;
direction = neg;
}
else {
direction = pos;
}
sb.append(temp / (3600 * 1000)); /* degrees */
temp = temp % (3600 * 1000);
sb.append(" ");
sb.append(temp / (60 * 1000)); /* minutes */
temp = temp % (60 * 1000);
sb.append(" ");
renderFixedPoint(sb, w3, temp, 1000); /* seconds */
sb.append(" ");
sb.append(direction);
return sb.toString();
}
private
void renderFixedPoint(StringBuilder sb, NumberFormat formatter, long value, long divisor) {
sb.append(value / divisor);
value %= divisor;
if (value != 0) {
sb.append(".");
sb.append(formatter.format(value));
}
}
private
int toLOCformat(long l) {
byte exp = 0;
while (l > 9) {
exp++;
l /= 10;
}
return (int) ((l << 4) + exp);
}
private static
long parseLOCformat(int b) throws WireParseException {
long out = b >> 4;
int exp = b & 0xF;
if (out > 9 || exp > 9) {
throw new WireParseException("Invalid LOC Encoding");
}
while (exp-- > 0) {
out *= 10;
}
return (out);
}
/**
* Creates an LOC Record from the given data
*
* @param latitude The latitude of the center of the sphere
* @param longitude The longitude of the center of the sphere
* @param altitude The altitude of the center of the sphere, in m
* @param size The diameter of a sphere enclosing the described entity, in m.
* @param hPrecision The horizontal precision of the data, in m.
* @param vPrecision The vertical precision of the data, in m.
*/
public
LOCRecord(Name name,
int dclass,
long ttl,
double latitude,
double longitude,
double altitude,
double size,
double hPrecision,
double vPrecision) {
super(name, DnsRecordType.LOC, dclass, ttl);
this.latitude = (long) (latitude * 3600 * 1000 + (1L << 31));
this.longitude = (long) (longitude * 3600 * 1000 + (1L << 31));
this.altitude = (long) ((altitude + 100000) * 100);
this.size = (long) (size * 100);
this.hPrecision = (long) (hPrecision * 100);
this.vPrecision = (long) (vPrecision * 100);
}
/**
* Returns the latitude
*/
public
double getLatitude() {
return ((double) (latitude - (1L << 31))) / (3600 * 1000);
}
/**
* Returns the longitude
*/
public
double getLongitude() {
return ((double) (longitude - (1L << 31))) / (3600 * 1000);
}
/**
* Returns the altitude
*/
public
double getAltitude() {
return ((double) (altitude - 10000000)) / 100;
}
/**
* Returns the diameter of the enclosing sphere
*/
public
double getSize() {
return ((double) size) / 100;
}
/**
* Returns the horizontal precision
*/
public
double getHPrecision() {
return ((double) hPrecision) / 100;
}
/**
* Returns the horizontal precision
*/
public
double getVPrecision() {
return ((double) vPrecision) / 100;
}
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Mailbox Record - specifies a host containing a mailbox.
*
* @author Brian Wellington
*/
public
class MBRecord extends SingleNameBase {
private static final long serialVersionUID = 532349543479150419L;
MBRecord() {}
@Override
DnsRecord getObject() {
return new MBRecord();
}
@Override
public
Name getAdditionalName() {
return getSingleName();
}
/**
* Creates a new MB Record with the given data
*
* @param mailbox The host containing the mailbox for the domain.
*/
public
MBRecord(Name name, int dclass, long ttl, Name mailbox) {
super(name, DnsRecordType.MB, dclass, ttl, mailbox, "mailbox");
}
/**
* Gets the mailbox for the domain
*/
public
Name getMailbox() {
return getSingleName();
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Mail Destination Record - specifies a mail agent which delivers mail
* for a domain (obsolete)
*
* @author Brian Wellington
*/
public
class MDRecord extends SingleNameBase {
private static final long serialVersionUID = 5268878603762942202L;
MDRecord() {}
@Override
DnsRecord getObject() {
return new MDRecord();
}
@Override
public
Name getAdditionalName() {
return getSingleName();
}
/**
* Creates a new MD Record with the given data
*
* @param mailAgent The mail agent that delivers mail for the domain.
*/
public
MDRecord(Name name, int dclass, long ttl, Name mailAgent) {
super(name, DnsRecordType.MD, dclass, ttl, mailAgent, "mail agent");
}
/**
* Gets the mail agent for the domain
*/
public
Name getMailAgent() {
return getSingleName();
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Mail Forwarder Record - specifies a mail agent which forwards mail
* for a domain (obsolete)
*
* @author Brian Wellington
*/
public
class MFRecord extends SingleNameBase {
private static final long serialVersionUID = -6670449036843028169L;
MFRecord() {}
@Override
DnsRecord getObject() {
return new MFRecord();
}
@Override
public
Name getAdditionalName() {
return getSingleName();
}
/**
* Creates a new MF Record with the given data
*
* @param mailAgent The mail agent that forwards mail for the domain.
*/
public
MFRecord(Name name, int dclass, long ttl, Name mailAgent) {
super(name, DnsRecordType.MF, dclass, ttl, mailAgent, "mail agent");
}
/**
* Gets the mail agent for the domain
*/
public
Name getMailAgent() {
return getSingleName();
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Mail Group Record - specifies a mailbox which is a member of a mail group.
*
* @author Brian Wellington
*/
public
class MGRecord extends SingleNameBase {
private static final long serialVersionUID = -3980055550863644582L;
MGRecord() {}
@Override
DnsRecord getObject() {
return new MGRecord();
}
/**
* Creates a new MG Record with the given data
*
* @param mailbox The mailbox that is a member of the group specified by the
* domain.
*/
public
MGRecord(Name name, int dclass, long ttl, Name mailbox) {
super(name, DnsRecordType.MG, dclass, ttl, mailbox, "mailbox");
}
/**
* Gets the mailbox in the mail group specified by the domain
*/
public
Name getMailbox() {
return getSingleName();
}
}

View File

@ -0,0 +1,98 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Mailbox information Record - lists the address responsible for a mailing
* list/mailbox and the address to receive error messages relating to the
* mailing list/mailbox.
*
* @author Brian Wellington
*/
public
class MINFORecord extends DnsRecord {
private static final long serialVersionUID = -3962147172340353796L;
private Name responsibleAddress;
private Name errorAddress;
MINFORecord() {}
@Override
DnsRecord getObject() {
return new MINFORecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
responsibleAddress = new Name(in);
errorAddress = new Name(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
responsibleAddress.toWire(out, null, canonical);
errorAddress.toWire(out, null, canonical);
}
/**
* Converts the MINFO Record to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(responsibleAddress);
sb.append(" ");
sb.append(errorAddress);
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
responsibleAddress = st.getName(origin);
errorAddress = st.getName(origin);
}
/**
* Creates an MINFO Record from the given data
*
* @param responsibleAddress The address responsible for the
* mailing list/mailbox.
* @param errorAddress The address to receive error messages relating to the
* mailing list/mailbox.
*/
public
MINFORecord(Name name, int dclass, long ttl, Name responsibleAddress, Name errorAddress) {
super(name, DnsRecordType.MINFO, dclass, ttl);
this.responsibleAddress = checkName("responsibleAddress", responsibleAddress);
this.errorAddress = checkName("errorAddress", errorAddress);
}
/**
* Gets the address responsible for the mailing list/mailbox.
*/
public
Name getResponsibleAddress() {
return responsibleAddress;
}
/**
* Gets the address to receive error messages relating to the mailing
* list/mailbox.
*/
public
Name getErrorAddress() {
return errorAddress;
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Mailbox Rename Record - specifies a rename of a mailbox.
*
* @author Brian Wellington
*/
public
class MRRecord extends SingleNameBase {
private static final long serialVersionUID = -5617939094209927533L;
MRRecord() {}
@Override
DnsRecord getObject() {
return new MRRecord();
}
/**
* Creates a new MR Record with the given data
*
* @param newName The new name of the mailbox specified by the domain.
* domain.
*/
public
MRRecord(Name name, int dclass, long ttl, Name newName) {
super(name, DnsRecordType.MR, dclass, ttl, newName, "new name");
}
/**
* Gets the new name of the mailbox specified by the domain
*/
public
Name getNewName() {
return getSingleName();
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Mail Exchange - specifies where mail to a domain is sent
*
* @author Brian Wellington
*/
public
class MXRecord extends U16NameBase {
private static final long serialVersionUID = 2914841027584208546L;
MXRecord() {}
@Override
DnsRecord getObject() {
return new MXRecord();
}
@Override
public
Name getAdditionalName() {
return getNameField();
}
/**
* Creates an MX Record from the given data
*
* @param priority The priority of this MX. Records with lower priority
* are preferred.
* @param target The host that mail is sent to
*/
public
MXRecord(Name name, int dclass, long ttl, int priority, Name target) {
super(name, DnsRecordType.MX, dclass, ttl, priority, "priority", target, "target");
}
/**
* Returns the target of the MX record
*/
public
Name getTarget() {
return getNameField();
}
/**
* Returns the priority of this MX record
*/
public
int getPriority() {
return getU16Field();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(u16Field);
nameField.toWire(out, c, canonical);
}
}

View File

@ -0,0 +1,174 @@
// Copyright (c) 2000-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.TextParseException;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Name Authority Pointer Record - specifies rewrite rule, that when applied
* to an existing string will produce a new domain.
*
* @author Chuck Santos
*/
public
class NAPTRRecord extends DnsRecord {
private static final long serialVersionUID = 5191232392044947002L;
private int order, preference;
private byte[] flags, service, regexp;
private Name replacement;
NAPTRRecord() {}
@Override
DnsRecord getObject() {
return new NAPTRRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
order = in.readU16();
preference = in.readU16();
flags = in.readCountedString();
service = in.readCountedString();
regexp = in.readCountedString();
replacement = new Name(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(order);
out.writeU16(preference);
out.writeCountedString(flags);
out.writeCountedString(service);
out.writeCountedString(regexp);
replacement.toWire(out, null, canonical);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(order);
sb.append(" ");
sb.append(preference);
sb.append(" ");
sb.append(byteArrayToString(flags, true));
sb.append(" ");
sb.append(byteArrayToString(service, true));
sb.append(" ");
sb.append(byteArrayToString(regexp, true));
sb.append(" ");
sb.append(replacement);
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
order = st.getUInt16();
preference = st.getUInt16();
try {
flags = byteArrayFromString(st.getString());
service = byteArrayFromString(st.getString());
regexp = byteArrayFromString(st.getString());
} catch (TextParseException e) {
throw st.exception(e.getMessage());
}
replacement = st.getName(origin);
}
@Override
public
Name getAdditionalName() {
return replacement;
}
/**
* Creates an NAPTR Record from the given data
*
* @param order The order of this NAPTR. Records with lower order are
* preferred.
* @param preference The preference, used to select between records at the
* same order.
* @param flags The control aspects of the NAPTRRecord.
* @param service The service or protocol available down the rewrite path.
* @param regexp The regular/substitution expression.
* @param replacement The domain-name to query for the next DNS resource
* record, depending on the value of the flags field.
*
* @throws IllegalArgumentException One of the strings has invalid escapes
*/
public
NAPTRRecord(Name name, int dclass, long ttl, int order, int preference, String flags, String service, String regexp, Name replacement) {
super(name, DnsRecordType.NAPTR, dclass, ttl);
this.order = checkU16("order", order);
this.preference = checkU16("preference", preference);
try {
this.flags = byteArrayFromString(flags);
this.service = byteArrayFromString(service);
this.regexp = byteArrayFromString(regexp);
} catch (TextParseException e) {
throw new IllegalArgumentException(e.getMessage());
}
this.replacement = checkName("replacement", replacement);
}
/**
* Returns the order
*/
public
int getOrder() {
return order;
}
/**
* Returns the preference
*/
public
int getPreference() {
return preference;
}
/**
* Returns flags
*/
public
String getFlags() {
return byteArrayToString(flags, false);
}
/**
* Returns service
*/
public
String getService() {
return byteArrayToString(service, false);
}
/**
* Returns regexp
*/
public
String getRegexp() {
return byteArrayToString(regexp, false);
}
/**
* Returns the replacement domain-name
*/
public
Name getReplacement() {
return replacement;
}
}

View File

@ -0,0 +1,120 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* NSAP Address Record.
*
* @author Brian Wellington
*/
public
class NSAPRecord extends DnsRecord {
private static final long serialVersionUID = -1037209403185658593L;
private byte[] address;
NSAPRecord() {}
@Override
DnsRecord getObject() {
return new NSAPRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
address = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeByteArray(address);
}
@Override
void rrToString(StringBuilder sb) {
sb.append("0x")
.append(base16.toString(address));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
String addr = st.getString();
this.address = checkAndConvertAddress(addr);
if (this.address == null) {
throw st.exception("invalid NSAP address " + addr);
}
}
/**
* Creates an NSAP Record from the given data
*
* @param address The NSAP address.
*
* @throws IllegalArgumentException The address is not a valid NSAP address.
*/
public
NSAPRecord(Name name, int dclass, long ttl, String address) {
super(name, DnsRecordType.NSAP, dclass, ttl);
this.address = checkAndConvertAddress(address);
if (this.address == null) {
throw new IllegalArgumentException("invalid NSAP address " + address);
}
}
private static
byte[] checkAndConvertAddress(String address) {
if (!address.substring(0, 2)
.equalsIgnoreCase("0x")) {
return null;
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
boolean partial = false;
int current = 0;
for (int i = 2; i < address.length(); i++) {
char c = address.charAt(i);
if (c == '.') {
continue;
}
int value = Character.digit(c, 16);
if (value == -1) {
return null;
}
if (partial) {
current += value;
bytes.write(current);
partial = false;
}
else {
current = value << 4;
partial = true;
}
}
if (partial) {
return null;
}
return bytes.toByteArray();
}
/**
* Returns the NSAP address.
*/
public
String getAddress() {
return byteArrayToString(address, false);
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* NSAP Pointer Record - maps a domain name representing an NSAP Address to
* a hostname.
*
* @author Brian Wellington
*/
public
class NSAP_PTRRecord extends SingleNameBase {
private static final long serialVersionUID = 2386284746382064904L;
NSAP_PTRRecord() {}
@Override
DnsRecord getObject() {
return new NSAP_PTRRecord();
}
/**
* Creates a new NSAP_PTR Record with the given data
*
* @param target The name of the host with this address
*/
public
NSAP_PTRRecord(Name name, int dclass, long ttl, Name target) {
super(name, DnsRecordType.NSAP_PTR, dclass, ttl, target, "target");
}
/**
* Gets the target of the NSAP_PTR Record
*/
public
Name getTarget() {
return getSingleName();
}
}

View File

@ -0,0 +1,188 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* Next SECure name 3 Parameters - this record contains the parameters (hash
* algorithm, salt, iterations) used for a valid, complete NSEC3 chain present
* in a zone. Zones signed using NSEC3 must include this record at the zone apex
* to inform authoritative servers that NSEC3 is being used with the given
* parameters.
*
* @author Brian Wellington
* @author David Blacka
*/
public
class NSEC3PARAMRecord extends DnsRecord {
private static final long serialVersionUID = -8689038598776316533L;
private int hashAlg;
private int flags;
private int iterations;
private byte salt[];
NSEC3PARAMRecord() {}
@Override
DnsRecord getObject() {
return new NSEC3PARAMRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
hashAlg = in.readU8();
flags = in.readU8();
iterations = in.readU16();
int salt_length = in.readU8();
if (salt_length > 0) {
salt = in.readByteArray(salt_length);
}
else {
salt = null;
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(hashAlg);
out.writeU8(flags);
out.writeU16(iterations);
if (salt != null) {
out.writeU8(salt.length);
out.writeByteArray(salt);
}
else {
out.writeU8(0);
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(hashAlg);
sb.append(' ');
sb.append(flags);
sb.append(' ');
sb.append(iterations);
sb.append(' ');
if (salt == null) {
sb.append('-');
}
else {
sb.append(base16.toString(salt));
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
hashAlg = st.getUInt8();
flags = st.getUInt8();
iterations = st.getUInt16();
String s = st.getString();
if (s.equals("-")) {
salt = null;
}
else {
st.unget();
salt = st.getHexString();
if (salt.length > 255) {
throw st.exception("salt value too long");
}
}
}
/**
* Creates an NSEC3PARAM record from the given data.
*
* @param name The ownername of the NSEC3PARAM record (generally the zone name).
* @param dclass The class.
* @param ttl The TTL.
* @param hashAlg The hash algorithm.
* @param flags The value of the flags field.
* @param iterations The number of hash iterations.
* @param salt The salt to use (may be null).
*/
public
NSEC3PARAMRecord(Name name, int dclass, long ttl, int hashAlg, int flags, int iterations, byte[] salt) {
super(name, DnsRecordType.NSEC3PARAM, dclass, ttl);
this.hashAlg = checkU8("hashAlg", hashAlg);
this.flags = checkU8("flags", flags);
this.iterations = checkU16("iterations", iterations);
if (salt != null) {
if (salt.length > 255) {
throw new IllegalArgumentException("Invalid salt " + "length");
}
if (salt.length > 0) {
this.salt = new byte[salt.length];
System.arraycopy(salt, 0, this.salt, 0, salt.length);
}
}
}
/**
* Returns the hash algorithm
*/
public
int getHashAlgorithm() {
return hashAlg;
}
/**
* Returns the flags
*/
public
int getFlags() {
return flags;
}
/**
* Returns the number of iterations
*/
public
int getIterations() {
return iterations;
}
/**
* Returns the salt
*/
public
byte[] getSalt() {
return salt;
}
/**
* Hashes a name with the parameters of this NSEC3PARAM record.
*
* @param name The name to hash
*
* @return The hashed version of the name
*
* @throws NoSuchAlgorithmException The hash algorithm is unknown.
*/
public
byte[] hashName(Name name) throws NoSuchAlgorithmException {
return NSEC3Record.hashName(name, hashAlg, iterations, salt);
}
}

View File

@ -0,0 +1,301 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
import dorkbox.network.dns.utils.base32;
/**
* Next SECure name 3 - this record contains the next hashed name in an
* ordered list of hashed names in the zone, and a set of types for which
* records exist for this name. The presence of this record in a response
* signifies a negative response from a DNSSEC-signed zone.
* <p>
* This replaces the NSEC and NXT records, when used.
*
* @author Brian Wellington
* @author David Blacka
*/
public
class NSEC3Record extends DnsRecord {
public static final int SHA1_DIGEST_ID = Digest.SHA1;
private static final long serialVersionUID = -7123504635968932855L;
private int hashAlg;
private int flags;
private int iterations;
private byte[] salt;
private byte[] next;
private TypeBitmap types;
private static final base32 b32 = new base32(base32.Alphabet.BASE32HEX, false, false);
public static
class Flags {
/**
* Unsigned delegation are not included in the NSEC3 chain.
*/
public static final int OPT_OUT = 0x01;
/**
* NSEC3 flags identifiers.
*/
private
Flags() {}
}
public static
class Digest {
/**
* SHA-1
*/
public static final int SHA1 = 1;
private
Digest() {}
}
NSEC3Record() {}
@Override
DnsRecord getObject() {
return new NSEC3Record();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
hashAlg = in.readU8();
flags = in.readU8();
iterations = in.readU16();
int salt_length = in.readU8();
if (salt_length > 0) {
salt = in.readByteArray(salt_length);
}
else {
salt = null;
}
int next_length = in.readU8();
next = in.readByteArray(next_length);
types = new TypeBitmap(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(hashAlg);
out.writeU8(flags);
out.writeU16(iterations);
if (salt != null) {
out.writeU8(salt.length);
out.writeByteArray(salt);
}
else {
out.writeU8(0);
}
out.writeU8(next.length);
out.writeByteArray(next);
types.toWire(out);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(hashAlg);
sb.append(' ');
sb.append(flags);
sb.append(' ');
sb.append(iterations);
sb.append(' ');
if (salt == null) {
sb.append('-');
}
else {
sb.append(base16.toString(salt));
}
sb.append(' ');
sb.append(b32.toString(next));
if (!types.empty()) {
sb.append(' ');
sb.append(types.toString());
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
hashAlg = st.getUInt8();
flags = st.getUInt8();
iterations = st.getUInt16();
String s = st.getString();
if (s.equals("-")) {
salt = null;
}
else {
st.unget();
salt = st.getHexString();
if (salt.length > 255) {
throw st.exception("salt value too long");
}
}
next = st.getBase32String(b32);
types = new TypeBitmap(st);
}
/**
* Creates an NSEC3 record from the given data.
*
* @param name The ownername of the NSEC3 record (base32'd hash plus zonename).
* @param dclass The class.
* @param ttl The TTL.
* @param hashAlg The hash algorithm.
* @param flags The value of the flags field.
* @param iterations The number of hash iterations.
* @param salt The salt to use (may be null).
* @param next The next hash (may not be null).
* @param types The types present at the original ownername.
*/
public
NSEC3Record(Name name, int dclass, long ttl, int hashAlg, int flags, int iterations, byte[] salt, byte[] next, int[] types) {
super(name, DnsRecordType.NSEC3, dclass, ttl);
this.hashAlg = checkU8("hashAlg", hashAlg);
this.flags = checkU8("flags", flags);
this.iterations = checkU16("iterations", iterations);
if (salt != null) {
if (salt.length > 255) {
throw new IllegalArgumentException("Invalid salt");
}
if (salt.length > 0) {
this.salt = new byte[salt.length];
System.arraycopy(salt, 0, this.salt, 0, salt.length);
}
}
if (next.length > 255) {
throw new IllegalArgumentException("Invalid next hash");
}
this.next = new byte[next.length];
System.arraycopy(next, 0, this.next, 0, next.length);
this.types = new TypeBitmap(types);
}
/**
* Returns the hash algorithm
*/
public
int getHashAlgorithm() {
return hashAlg;
}
/**
* Returns the flags
*/
public
int getFlags() {
return flags;
}
/**
* Returns the number of iterations
*/
public
int getIterations() {
return iterations;
}
/**
* Returns the salt
*/
public
byte[] getSalt() {
return salt;
}
/**
* Returns the next hash
*/
public
byte[] getNext() {
return next;
}
/**
* Returns the set of types defined for this name
*/
public
int[] getTypes() {
return types.toArray();
}
/**
* Returns whether a specific type is in the set of types.
*/
public
boolean hasType(int type) {
return types.contains(type);
}
/**
* Hashes a name with the parameters of this NSEC3 record.
*
* @param name The name to hash
*
* @return The hashed version of the name
*
* @throws NoSuchAlgorithmException The hash algorithm is unknown.
*/
public
byte[] hashName(Name name) throws NoSuchAlgorithmException {
return hashName(name, hashAlg, iterations, salt);
}
static
byte[] hashName(Name name, int hashAlg, int iterations, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest digest;
switch (hashAlg) {
case Digest.SHA1:
digest = MessageDigest.getInstance("sha-1");
break;
default:
throw new NoSuchAlgorithmException("Unknown NSEC3 algorithm" + "identifier: " + hashAlg);
}
byte[] hash = null;
for (int i = 0; i <= iterations; i++) {
digest.reset();
if (i == 0) {
digest.update(name.toWireCanonical());
}
else {
digest.update(hash);
}
if (salt != null) {
digest.update(salt);
}
hash = digest.digest();
}
return hash;
}
}

View File

@ -0,0 +1,113 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Next SECure name - this record contains the following name in an
* ordered list of names in the zone, and a set of types for which
* records exist for this name. The presence of this record in a response
* signifies a negative response from a DNSSEC-signed zone.
* <p>
* This replaces the NXT record.
*
* @author Brian Wellington
* @author David Blacka
*/
public
class NSECRecord extends DnsRecord {
private static final long serialVersionUID = -5165065768816265385L;
private Name next;
private TypeBitmap types;
NSECRecord() {}
@Override
DnsRecord getObject() {
return new NSECRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
next = new Name(in);
types = new TypeBitmap(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
// Note: The next name is not lowercased.
next.toWire(out, null, false);
types.toWire(out);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(next);
if (!types.empty()) {
sb.append(' ');
sb.append(types.toString());
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
next = st.getName(origin);
types = new TypeBitmap(st);
}
/**
* Creates an NSEC Record from the given data.
*
* @param next The following name in an ordered list of the zone
* @param types An array containing the types present.
*/
public
NSECRecord(Name name, int dclass, long ttl, Name next, int[] types) {
super(name, DnsRecordType.NSEC, dclass, ttl);
this.next = checkName("next", next);
for (int i = 0; i < types.length; i++) {
DnsRecordType.check(types[i]);
}
this.types = new TypeBitmap(types);
}
/**
* Returns the next name
*/
public
Name getNext() {
return next;
}
/**
* Returns the set of types defined for this name
*/
public
int[] getTypes() {
return types.toArray();
}
/**
* Returns whether a specific type is in the set of types.
*/
public
boolean hasType(int type) {
return types.contains(type);
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
/**
* The Name Server Identifier Option, define in RFC 5001.
*
* @author Brian Wellington
* @see OPTRecord
*/
public
class NSIDOption extends GenericEDNSOption {
private static final long serialVersionUID = 74739759292589056L;
NSIDOption() {
super(EDNSOption.Code.NSID);
}
/**
* Construct an NSID option.
*
* @param data The contents of the option.
*/
public
NSIDOption(byte[] data) {
super(EDNSOption.Code.NSID, data);
}
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Name Server Record - contains the name server serving the named zone
*
* @author Brian Wellington
*/
public
class NSRecord extends SingleCompressedNameBase {
private static final long serialVersionUID = 487170758138268838L;
NSRecord() {}
@Override
DnsRecord getObject() {
return new NSRecord();
}
@Override
public
Name getAdditionalName() {
return getSingleName();
}
/**
* Creates a new NS Record with the given data
*
* @param target The name server for the given domain
*/
public
NSRecord(Name name, int dclass, long ttl, Name target) {
super(name, DnsRecordType.NS, dclass, ttl, target, "target");
}
/**
* Gets the target of the NS Record
*/
public
Name getTarget() {
return getSingleName();
}
}

View File

@ -0,0 +1,78 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* The NULL Record. This has no defined purpose, but can be used to
* hold arbitrary data.
*
* @author Brian Wellington
*/
public
class NULLRecord extends DnsRecord {
private static final long serialVersionUID = -5796493183235216538L;
private byte[] data;
NULLRecord() {}
@Override
DnsRecord getObject() {
return new NULLRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
data = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeByteArray(data);
}
@Override
void rrToString(StringBuilder sb) {
sb.append(unknownToString(data));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
throw st.exception("no defined text format for NULL records");
}
/**
* Creates a NULL record from the given data.
*
* @param data The contents of the record.
*/
public
NULLRecord(Name name, int dclass, long ttl, byte[] data) {
super(name, DnsRecordType.NULL, dclass, ttl);
if (data.length > 0xFFFF) {
throw new IllegalArgumentException("data must be <65536 bytes");
}
this.data = data;
}
/**
* Returns the contents of this record.
*/
public
byte[] getData() {
return data;
}
}

View File

@ -0,0 +1,129 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.util.BitSet;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Next name - this record contains the following name in an ordered list
* of names in the zone, and a set of types for which records exist for
* this name. The presence of this record in a response signifies a
* failed query for data in a DNSSEC-signed zone.
*
* @author Brian Wellington
*/
public
class NXTRecord extends DnsRecord {
private static final long serialVersionUID = -8851454400765507520L;
private Name next;
private BitSet bitmap;
NXTRecord() {}
@Override
DnsRecord getObject() {
return new NXTRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
next = new Name(in);
bitmap = new BitSet();
int bitmapLength = in.remaining();
for (int i = 0; i < bitmapLength; i++) {
int t = in.readU8();
for (int j = 0; j < 8; j++) {
if ((t & (1 << (7 - j))) != 0) {
bitmap.set(i * 8 + j);
}
}
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
next.toWire(out, null, canonical);
int length = bitmap.length();
for (int i = 0, t = 0; i < length; i++) {
t |= (bitmap.get(i) ? (1 << (7 - i % 8)) : 0);
if (i % 8 == 7 || i == length - 1) {
out.writeU8(t);
t = 0;
}
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(next);
int length = bitmap.length();
for (short i = 0; i < length; i++) {
if (bitmap.get(i)) {
sb.append(" ");
sb.append(DnsRecordType.string(i));
}
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
next = st.getName(origin);
bitmap = new BitSet();
while (true) {
Tokenizer.Token t = st.get();
if (!t.isString()) {
break;
}
int typecode = DnsRecordType.value(t.value, true);
if (typecode <= 0 || typecode > 128) {
throw st.exception("Invalid type: " + t.value);
}
bitmap.set(typecode);
}
st.unget();
}
/**
* Creates an NXT Record from the given data
*
* @param next The following name in an ordered list of the zone
* @param bitmap The set of type for which records exist at this name
*/
public
NXTRecord(Name name, int dclass, long ttl, Name next, BitSet bitmap) {
super(name, DnsRecordType.NXT, dclass, ttl);
this.next = checkName("next", next);
this.bitmap = bitmap;
}
/**
* Returns the next name
*/
public
Name getNext() {
return next;
}
/**
* Returns the set of types defined for this name
*/
public
BitSet getBitmap() {
return bitmap;
}
}

View File

@ -0,0 +1,88 @@
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Options;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.Base64Fast;
import dorkbox.util.OS;
/**
* OPENPGPKEY Record - Stores an OpenPGP certificate associated with a name.
* RFC 7929.
*
* @author Brian Wellington
* @author Valentin Hauner
*/
public
class OPENPGPKEYRecord extends DnsRecord {
private static final long serialVersionUID = -1277262990243423062L;
private byte[] cert;
OPENPGPKEYRecord() {}
@Override
DnsRecord getObject() {
return new OPENPGPKEYRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
cert = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeByteArray(cert);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
if (cert != null) {
if (Options.check("multiline")) {
sb.append("(")
.append(OS.LINE_SEPARATOR);
sb.append(Base64Fast.formatString(Base64Fast.encode2(cert), 64, "\t", true));
}
else {
sb.append("\t");
sb.append(Base64Fast.encode2(cert));
}
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
cert = st.getBase64();
}
/**
* Creates an OPENPGPKEY Record from the given data
*
* @param cert Binary data representing the certificate
*/
public
OPENPGPKEYRecord(Name name, int dclass, long ttl, byte[] cert) {
super(name, DnsRecordType.OPENPGPKEY, dclass, ttl);
this.cert = cert;
}
/**
* Returns the binary representation of the certificate
*/
public
byte[] getCert() {
return cert;
}
}

View File

@ -0,0 +1,232 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsResponseCode;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Options - describes Extended DNS (EDNS) properties of a DnsMessage.
* No specific options are defined other than those specified in the
* header. An OPT should be generated by Resolver.
* <p>
* EDNS is a method to extend the DNS protocol while providing backwards
* compatibility and not significantly changing the protocol. This
* implementation of EDNS is mostly complete at level 0.
*
* @author Brian Wellington
* @see DnsMessage
*/
public
class OPTRecord extends DnsRecord {
private static final long serialVersionUID = -6254521894809367938L;
private List options;
OPTRecord() {}
@Override
DnsRecord getObject() {
return new OPTRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
if (in.remaining() > 0) {
options = new ArrayList();
}
while (in.remaining() > 0) {
EDNSOption option = EDNSOption.fromWire(in);
options.add(option);
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
if (options == null) {
return;
}
Iterator it = options.iterator();
while (it.hasNext()) {
EDNSOption option = (EDNSOption) it.next();
option.toWire(out);
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
if (options != null) {
sb.append(options);
sb.append(" ");
}
sb.append(" ; payload ");
sb.append(getPayloadSize());
sb.append(", xrcode ");
sb.append(getExtendedRcode());
sb.append(", version ");
sb.append(getVersion());
sb.append(", flags ");
sb.append(getFlags());
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
throw st.exception("no text format defined for OPT");
}
/**
* Determines if two OPTRecords are identical. This compares the name, type,
* class, and rdata (with names canonicalized). Additionally, because TTLs
* are relevant for OPT records, the TTLs are compared.
*
* @param arg The record to compare to
*
* @return true if the records are equal, false otherwise.
*/
public
boolean equals(final Object arg) {
return super.equals(arg) && ttl == ((OPTRecord) arg).ttl;
}
/**
* Returns the maximum allowed payload size.
*/
public
int getPayloadSize() {
return dclass;
}
/**
* Returns the extended DnsResponseCode
*
* @see DnsResponseCode
*/
public
int getExtendedRcode() {
return (int) (ttl >>> 24);
}
/**
* Returns the highest supported EDNS version
*/
public
int getVersion() {
return (int) ((ttl >>> 16) & 0xFF);
}
/**
* Returns the EDNS flags
*/
public
int getFlags() {
return (int) (ttl & 0xFFFF);
}
/**
* Creates an OPT Record with no data. This is normally called by
* SimpleResolver, but can also be called by a server.
*
* @param payloadSize The size of a packet that can be reassembled on the
* sending host.
* @param xrcode The value of the extended rcode field. This is the upper
* 16 bits of the full rcode.
* @param flags Additional message flags.
* @param version The EDNS version that this DNS implementation supports.
* This should be 0 for dnsjava.
*
* @see ExtendedFlags
*/
public
OPTRecord(int payloadSize, int xrcode, int version, int flags) {
this(payloadSize, xrcode, version, flags, null);
}
/**
* Creates an OPT Record. This is normally called by SimpleResolver, but can
* also be called by a server.
*
* @param payloadSize The size of a packet that can be reassembled on the
* sending host.
* @param xrcode The value of the extended rcode field. This is the upper
* 16 bits of the full rcode.
* @param flags Additional message flags.
* @param version The EDNS version that this DNS implementation supports.
* This should be 0 for dnsjava.
* @param options The list of options that comprise the data field. There
* are currently no defined options.
*
* @see ExtendedFlags
*/
public
OPTRecord(int payloadSize, int xrcode, int version, int flags, List options) {
super(Name.root, DnsRecordType.OPT, payloadSize, 0);
checkU16("payloadSize", payloadSize);
checkU8("xrcode", xrcode);
checkU8("version", version);
checkU16("flags", flags);
ttl = ((long) xrcode << 24) + ((long) version << 16) + flags;
if (options != null) {
this.options = new ArrayList(options);
}
}
/**
* Creates an OPT Record with no data. This is normally called by
* SimpleResolver, but can also be called by a server.
*/
public
OPTRecord(int payloadSize, int xrcode, int version) {
this(payloadSize, xrcode, version, 0, null);
}
/**
* Gets all options in the OPTRecord. This returns a list of EDNSOptions.
*/
public
List getOptions() {
if (options == null) {
return Collections.EMPTY_LIST;
}
return Collections.unmodifiableList(options);
}
/**
* Gets all options in the OPTRecord with a specific code. This returns a list
* of EDNSOptions.
*/
public
List getOptions(int code) {
if (options == null) {
return Collections.EMPTY_LIST;
}
List list = Collections.EMPTY_LIST;
for (Iterator it = options.iterator(); it.hasNext(); ) {
EDNSOption opt = (EDNSOption) it.next();
if (opt.getCode() == code) {
if (list == Collections.EMPTY_LIST) {
list = new ArrayList();
}
list.add(opt);
}
}
return list;
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Pointer Record - maps a domain name representing an Internet Address to
* a hostname.
*
* @author Brian Wellington
*/
public
class PTRRecord extends SingleCompressedNameBase {
private static final long serialVersionUID = -8321636610425434192L;
PTRRecord() {}
@Override
DnsRecord getObject() {
return new PTRRecord();
}
/**
* Creates a new PTR Record with the given data
*
* @param target The name of the machine with this address
*/
public
PTRRecord(Name name, int dclass, long ttl, Name target) {
super(name, DnsRecordType.PTR, dclass, ttl, target, "target");
}
/**
* Gets the target of the PTR Record
*/
public
Name getTarget() {
return getSingleName();
}
}

View File

@ -0,0 +1,109 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* X.400 mail mapping record.
*
* @author Brian Wellington
*/
public
class PXRecord extends DnsRecord {
private static final long serialVersionUID = 1811540008806660667L;
private int preference;
private Name map822;
private Name mapX400;
PXRecord() {}
@Override
DnsRecord getObject() {
return new PXRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
preference = in.readU16();
map822 = new Name(in);
mapX400 = new Name(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(preference);
map822.toWire(out, null, canonical);
mapX400.toWire(out, null, canonical);
}
/**
* Converts the PX Record to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(preference);
sb.append(" ");
sb.append(map822);
sb.append(" ");
sb.append(mapX400);
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
preference = st.getUInt16();
map822 = st.getName(origin);
mapX400 = st.getName(origin);
}
/**
* Creates an PX Record from the given data
*
* @param preference The preference of this mail address.
* @param map822 The RFC 822 component of the mail address.
* @param mapX400 The X.400 component of the mail address.
*/
public
PXRecord(Name name, int dclass, long ttl, int preference, Name map822, Name mapX400) {
super(name, DnsRecordType.PX, dclass, ttl);
this.preference = checkU16("preference", preference);
this.map822 = checkName("map822", map822);
this.mapX400 = checkName("mapX400", mapX400);
}
/**
* Gets the preference of the route.
*/
public
int getPreference() {
return preference;
}
/**
* Gets the RFC 822 component of the mail address.
*/
public
Name getMap822() {
return map822;
}
/**
* Gets the X.400 component of the mail address.
*/
public
Name getMapX400() {
return mapX400;
}
}

View File

@ -0,0 +1,95 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Responsible Person Record - lists the mail address of a responsible person
* and a domain where TXT records are available.
*
* @author Tom Scola (tscola@research.att.com)
* @author Brian Wellington
*/
public
class RPRecord extends DnsRecord {
private static final long serialVersionUID = 8124584364211337460L;
private Name mailbox;
private Name textDomain;
RPRecord() {}
@Override
DnsRecord getObject() {
return new RPRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
mailbox = new Name(in);
textDomain = new Name(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
mailbox.toWire(out, null, canonical);
textDomain.toWire(out, null, canonical);
}
/**
* Converts the RP Record to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(mailbox);
sb.append(" ");
sb.append(textDomain);
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
mailbox = st.getName(origin);
textDomain = st.getName(origin);
}
/**
* Creates an RP Record from the given data
*
* @param mailbox The responsible person
* @param textDomain The address where TXT records can be found
*/
public
RPRecord(Name name, int dclass, long ttl, Name mailbox, Name textDomain) {
super(name, DnsRecordType.RP, dclass, ttl);
this.mailbox = checkName("mailbox", mailbox);
this.textDomain = checkName("textDomain", textDomain);
}
/**
* Gets the mailbox address of the RP Record
*/
public
Name getMailbox() {
return mailbox;
}
/**
* Gets the text domain info of the RP Record
*/
public
Name getTextDomain() {
return textDomain;
}
}

View File

@ -0,0 +1,61 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.util.Date;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Recource Record Signature - An RRSIG provides the digital signature of an
* RRset, so that the data can be authenticated by a DNSSEC-capable resolver.
* The signature is generated by a key contained in a DNSKEY Record.
*
* @author Brian Wellington
* @see RRset
* @see DNSSEC
* @see KEYRecord
*/
public
class RRSIGRecord extends SIGBase {
private static final long serialVersionUID = -2609150673537226317L;
RRSIGRecord() {}
@Override
DnsRecord getObject() {
return new RRSIGRecord();
}
/**
* Creates an RRSIG Record from the given data
*
* @param covered The RRset type covered by this signature
* @param alg The cryptographic algorithm of the key that generated the
* signature
* @param origttl The original TTL of the RRset
* @param expire The time at which the signature expires
* @param timeSigned The time at which this signature was generated
* @param footprint The footprint/key id of the signing key.
* @param signer The owner of the signing key
* @param signature Binary data representing the signature
*/
public
RRSIGRecord(Name name,
int dclass,
long ttl,
int covered,
int alg,
long origttl,
Date expire,
Date timeSigned,
int footprint,
Name signer,
byte[] signature) {
super(name, DnsRecordType.RRSIG, dclass, ttl, covered, alg, origttl, expire, timeSigned, footprint, signer, signature);
}
}

View File

@ -0,0 +1,308 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsClass;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* A set of Records with the same name, type, and class. Also included
* are all RRSIG records signing the data records.
*
* @author Brian Wellington
* @see DnsRecord
* @see RRSIGRecord
*/
public
class RRset implements Serializable {
private static final long serialVersionUID = -3270249290171239695L;
/*
* rrs contains both normal and RRSIG records, with the RRSIG records
* at the end.
*/
private List rrs;
private short nsigs;
private short position;
/**
* Creates an RRset and sets its contents to the specified record
*/
public
RRset(DnsRecord record) {
this();
safeAddRR(record);
}
/**
* Creates an empty RRset
*/
public
RRset() {
rrs = new ArrayList(1);
nsigs = 0;
position = 0;
}
private
void safeAddRR(DnsRecord r) {
if (!(r instanceof RRSIGRecord)) {
if (nsigs == 0) {
rrs.add(r);
}
else {
rrs.add(rrs.size() - nsigs, r);
}
}
else {
rrs.add(r);
nsigs++;
}
}
/**
* Creates an RRset with the contents of an existing RRset
*/
public
RRset(RRset rrset) {
synchronized (rrset) {
rrs = (List) ((ArrayList) rrset.rrs).clone();
nsigs = rrset.nsigs;
position = rrset.position;
}
}
/**
* Adds a Record to an RRset
*/
public synchronized
void addRR(DnsRecord r) {
if (rrs.size() == 0) {
safeAddRR(r);
return;
}
DnsRecord first = first();
if (!r.sameRRset(first)) {
throw new IllegalArgumentException("record does not match " + "rrset");
}
if (r.getTTL() != first.getTTL()) {
if (r.getTTL() > first.getTTL()) {
r = r.cloneRecord();
r.setTTL(first.getTTL());
}
else {
for (int i = 0; i < rrs.size(); i++) {
DnsRecord tmp = (DnsRecord) rrs.get(i);
tmp = tmp.cloneRecord();
tmp.setTTL(r.getTTL());
rrs.set(i, tmp);
}
}
}
if (!rrs.contains(r)) {
safeAddRR(r);
}
}
/**
* Returns the first record
*
* @throws IllegalStateException if the rrset is empty
*/
public synchronized
DnsRecord first() {
if (rrs.size() == 0) {
throw new IllegalStateException("rrset is empty");
}
return (DnsRecord) rrs.get(0);
}
/**
* Deletes a Record from an RRset
*/
public synchronized
void deleteRR(DnsRecord r) {
if (rrs.remove(r) && (r instanceof RRSIGRecord)) {
nsigs--;
}
}
/**
* Deletes all Records from an RRset
*/
public synchronized
void clear() {
rrs.clear();
position = 0;
nsigs = 0;
}
/**
* Returns an Iterator listing all (data) records.
*
* @param cycle If true, cycle through the records so that each Iterator will
* start with a different record.
*/
public synchronized
Iterator rrs(boolean cycle) {
return iterator(true, cycle);
}
private synchronized
Iterator iterator(boolean data, boolean cycle) {
int size, start, total;
total = rrs.size();
if (data) {
size = total - nsigs;
}
else {
size = nsigs;
}
if (size == 0) {
return Collections.EMPTY_LIST.iterator();
}
if (data) {
if (!cycle) {
start = 0;
}
else {
if (position >= size) {
position = 0;
}
start = position++;
}
}
else {
start = total - nsigs;
}
List list = new ArrayList(size);
if (data) {
list.addAll(rrs.subList(start, size));
if (start != 0) {
list.addAll(rrs.subList(0, start));
}
}
else {
list.addAll(rrs.subList(start, total));
}
return list.iterator();
}
/**
* Returns an Iterator listing all (data) records. This cycles through
* the records, so each Iterator will start with a different record.
*/
public synchronized
Iterator rrs() {
return iterator(true, true);
}
/**
* Returns an Iterator listing all signature records
*/
public synchronized
Iterator sigs() {
return iterator(false, false);
}
/**
* Returns the number of (data) records
*/
public synchronized
int size() {
return rrs.size() - nsigs;
}
/**
* Converts the RRset to a String
*/
public
String toString() {
if (rrs.size() == 0) {
return ("{empty}");
}
StringBuilder sb = new StringBuilder();
sb.append("{ ");
sb.append(getName() + " ");
sb.append(getTTL() + " ");
sb.append(DnsClass.string(getDClass()) + " ");
sb.append(DnsRecordType.string(getType()) + " ");
sb.append(iteratorToString(iterator(true, false)));
if (nsigs > 0) {
sb.append(" sigs: ");
sb.append(iteratorToString(iterator(false, false)));
}
sb.append(" }");
return sb.toString();
}
/**
* Returns the name of the records
*
* @see Name
*/
public
Name getName() {
return first().getName();
}
/**
* Returns the type of the records
*
* @see DnsRecordType
*/
public
int getType() {
return first().getRRsetType();
}
/**
* Returns the class of the records
*
* @see DnsClass
*/
public
int getDClass() {
return first().getDClass();
}
/**
* Returns the ttl of the records
*/
public synchronized
long getTTL() {
return first().getTTL();
}
private
String iteratorToString(Iterator it) {
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
DnsRecord rr = (DnsRecord) it.next();
sb.append("[");
rr.rdataToString(sb);
sb.append("]");
if (it.hasNext()) {
sb.append(" ");
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Route Through Record - lists a route preference and intermediate host.
*
* @author Brian Wellington
*/
public
class RTRecord extends U16NameBase {
private static final long serialVersionUID = -3206215651648278098L;
RTRecord() {}
@Override
DnsRecord getObject() {
return new RTRecord();
}
/**
* Creates an RT Record from the given data
*
* @param preference The preference of the route. Smaller numbers indicate
* more preferred routes.
* @param intermediateHost The domain name of the host to use as a router.
*/
public
RTRecord(Name name, int dclass, long ttl, int preference, Name intermediateHost) {
super(name, DnsRecordType.RT, dclass, ttl, preference, "preference", intermediateHost, "intermediateHost");
}
/**
* Gets the preference of the route.
*/
public
int getPreference() {
return getU16Field();
}
/**
* Gets the host to use as a router.
*/
public
Name getIntermediateHost() {
return getNameField();
}
}

View File

@ -0,0 +1,84 @@
// Copyright (c) 2001-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.security.PrivateKey;
import java.util.Date;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.utils.Options;
/**
* Creates SIG(0) transaction signatures.
*
* @author Pasi Eronen
* @author Brian Wellington
*/
public
class SIG0 {
/**
* The default validity period for outgoing SIG(0) signed messages.
* Can be overriden by the sig0validity option.
*/
private static final short VALIDITY = 300;
private
SIG0() { }
/**
* Sign a dnsMessage with SIG(0). The DNS key and private key must refer to the
* same underlying cryptographic key.
*
* @param dnsMessage The dnsMessage to be signed
* @param key The DNSKEY record to use as part of signing
* @param privkey The PrivateKey to use when signing
* @param previous If this dnsMessage is a response, the SIG(0) from the query
*/
public static
void signMessage(DnsMessage dnsMessage, KEYRecord key, PrivateKey privkey, SIGRecord previous) throws DNSSEC.DNSSECException {
int validity = Options.intValue("sig0validity");
if (validity < 0) {
validity = VALIDITY;
}
long now = System.currentTimeMillis();
Date timeSigned = new Date(now);
Date timeExpires = new Date(now + validity * 1000);
SIGRecord sig = DNSSEC.signMessage(dnsMessage, previous, key, privkey, timeSigned, timeExpires);
dnsMessage.addRecord(sig, DnsSection.ADDITIONAL);
}
/**
* Verify a dnsMessage using SIG(0).
*
* @param dnsMessage The dnsMessage to be signed
* @param b An array containing the dnsMessage in unparsed form. This is
* necessary since SIG(0) signs the dnsMessage in wire format, and we can't
* recreate the exact wire format (with the same name compression).
* @param key The KEY record to verify the signature with.
* @param previous If this dnsMessage is a response, the SIG(0) from the query
*/
public static
void verifyMessage(DnsMessage dnsMessage, byte[] b, KEYRecord key, SIGRecord previous) throws DNSSEC.DNSSECException {
SIGRecord sig = null;
DnsRecord[] additional = dnsMessage.getSectionArray(DnsSection.ADDITIONAL);
for (int i = 0; i < additional.length; i++) {
if (additional[i].getType() != DnsRecordType.SIG) {
continue;
}
if (((SIGRecord) additional[i]).getTypeCovered() != 0) {
continue;
}
sig = (SIGRecord) additional[i];
break;
}
DNSSEC.verifyMessage(dnsMessage, b, sig, previous, key);
}
}

View File

@ -0,0 +1,229 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.util.Date;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.FormattedTime;
import dorkbox.network.dns.utils.Options;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.Base64Fast;
import dorkbox.util.OS;
/**
* The base class for SIG/RRSIG records, which have identical formats
*
* @author Brian Wellington
*/
abstract
class SIGBase extends DnsRecord {
private static final long serialVersionUID = -3738444391533812369L;
protected int covered;
protected int alg, labels;
protected long origttl;
protected Date expire, timeSigned;
protected int footprint;
protected Name signer;
protected byte[] signature;
protected
SIGBase() {}
public
SIGBase(Name name,
int type,
int dclass,
long ttl,
int covered,
int alg,
long origttl,
Date expire,
Date timeSigned,
int footprint,
Name signer,
byte[] signature) {
super(name, type, dclass, ttl);
DnsRecordType.check(covered);
TTL.check(origttl);
this.covered = covered;
this.alg = checkU8("alg", alg);
this.labels = name.labels() - 1;
if (name.isWild()) {
this.labels--;
}
this.origttl = origttl;
this.expire = expire;
this.timeSigned = timeSigned;
this.footprint = checkU16("footprint", footprint);
this.signer = checkName("signer", signer);
this.signature = signature;
}
@Override
void rrFromWire(DnsInput in) throws IOException {
covered = in.readU16();
alg = in.readU8();
labels = in.readU8();
origttl = in.readU32();
expire = new Date(1000 * in.readU32());
timeSigned = new Date(1000 * in.readU32());
footprint = in.readU16();
signer = new Name(in);
signature = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(covered);
out.writeU8(alg);
out.writeU8(labels);
out.writeU32(origttl);
out.writeU32(expire.getTime() / 1000);
out.writeU32(timeSigned.getTime() / 1000);
out.writeU16(footprint);
signer.toWire(out, null, canonical);
out.writeByteArray(signature);
}
/**
* Converts the RRSIG/SIG Record to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(DnsRecordType.string(covered));
sb.append(" ");
sb.append(alg);
sb.append(" ");
sb.append(labels);
sb.append(" ");
sb.append(origttl);
sb.append(" ");
if (Options.check("multiline")) {
sb.append("(\n\t");
}
sb.append(FormattedTime.format(expire));
sb.append(" ");
sb.append(FormattedTime.format(timeSigned));
sb.append(" ");
sb.append(footprint);
sb.append(" ");
sb.append(signer);
if (Options.check("multiline")) {
sb.append(OS.LINE_SEPARATOR);
sb.append(Base64Fast.formatString(Base64Fast.encode2(signature), 64, "\t", true));
}
else {
sb.append(" ");
sb.append(Base64Fast.encode2(signature));
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
String typeString = st.getString();
covered = DnsRecordType.value(typeString);
if (covered < 0) {
throw st.exception("Invalid type: " + typeString);
}
String algString = st.getString();
alg = DNSSEC.Algorithm.value(algString);
if (alg < 0) {
throw st.exception("Invalid algorithm: " + algString);
}
labels = st.getUInt8();
origttl = st.getTTL();
expire = FormattedTime.parse(st.getString());
timeSigned = FormattedTime.parse(st.getString());
footprint = st.getUInt16();
signer = st.getName(origin);
signature = st.getBase64();
}
/**
* Returns the RRset type covered by this signature
*/
public
int getTypeCovered() {
return covered;
}
/**
* Returns the cryptographic algorithm of the key that generated the signature
*/
public
int getAlgorithm() {
return alg;
}
/**
* Returns the number of labels in the signed domain name. This may be
* different than the record's domain name if the record is a wildcard
* record.
*/
public
int getLabels() {
return labels;
}
/**
* Returns the original TTL of the RRset
*/
public
long getOrigTTL() {
return origttl;
}
/**
* Returns the time at which the signature expires
*/
public
Date getExpire() {
return expire;
}
/**
* Returns the time at which this signature was generated
*/
public
Date getTimeSigned() {
return timeSigned;
}
/**
* Returns The footprint/key id of the signing key.
*/
public
int getFootprint() {
return footprint;
}
/**
* Returns the owner of the signing key
*/
public
Name getSigner() {
return signer;
}
/**
* Returns the binary data representing the signature
*/
public
byte[] getSignature() {
return signature;
}
void setSignature(byte[] signature) {
this.signature = signature;
}
}

View File

@ -0,0 +1,61 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.util.Date;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Signature - A SIG provides the digital signature of an RRset, so that
* the data can be authenticated by a DNSSEC-capable resolver. The
* signature is usually generated by a key contained in a KEYRecord
*
* @author Brian Wellington
* @see RRset
* @see DNSSEC
* @see KEYRecord
*/
public
class SIGRecord extends SIGBase {
private static final long serialVersionUID = 4963556060953589058L;
SIGRecord() {}
@Override
DnsRecord getObject() {
return new SIGRecord();
}
/**
* Creates an SIG Record from the given data
*
* @param covered The RRset type covered by this signature
* @param alg The cryptographic algorithm of the key that generated the
* signature
* @param origttl The original TTL of the RRset
* @param expire The time at which the signature expires
* @param timeSigned The time at which this signature was generated
* @param footprint The footprint/key id of the signing key.
* @param signer The owner of the signing key
* @param signature Binary data representing the signature
*/
public
SIGRecord(Name name,
int dclass,
long ttl,
int covered,
int alg,
long origttl,
Date expire,
Date timeSigned,
int footprint,
Name signer,
byte[] signature) {
super(name, DnsRecordType.SIG, dclass, ttl, covered, alg, origttl, expire, timeSigned, footprint, signer, signature);
}
}

View File

@ -0,0 +1,178 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* S/MIME cert association, draft-ietf-dane-smime.
*
* @author Brian Wellington
*/
public
class SMIMEARecord extends DnsRecord {
private static final long serialVersionUID = 1640247915216425235L;
// Note; these are copied from the TLSA type.
private int certificateUsage;
private int selector;
private int matchingType;
private byte[] certificateAssociationData;
public static
class CertificateUsage {
public static final int CA_CONSTRAINT = 0;
public static final int SERVICE_CERTIFICATE_CONSTRAINT = 1;
public static final int TRUST_ANCHOR_ASSERTION = 2;
public static final int DOMAIN_ISSUED_CERTIFICATE = 3;
private
CertificateUsage() {}
}
public static
class Selector {
/**
* Full certificate; the Certificate binary structure defined in
* [RFC5280]
*/
public static final int FULL_CERTIFICATE = 0;
/**
* SubjectPublicKeyInfo; DER-encoded binary structure defined in
* [RFC5280]
*/
public static final int SUBJECT_PUBLIC_KEY_INFO = 1;
private
Selector() {}
}
public static
class MatchingType {
/**
* Exact match on selected content
*/
public static final int EXACT = 0;
/**
* SHA-256 hash of selected content [RFC6234]
*/
public static final int SHA256 = 1;
/**
* SHA-512 hash of selected content [RFC6234]
*/
public static final int SHA512 = 2;
private
MatchingType() {}
}
SMIMEARecord() {}
@Override
DnsRecord getObject() {
return new SMIMEARecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
certificateUsage = in.readU8();
selector = in.readU8();
matchingType = in.readU8();
certificateAssociationData = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(certificateUsage);
out.writeU8(selector);
out.writeU8(matchingType);
out.writeByteArray(certificateAssociationData);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(certificateUsage);
sb.append(" ");
sb.append(selector);
sb.append(" ");
sb.append(matchingType);
sb.append(" ");
sb.append(base16.toString(certificateAssociationData));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
certificateUsage = st.getUInt8();
selector = st.getUInt8();
matchingType = st.getUInt8();
certificateAssociationData = st.getHex();
}
/**
* Creates an SMIMEA Record from the given data
*
* @param certificateUsage The provided association that will be used to
* match the certificate presented in the S/MIME handshake.
* @param selector The part of the S/MIME certificate presented by the server
* that will be matched against the association data.
* @param matchingType How the certificate association is presented.
* @param certificateAssociationData The "certificate association data" to be
* matched.
*/
public
SMIMEARecord(Name name, int dclass, long ttl, int certificateUsage, int selector, int matchingType, byte[] certificateAssociationData) {
super(name, DnsRecordType.SMIMEA, dclass, ttl);
this.certificateUsage = checkU8("certificateUsage", certificateUsage);
this.selector = checkU8("selector", selector);
this.matchingType = checkU8("matchingType", matchingType);
this.certificateAssociationData = checkByteArrayLength("certificateAssociationData", certificateAssociationData, 0xFFFF);
}
/**
* Returns the certificate usage of the SMIMEA record
*/
public
int getCertificateUsage() {
return certificateUsage;
}
/**
* Returns the selector of the SMIMEA record
*/
public
int getSelector() {
return selector;
}
/**
* Returns the matching type of the SMIMEA record
*/
public
int getMatchingType() {
return matchingType;
}
/**
* Returns the certificate associate data of this SMIMEA record
*/
public final
byte[] getCertificateAssociationData() {
return certificateAssociationData;
}
}

View File

@ -0,0 +1,186 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Options;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Start of Authority - describes properties of a zone.
*
* @author Brian Wellington
*/
public
class SOARecord extends DnsRecord {
private static final long serialVersionUID = 1049740098229303931L;
private Name host, admin;
private long serial, refresh, retry, expire, minimum;
SOARecord() {}
@Override
DnsRecord getObject() {
return new SOARecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
host = new Name(in);
admin = new Name(in);
serial = in.readU32();
refresh = in.readU32();
retry = in.readU32();
expire = in.readU32();
minimum = in.readU32();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
host.toWire(out, c, canonical);
admin.toWire(out, c, canonical);
out.writeU32(serial);
out.writeU32(refresh);
out.writeU32(retry);
out.writeU32(expire);
out.writeU32(minimum);
}
/**
* Convert to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(host);
sb.append(" ");
sb.append(admin);
if (Options.check("multiline")) {
sb.append(" (\n\t\t\t\t\t");
sb.append(serial);
sb.append("\t; serial\n\t\t\t\t\t");
sb.append(refresh);
sb.append("\t; refresh\n\t\t\t\t\t");
sb.append(retry);
sb.append("\t; retry\n\t\t\t\t\t");
sb.append(expire);
sb.append("\t; expire\n\t\t\t\t\t");
sb.append(minimum);
sb.append(" )\t; minimum");
}
else {
sb.append(" ");
sb.append(serial);
sb.append(" ");
sb.append(refresh);
sb.append(" ");
sb.append(retry);
sb.append(" ");
sb.append(expire);
sb.append(" ");
sb.append(minimum);
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
host = st.getName(origin);
admin = st.getName(origin);
serial = st.getUInt32();
refresh = st.getTTLLike();
retry = st.getTTLLike();
expire = st.getTTLLike();
minimum = st.getTTLLike();
}
/**
* Creates an SOA Record from the given data
*
* @param host The primary name server for the zone
* @param admin The zone administrator's address
* @param serial The zone's serial number
* @param refresh The amount of time until a secondary checks for a new serial
* number
* @param retry The amount of time between a secondary's checks for a new
* serial number
* @param expire The amount of time until a secondary expires a zone
* @param minimum The minimum TTL for records in the zone
*/
public
SOARecord(Name name, int dclass, long ttl, Name host, Name admin, long serial, long refresh, long retry, long expire, long minimum) {
super(name, DnsRecordType.SOA, dclass, ttl);
this.host = checkName("host", host);
this.admin = checkName("admin", admin);
this.serial = checkU32("serial", serial);
this.refresh = checkU32("refresh", refresh);
this.retry = checkU32("retry", retry);
this.expire = checkU32("expire", expire);
this.minimum = checkU32("minimum", minimum);
}
/**
* Returns the primary name server
*/
public
Name getHost() {
return host;
}
/**
* Returns the zone administrator's address
*/
public
Name getAdmin() {
return admin;
}
/**
* Returns the zone's serial number
*/
public
long getSerial() {
return serial;
}
/**
* Returns the zone refresh interval
*/
public
long getRefresh() {
return refresh;
}
/**
* Returns the zone retry interval
*/
public
long getRetry() {
return retry;
}
/**
* Returns the time until a secondary expires a zone
*/
public
long getExpire() {
return expire;
}
/**
* Returns the minimum TTL for records in the zone
*/
public
long getMinimum() {
return minimum;
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.util.List;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
/**
* Sender Policy Framework (RFC 4408, experimental)
*
* @author Brian Wellington
*/
public
class SPFRecord extends TXTBase {
private static final long serialVersionUID = -2100754352801658722L;
SPFRecord() {}
@Override
DnsRecord getObject() {
return new SPFRecord();
}
/**
* Creates a SPF Record from the given data
*
* @param strings The text strings
*
* @throws IllegalArgumentException One of the strings has invalid escapes
*/
public
SPFRecord(Name name, int dclass, long ttl, List strings) {
super(name, DnsRecordType.SPF, dclass, ttl, strings);
}
/**
* Creates a SPF Record from the given data
*
* @param string One text string
*
* @throws IllegalArgumentException The string has invalid escapes
*/
public
SPFRecord(Name name, int dclass, long ttl, String string) {
super(name, DnsRecordType.SPF, dclass, ttl, string);
}
}

View File

@ -0,0 +1,130 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Server Selection Record - finds hosts running services in a domain. An
* SRV record will normally be named _&lt;service&gt;._&lt;protocol&gt;.domain
* - examples would be _sips._tcp.example.org (for the secure SIP protocol) and
* _http._tcp.example.com (if HTTP used SRV records)
*
* @author Brian Wellington
*/
public
class SRVRecord extends DnsRecord {
private static final long serialVersionUID = -3886460132387522052L;
private int priority, weight, port;
private Name target;
SRVRecord() {}
@Override
DnsRecord getObject() {
return new SRVRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
priority = in.readU16();
weight = in.readU16();
port = in.readU16();
target = new Name(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU16(priority);
out.writeU16(weight);
out.writeU16(port);
target.toWire(out, null, canonical);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(priority + " ");
sb.append(weight + " ");
sb.append(port + " ");
sb.append(target);
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
priority = st.getUInt16();
weight = st.getUInt16();
port = st.getUInt16();
target = st.getName(origin);
}
@Override
public
Name getAdditionalName() {
return target;
}
/**
* Creates an SRV Record from the given data
*
* @param priority The priority of this SRV. Records with lower priority
* are preferred.
* @param weight The weight, used to select between records at the same
* priority.
* @param port The TCP/UDP port that the service uses
* @param target The host running the service
*/
public
SRVRecord(Name name, int dclass, long ttl, int priority, int weight, int port, Name target) {
super(name, DnsRecordType.SRV, dclass, ttl);
this.priority = checkU16("priority", priority);
this.weight = checkU16("weight", weight);
this.port = checkU16("port", port);
this.target = checkName("target", target);
}
/**
* Returns the priority
*/
public
int getPriority() {
return priority;
}
/**
* Returns the weight
*/
public
int getWeight() {
return weight;
}
/**
* Returns the port that the service runs on
*/
public
int getPort() {
return port;
}
/**
* Returns the host running that the service
*/
public
Name getTarget() {
return target;
}
}

View File

@ -0,0 +1,123 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* SSH Fingerprint - stores the fingerprint of an SSH host key.
*
* @author Brian Wellington
*/
public
class SSHFPRecord extends DnsRecord {
private static final long serialVersionUID = -8104701402654687025L;
private int alg;
private int digestType;
private byte[] fingerprint;
public static
class Algorithm {
public static final int RSA = 1;
public static final int DSS = 2;
private
Algorithm() {}
}
public static
class Digest {
public static final int SHA1 = 1;
private
Digest() {}
}
SSHFPRecord() {}
@Override
DnsRecord getObject() {
return new SSHFPRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
alg = in.readU8();
digestType = in.readU8();
fingerprint = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(alg);
out.writeU8(digestType);
out.writeByteArray(fingerprint);
}
@Override
void rrToString(StringBuilder sb) {
sb.append(alg);
sb.append(" ");
sb.append(digestType);
sb.append(" ");
sb.append(base16.toString(fingerprint));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
alg = st.getUInt8();
digestType = st.getUInt8();
fingerprint = st.getHex(true);
}
/**
* Creates an SSHFP Record from the given data.
*
* @param alg The public key's algorithm.
* @param digestType The public key's digest type.
* @param fingerprint The public key's fingerprint.
*/
public
SSHFPRecord(Name name, int dclass, long ttl, int alg, int digestType, byte[] fingerprint) {
super(name, DnsRecordType.SSHFP, dclass, ttl);
this.alg = checkU8("alg", alg);
this.digestType = checkU8("digestType", digestType);
this.fingerprint = fingerprint;
}
/**
* Returns the public key's algorithm.
*/
public
int getAlgorithm() {
return alg;
}
/**
* Returns the public key's digest type.
*/
public
int getDigestType() {
return digestType;
}
/**
* Returns the fingerprint
*/
public
byte[] getFingerPrint() {
return fingerprint;
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
/**
* Implements common functionality for the many record types whose format
* is a single compressed name.
*
* @author Brian Wellington
*/
abstract
class SingleCompressedNameBase extends SingleNameBase {
private static final long serialVersionUID = -236435396815460677L;
protected
SingleCompressedNameBase() {}
protected
SingleCompressedNameBase(Name name, int type, int dclass, long ttl, Name singleName, String description) {
super(name, type, dclass, ttl, singleName, description);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
singleName.toWire(out, c, canonical);
}
}

View File

@ -0,0 +1,66 @@
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.utils.Tokenizer;
/**
* Implements common functionality for the many record types whose format
* is a single name.
*
* @author Brian Wellington
*/
abstract
class SingleNameBase extends DnsRecord {
private static final long serialVersionUID = -18595042501413L;
protected Name singleName;
protected
SingleNameBase() {}
protected
SingleNameBase(Name name, int type, int dclass, long ttl) {
super(name, type, dclass, ttl);
}
protected
SingleNameBase(Name name, int type, int dclass, long ttl, Name singleName, String description) {
super(name, type, dclass, ttl);
this.singleName = checkName(description, singleName);
}
@Override
void rrFromWire(DnsInput in) throws IOException {
singleName = new Name(in);
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
singleName.toWire(out, null, canonical);
}
@Override
void rrToString(StringBuilder sb) {
sb.append(singleName.toString());
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
singleName = st.getName(origin);
}
protected
Name getSingleName() {
return singleName;
}
}

View File

@ -0,0 +1,285 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import java.util.Date;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsResponseCode;
import dorkbox.network.dns.utils.FormattedTime;
import dorkbox.network.dns.utils.Options;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.Base64Fast;
import dorkbox.util.OS;
/**
* Transaction Key - used to compute and/or securely transport a shared
* secret to be used with TSIG.
*
* @author Brian Wellington
* @see TSIG
*/
public
class TKEYRecord extends DnsRecord {
private static final long serialVersionUID = 8828458121926391756L;
private Name alg;
private Date timeInception;
private Date timeExpire;
private int mode, error;
private byte[] key;
private byte[] other;
/**
* The key is assigned by the server (unimplemented)
*/
public static final int SERVERASSIGNED = 1;
/**
* The key is computed using a Diffie-Hellman key exchange
*/
public static final int DIFFIEHELLMAN = 2;
/**
* The key is computed using GSS_API (unimplemented)
*/
public static final int GSSAPI = 3;
/**
* The key is assigned by the resolver (unimplemented)
*/
public static final int RESOLVERASSIGNED = 4;
/**
* The key should be deleted
*/
public static final int DELETE = 5;
TKEYRecord() {}
@Override
DnsRecord getObject() {
return new TKEYRecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
alg = new Name(in);
timeInception = new Date(1000 * in.readU32());
timeExpire = new Date(1000 * in.readU32());
mode = in.readU16();
error = in.readU16();
int keylen = in.readU16();
if (keylen > 0) {
key = in.readByteArray(keylen);
}
else {
key = null;
}
int otherlen = in.readU16();
if (otherlen > 0) {
other = in.readByteArray(otherlen);
}
else {
other = null;
}
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
alg.toWire(out, null, canonical);
out.writeU32(timeInception.getTime() / 1000);
out.writeU32(timeExpire.getTime() / 1000);
out.writeU16(mode);
out.writeU16(error);
if (key != null) {
out.writeU16(key.length);
out.writeByteArray(key);
}
else {
out.writeU16(0);
}
if (other != null) {
out.writeU16(other.length);
out.writeByteArray(other);
}
else {
out.writeU16(0);
}
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(alg);
sb.append(" ");
if (Options.check("multiline")) {
sb.append("(")
.append(OS.LINE_SEPARATOR)
.append("\t");
}
sb.append(FormattedTime.format(timeInception));
sb.append(" ");
sb.append(FormattedTime.format(timeExpire));
sb.append(" ");
sb.append(modeString());
sb.append(" ");
sb.append(DnsResponseCode.TSIGstring(error));
if (Options.check("multiline")) {
sb.append(OS.LINE_SEPARATOR);
if (key != null) {
sb.append(Base64Fast.formatString(Base64Fast.encode2(key), 64, "\t", true));
sb.append(OS.LINE_SEPARATOR);
}
if (other != null) {
sb.append(Base64Fast.formatString(Base64Fast.encode2(other), 64, "\t", true));
}
sb.append(" )");
}
else {
sb.append(" ");
if (key != null) {
sb.append("\t");
sb.append(Base64Fast.encode2(key));
sb.append(" ");
}
if (other != null) {
sb.append("\t");
sb.append(Base64Fast.encode2(other));
}
}
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
throw st.exception("no text format defined for TKEY");
}
protected
String modeString() {
switch (mode) {
case SERVERASSIGNED:
return "SERVERASSIGNED";
case DIFFIEHELLMAN:
return "DIFFIEHELLMAN";
case GSSAPI:
return "GSSAPI";
case RESOLVERASSIGNED:
return "RESOLVERASSIGNED";
case DELETE:
return "DELETE";
default:
return Integer.toString(mode);
}
}
/**
* Creates a TKEY Record from the given data.
*
* @param alg The shared key's algorithm
* @param timeInception The beginning of the validity period of the shared
* secret or keying material
* @param timeExpire The end of the validity period of the shared
* secret or keying material
* @param mode The mode of key agreement
* @param error The extended error field. Should be 0 in queries
* @param key The shared secret
* @param other The other data field. Currently unused
* responses.
*/
public
TKEYRecord(Name name,
int dclass,
long ttl,
Name alg,
Date timeInception,
Date timeExpire,
int mode,
int error,
byte[] key,
byte other[]) {
super(name, DnsRecordType.TKEY, dclass, ttl);
this.alg = checkName("alg", alg);
this.timeInception = timeInception;
this.timeExpire = timeExpire;
this.mode = checkU16("mode", mode);
this.error = checkU16("error", error);
this.key = key;
this.other = other;
}
/**
* Returns the shared key's algorithm
*/
public
Name getAlgorithm() {
return alg;
}
/**
* Returns the beginning of the validity period of the shared secret or
* keying material
*/
public
Date getTimeInception() {
return timeInception;
}
/**
* Returns the end of the validity period of the shared secret or
* keying material
*/
public
Date getTimeExpire() {
return timeExpire;
}
/**
* Returns the key agreement mode
*/
public
int getMode() {
return mode;
}
/**
* Returns the extended error
*/
public
int getError() {
return error;
}
/**
* Returns the shared secret or keying material
*/
public
byte[] getKey() {
return key;
}
/**
* Returns the other data
*/
public
byte[] getOther() {
return other;
}
}

View File

@ -0,0 +1,176 @@
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package dorkbox.network.dns.records;
import java.io.IOException;
import dorkbox.network.dns.Compression;
import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.network.dns.utils.base16;
/**
* Transport Layer Security Authentication
*
* @author Brian Wellington
*/
public
class TLSARecord extends DnsRecord {
private static final long serialVersionUID = 356494267028580169L;
private int certificateUsage;
private int selector;
private int matchingType;
private byte[] certificateAssociationData;
public static
class CertificateUsage {
public static final int CA_CONSTRAINT = 0;
public static final int SERVICE_CERTIFICATE_CONSTRAINT = 1;
public static final int TRUST_ANCHOR_ASSERTION = 2;
public static final int DOMAIN_ISSUED_CERTIFICATE = 3;
private
CertificateUsage() {}
}
public static
class Selector {
/**
* Full certificate; the Certificate binary structure defined in
* [RFC5280]
*/
public static final int FULL_CERTIFICATE = 0;
/**
* SubjectPublicKeyInfo; DER-encoded binary structure defined in
* [RFC5280]
*/
public static final int SUBJECT_PUBLIC_KEY_INFO = 1;
private
Selector() {}
}
public static
class MatchingType {
/**
* Exact match on selected content
*/
public static final int EXACT = 0;
/**
* SHA-256 hash of selected content [RFC6234]
*/
public static final int SHA256 = 1;
/**
* SHA-512 hash of selected content [RFC6234]
*/
public static final int SHA512 = 2;
private
MatchingType() {}
}
TLSARecord() {}
@Override
DnsRecord getObject() {
return new TLSARecord();
}
@Override
void rrFromWire(DnsInput in) throws IOException {
certificateUsage = in.readU8();
selector = in.readU8();
matchingType = in.readU8();
certificateAssociationData = in.readByteArray();
}
@Override
void rrToWire(DnsOutput out, Compression c, boolean canonical) {
out.writeU8(certificateUsage);
out.writeU8(selector);
out.writeU8(matchingType);
out.writeByteArray(certificateAssociationData);
}
/**
* Converts rdata to a String
*/
@Override
void rrToString(StringBuilder sb) {
sb.append(certificateUsage);
sb.append(" ");
sb.append(selector);
sb.append(" ");
sb.append(matchingType);
sb.append(" ");
sb.append(base16.toString(certificateAssociationData));
}
@Override
void rdataFromString(Tokenizer st, Name origin) throws IOException {
certificateUsage = st.getUInt8();
selector = st.getUInt8();
matchingType = st.getUInt8();
certificateAssociationData = st.getHex();
}
/**
* Creates an TLSA Record from the given data
*
* @param certificateUsage The provided association that will be used to
* match the certificate presented in the TLS handshake.
* @param selector The part of the TLS certificate presented by the server
* that will be matched against the association data.
* @param matchingType How the certificate association is presented.
* @param certificateAssociationData The "certificate association data" to be
* matched.
*/
public
TLSARecord(Name name, int dclass, long ttl, int certificateUsage, int selector, int matchingType, byte[] certificateAssociationData) {
super(name, DnsRecordType.TLSA, dclass, ttl);
this.certificateUsage = checkU8("certificateUsage", certificateUsage);
this.selector = checkU8("selector", selector);
this.matchingType = checkU8("matchingType", matchingType);
this.certificateAssociationData = checkByteArrayLength("certificateAssociationData", certificateAssociationData, 0xFFFF);
}
/**
* Returns the certificate usage of the TLSA record
*/
public
int getCertificateUsage() {
return certificateUsage;
}
/**
* Returns the selector of the TLSA record
*/
public
int getSelector() {
return selector;
}
/**
* Returns the matching type of the TLSA record
*/
public
int getMatchingType() {
return matchingType;
}
/**
* Returns the certificate associate data of this TLSA record
*/
public final
byte[] getCertificateAssociationData() {
return certificateAssociationData;
}
}

Some files were not shown because too many files have changed in this diff Show More