001: // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
002:
003: package org.xbill.DNS;
004:
005: import java.util.*;
006: import java.io.*;
007:
008: /**
009: * A DNS Message. A message is the basic unit of communication between
010: * the client and server of a DNS operation. A message consists of a Header
011: * and 4 message sections.
012: * @see Resolver
013: * @see Header
014: * @see Section
015: *
016: * @author Brian Wellington
017: */
018:
019: public class Message implements Cloneable {
020:
021: /** The maximum length of a message in wire format. */
022: public static final int MAXLENGTH = 65535;
023:
024: private Header header;
025: private List[] sections;
026: private int size;
027: private TSIG tsigkey;
028: private TSIGRecord querytsig;
029: private int tsigerror;
030:
031: int tsigstart;
032: int tsigState;
033:
034: /* The message was not signed */
035: static final int TSIG_UNSIGNED = 0;
036:
037: /* The message was signed and verification succeeded */
038: static final int TSIG_VERIFIED = 1;
039:
040: /* The message was an unsigned message in multiple-message response */
041: static final int TSIG_INTERMEDIATE = 2;
042:
043: /* The message was signed and no verification was attempted. */
044: static final int TSIG_SIGNED = 3;
045:
046: /*
047: * The message was signed and verification failed, or was not signed
048: * when it should have been.
049: */
050: static final int TSIG_FAILED = 4;
051:
052: private static Record[] emptyRecordArray = new Record[0];
053: private static RRset[] emptyRRsetArray = new RRset[0];
054:
055: private Message(Header header) {
056: sections = new List[4];
057: this .header = header;
058: }
059:
060: /** Creates a new Message with the specified Message ID */
061: public Message(int id) {
062: this (new Header(id));
063: }
064:
065: /** Creates a new Message with a random Message ID */
066: public Message() {
067: this (new Header());
068: }
069:
070: /**
071: * Creates a new Message with a random Message ID suitable for sending as a
072: * query.
073: * @param r A record containing the question
074: */
075: public static Message newQuery(Record r) {
076: Message m = new Message();
077: m.header.setOpcode(Opcode.QUERY);
078: m.header.setFlag(Flags.RD);
079: m.addRecord(r, Section.QUESTION);
080: return m;
081: }
082:
083: /**
084: * Creates a new Message to contain a dynamic update. A random Message ID
085: * and the zone are filled in.
086: * @param zone The zone to be updated
087: */
088: public static Message newUpdate(Name zone) {
089: return new Update(zone);
090: }
091:
092: Message(DNSInput in) throws IOException {
093: this (new Header(in));
094: boolean isUpdate = (header.getOpcode() == Opcode.UPDATE);
095: boolean truncated = header.getFlag(Flags.TC);
096: try {
097: for (int i = 0; i < 4; i++) {
098: int count = header.getCount(i);
099: if (count > 0)
100: sections[i] = new ArrayList(count);
101: for (int j = 0; j < count; j++) {
102: int pos = in.current();
103: Record rec = Record.fromWire(in, i, isUpdate);
104: sections[i].add(rec);
105: if (rec.getType() == Type.TSIG)
106: tsigstart = pos;
107: }
108: }
109: } catch (WireParseException e) {
110: if (!truncated)
111: throw e;
112: }
113: size = in.current();
114: }
115:
116: /**
117: * Creates a new Message from its DNS wire format representation
118: * @param b A byte array containing the DNS Message.
119: */
120: public Message(byte[] b) throws IOException {
121: this (new DNSInput(b));
122: }
123:
124: /**
125: * Replaces the Header with a new one.
126: * @see Header
127: */
128: public void setHeader(Header h) {
129: header = h;
130: }
131:
132: /**
133: * Retrieves the Header.
134: * @see Header
135: */
136: public Header getHeader() {
137: return header;
138: }
139:
140: /**
141: * Adds a record to a section of the Message, and adjusts the header.
142: * @see Record
143: * @see Section
144: */
145: public void addRecord(Record r, int section) {
146: if (sections[section] == null)
147: sections[section] = new LinkedList();
148: header.incCount(section);
149: sections[section].add(r);
150: }
151:
152: /**
153: * Removes a record from a section of the Message, and adjusts the header.
154: * @see Record
155: * @see Section
156: */
157: public boolean removeRecord(Record r, int section) {
158: if (sections[section] != null && sections[section].remove(r)) {
159: header.decCount(section);
160: return true;
161: } else
162: return false;
163: }
164:
165: /**
166: * Removes all records from a section of the Message, and adjusts the header.
167: * @see Record
168: * @see Section
169: */
170: public void removeAllRecords(int section) {
171: sections[section] = null;
172: header.setCount(section, 0);
173: }
174:
175: /**
176: * Determines if the given record is already present in the given section.
177: * @see Record
178: * @see Section
179: */
180: public boolean findRecord(Record r, int section) {
181: return (sections[section] != null && sections[section]
182: .contains(r));
183: }
184:
185: /**
186: * Determines if the given record is already present in any section.
187: * @see Record
188: * @see Section
189: */
190: public boolean findRecord(Record r) {
191: for (int i = Section.ANSWER; i <= Section.ADDITIONAL; i++)
192: if (sections[i] != null && sections[i].contains(r))
193: return true;
194: return false;
195: }
196:
197: /**
198: * Determines if an RRset with the given name and type is already
199: * present in the given section.
200: * @see RRset
201: * @see Section
202: */
203: public boolean findRRset(Name name, int type, int section) {
204: if (sections[section] == null)
205: return false;
206: for (int i = 0; i < sections[section].size(); i++) {
207: Record r = (Record) sections[section].get(i);
208: if (r.getType() == type && name.equals(r.getName()))
209: return true;
210: }
211: return false;
212: }
213:
214: /**
215: * Determines if an RRset with the given name and type is already
216: * present in any section.
217: * @see RRset
218: * @see Section
219: */
220: public boolean findRRset(Name name, int type) {
221: return (findRRset(name, type, Section.ANSWER)
222: || findRRset(name, type, Section.AUTHORITY) || findRRset(
223: name, type, Section.ADDITIONAL));
224: }
225:
226: /**
227: * Returns the first record in the QUESTION section.
228: * @see Record
229: * @see Section
230: */
231: public Record getQuestion() {
232: List l = sections[Section.QUESTION];
233: if (l == null || l.size() == 0)
234: return null;
235: return (Record) l.get(0);
236: }
237:
238: /**
239: * Returns the TSIG record from the ADDITIONAL section, if one is present.
240: * @see TSIGRecord
241: * @see TSIG
242: * @see Section
243: */
244: public TSIGRecord getTSIG() {
245: int count = header.getCount(Section.ADDITIONAL);
246: if (count == 0)
247: return null;
248: List l = sections[Section.ADDITIONAL];
249: Record rec = (Record) l.get(count - 1);
250: if (rec.type != Type.TSIG)
251: return null;
252: return (TSIGRecord) rec;
253: }
254:
255: /**
256: * Was this message signed by a TSIG?
257: * @see TSIG
258: */
259: public boolean isSigned() {
260: return (tsigState == TSIG_SIGNED || tsigState == TSIG_VERIFIED || tsigState == TSIG_FAILED);
261: }
262:
263: /**
264: * If this message was signed by a TSIG, was the TSIG verified?
265: * @see TSIG
266: */
267: public boolean isVerified() {
268: return (tsigState == TSIG_VERIFIED);
269: }
270:
271: /**
272: * Returns the OPT record from the ADDITIONAL section, if one is present.
273: * @see OPTRecord
274: * @see Section
275: */
276: public OPTRecord getOPT() {
277: Record[] additional = getSectionArray(Section.ADDITIONAL);
278: for (int i = 0; i < additional.length; i++)
279: if (additional[i] instanceof OPTRecord)
280: return (OPTRecord) additional[i];
281: return null;
282: }
283:
284: /**
285: * Returns the message's rcode (error code). This incorporates the EDNS
286: * extended rcode.
287: */
288: public int getRcode() {
289: int rcode = header.getRcode();
290: OPTRecord opt = getOPT();
291: if (opt != null)
292: rcode += (opt.getExtendedRcode() << 4);
293: return rcode;
294: }
295:
296: /**
297: * Returns an array containing all records in the given section, or an
298: * empty array if the section is empty.
299: * @see Record
300: * @see Section
301: */
302: public Record[] getSectionArray(int section) {
303: if (sections[section] == null)
304: return emptyRecordArray;
305: List l = sections[section];
306: return (Record[]) l.toArray(new Record[l.size()]);
307: }
308:
309: private static boolean sameSet(Record r1, Record r2) {
310: return (r1.getRRsetType() == r2.getRRsetType()
311: && r1.getDClass() == r2.getDClass() && r1.getName()
312: .equals(r2.getName()));
313: }
314:
315: /**
316: * Returns an array containing all records in the given section grouped into
317: * RRsets.
318: * @see RRset
319: * @see Section
320: */
321: public RRset[] getSectionRRsets(int section) {
322: if (sections[section] == null)
323: return emptyRRsetArray;
324: List sets = new LinkedList();
325: Record[] recs = getSectionArray(section);
326: Set hash = new HashSet();
327: for (int i = 0; i < recs.length; i++) {
328: Name name = recs[i].getName();
329: boolean newset = true;
330: if (hash.contains(name)) {
331: for (int j = sets.size() - 1; j >= 0; j--) {
332: RRset set = (RRset) sets.get(j);
333: if (set.getType() == recs[i].getRRsetType()
334: && set.getDClass() == recs[i].getDClass()
335: && set.getName().equals(name)) {
336: set.addRR(recs[i]);
337: newset = false;
338: break;
339: }
340: }
341: }
342: if (newset) {
343: RRset set = new RRset(recs[i]);
344: sets.add(set);
345: hash.add(name);
346: }
347: }
348: return (RRset[]) sets.toArray(new RRset[sets.size()]);
349: }
350:
351: void toWire(DNSOutput out) {
352: header.toWire(out);
353: Compression c = new Compression();
354: for (int i = 0; i < 4; i++) {
355: if (sections[i] == null)
356: continue;
357: for (int j = 0; j < sections[i].size(); j++) {
358: Record rec = (Record) sections[i].get(j);
359: rec.toWire(out, i, c);
360: }
361: }
362: }
363:
364: /* Returns the number of records not successfully rendered. */
365: private int sectionToWire(DNSOutput out, int section,
366: Compression c, int maxLength) {
367: int n = sections[section].size();
368: int pos = out.current();
369: int rendered = 0;
370: Record lastrec = null;
371:
372: for (int i = 0; i < n; i++) {
373: Record rec = (Record) sections[section].get(i);
374: if (lastrec != null && !sameSet(rec, lastrec)) {
375: pos = out.current();
376: rendered = i;
377: }
378: lastrec = rec;
379: rec.toWire(out, section, c);
380: if (out.current() > maxLength) {
381: out.jump(pos);
382: return n - rendered;
383: }
384: }
385: return 0;
386: }
387:
388: /* Returns true if the message could be rendered. */
389: private boolean toWire(DNSOutput out, int maxLength) {
390: if (maxLength < Header.LENGTH)
391: return false;
392:
393: Header newheader = null;
394:
395: int tempMaxLength = maxLength;
396: if (tsigkey != null)
397: tempMaxLength -= tsigkey.recordLength();
398:
399: int startpos = out.current();
400: header.toWire(out);
401: Compression c = new Compression();
402: for (int i = 0; i < 4; i++) {
403: int skipped;
404: if (sections[i] == null)
405: continue;
406: skipped = sectionToWire(out, i, c, tempMaxLength);
407: if (skipped != 0) {
408: if (i != Section.ADDITIONAL) {
409: if (newheader == null)
410: newheader = (Header) header.clone();
411: newheader.setFlag(Flags.TC);
412: int count = newheader.getCount(i);
413: newheader.setCount(i, count - skipped);
414: for (int j = i + 1; j < 4; j++)
415: newheader.setCount(j, 0);
416:
417: out.save();
418: out.jump(startpos);
419: newheader.toWire(out);
420: out.restore();
421: }
422: break;
423: }
424: }
425:
426: if (tsigkey != null) {
427: TSIGRecord tsigrec = tsigkey.generate(this , out
428: .toByteArray(), tsigerror, querytsig);
429:
430: if (newheader == null)
431: newheader = (Header) header.clone();
432: tsigrec.toWire(out, Section.ADDITIONAL, c);
433: newheader.incCount(Section.ADDITIONAL);
434:
435: out.save();
436: out.jump(startpos);
437: newheader.toWire(out);
438: out.restore();
439: }
440:
441: return true;
442: }
443:
444: /**
445: * Returns an array containing the wire format representation of the Message.
446: */
447: public byte[] toWire() {
448: DNSOutput out = new DNSOutput();
449: toWire(out);
450: size = out.current();
451: return out.toByteArray();
452: }
453:
454: /**
455: * Returns an array containing the wire format representation of the Message
456: * with the specified maximum length. This will generate a truncated
457: * message (with the TC bit) if the message doesn't fit, and will also
458: * sign the message with the TSIG key set by a call to setTSIG(). This
459: * method may return null if the message could not be rendered at all; this
460: * could happen if maxLength is smaller than a DNS header, for example.
461: * @param maxLength The maximum length of the message.
462: * @return The wire format of the message, or null if the message could not be
463: * rendered into the specified length.
464: * @see Flags
465: * @see TSIG
466: */
467: public byte[] toWire(int maxLength) {
468: DNSOutput out = new DNSOutput();
469: toWire(out, maxLength);
470: size = out.current();
471: return out.toByteArray();
472: }
473:
474: /**
475: * Sets the TSIG key and other necessary information to sign a message.
476: * @param key The TSIG key.
477: * @param error The value of the TSIG error field.
478: * @param querytsig If this is a response, the TSIG from the request.
479: */
480: public void setTSIG(TSIG key, int error, TSIGRecord querytsig) {
481: this .tsigkey = key;
482: this .tsigerror = error;
483: this .querytsig = querytsig;
484: }
485:
486: /**
487: * Returns the size of the message. Only valid if the message has been
488: * converted to or from wire format.
489: */
490: public int numBytes() {
491: return size;
492: }
493:
494: /**
495: * Converts the given section of the Message to a String.
496: * @see Section
497: */
498: public String sectionToString(int i) {
499: if (i > 3)
500: return null;
501:
502: StringBuffer sb = new StringBuffer();
503:
504: Record[] records = getSectionArray(i);
505: for (int j = 0; j < records.length; j++) {
506: Record rec = records[j];
507: if (i == Section.QUESTION) {
508: sb.append(";;\t" + rec.name);
509: sb.append(", type = " + Type.string(rec.type));
510: sb.append(", class = " + DClass.string(rec.dclass));
511: } else
512: sb.append(rec);
513: sb.append("\n");
514: }
515: return sb.toString();
516: }
517:
518: /**
519: * Converts the Message to a String.
520: */
521: public String toString() {
522: StringBuffer sb = new StringBuffer();
523: OPTRecord opt = getOPT();
524: if (opt != null)
525: sb.append(header.toStringWithRcode(getRcode()) + "\n");
526: else
527: sb.append(header + "\n");
528: if (isSigned()) {
529: sb.append(";; TSIG ");
530: if (isVerified())
531: sb.append("ok");
532: else
533: sb.append("invalid");
534: sb.append('\n');
535: }
536: for (int i = 0; i < 4; i++) {
537: if (header.getOpcode() != Opcode.UPDATE)
538: sb.append(";; " + Section.longString(i) + ":\n");
539: else
540: sb.append(";; " + Section.updString(i) + ":\n");
541: sb.append(sectionToString(i) + "\n");
542: }
543: sb.append(";; Message size: " + numBytes() + " bytes");
544: return sb.toString();
545: }
546:
547: /**
548: * Creates a copy of this Message. This is done by the Resolver before adding
549: * TSIG and OPT records, for example.
550: * @see Resolver
551: * @see TSIGRecord
552: * @see OPTRecord
553: */
554: public Object clone() {
555: Message m = new Message();
556: for (int i = 0; i < sections.length; i++) {
557: if (sections[i] != null)
558: m.sections[i] = new LinkedList(sections[i]);
559: }
560: m.header = (Header) header.clone();
561: m.size = size;
562: return m;
563: }
564:
565: }
|