blob: fe0c3c901d64ee1d757e2cbd79ad4e1c57410f78 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.util.*;
6import java.io.*;
7
8/**
9 * A DNS Message. A message is the basic unit of communication between
10 * the client and server of a DNS operation. A message consists of a Header
11 * and 4 message sections.
12 * @see Resolver
13 * @see Header
14 * @see Section
15 *
16 * @author Brian Wellington
17 */
18
19public class Message implements Cloneable {
20
21/** The maximum length of a message in wire format. */
22public static final int MAXLENGTH = 65535;
23
24private Header header;
25private List [] sections;
26private int size;
27private TSIG tsigkey;
28private TSIGRecord querytsig;
29private int tsigerror;
30
31int tsigstart;
32int tsigState;
33int sig0start;
34
35/* The message was not signed */
36static final int TSIG_UNSIGNED = 0;
37
38/* The message was signed and verification succeeded */
39static final int TSIG_VERIFIED = 1;
40
41/* The message was an unsigned message in multiple-message response */
42static final int TSIG_INTERMEDIATE = 2;
43
44/* The message was signed and no verification was attempted. */
45static final int TSIG_SIGNED = 3;
46
47/*
48 * The message was signed and verification failed, or was not signed
49 * when it should have been.
50 */
51static final int TSIG_FAILED = 4;
52
53private static Record [] emptyRecordArray = new Record[0];
54private static RRset [] emptyRRsetArray = new RRset[0];
55
56private
57Message(Header header) {
58 sections = new List[4];
59 this.header = header;
60}
61
62/** Creates a new Message with the specified Message ID */
63public
64Message(int id) {
65 this(new Header(id));
66}
67
68/** Creates a new Message with a random Message ID */
69public
70Message() {
71 this(new Header());
72}
73
74/**
75 * Creates a new Message with a random Message ID suitable for sending as a
76 * query.
77 * @param r A record containing the question
78 */
79public static Message
80newQuery(Record r) {
81 Message m = new Message();
82 m.header.setOpcode(Opcode.QUERY);
83 m.header.setFlag(Flags.RD);
84 m.addRecord(r, Section.QUESTION);
85 return m;
86}
87
88/**
89 * Creates a new Message to contain a dynamic update. A random Message ID
90 * and the zone are filled in.
91 * @param zone The zone to be updated
92 */
93public static Message
94newUpdate(Name zone) {
95 return new Update(zone);
96}
97
98Message(DNSInput in) throws IOException {
99 this(new Header(in));
100 boolean isUpdate = (header.getOpcode() == Opcode.UPDATE);
101 boolean truncated = header.getFlag(Flags.TC);
102 try {
103 for (int i = 0; i < 4; i++) {
104 int count = header.getCount(i);
105 if (count > 0)
106 sections[i] = new ArrayList(count);
107 for (int j = 0; j < count; j++) {
108 int pos = in.current();
109 Record rec = Record.fromWire(in, i, isUpdate);
110 sections[i].add(rec);
111 if (i == Section.ADDITIONAL) {
112 if (rec.getType() == Type.TSIG)
113 tsigstart = pos;
114 if (rec.getType() == Type.SIG) {
115 SIGRecord sig = (SIGRecord) rec;
116 if (sig.getTypeCovered() == 0)
117 sig0start = pos;
118 }
119 }
120 }
121 }
122 } catch (WireParseException e) {
123 if (!truncated)
124 throw e;
125 }
126 size = in.current();
127}
128
129/**
130 * Creates a new Message from its DNS wire format representation
131 * @param b A byte array containing the DNS Message.
132 */
133public
134Message(byte [] b) throws IOException {
135 this(new DNSInput(b));
136}
137
138/**
139 * Replaces the Header with a new one.
140 * @see Header
141 */
142public void
143setHeader(Header h) {
144 header = h;
145}
146
147/**
148 * Retrieves the Header.
149 * @see Header
150 */
151public Header
152getHeader() {
153 return header;
154}
155
156/**
157 * Adds a record to a section of the Message, and adjusts the header.
158 * @see Record
159 * @see Section
160 */
161public void
162addRecord(Record r, int section) {
163 if (sections[section] == null)
164 sections[section] = new LinkedList();
165 header.incCount(section);
166 sections[section].add(r);
167}
168
169/**
170 * Removes a record from a section of the Message, and adjusts the header.
171 * @see Record
172 * @see Section
173 */
174public boolean
175removeRecord(Record r, int section) {
176 if (sections[section] != null && sections[section].remove(r)) {
177 header.decCount(section);
178 return true;
179 }
180 else
181 return false;
182}
183
184/**
185 * Removes all records from a section of the Message, and adjusts the header.
186 * @see Record
187 * @see Section
188 */
189public void
190removeAllRecords(int section) {
191 sections[section] = null;
192 header.setCount(section, 0);
193}
194
195/**
196 * Determines if the given record is already present in the given section.
197 * @see Record
198 * @see Section
199 */
200public boolean
201findRecord(Record r, int section) {
202 return (sections[section] != null && sections[section].contains(r));
203}
204
205/**
206 * Determines if the given record is already present in any section.
207 * @see Record
208 * @see Section
209 */
210public boolean
211findRecord(Record r) {
212 for (int i = Section.ANSWER; i <= Section.ADDITIONAL; i++)
213 if (sections[i] != null && sections[i].contains(r))
214 return true;
215 return false;
216}
217
218/**
219 * Determines if an RRset with the given name and type is already
220 * present in the given section.
221 * @see RRset
222 * @see Section
223 */
224public boolean
225findRRset(Name name, int type, int section) {
226 if (sections[section] == null)
227 return false;
228 for (int i = 0; i < sections[section].size(); i++) {
229 Record r = (Record) sections[section].get(i);
230 if (r.getType() == type && name.equals(r.getName()))
231 return true;
232 }
233 return false;
234}
235
236/**
237 * Determines if an RRset with the given name and type is already
238 * present in any section.
239 * @see RRset
240 * @see Section
241 */
242public boolean
243findRRset(Name name, int type) {
244 return (findRRset(name, type, Section.ANSWER) ||
245 findRRset(name, type, Section.AUTHORITY) ||
246 findRRset(name, type, Section.ADDITIONAL));
247}
248
249/**
250 * Returns the first record in the QUESTION section.
251 * @see Record
252 * @see Section
253 */
254public Record
255getQuestion() {
256 List l = sections[Section.QUESTION];
257 if (l == null || l.size() == 0)
258 return null;
259 return (Record) l.get(0);
260}
261
262/**
263 * Returns the TSIG record from the ADDITIONAL section, if one is present.
264 * @see TSIGRecord
265 * @see TSIG
266 * @see Section
267 */
268public TSIGRecord
269getTSIG() {
270 int count = header.getCount(Section.ADDITIONAL);
271 if (count == 0)
272 return null;
273 List l = sections[Section.ADDITIONAL];
274 Record rec = (Record) l.get(count - 1);
275 if (rec.type != Type.TSIG)
276 return null;
277 return (TSIGRecord) rec;
278}
279
280/**
281 * Was this message signed by a TSIG?
282 * @see TSIG
283 */
284public boolean
285isSigned() {
286 return (tsigState == TSIG_SIGNED ||
287 tsigState == TSIG_VERIFIED ||
288 tsigState == TSIG_FAILED);
289}
290
291/**
292 * If this message was signed by a TSIG, was the TSIG verified?
293 * @see TSIG
294 */
295public boolean
296isVerified() {
297 return (tsigState == TSIG_VERIFIED);
298}
299
300/**
301 * Returns the OPT record from the ADDITIONAL section, if one is present.
302 * @see OPTRecord
303 * @see Section
304 */
305public OPTRecord
306getOPT() {
307 Record [] additional = getSectionArray(Section.ADDITIONAL);
308 for (int i = 0; i < additional.length; i++)
309 if (additional[i] instanceof OPTRecord)
310 return (OPTRecord) additional[i];
311 return null;
312}
313
314/**
315 * Returns the message's rcode (error code). This incorporates the EDNS
316 * extended rcode.
317 */
318public int
319getRcode() {
320 int rcode = header.getRcode();
321 OPTRecord opt = getOPT();
322 if (opt != null)
323 rcode += (opt.getExtendedRcode() << 4);
324 return rcode;
325}
326
327/**
328 * Returns an array containing all records in the given section, or an
329 * empty array if the section is empty.
330 * @see Record
331 * @see Section
332 */
333public Record []
334getSectionArray(int section) {
335 if (sections[section] == null)
336 return emptyRecordArray;
337 List l = sections[section];
338 return (Record []) l.toArray(new Record[l.size()]);
339}
340
341private static boolean
342sameSet(Record r1, Record r2) {
343 return (r1.getRRsetType() == r2.getRRsetType() &&
344 r1.getDClass() == r2.getDClass() &&
345 r1.getName().equals(r2.getName()));
346}
347
348/**
349 * Returns an array containing all records in the given section grouped into
350 * RRsets.
351 * @see RRset
352 * @see Section
353 */
354public RRset []
355getSectionRRsets(int section) {
356 if (sections[section] == null)
357 return emptyRRsetArray;
358 List sets = new LinkedList();
359 Record [] recs = getSectionArray(section);
360 Set hash = new HashSet();
361 for (int i = 0; i < recs.length; i++) {
362 Name name = recs[i].getName();
363 boolean newset = true;
364 if (hash.contains(name)) {
365 for (int j = sets.size() - 1; j >= 0; j--) {
366 RRset set = (RRset) sets.get(j);
367 if (set.getType() == recs[i].getRRsetType() &&
368 set.getDClass() == recs[i].getDClass() &&
369 set.getName().equals(name))
370 {
371 set.addRR(recs[i]);
372 newset = false;
373 break;
374 }
375 }
376 }
377 if (newset) {
378 RRset set = new RRset(recs[i]);
379 sets.add(set);
380 hash.add(name);
381 }
382 }
383 return (RRset []) sets.toArray(new RRset[sets.size()]);
384}
385
386void
387toWire(DNSOutput out) {
388 header.toWire(out);
389 Compression c = new Compression();
390 for (int i = 0; i < 4; i++) {
391 if (sections[i] == null)
392 continue;
393 for (int j = 0; j < sections[i].size(); j++) {
394 Record rec = (Record)sections[i].get(j);
395 rec.toWire(out, i, c);
396 }
397 }
398}
399
400/* Returns the number of records not successfully rendered. */
401private int
402sectionToWire(DNSOutput out, int section, Compression c,
403 int maxLength)
404{
405 int n = sections[section].size();
406 int pos = out.current();
407 int rendered = 0;
408 Record lastrec = null;
409
410 for (int i = 0; i < n; i++) {
411 Record rec = (Record)sections[section].get(i);
412 if (lastrec != null && !sameSet(rec, lastrec)) {
413 pos = out.current();
414 rendered = i;
415 }
416 lastrec = rec;
417 rec.toWire(out, section, c);
418 if (out.current() > maxLength) {
419 out.jump(pos);
420 return n - rendered;
421 }
422 }
423 return 0;
424}
425
426/* Returns true if the message could be rendered. */
427private boolean
428toWire(DNSOutput out, int maxLength) {
429 if (maxLength < Header.LENGTH)
430 return false;
431
432 Header newheader = null;
433
434 int tempMaxLength = maxLength;
435 if (tsigkey != null)
436 tempMaxLength -= tsigkey.recordLength();
437
438 int startpos = out.current();
439 header.toWire(out);
440 Compression c = new Compression();
441 for (int i = 0; i < 4; i++) {
442 int skipped;
443 if (sections[i] == null)
444 continue;
445 skipped = sectionToWire(out, i, c, tempMaxLength);
446 if (skipped != 0) {
447 if (newheader == null)
448 newheader = (Header) header.clone();
449 if (i != Section.ADDITIONAL)
450 newheader.setFlag(Flags.TC);
451 int count = newheader.getCount(i);
452 newheader.setCount(i, count - skipped);
453 for (int j = i + 1; j < 4; j++)
454 newheader.setCount(j, 0);
455
456 out.save();
457 out.jump(startpos);
458 newheader.toWire(out);
459 out.restore();
460 break;
461 }
462 }
463
464 if (tsigkey != null) {
465 TSIGRecord tsigrec = tsigkey.generate(this, out.toByteArray(),
466 tsigerror, querytsig);
467
468 if (newheader == null)
469 newheader = (Header) header.clone();
470 tsigrec.toWire(out, Section.ADDITIONAL, c);
471 newheader.incCount(Section.ADDITIONAL);
472
473 out.save();
474 out.jump(startpos);
475 newheader.toWire(out);
476 out.restore();
477 }
478
479 return true;
480}
481
482/**
483 * Returns an array containing the wire format representation of the Message.
484 */
485public byte []
486toWire() {
487 DNSOutput out = new DNSOutput();
488 toWire(out);
489 size = out.current();
490 return out.toByteArray();
491}
492
493/**
494 * Returns an array containing the wire format representation of the Message
495 * with the specified maximum length. This will generate a truncated
496 * message (with the TC bit) if the message doesn't fit, and will also
497 * sign the message with the TSIG key set by a call to setTSIG(). This
498 * method may return null if the message could not be rendered at all; this
499 * could happen if maxLength is smaller than a DNS header, for example.
500 * @param maxLength The maximum length of the message.
501 * @return The wire format of the message, or null if the message could not be
502 * rendered into the specified length.
503 * @see Flags
504 * @see TSIG
505 */
506public byte []
507toWire(int maxLength) {
508 DNSOutput out = new DNSOutput();
509 toWire(out, maxLength);
510 size = out.current();
511 return out.toByteArray();
512}
513
514/**
515 * Sets the TSIG key and other necessary information to sign a message.
516 * @param key The TSIG key.
517 * @param error The value of the TSIG error field.
518 * @param querytsig If this is a response, the TSIG from the request.
519 */
520public void
521setTSIG(TSIG key, int error, TSIGRecord querytsig) {
522 this.tsigkey = key;
523 this.tsigerror = error;
524 this.querytsig = querytsig;
525}
526
527/**
528 * Returns the size of the message. Only valid if the message has been
529 * converted to or from wire format.
530 */
531public int
532numBytes() {
533 return size;
534}
535
536/**
537 * Converts the given section of the Message to a String.
538 * @see Section
539 */
540public String
541sectionToString(int i) {
542 if (i > 3)
543 return null;
544
545 StringBuffer sb = new StringBuffer();
546
547 Record [] records = getSectionArray(i);
548 for (int j = 0; j < records.length; j++) {
549 Record rec = records[j];
550 if (i == Section.QUESTION) {
551 sb.append(";;\t" + rec.name);
552 sb.append(", type = " + Type.string(rec.type));
553 sb.append(", class = " + DClass.string(rec.dclass));
554 }
555 else
556 sb.append(rec);
557 sb.append("\n");
558 }
559 return sb.toString();
560}
561
562/**
563 * Converts the Message to a String.
564 */
565public String
566toString() {
567 StringBuffer sb = new StringBuffer();
568 OPTRecord opt = getOPT();
569 if (opt != null)
570 sb.append(header.toStringWithRcode(getRcode()) + "\n");
571 else
572 sb.append(header + "\n");
573 if (isSigned()) {
574 sb.append(";; TSIG ");
575 if (isVerified())
576 sb.append("ok");
577 else
578 sb.append("invalid");
579 sb.append('\n');
580 }
581 for (int i = 0; i < 4; i++) {
582 if (header.getOpcode() != Opcode.UPDATE)
583 sb.append(";; " + Section.longString(i) + ":\n");
584 else
585 sb.append(";; " + Section.updString(i) + ":\n");
586 sb.append(sectionToString(i) + "\n");
587 }
588 sb.append(";; Message size: " + numBytes() + " bytes");
589 return sb.toString();
590}
591
592/**
593 * Creates a copy of this Message. This is done by the Resolver before adding
594 * TSIG and OPT records, for example.
595 * @see Resolver
596 * @see TSIGRecord
597 * @see OPTRecord
598 */
599public Object
600clone() {
601 Message m = new Message();
602 for (int i = 0; i < sections.length; i++) {
603 if (sections[i] != null)
604 m.sections[i] = new LinkedList(sections[i]);
605 }
606 m.header = (Header) header.clone();
607 m.size = size;
608 return m;
609}
610
611}