package de.measite.minidns;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import de.measite.minidns.record.A;
import de.measite.minidns.record.AAAA;
import de.measite.minidns.record.CNAME;
import de.measite.minidns.record.Data;
import de.measite.minidns.record.NS;
import de.measite.minidns.record.SRV;
import de.measite.minidns.util.NameUtil;
/**
* A generic DNS record.
*/
public class Record {
/**
* The record type.
* @see IANA DNS Parameters
*/
public static enum TYPE {
A(1),
NS(2),
MD(3),
MF(4),
CNAME(5),
SOA(6),
MB(7),
MG(8),
MR(9),
NULL(10),
WKS(11),
PTR(12),
HINFO(13),
MINFO(14),
MX(15),
TXT(16),
RP(17),
AFSDB(18),
X25(19),
ISDN(20),
RT(21),
NSAP(22),
NSAP_PTR(23),
SIG(24),
KEY(25),
PX(26),
GPOS(27),
AAAA(28),
LOC(29),
NXT(30),
EID(31),
NIMLOC(32),
SRV(33),
ATMA(34),
NAPTR(35),
KX(36),
CERT(37),
A6(38),
DNAME(39),
SINK(40),
OPT(41),
APL(42),
DS(43),
SSHFP(44),
IPSECKEY(45),
RRSIG(46),
NSEC(47),
DNSKEY(48),
DHCID(49),
NSEC3(50),
NSEC3PARAM(51),
HIP(55),
NINFO(56),
RKEY(57),
TALINK(58),
SPF(99),
UINFO(100),
UID(101),
GID(102),
TKEY(249),
TSIG(250),
IXFR(251),
AXFR(252),
MAILB(253),
MAILA(254),
ANY(255),
TA(32768),
DLV(32769);
/**
* The value of this DNS record type.
*/
private final int value;
/**
* Internal lookup table to map values to types.
*/
private final static HashMap INVERSE_LUT =
new HashMap();
/**
* Initialize the reverse lookup table.
*/
static {
for(TYPE t: TYPE.values()) {
INVERSE_LUT.put(t.getValue(), t);
}
}
/**
* Create a new record type.
* @param value The binary value of this type.
*/
private TYPE(int value) {
this.value = value;
}
/**
* Retrieve the binary value of this type.
* @return The binary value.
*/
public int getValue() {
return value;
}
/**
* Retrieve the symbolic type of the binary value.
* @param value The binary type value.
* @return The symbolic tpye.
*/
public static TYPE getType(int value) {
return INVERSE_LUT.get(value);
}
};
/**
* The symbolic class of a DNS record (usually IN for Internet).
*/
public static enum CLASS {
IN(1),
CH(3),
HS(4),
NONE(254),
ANY(255);
/**
* Internal reverse lookup table to map binary class values to symbolic
* names.
*/
private final static HashMap INVERSE_LUT =
new HashMap();
/**
* Initialize the interal reverse lookup table.
*/
static {
for(CLASS c: CLASS.values()) {
INVERSE_LUT.put(c.getValue(), c);
}
}
/**
* The binary value of this dns class.
*/
private final int value;
/**
* Create a new DNS class based on a binary value.
* @param value The binary value of this DNS class.
*/
private CLASS(int value) {
this.value = value;
}
/**
* Retrieve the binary value of this DNS class.
* @return The binary value of this DNS class.
*/
public int getValue() {
return value;
}
/**
* Retrieve the symbolic DNS class for a binary class value.
* @param value The binary DNS class value.
* @return The symbolic class instance.
*/
public static CLASS getClass(int value) {
return INVERSE_LUT.get(value);
}
}
/**
* The generic name of this record.
*/
protected String name;
/**
* The type (and payload type) of this record.
*/
protected TYPE type;
/**
* The record class (usually CLASS.IN).
*/
protected CLASS clazz;
/**
* The ttl of this record.
*/
protected long ttl;
/**
* The payload object of this record.
*/
protected Data payloadData;
/**
* Parse a given record based on the full message data and the current
* stream position.
* @param dis The DataInputStream positioned at the first record byte.
* @param data The full message data.
* @throws IOException In case of malformed replies.
*/
public void parse(DataInputStream dis, byte[] data) throws IOException {
this.name = NameUtil.parse(dis, data);
this.type = TYPE.getType(dis.readUnsignedShort());
this.clazz = CLASS.getClass(dis.readUnsignedShort());
this.ttl = (((long)dis.readUnsignedShort()) << 32) +
dis.readUnsignedShort();
int payloadLength = dis.readUnsignedShort();
switch (this.type) {
case SRV:
this.payloadData = new SRV();
break;
case AAAA:
this.payloadData = new AAAA();
break;
case A:
this.payloadData = new A();
break;
case NS:
this.payloadData = new NS();
break;
case CNAME:
this.payloadData = new CNAME();
break;
default:
System.out.println("Unparsed type " + type);
this.payloadData = null;
for (int i = 0; i < payloadLength; i++) {
dis.readByte();
}
break;
}
if (this.payloadData != null) {
this.payloadData.parse(dis, data, payloadLength);
}
}
/**
* Retrieve a textual representation of this resource record.
* @return String
*/
@Override
public String toString() {
if (payloadData == null) {
return "RR " + type + "/" + clazz;
}
return "RR " + type + "/" + clazz + ": " + payloadData.toString();
};
/**
* Check if this record answers a given query.
* @param q The query.
* @return True if this record is a valid answer.
*/
public boolean isAnswer(Question q) {
return ((q.getType() == type) || (q.getType() == TYPE.ANY)) &&
((q.getClazz() == clazz) || (q.getClazz() == CLASS.ANY)) &&
(q.getName().equals(name));
}
/**
* The generic record name, e.g. "measite.de".
* @return The record name.
*/
public String getName() {
return name;
}
/**
* The payload data, usually a subclass of data (A, AAAA, CNAME, ...).
* @return The payload data.
*/
public Data getPayload() {
return payloadData;
}
/**
* Retrieve the record ttl.
* @return The record ttl.
*/
public long getTtl() {
return ttl;
}
}