1package de.measite.minidns;
2
3import java.io.DataInputStream;
4import java.io.IOException;
5import java.util.HashMap;
6
7import de.measite.minidns.record.A;
8import de.measite.minidns.record.AAAA;
9import de.measite.minidns.record.CNAME;
10import de.measite.minidns.record.Data;
11import de.measite.minidns.record.NS;
12import de.measite.minidns.record.SRV;
13import de.measite.minidns.util.NameUtil;
14
15/**
16 * A generic DNS record.
17 */
18public class Record {
19
20 /**
21 * The record type.
22 * @see <a href="http://www.iana.org/assignments/dns-parameters">IANA DNS Parameters</a>
23 */
24 public static enum TYPE {
25 A(1),
26 NS(2),
27 MD(3),
28 MF(4),
29 CNAME(5),
30 SOA(6),
31 MB(7),
32 MG(8),
33 MR(9),
34 NULL(10),
35 WKS(11),
36 PTR(12),
37 HINFO(13),
38 MINFO(14),
39 MX(15),
40 TXT(16),
41 RP(17),
42 AFSDB(18),
43 X25(19),
44 ISDN(20),
45 RT(21),
46 NSAP(22),
47 NSAP_PTR(23),
48 SIG(24),
49 KEY(25),
50 PX(26),
51 GPOS(27),
52 AAAA(28),
53 LOC(29),
54 NXT(30),
55 EID(31),
56 NIMLOC(32),
57 SRV(33),
58 ATMA(34),
59 NAPTR(35),
60 KX(36),
61 CERT(37),
62 A6(38),
63 DNAME(39),
64 SINK(40),
65 OPT(41),
66 APL(42),
67 DS(43),
68 SSHFP(44),
69 IPSECKEY(45),
70 RRSIG(46),
71 NSEC(47),
72 DNSKEY(48),
73 DHCID(49),
74 NSEC3(50),
75 NSEC3PARAM(51),
76 HIP(55),
77 NINFO(56),
78 RKEY(57),
79 TALINK(58),
80 SPF(99),
81 UINFO(100),
82 UID(101),
83 GID(102),
84 TKEY(249),
85 TSIG(250),
86 IXFR(251),
87 AXFR(252),
88 MAILB(253),
89 MAILA(254),
90 ANY(255),
91 TA(32768),
92 DLV(32769);
93
94 /**
95 * The value of this DNS record type.
96 */
97 private final int value;
98
99 /**
100 * Internal lookup table to map values to types.
101 */
102 private final static HashMap<Integer, TYPE> INVERSE_LUT =
103 new HashMap<Integer, TYPE>();
104
105 /**
106 * Initialize the reverse lookup table.
107 */
108 static {
109 for(TYPE t: TYPE.values()) {
110 INVERSE_LUT.put(t.getValue(), t);
111 }
112 }
113
114 /**
115 * Create a new record type.
116 * @param value The binary value of this type.
117 */
118 private TYPE(int value) {
119 this.value = value;
120 }
121
122 /**
123 * Retrieve the binary value of this type.
124 * @return The binary value.
125 */
126 public int getValue() {
127 return value;
128 }
129
130 /**
131 * Retrieve the symbolic type of the binary value.
132 * @param value The binary type value.
133 * @return The symbolic tpye.
134 */
135 public static TYPE getType(int value) {
136 return INVERSE_LUT.get(value);
137 }
138 };
139
140 /**
141 * The symbolic class of a DNS record (usually IN for Internet).
142 */
143 public static enum CLASS {
144 IN(1),
145 CH(3),
146 HS(4),
147 NONE(254),
148 ANY(255);
149
150 /**
151 * Internal reverse lookup table to map binary class values to symbolic
152 * names.
153 */
154 private final static HashMap<Integer, CLASS> INVERSE_LUT =
155 new HashMap<Integer, CLASS>();
156
157 /**
158 * Initialize the interal reverse lookup table.
159 */
160 static {
161 for(CLASS c: CLASS.values()) {
162 INVERSE_LUT.put(c.getValue(), c);
163 }
164 }
165
166 /**
167 * The binary value of this dns class.
168 */
169 private final int value;
170
171 /**
172 * Create a new DNS class based on a binary value.
173 * @param value The binary value of this DNS class.
174 */
175 private CLASS(int value) {
176 this.value = value;
177 }
178
179 /**
180 * Retrieve the binary value of this DNS class.
181 * @return The binary value of this DNS class.
182 */
183 public int getValue() {
184 return value;
185 }
186
187 /**
188 * Retrieve the symbolic DNS class for a binary class value.
189 * @param value The binary DNS class value.
190 * @return The symbolic class instance.
191 */
192 public static CLASS getClass(int value) {
193 return INVERSE_LUT.get(value);
194 }
195
196 }
197
198 /**
199 * The generic name of this record.
200 */
201 protected String name;
202
203 /**
204 * The type (and payload type) of this record.
205 */
206 protected TYPE type;
207
208 /**
209 * The record class (usually CLASS.IN).
210 */
211 protected CLASS clazz;
212
213 /**
214 * The ttl of this record.
215 */
216 protected long ttl;
217
218 /**
219 * The payload object of this record.
220 */
221 protected Data payloadData;
222
223 /**
224 * Parse a given record based on the full message data and the current
225 * stream position.
226 * @param dis The DataInputStream positioned at the first record byte.
227 * @param data The full message data.
228 * @throws IOException In case of malformed replies.
229 */
230 public void parse(DataInputStream dis, byte[] data) throws IOException {
231 this.name = NameUtil.parse(dis, data);
232 this.type = TYPE.getType(dis.readUnsignedShort());
233 this.clazz = CLASS.getClass(dis.readUnsignedShort());
234 this.ttl = (((long)dis.readUnsignedShort()) << 32) +
235 dis.readUnsignedShort();
236 int payloadLength = dis.readUnsignedShort();
237 switch (this.type) {
238 case SRV:
239 this.payloadData = new SRV();
240 break;
241 case AAAA:
242 this.payloadData = new AAAA();
243 break;
244 case A:
245 this.payloadData = new A();
246 break;
247 case NS:
248 this.payloadData = new NS();
249 break;
250 case CNAME:
251 this.payloadData = new CNAME();
252 break;
253 default:
254 System.out.println("Unparsed type " + type);
255 this.payloadData = null;
256 for (int i = 0; i < payloadLength; i++) {
257 dis.readByte();
258 }
259 break;
260 }
261 if (this.payloadData != null) {
262 this.payloadData.parse(dis, data, payloadLength);
263 }
264 }
265
266 /**
267 * Retrieve a textual representation of this resource record.
268 * @return String
269 */
270 @Override
271 public String toString() {
272 if (payloadData == null) {
273 return "RR " + type + "/" + clazz;
274 }
275 return "RR " + type + "/" + clazz + ": " + payloadData.toString();
276 };
277
278 /**
279 * Check if this record answers a given query.
280 * @param q The query.
281 * @return True if this record is a valid answer.
282 */
283 public boolean isAnswer(Question q) {
284 return ((q.getType() == type) || (q.getType() == TYPE.ANY)) &&
285 ((q.getClazz() == clazz) || (q.getClazz() == CLASS.ANY)) &&
286 (q.getName().equals(name));
287 }
288
289 /**
290 * The generic record name, e.g. "measite.de".
291 * @return The record name.
292 */
293 public String getName() {
294 return name;
295 }
296
297 /**
298 * The payload data, usually a subclass of data (A, AAAA, CNAME, ...).
299 * @return The payload data.
300 */
301 public Data getPayload() {
302 return payloadData;
303 }
304
305 /**
306 * Retrieve the record ttl.
307 * @return The record ttl.
308 */
309 public long getTtl() {
310 return ttl;
311 }
312
313}