blob: 1331ad9193eea1f3d5f21361b28c41c5ea8679c2 [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.io.*;
6import java.text.*;
7
8/**
9 * A representation of a domain name. It may either be absolute (fully
10 * qualified) or relative.
11 *
12 * @author Brian Wellington
13 */
14
15public class Name implements Comparable, Serializable {
16
17private static final long serialVersionUID = -7257019940971525644L;
18
19private static final int LABEL_NORMAL = 0;
20private static final int LABEL_COMPRESSION = 0xC0;
21private static final int LABEL_MASK = 0xC0;
22
23/* The name data */
24private byte [] name;
25
26/*
27 * Effectively an 8 byte array, where the low order byte stores the number
28 * of labels and the 7 higher order bytes store per-label offsets.
29 */
30private long offsets;
31
32/* Precomputed hashcode. */
33private int hashcode;
34
35private static final byte [] emptyLabel = new byte[] {(byte)0};
36private static final byte [] wildLabel = new byte[] {(byte)1, (byte)'*'};
37
38/** The root name */
39public static final Name root;
40
41/** The root name */
42public static final Name empty;
43
44/** The maximum length of a Name */
45private static final int MAXNAME = 255;
46
47/** The maximum length of a label a Name */
48private static final int MAXLABEL = 63;
49
50/** The maximum number of labels in a Name */
51private static final int MAXLABELS = 128;
52
53/** The maximum number of cached offsets */
54private static final int MAXOFFSETS = 7;
55
56/* Used for printing non-printable characters */
57private static final DecimalFormat byteFormat = new DecimalFormat();
58
59/* Used to efficiently convert bytes to lowercase */
60private static final byte lowercase[] = new byte[256];
61
62/* Used in wildcard names. */
63private static final Name wild;
64
65static {
66 byteFormat.setMinimumIntegerDigits(3);
67 for (int i = 0; i < lowercase.length; i++) {
68 if (i < 'A' || i > 'Z')
69 lowercase[i] = (byte)i;
70 else
71 lowercase[i] = (byte)(i - 'A' + 'a');
72 }
73 root = new Name();
74 root.appendSafe(emptyLabel, 0, 1);
75 empty = new Name();
76 empty.name = new byte[0];
77 wild = new Name();
78 wild.appendSafe(wildLabel, 0, 1);
79}
80
81private
82Name() {
83}
84
85private final void
86setoffset(int n, int offset) {
87 if (n >= MAXOFFSETS)
88 return;
89 int shift = 8 * (7 - n);
90 offsets &= (~(0xFFL << shift));
91 offsets |= ((long)offset << shift);
92}
93
94private final int
95offset(int n) {
96 if (n == 0 && getlabels() == 0)
97 return 0;
98 if (n < 0 || n >= getlabels())
99 throw new IllegalArgumentException("label out of range");
100 if (n < MAXOFFSETS) {
101 int shift = 8 * (7 - n);
102 return ((int)(offsets >>> shift) & 0xFF);
103 } else {
104 int pos = offset(MAXOFFSETS - 1);
105 for (int i = MAXOFFSETS - 1; i < n; i++)
106 pos += (name[pos] + 1);
107 return (pos);
108 }
109}
110
111private final void
112setlabels(int labels) {
113 offsets &= ~(0xFF);
114 offsets |= labels;
115}
116
117private final int
118getlabels() {
119 return (int)(offsets & 0xFF);
120}
121
122private static final void
123copy(Name src, Name dst) {
124 if (src.offset(0) == 0) {
125 dst.name = src.name;
126 dst.offsets = src.offsets;
127 } else {
128 int offset0 = src.offset(0);
129 int namelen = src.name.length - offset0;
130 int labels = src.labels();
131 dst.name = new byte[namelen];
132 System.arraycopy(src.name, offset0, dst.name, 0, namelen);
133 for (int i = 0; i < labels && i < MAXOFFSETS; i++)
134 dst.setoffset(i, src.offset(i) - offset0);
135 dst.setlabels(labels);
136 }
137}
138
139private final void
140append(byte [] array, int start, int n) throws NameTooLongException {
141 int length = (name == null ? 0 : (name.length - offset(0)));
142 int alength = 0;
143 for (int i = 0, pos = start; i < n; i++) {
144 int len = array[pos];
145 if (len > MAXLABEL)
146 throw new IllegalStateException("invalid label");
147 len++;
148 pos += len;
149 alength += len;
150 }
151 int newlength = length + alength;
152 if (newlength > MAXNAME)
153 throw new NameTooLongException();
154 int labels = getlabels();
155 int newlabels = labels + n;
156 if (newlabels > MAXLABELS)
157 throw new IllegalStateException("too many labels");
158 byte [] newname = new byte[newlength];
159 if (length != 0)
160 System.arraycopy(name, offset(0), newname, 0, length);
161 System.arraycopy(array, start, newname, length, alength);
162 name = newname;
163 for (int i = 0, pos = length; i < n; i++) {
164 setoffset(labels + i, pos);
165 pos += (newname[pos] + 1);
166 }
167 setlabels(newlabels);
168}
169
170private static TextParseException
171parseException(String str, String message) {
172 return new TextParseException("'" + str + "': " + message);
173}
174
175private final void
176appendFromString(String fullName, byte [] array, int start, int n)
177throws TextParseException
178{
179 try {
180 append(array, start, n);
181 }
182 catch (NameTooLongException e) {
183 throw parseException(fullName, "Name too long");
184 }
185}
186
187private final void
188appendSafe(byte [] array, int start, int n) {
189 try {
190 append(array, start, n);
191 }
192 catch (NameTooLongException e) {
193 }
194}
195
196/**
197 * Create a new name from a string and an origin. This does not automatically
198 * make the name absolute; it will be absolute if it has a trailing dot or an
199 * absolute origin is appended.
200 * @param s The string to be converted
201 * @param origin If the name is not absolute, the origin to be appended.
202 * @throws TextParseException The name is invalid.
203 */
204public
205Name(String s, Name origin) throws TextParseException {
206 if (s.equals(""))
207 throw parseException(s, "empty name");
208 else if (s.equals("@")) {
209 if (origin == null)
210 copy(empty, this);
211 else
212 copy(origin, this);
213 return;
214 } else if (s.equals(".")) {
215 copy(root, this);
216 return;
217 }
218 int labelstart = -1;
219 int pos = 1;
220 byte [] label = new byte[MAXLABEL + 1];
221 boolean escaped = false;
222 int digits = 0;
223 int intval = 0;
224 boolean absolute = false;
225 for (int i = 0; i < s.length(); i++) {
226 byte b = (byte) s.charAt(i);
227 if (escaped) {
228 if (b >= '0' && b <= '9' && digits < 3) {
229 digits++;
230 intval *= 10;
231 intval += (b - '0');
232 if (intval > 255)
233 throw parseException(s, "bad escape");
234 if (digits < 3)
235 continue;
236 b = (byte) intval;
237 }
238 else if (digits > 0 && digits < 3)
239 throw parseException(s, "bad escape");
240 if (pos > MAXLABEL)
241 throw parseException(s, "label too long");
242 labelstart = pos;
243 label[pos++] = b;
244 escaped = false;
245 } else if (b == '\\') {
246 escaped = true;
247 digits = 0;
248 intval = 0;
249 } else if (b == '.') {
250 if (labelstart == -1)
251 throw parseException(s, "invalid empty label");
252 label[0] = (byte)(pos - 1);
253 appendFromString(s, label, 0, 1);
254 labelstart = -1;
255 pos = 1;
256 } else {
257 if (labelstart == -1)
258 labelstart = i;
259 if (pos > MAXLABEL)
260 throw parseException(s, "label too long");
261 label[pos++] = b;
262 }
263 }
264 if (digits > 0 && digits < 3)
265 throw parseException(s, "bad escape");
266 if (escaped)
267 throw parseException(s, "bad escape");
268 if (labelstart == -1) {
269 appendFromString(s, emptyLabel, 0, 1);
270 absolute = true;
271 } else {
272 label[0] = (byte)(pos - 1);
273 appendFromString(s, label, 0, 1);
274 }
275 if (origin != null && !absolute)
276 appendFromString(s, origin.name, 0, origin.getlabels());
277}
278
279/**
280 * Create a new name from a string. This does not automatically make the name
281 * absolute; it will be absolute if it has a trailing dot.
282 * @param s The string to be converted
283 * @throws TextParseException The name is invalid.
284 */
285public
286Name(String s) throws TextParseException {
287 this(s, null);
288}
289
290/**
291 * Create a new name from a string and an origin. This does not automatically
292 * make the name absolute; it will be absolute if it has a trailing dot or an
293 * absolute origin is appended. This is identical to the constructor, except
294 * that it will avoid creating new objects in some cases.
295 * @param s The string to be converted
296 * @param origin If the name is not absolute, the origin to be appended.
297 * @throws TextParseException The name is invalid.
298 */
299public static Name
300fromString(String s, Name origin) throws TextParseException {
301 if (s.equals("@") && origin != null)
302 return origin;
303 else if (s.equals("."))
304 return (root);
305
306 return new Name(s, origin);
307}
308
309/**
310 * Create a new name from a string. This does not automatically make the name
311 * absolute; it will be absolute if it has a trailing dot. This is identical
312 * to the constructor, except that it will avoid creating new objects in some
313 * cases.
314 * @param s The string to be converted
315 * @throws TextParseException The name is invalid.
316 */
317public static Name
318fromString(String s) throws TextParseException {
319 return fromString(s, null);
320}
321
322/**
323 * Create a new name from a constant string. This should only be used when
324 the name is known to be good - that is, when it is constant.
325 * @param s The string to be converted
326 * @throws IllegalArgumentException The name is invalid.
327 */
328public static Name
329fromConstantString(String s) {
330 try {
331 return fromString(s, null);
332 }
333 catch (TextParseException e) {
334 throw new IllegalArgumentException("Invalid name '" + s + "'");
335 }
336}
337
338/**
339 * Create a new name from DNS a wire format message
340 * @param in A stream containing the DNS message which is currently
341 * positioned at the start of the name to be read.
342 */
343public
344Name(DNSInput in) throws WireParseException {
345 int len, pos;
346 boolean done = false;
347 byte [] label = new byte[MAXLABEL + 1];
348 boolean savedState = false;
349
350 while (!done) {
351 len = in.readU8();
352 switch (len & LABEL_MASK) {
353 case LABEL_NORMAL:
354 if (getlabels() >= MAXLABELS)
355 throw new WireParseException("too many labels");
356 if (len == 0) {
357 append(emptyLabel, 0, 1);
358 done = true;
359 } else {
360 label[0] = (byte)len;
361 in.readByteArray(label, 1, len);
362 append(label, 0, 1);
363 }
364 break;
365 case LABEL_COMPRESSION:
366 pos = in.readU8();
367 pos += ((len & ~LABEL_MASK) << 8);
368 if (Options.check("verbosecompression"))
369 System.err.println("currently " + in.current() +
370 ", pointer to " + pos);
371
372 if (pos >= in.current() - 2)
373 throw new WireParseException("bad compression");
374 if (!savedState) {
375 in.save();
376 savedState = true;
377 }
378 in.jump(pos);
379 if (Options.check("verbosecompression"))
380 System.err.println("current name '" + this +
381 "', seeking to " + pos);
382 break;
383 default:
384 throw new WireParseException("bad label type");
385 }
386 }
387 if (savedState) {
388 in.restore();
389 }
390}
391
392/**
393 * Create a new name from DNS wire format
394 * @param b A byte array containing the wire format of the name.
395 */
396public
397Name(byte [] b) throws IOException {
398 this(new DNSInput(b));
399}
400
401/**
402 * Create a new name by removing labels from the beginning of an existing Name
403 * @param src An existing Name
404 * @param n The number of labels to remove from the beginning in the copy
405 */
406public
407Name(Name src, int n) {
408 int slabels = src.labels();
409 if (n > slabels)
410 throw new IllegalArgumentException("attempted to remove too " +
411 "many labels");
412 name = src.name;
413 setlabels(slabels - n);
414 for (int i = 0; i < MAXOFFSETS && i < slabels - n; i++)
415 setoffset(i, src.offset(i + n));
416}
417
418/**
419 * Creates a new name by concatenating two existing names.
420 * @param prefix The prefix name.
421 * @param suffix The suffix name.
422 * @return The concatenated name.
423 * @throws NameTooLongException The name is too long.
424 */
425public static Name
426concatenate(Name prefix, Name suffix) throws NameTooLongException {
427 if (prefix.isAbsolute())
428 return (prefix);
429 Name newname = new Name();
430 copy(prefix, newname);
431 newname.append(suffix.name, suffix.offset(0), suffix.getlabels());
432 return newname;
433}
434
435/**
436 * If this name is a subdomain of origin, return a new name relative to
437 * origin with the same value. Otherwise, return the existing name.
438 * @param origin The origin to remove.
439 * @return The possibly relativized name.
440 */
441public Name
442relativize(Name origin) {
443 if (origin == null || !subdomain(origin))
444 return this;
445 Name newname = new Name();
446 copy(this, newname);
447 int length = length() - origin.length();
448 int labels = newname.labels() - origin.labels();
449 newname.setlabels(labels);
450 newname.name = new byte[length];
451 System.arraycopy(name, offset(0), newname.name, 0, length);
452 return newname;
453}
454
455/**
456 * Generates a new Name with the first n labels replaced by a wildcard
457 * @return The wildcard name
458 */
459public Name
460wild(int n) {
461 if (n < 1)
462 throw new IllegalArgumentException("must replace 1 or more " +
463 "labels");
464 try {
465 Name newname = new Name();
466 copy(wild, newname);
467 newname.append(name, offset(n), getlabels() - n);
468 return newname;
469 }
470 catch (NameTooLongException e) {
471 throw new IllegalStateException
472 ("Name.wild: concatenate failed");
473 }
474}
475
476/**
477 * Generates a new Name to be used when following a DNAME.
478 * @param dname The DNAME record to follow.
479 * @return The constructed name.
480 * @throws NameTooLongException The resulting name is too long.
481 */
482public Name
483fromDNAME(DNAMERecord dname) throws NameTooLongException {
484 Name dnameowner = dname.getName();
485 Name dnametarget = dname.getTarget();
486 if (!subdomain(dnameowner))
487 return null;
488
489 int plabels = labels() - dnameowner.labels();
490 int plength = length() - dnameowner.length();
491 int pstart = offset(0);
492
493 int dlabels = dnametarget.labels();
494 int dlength = dnametarget.length();
495
496 if (plength + dlength > MAXNAME)
497 throw new NameTooLongException();
498
499 Name newname = new Name();
500 newname.setlabels(plabels + dlabels);
501 newname.name = new byte[plength + dlength];
502 System.arraycopy(name, pstart, newname.name, 0, plength);
503 System.arraycopy(dnametarget.name, 0, newname.name, plength, dlength);
504
505 for (int i = 0, pos = 0; i < MAXOFFSETS && i < plabels + dlabels; i++) {
506 newname.setoffset(i, pos);
507 pos += (newname.name[pos] + 1);
508 }
509 return newname;
510}
511
512/**
513 * Is this name a wildcard?
514 */
515public boolean
516isWild() {
517 if (labels() == 0)
518 return false;
519 return (name[0] == (byte)1 && name[1] == (byte)'*');
520}
521
522/**
523 * Is this name absolute?
524 */
525public boolean
526isAbsolute() {
527 if (labels() == 0)
528 return false;
529 return (name[name.length - 1] == 0);
530}
531
532/**
533 * The length of the name.
534 */
535public short
536length() {
537 if (getlabels() == 0)
538 return 0;
539 return (short)(name.length - offset(0));
540}
541
542/**
543 * The number of labels in the name.
544 */
545public int
546labels() {
547 return getlabels();
548}
549
550/**
551 * Is the current Name a subdomain of the specified name?
552 */
553public boolean
554subdomain(Name domain) {
555 int labels = labels();
556 int dlabels = domain.labels();
557 if (dlabels > labels)
558 return false;
559 if (dlabels == labels)
560 return equals(domain);
561 return domain.equals(name, offset(labels - dlabels));
562}
563
564private String
565byteString(byte [] array, int pos) {
566 StringBuffer sb = new StringBuffer();
567 int len = array[pos++];
568 for (int i = pos; i < pos + len; i++) {
569 int b = array[i] & 0xFF;
570 if (b <= 0x20 || b >= 0x7f) {
571 sb.append('\\');
572 sb.append(byteFormat.format(b));
573 }
574 else if (b == '"' || b == '(' || b == ')' || b == '.' ||
575 b == ';' || b == '\\' || b == '@' || b == '$')
576 {
577 sb.append('\\');
578 sb.append((char)b);
579 }
580 else
581 sb.append((char)b);
582 }
583 return sb.toString();
584}
585
586/**
587 * Convert a Name to a String
588 * @return The representation of this name as a (printable) String.
589 */
590public String
591toString() {
592 int labels = labels();
593 if (labels == 0)
594 return "@";
595 else if (labels == 1 && name[offset(0)] == 0)
596 return ".";
597 StringBuffer sb = new StringBuffer();
598 for (int i = 0, pos = offset(0); i < labels; i++) {
599 int len = name[pos];
600 if (len > MAXLABEL)
601 throw new IllegalStateException("invalid label");
602 if (len == 0)
603 break;
604 sb.append(byteString(name, pos));
605 sb.append('.');
606 pos += (1 + len);
607 }
608 if (!isAbsolute())
609 sb.deleteCharAt(sb.length() - 1);
610 return sb.toString();
611}
612
613/**
614 * Retrieve the nth label of a Name. This makes a copy of the label; changing
615 * this does not change the Name.
616 * @param n The label to be retrieved. The first label is 0.
617 */
618public byte []
619getLabel(int n) {
620 int pos = offset(n);
621 byte len = (byte)(name[pos] + 1);
622 byte [] label = new byte[len];
623 System.arraycopy(name, pos, label, 0, len);
624 return label;
625}
626
627/**
628 * Convert the nth label in a Name to a String
629 * @param n The label to be converted to a (printable) String. The first
630 * label is 0.
631 */
632public String
633getLabelString(int n) {
634 int pos = offset(n);
635 return byteString(name, pos);
636}
637
638/**
639 * Emit a Name in DNS wire format
640 * @param out The output stream containing the DNS message.
641 * @param c The compression context, or null of no compression is desired.
642 * @throws IllegalArgumentException The name is not absolute.
643 */
644public void
645toWire(DNSOutput out, Compression c) {
646 if (!isAbsolute())
647 throw new IllegalArgumentException("toWire() called on " +
648 "non-absolute name");
649
650 int labels = labels();
651 for (int i = 0; i < labels - 1; i++) {
652 Name tname;
653 if (i == 0)
654 tname = this;
655 else
656 tname = new Name(this, i);
657 int pos = -1;
658 if (c != null)
659 pos = c.get(tname);
660 if (pos >= 0) {
661 pos |= (LABEL_MASK << 8);
662 out.writeU16(pos);
663 return;
664 } else {
665 if (c != null)
666 c.add(out.current(), tname);
667 int off = offset(i);
668 out.writeByteArray(name, off, name[off] + 1);
669 }
670 }
671 out.writeU8(0);
672}
673
674/**
675 * Emit a Name in DNS wire format
676 * @throws IllegalArgumentException The name is not absolute.
677 */
678public byte []
679toWire() {
680 DNSOutput out = new DNSOutput();
681 toWire(out, null);
682 return out.toByteArray();
683}
684
685/**
686 * Emit a Name in canonical DNS wire format (all lowercase)
687 * @param out The output stream to which the message is written.
688 */
689public void
690toWireCanonical(DNSOutput out) {
691 byte [] b = toWireCanonical();
692 out.writeByteArray(b);
693}
694
695/**
696 * Emit a Name in canonical DNS wire format (all lowercase)
697 * @return The canonical form of the name.
698 */
699public byte []
700toWireCanonical() {
701 int labels = labels();
702 if (labels == 0)
703 return (new byte[0]);
704 byte [] b = new byte[name.length - offset(0)];
705 for (int i = 0, spos = offset(0), dpos = 0; i < labels; i++) {
706 int len = name[spos];
707 if (len > MAXLABEL)
708 throw new IllegalStateException("invalid label");
709 b[dpos++] = name[spos++];
710 for (int j = 0; j < len; j++)
711 b[dpos++] = lowercase[(name[spos++] & 0xFF)];
712 }
713 return b;
714}
715
716/**
717 * Emit a Name in DNS wire format
718 * @param out The output stream containing the DNS message.
719 * @param c The compression context, or null of no compression is desired.
720 * @param canonical If true, emit the name in canonicalized form
721 * (all lowercase).
722 * @throws IllegalArgumentException The name is not absolute.
723 */
724public void
725toWire(DNSOutput out, Compression c, boolean canonical) {
726 if (canonical)
727 toWireCanonical(out);
728 else
729 toWire(out, c);
730}
731
732private final boolean
733equals(byte [] b, int bpos) {
734 int labels = labels();
735 for (int i = 0, pos = offset(0); i < labels; i++) {
736 if (name[pos] != b[bpos])
737 return false;
738 int len = name[pos++];
739 bpos++;
740 if (len > MAXLABEL)
741 throw new IllegalStateException("invalid label");
742 for (int j = 0; j < len; j++)
743 if (lowercase[(name[pos++] & 0xFF)] !=
744 lowercase[(b[bpos++] & 0xFF)])
745 return false;
746 }
747 return true;
748}
749
750/**
751 * Are these two Names equivalent?
752 */
753public boolean
754equals(Object arg) {
755 if (arg == this)
756 return true;
757 if (arg == null || !(arg instanceof Name))
758 return false;
759 Name d = (Name) arg;
760 if (d.hashcode == 0)
761 d.hashCode();
762 if (hashcode == 0)
763 hashCode();
764 if (d.hashcode != hashcode)
765 return false;
766 if (d.labels() != labels())
767 return false;
768 return equals(d.name, d.offset(0));
769}
770
771/**
772 * Computes a hashcode based on the value
773 */
774public int
775hashCode() {
776 if (hashcode != 0)
777 return (hashcode);
778 int code = 0;
779 for (int i = offset(0); i < name.length; i++)
780 code += ((code << 3) + lowercase[(name[i] & 0xFF)]);
781 hashcode = code;
782 return hashcode;
783}
784
785/**
786 * Compares this Name to another Object.
787 * @param o The Object to be compared.
788 * @return The value 0 if the argument is a name equivalent to this name;
789 * a value less than 0 if the argument is less than this name in the canonical
790 * ordering, and a value greater than 0 if the argument is greater than this
791 * name in the canonical ordering.
792 * @throws ClassCastException if the argument is not a Name.
793 */
794public int
795compareTo(Object o) {
796 Name arg = (Name) o;
797
798 if (this == arg)
799 return (0);
800
801 int labels = labels();
802 int alabels = arg.labels();
803 int compares = labels > alabels ? alabels : labels;
804
805 for (int i = 1; i <= compares; i++) {
806 int start = offset(labels - i);
807 int astart = arg.offset(alabels - i);
808 int length = name[start];
809 int alength = arg.name[astart];
810 for (int j = 0; j < length && j < alength; j++) {
811 int n = lowercase[(name[j + start + 1]) & 0xFF] -
812 lowercase[(arg.name[j + astart + 1]) & 0xFF];
813 if (n != 0)
814 return (n);
815 }
816 if (length != alength)
817 return (length - alength);
818 }
819 return (labels - alabels);
820}
821
822}