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