363 lines
8.4 KiB
Java
Executable File
363 lines
8.4 KiB
Java
Executable File
package org.handwerkszeug.dns;
|
|
|
|
import java.security.SecureRandom;
|
|
import java.text.MessageFormat;
|
|
|
|
import org.handwerkszeug.dns.nls.Messages;
|
|
|
|
import io.netty.buffer.ByteBuf;
|
|
|
|
/**
|
|
* RFC1035 4.1.1. Header section format
|
|
*
|
|
* <pre>
|
|
* 1 1 1 1 1 1
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* | ID |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* |QR| DnsOpCode |AA|TC|RD|RA| Z | RCODE |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* | QDCOUNT |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* | ANCOUNT |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* | NSCOUNT |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* | ARCOUNT |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* </pre>
|
|
*/
|
|
public class Header {
|
|
|
|
static final int MIN_USHORT = 0;
|
|
static final int MAX_USHORT = 0xFFFF;
|
|
|
|
static final int FLAGS_QR = 15;
|
|
static final int FLAGS_Opcode = 11;
|
|
static final int FLAGS_AA = 10;
|
|
static final int FLAGS_TC = 9;
|
|
static final int FLAGS_RD = 8;
|
|
static final int FLAGS_RA = 7;
|
|
static final int FLAGS_Z = 4;
|
|
static final int FLAGS_RCODE = 0;
|
|
|
|
static final int[] FLAGS_ALL = { FLAGS_QR, FLAGS_AA, FLAGS_TC, FLAGS_RD, FLAGS_RA };
|
|
static final String[] FLAGS_TXT = { "qr", "aa", "tc", "rd", "ra" };
|
|
|
|
static final SecureRandom RANDOM;
|
|
|
|
static {
|
|
RANDOM = new SecureRandom();
|
|
byte[] seed = RANDOM.generateSeed(20); // TODO more ? from config?
|
|
RANDOM.setSeed(seed);
|
|
}
|
|
|
|
protected int id;
|
|
// include there flags |QR| DnsOpCode |AA|TC|RD|RA| Z | RCODE|
|
|
protected int flags;
|
|
|
|
protected int qdcount;
|
|
protected int ancount;
|
|
protected int nscount;
|
|
protected int arcount;
|
|
|
|
public Header() {
|
|
this(RANDOM.nextInt(MAX_USHORT));
|
|
}
|
|
|
|
protected Header(int id) {
|
|
id(id);
|
|
}
|
|
|
|
public Header(ByteBuf in) {
|
|
this(in.readUnsignedShort());
|
|
flags(in.readUnsignedShort());
|
|
qdcount(in.readUnsignedShort());
|
|
ancount(in.readUnsignedShort());
|
|
nscount(in.readUnsignedShort());
|
|
arcount(in.readUnsignedShort());
|
|
}
|
|
|
|
public Header(Header from) {
|
|
this();
|
|
|
|
this.flags(from.flags());
|
|
this.qdcount(from.qdcount());
|
|
this.ancount(from.ancount());
|
|
this.nscount(from.nscount());
|
|
this.arcount(from.arcount());
|
|
}
|
|
|
|
public void write(ByteBuf out) {
|
|
out.writeShort(id());
|
|
out.writeShort(flags());
|
|
out.writeShort(qdcount());
|
|
out.writeShort(ancount());
|
|
out.writeShort(nscount());
|
|
out.writeShort(arcount());
|
|
}
|
|
|
|
/**
|
|
* A 16 bit identifier assigned by the program that generates any kind of
|
|
* query. This identifier is copied the corresponding reply and can be used
|
|
* by the requester to match up replies to outstanding queries.
|
|
*
|
|
*/
|
|
public int id() {
|
|
return this.id;
|
|
}
|
|
|
|
public void id(int i) {
|
|
this.id = verify16bitValue("ID", i);
|
|
}
|
|
|
|
private static int verify16bitValue(String column, int i) {
|
|
if ((i < MIN_USHORT) || (MAX_USHORT < i)) {
|
|
throw new IllegalArgumentException(String.format(
|
|
Messages.Not16bitValue, column, i));
|
|
}
|
|
return i;
|
|
}
|
|
|
|
public int flags() {
|
|
return this.flags;
|
|
}
|
|
|
|
protected void flags(int flags) {
|
|
this.flags = verify16bitValue("Flags", flags);
|
|
}
|
|
|
|
protected int flag(int shift, int mask) {
|
|
return (this.flags >> shift) & mask;
|
|
}
|
|
|
|
/**
|
|
* A one bit field that specifies whether this message is a query (0), or a
|
|
* response (1).
|
|
*/
|
|
public boolean qr() {
|
|
return flag(FLAGS_QR, 0x1) != 0;
|
|
}
|
|
|
|
public void qr(boolean is) {
|
|
flip(FLAGS_QR, is);
|
|
}
|
|
|
|
/**
|
|
* @see OpCode
|
|
*/
|
|
public OpCode opcode() {
|
|
int code = flag(FLAGS_Opcode, 0xF);
|
|
return OpCode.valueOf(code); // TODO cache?
|
|
}
|
|
|
|
public void opcode(OpCode op) {
|
|
// clear current opcode
|
|
this.flags &= 0x87FF; // 1000 0111 1111 1111
|
|
// set opcode
|
|
this.flags |= op.value() << FLAGS_Opcode;
|
|
}
|
|
|
|
/**
|
|
* Authoritative Answer - this bit is valid in responses, and specifies that
|
|
* the responding name server is an authority for the domain name in
|
|
* question section.
|
|
*/
|
|
public boolean aa() {
|
|
return flag(FLAGS_AA, 0x1) != 0;
|
|
}
|
|
|
|
public void aa(boolean is) {
|
|
flip(FLAGS_AA, is);
|
|
}
|
|
|
|
private void flip(int index, boolean is) {
|
|
int i = 1 << index; // TODO move to caller ?
|
|
if (is) {
|
|
this.flags |= i;
|
|
} else {
|
|
this.flags &= i ^ 0xFFFF;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TrunCation - specifies that this message was truncated due to length
|
|
* greater than that permitted on the transmission channel.
|
|
*/
|
|
public boolean tc() {
|
|
return flag(FLAGS_TC, 0x1) != 0;
|
|
}
|
|
|
|
public void tc(boolean is) {
|
|
flip(FLAGS_TC, is);
|
|
}
|
|
|
|
/**
|
|
* Recursion Desired - this bit may be set in a query and is copied into the
|
|
* response. If RD is set, it directs the name server to pursue the query
|
|
* recursively. Recursive query support is optional.
|
|
*/
|
|
public boolean rd() {
|
|
return flag(FLAGS_RD, 0x1) != 0;
|
|
}
|
|
|
|
public void rd(boolean is) {
|
|
flip(FLAGS_RD, is);
|
|
}
|
|
|
|
/**
|
|
* Recursion Available - this be is set or cleared in a response, and
|
|
* denotes whether recursive query support is available in the name server.
|
|
*/
|
|
public boolean ra() {
|
|
return flag(FLAGS_RA, 0x1) != 0;
|
|
}
|
|
|
|
public void ra(boolean is) {
|
|
flip(FLAGS_RA, is);
|
|
}
|
|
|
|
/**
|
|
* Reserved for future use. Must be zero in all queries and responses.
|
|
*/
|
|
public int z() {
|
|
return flag(FLAGS_Z, 0x7);
|
|
}
|
|
|
|
/**
|
|
* @see RCode
|
|
*/
|
|
public RCode rcode() {
|
|
int code = flag(FLAGS_RCODE, 0xF);
|
|
return RCode.valueOf(code); // TODO cache ?
|
|
}
|
|
|
|
public void rcode(RCode rc) {
|
|
// clear current response code
|
|
this.flags &= 0xFFF0; // 1111 1111 1111 0000
|
|
// set response code
|
|
this.flags |= rc.value();
|
|
}
|
|
|
|
/**
|
|
* an unsigned 16 bit integer specifying the number of entries in the
|
|
* question section.
|
|
*/
|
|
public int qdcount() {
|
|
return this.qdcount;
|
|
}
|
|
|
|
public void qdcount(int value) {
|
|
this.qdcount = verify16bitValue("qdcount", value);
|
|
}
|
|
|
|
/**
|
|
* an unsigned 16 bit integer specifying the number of resource records in
|
|
* the answer section.
|
|
*/
|
|
public int ancount() {
|
|
return this.ancount;
|
|
}
|
|
|
|
public void ancount(int value) {
|
|
this.ancount = verify16bitValue("ancount", value);
|
|
}
|
|
|
|
/**
|
|
* an unsigned 16 bit integer specifying the number of name server resource
|
|
* records in the authority records section.
|
|
*/
|
|
public int nscount() {
|
|
return this.nscount;
|
|
}
|
|
|
|
public void nscount(int value) {
|
|
this.nscount = verify16bitValue("nscount", value);
|
|
}
|
|
|
|
/**
|
|
* an unsigned 16 bit integer specifying the number of resource records in
|
|
* the additional records section.
|
|
*/
|
|
public int arcount() {
|
|
return this.arcount;
|
|
}
|
|
|
|
public void arcount(int value) {
|
|
this.arcount = verify16bitValue("arcount", value);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
MessageFormat form = new MessageFormat(";; ->>HEADER<<- "
|
|
+ "opcode: {0}, rcode: {1}, id: {2,number,#}\n"
|
|
+ ";; flags: {3}; QUERY: {4,number,#}, "
|
|
+ "ANSWER: {5,number,#}, " + "AUTHORITY: {6,number,#}, "
|
|
+ "ADDITIONAL: {7,number,#}");
|
|
Object[] args = { opcode().name(), rcode().name(), id(),
|
|
toFlagsString(), qdcount(), ancount(), nscount(), arcount() };
|
|
return form.format(args);
|
|
}
|
|
|
|
protected String toFlagsString() {
|
|
StringBuilder stb = new StringBuilder();
|
|
for (int i = 0, length = FLAGS_ALL.length; i < length; i++) {
|
|
if (flag(FLAGS_ALL[i], 0x1) != 0) {
|
|
stb.append(FLAGS_TXT[i]);
|
|
stb.append(" ");
|
|
}
|
|
}
|
|
return stb.toString();
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
final int prime = 31;
|
|
int result = 1;
|
|
result = prime * result + this.id;
|
|
result = prime * result + this.flags;
|
|
result = prime * result + this.qdcount;
|
|
result = prime * result + this.ancount;
|
|
result = prime * result + this.nscount;
|
|
result = prime * result + this.arcount;
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
} else if (obj instanceof Header) {
|
|
Header other = (Header) obj;
|
|
return equals(other);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean equals(Header other) {
|
|
if (other == null) {
|
|
return false;
|
|
}
|
|
if (this.id != other.id) {
|
|
return false;
|
|
}
|
|
if (this.flags != other.flags) {
|
|
return false;
|
|
}
|
|
if (this.qdcount != other.qdcount) {
|
|
return false;
|
|
}
|
|
if (this.ancount != other.ancount) {
|
|
return false;
|
|
}
|
|
if (this.nscount != other.nscount) {
|
|
return false;
|
|
}
|
|
if (this.arcount != other.arcount) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|