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