1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3 package org.xbill.DNS;
4
5 import java.io.*;
6 import 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
15 public class Name implements Comparable, Serializable {
16
17 private static final long serialVersionUID = -7257019940971525644L;
18
19 private static final int LABEL_NORMAL = 0;
20 private static final int LABEL_COMPRESSION = 0xC0;
21 private static final int LABEL_MASK = 0xC0;
22
23 /* The name data */
24 private 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 */
30 private long offsets;
31
32 /* Precomputed hashcode. */
33 private int hashcode;
34
35 private static final byte [] emptyLabel = new byte[] {(byte)0};
36 private static final byte [] wildLabel = new byte[] {(byte)1, (byte)'*'};
37
38 /** The root name */
39 public static final Name root;
40
41 /** The root name */
42 public static final Name empty;
43
44 /** The maximum length of a Name */
45 private static final int MAXNAME = 255;
46
47 /** The maximum length of a label a Name */
48 private static final int MAXLABEL = 63;
49
50 /** The maximum number of labels in a Name */
51 private static final int MAXLABELS = 128;
52
53 /** The maximum number of cached offsets */
54 private static final int MAXOFFSETS = 7;
55
56 /* Used for printing non-printable characters */
57 private static final DecimalFormat byteFormat = new DecimalFormat();
58
59 /* Used to efficiently convert bytes to lowercase */
60 private static final byte lowercase[] = new byte[256];
61
62 /* Used in wildcard names. */
63 private static final Name wild;
64
65 static {
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();
root.appendSafe(emptyLabel, 0, 1)74 root.appendSafe(emptyLabel, 0, 1);
75 empty = new Name();
76 empty.name = new byte[0];
77 wild = new Name();
wild.appendSafe(wildLabel, 0, 1)78 wild.appendSafe(wildLabel, 0, 1);
79 }
80
81 private
Name()82 Name() {
83 }
84
85 private final void
setoffset(int n, int offset)86 setoffset(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
94 private final int
offset(int n)95 offset(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
111 private final void
setlabels(int labels)112 setlabels(int labels) {
113 offsets &= ~(0xFF);
114 offsets |= labels;
115 }
116
117 private final int
getlabels()118 getlabels() {
119 return (int)(offsets & 0xFF);
120 }
121
122 private static final void
copy(Name src, Name dst)123 copy(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
139 private final void
append(byte [] array, int start, int n)140 append(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
170 private static TextParseException
parseException(String str, String message)171 parseException(String str, String message) {
172 return new TextParseException("'" + str + "': " + message);
173 }
174
175 private final void
appendFromString(String fullName, byte [] array, int start, int n)176 appendFromString(String fullName, byte [] array, int start, int n)
177 throws TextParseException
178 {
179 try {
180 append(array, start, n);
181 }
182 catch (NameTooLongException e) {
183 throw parseException(fullName, "Name too long");
184 }
185 }
186
187 private final void
appendSafe(byte [] array, int start, int n)188 appendSafe(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 */
204 public
Name(String s, Name origin)205 Name(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, origin.offset(0),
277 origin.getlabels());
278 }
279
280 /**
281 * Create a new name from a string. This does not automatically make the name
282 * absolute; it will be absolute if it has a trailing dot.
283 * @param s The string to be converted
284 * @throws TextParseException The name is invalid.
285 */
286 public
Name(String s)287 Name(String s) throws TextParseException {
288 this(s, null);
289 }
290
291 /**
292 * Create a new name from a string and an origin. This does not automatically
293 * make the name absolute; it will be absolute if it has a trailing dot or an
294 * absolute origin is appended. This is identical to the constructor, except
295 * that it will avoid creating new objects in some cases.
296 * @param s The string to be converted
297 * @param origin If the name is not absolute, the origin to be appended.
298 * @throws TextParseException The name is invalid.
299 */
300 public static Name
fromString(String s, Name origin)301 fromString(String s, Name origin) throws TextParseException {
302 if (s.equals("@") && origin != null)
303 return origin;
304 else if (s.equals("."))
305 return (root);
306
307 return new Name(s, origin);
308 }
309
310 /**
311 * Create a new name from a string. This does not automatically make the name
312 * absolute; it will be absolute if it has a trailing dot. This is identical
313 * to the constructor, except that it will avoid creating new objects in some
314 * cases.
315 * @param s The string to be converted
316 * @throws TextParseException The name is invalid.
317 */
318 public static Name
fromString(String s)319 fromString(String s) throws TextParseException {
320 return fromString(s, null);
321 }
322
323 /**
324 * Create a new name from a constant string. This should only be used when
325 the name is known to be good - that is, when it is constant.
326 * @param s The string to be converted
327 * @throws IllegalArgumentException The name is invalid.
328 */
329 public static Name
fromConstantString(String s)330 fromConstantString(String s) {
331 try {
332 return fromString(s, null);
333 }
334 catch (TextParseException e) {
335 throw new IllegalArgumentException("Invalid name '" + s + "'");
336 }
337 }
338
339 /**
340 * Create a new name from DNS a wire format message
341 * @param in A stream containing the DNS message which is currently
342 * positioned at the start of the name to be read.
343 */
344 public
Name(DNSInput in)345 Name(DNSInput in) throws WireParseException {
346 int len, pos;
347 boolean done = false;
348 byte [] label = new byte[MAXLABEL + 1];
349 boolean savedState = false;
350
351 while (!done) {
352 len = in.readU8();
353 switch (len & LABEL_MASK) {
354 case LABEL_NORMAL:
355 if (getlabels() >= MAXLABELS)
356 throw new WireParseException("too many labels");
357 if (len == 0) {
358 append(emptyLabel, 0, 1);
359 done = true;
360 } else {
361 label[0] = (byte)len;
362 in.readByteArray(label, 1, len);
363 append(label, 0, 1);
364 }
365 break;
366 case LABEL_COMPRESSION:
367 pos = in.readU8();
368 pos += ((len & ~LABEL_MASK) << 8);
369 if (Options.check("verbosecompression"))
370 System.err.println("currently " + in.current() +
371 ", pointer to " + pos);
372
373 if (pos >= in.current() - 2)
374 throw new WireParseException("bad compression");
375 if (!savedState) {
376 in.save();
377 savedState = true;
378 }
379 in.jump(pos);
380 if (Options.check("verbosecompression"))
381 System.err.println("current name '" + this +
382 "', seeking to " + pos);
383 break;
384 default:
385 throw new WireParseException("bad label type");
386 }
387 }
388 if (savedState) {
389 in.restore();
390 }
391 }
392
393 /**
394 * Create a new name from DNS wire format
395 * @param b A byte array containing the wire format of the name.
396 */
397 public
Name(byte [] b)398 Name(byte [] b) throws IOException {
399 this(new DNSInput(b));
400 }
401
402 /**
403 * Create a new name by removing labels from the beginning of an existing Name
404 * @param src An existing Name
405 * @param n The number of labels to remove from the beginning in the copy
406 */
407 public
Name(Name src, int n)408 Name(Name src, int n) {
409 int slabels = src.labels();
410 if (n > slabels)
411 throw new IllegalArgumentException("attempted to remove too " +
412 "many labels");
413 name = src.name;
414 setlabels(slabels - n);
415 for (int i = 0; i < MAXOFFSETS && i < slabels - n; i++)
416 setoffset(i, src.offset(i + n));
417 }
418
419 /**
420 * Creates a new name by concatenating two existing names.
421 * @param prefix The prefix name.
422 * @param suffix The suffix name.
423 * @return The concatenated name.
424 * @throws NameTooLongException The name is too long.
425 */
426 public static Name
concatenate(Name prefix, Name suffix)427 concatenate(Name prefix, Name suffix) throws NameTooLongException {
428 if (prefix.isAbsolute())
429 return (prefix);
430 Name newname = new Name();
431 copy(prefix, newname);
432 newname.append(suffix.name, suffix.offset(0), suffix.getlabels());
433 return newname;
434 }
435
436 /**
437 * If this name is a subdomain of origin, return a new name relative to
438 * origin with the same value. Otherwise, return the existing name.
439 * @param origin The origin to remove.
440 * @return The possibly relativized name.
441 */
442 public Name
relativize(Name origin)443 relativize(Name origin) {
444 if (origin == null || !subdomain(origin))
445 return this;
446 Name newname = new Name();
447 copy(this, newname);
448 int length = length() - origin.length();
449 int labels = newname.labels() - origin.labels();
450 newname.setlabels(labels);
451 newname.name = new byte[length];
452 System.arraycopy(name, offset(0), newname.name, 0, length);
453 return newname;
454 }
455
456 /**
457 * Generates a new Name with the first n labels replaced by a wildcard
458 * @return The wildcard name
459 */
460 public Name
wild(int n)461 wild(int n) {
462 if (n < 1)
463 throw new IllegalArgumentException("must replace 1 or more " +
464 "labels");
465 try {
466 Name newname = new Name();
467 copy(wild, newname);
468 newname.append(name, offset(n), getlabels() - n);
469 return newname;
470 }
471 catch (NameTooLongException e) {
472 throw new IllegalStateException
473 ("Name.wild: concatenate failed");
474 }
475 }
476
477 /**
478 * Returns a canonicalized version of the Name (all lowercase). This may be
479 * the same name, if the input Name is already canonical.
480 */
481 public Name
canonicalize()482 canonicalize() {
483 boolean canonical = true;
484 for (int i = 0; i < name.length; i++) {
485 if (lowercase[name[i] & 0xFF] != name[i]) {
486 canonical = false;
487 break;
488 }
489 }
490 if (canonical)
491 return this;
492
493 Name newname = new Name();
494 newname.appendSafe(name, offset(0), getlabels());
495 for (int i = 0; i < newname.name.length; i++)
496 newname.name[i] = lowercase[newname.name[i] & 0xFF];
497
498 return newname;
499 }
500
501 /**
502 * Generates a new Name to be used when following a DNAME.
503 * @param dname The DNAME record to follow.
504 * @return The constructed name.
505 * @throws NameTooLongException The resulting name is too long.
506 */
507 public Name
fromDNAME(DNAMERecord dname)508 fromDNAME(DNAMERecord dname) throws NameTooLongException {
509 Name dnameowner = dname.getName();
510 Name dnametarget = dname.getTarget();
511 if (!subdomain(dnameowner))
512 return null;
513
514 int plabels = labels() - dnameowner.labels();
515 int plength = length() - dnameowner.length();
516 int pstart = offset(0);
517
518 int dlabels = dnametarget.labels();
519 int dlength = dnametarget.length();
520
521 if (plength + dlength > MAXNAME)
522 throw new NameTooLongException();
523
524 Name newname = new Name();
525 newname.setlabels(plabels + dlabels);
526 newname.name = new byte[plength + dlength];
527 System.arraycopy(name, pstart, newname.name, 0, plength);
528 System.arraycopy(dnametarget.name, 0, newname.name, plength, dlength);
529
530 for (int i = 0, pos = 0; i < MAXOFFSETS && i < plabels + dlabels; i++) {
531 newname.setoffset(i, pos);
532 pos += (newname.name[pos] + 1);
533 }
534 return newname;
535 }
536
537 /**
538 * Is this name a wildcard?
539 */
540 public boolean
isWild()541 isWild() {
542 if (labels() == 0)
543 return false;
544 return (name[0] == (byte)1 && name[1] == (byte)'*');
545 }
546
547 /**
548 * Is this name absolute?
549 */
550 public boolean
isAbsolute()551 isAbsolute() {
552 int nlabels = labels();
553 if (nlabels == 0)
554 return false;
555 return name[offset(nlabels - 1)] == 0;
556 }
557
558 /**
559 * The length of the name.
560 */
561 public short
length()562 length() {
563 if (getlabels() == 0)
564 return 0;
565 return (short)(name.length - offset(0));
566 }
567
568 /**
569 * The number of labels in the name.
570 */
571 public int
labels()572 labels() {
573 return getlabels();
574 }
575
576 /**
577 * Is the current Name a subdomain of the specified name?
578 */
579 public boolean
subdomain(Name domain)580 subdomain(Name domain) {
581 int labels = labels();
582 int dlabels = domain.labels();
583 if (dlabels > labels)
584 return false;
585 if (dlabels == labels)
586 return equals(domain);
587 return domain.equals(name, offset(labels - dlabels));
588 }
589
590 private String
byteString(byte [] array, int pos)591 byteString(byte [] array, int pos) {
592 StringBuffer sb = new StringBuffer();
593 int len = array[pos++];
594 for (int i = pos; i < pos + len; i++) {
595 int b = array[i] & 0xFF;
596 if (b <= 0x20 || b >= 0x7f) {
597 sb.append('\\');
598 sb.append(byteFormat.format(b));
599 }
600 else if (b == '"' || b == '(' || b == ')' || b == '.' ||
601 b == ';' || b == '\\' || b == '@' || b == '$')
602 {
603 sb.append('\\');
604 sb.append((char)b);
605 }
606 else
607 sb.append((char)b);
608 }
609 return sb.toString();
610 }
611
612 /**
613 * Convert a Name to a String
614 * @param omitFinalDot If true, and the name is absolute, omit the final dot.
615 * @return The representation of this name as a (printable) String.
616 */
617 public String
toString(boolean omitFinalDot)618 toString(boolean omitFinalDot) {
619 int labels = labels();
620 if (labels == 0)
621 return "@";
622 else if (labels == 1 && name[offset(0)] == 0)
623 return ".";
624 StringBuffer sb = new StringBuffer();
625 for (int i = 0, pos = offset(0); i < labels; i++) {
626 int len = name[pos];
627 if (len > MAXLABEL)
628 throw new IllegalStateException("invalid label");
629 if (len == 0) {
630 if (!omitFinalDot)
631 sb.append('.');
632 break;
633 }
634 if (i > 0)
635 sb.append('.');
636 sb.append(byteString(name, pos));
637 pos += (1 + len);
638 }
639 return sb.toString();
640 }
641
642 /**
643 * Convert a Name to a String
644 * @return The representation of this name as a (printable) String.
645 */
646 public String
toString()647 toString() {
648 return toString(false);
649 }
650
651 /**
652 * Retrieve the nth label of a Name. This makes a copy of the label; changing
653 * this does not change the Name.
654 * @param n The label to be retrieved. The first label is 0.
655 */
656 public byte []
getLabel(int n)657 getLabel(int n) {
658 int pos = offset(n);
659 byte len = (byte)(name[pos] + 1);
660 byte [] label = new byte[len];
661 System.arraycopy(name, pos, label, 0, len);
662 return label;
663 }
664
665 /**
666 * Convert the nth label in a Name to a String
667 * @param n The label to be converted to a (printable) String. The first
668 * label is 0.
669 */
670 public String
getLabelString(int n)671 getLabelString(int n) {
672 int pos = offset(n);
673 return byteString(name, pos);
674 }
675
676 /**
677 * Emit a Name in DNS wire format
678 * @param out The output stream containing the DNS message.
679 * @param c The compression context, or null of no compression is desired.
680 * @throws IllegalArgumentException The name is not absolute.
681 */
682 public void
toWire(DNSOutput out, Compression c)683 toWire(DNSOutput out, Compression c) {
684 if (!isAbsolute())
685 throw new IllegalArgumentException("toWire() called on " +
686 "non-absolute name");
687
688 int labels = labels();
689 for (int i = 0; i < labels - 1; i++) {
690 Name tname;
691 if (i == 0)
692 tname = this;
693 else
694 tname = new Name(this, i);
695 int pos = -1;
696 if (c != null)
697 pos = c.get(tname);
698 if (pos >= 0) {
699 pos |= (LABEL_MASK << 8);
700 out.writeU16(pos);
701 return;
702 } else {
703 if (c != null)
704 c.add(out.current(), tname);
705 int off = offset(i);
706 out.writeByteArray(name, off, name[off] + 1);
707 }
708 }
709 out.writeU8(0);
710 }
711
712 /**
713 * Emit a Name in DNS wire format
714 * @throws IllegalArgumentException The name is not absolute.
715 */
716 public byte []
toWire()717 toWire() {
718 DNSOutput out = new DNSOutput();
719 toWire(out, null);
720 return out.toByteArray();
721 }
722
723 /**
724 * Emit a Name in canonical DNS wire format (all lowercase)
725 * @param out The output stream to which the message is written.
726 */
727 public void
toWireCanonical(DNSOutput out)728 toWireCanonical(DNSOutput out) {
729 byte [] b = toWireCanonical();
730 out.writeByteArray(b);
731 }
732
733 /**
734 * Emit a Name in canonical DNS wire format (all lowercase)
735 * @return The canonical form of the name.
736 */
737 public byte []
toWireCanonical()738 toWireCanonical() {
739 int labels = labels();
740 if (labels == 0)
741 return (new byte[0]);
742 byte [] b = new byte[name.length - offset(0)];
743 for (int i = 0, spos = offset(0), dpos = 0; i < labels; i++) {
744 int len = name[spos];
745 if (len > MAXLABEL)
746 throw new IllegalStateException("invalid label");
747 b[dpos++] = name[spos++];
748 for (int j = 0; j < len; j++)
749 b[dpos++] = lowercase[(name[spos++] & 0xFF)];
750 }
751 return b;
752 }
753
754 /**
755 * Emit a Name in DNS wire format
756 * @param out The output stream containing the DNS message.
757 * @param c The compression context, or null of no compression is desired.
758 * @param canonical If true, emit the name in canonicalized form
759 * (all lowercase).
760 * @throws IllegalArgumentException The name is not absolute.
761 */
762 public void
toWire(DNSOutput out, Compression c, boolean canonical)763 toWire(DNSOutput out, Compression c, boolean canonical) {
764 if (canonical)
765 toWireCanonical(out);
766 else
767 toWire(out, c);
768 }
769
770 private final boolean
equals(byte [] b, int bpos)771 equals(byte [] b, int bpos) {
772 int labels = labels();
773 for (int i = 0, pos = offset(0); i < labels; i++) {
774 if (name[pos] != b[bpos])
775 return false;
776 int len = name[pos++];
777 bpos++;
778 if (len > MAXLABEL)
779 throw new IllegalStateException("invalid label");
780 for (int j = 0; j < len; j++)
781 if (lowercase[(name[pos++] & 0xFF)] !=
782 lowercase[(b[bpos++] & 0xFF)])
783 return false;
784 }
785 return true;
786 }
787
788 /**
789 * Are these two Names equivalent?
790 */
791 public boolean
equals(Object arg)792 equals(Object arg) {
793 if (arg == this)
794 return true;
795 if (arg == null || !(arg instanceof Name))
796 return false;
797 Name d = (Name) arg;
798 if (d.hashcode == 0)
799 d.hashCode();
800 if (hashcode == 0)
801 hashCode();
802 if (d.hashcode != hashcode)
803 return false;
804 if (d.labels() != labels())
805 return false;
806 return equals(d.name, d.offset(0));
807 }
808
809 /**
810 * Computes a hashcode based on the value
811 */
812 public int
hashCode()813 hashCode() {
814 if (hashcode != 0)
815 return (hashcode);
816 int code = 0;
817 for (int i = offset(0); i < name.length; i++)
818 code += ((code << 3) + lowercase[(name[i] & 0xFF)]);
819 hashcode = code;
820 return hashcode;
821 }
822
823 /**
824 * Compares this Name to another Object.
825 * @param o The Object to be compared.
826 * @return The value 0 if the argument is a name equivalent to this name;
827 * a value less than 0 if the argument is less than this name in the canonical
828 * ordering, and a value greater than 0 if the argument is greater than this
829 * name in the canonical ordering.
830 * @throws ClassCastException if the argument is not a Name.
831 */
832 public int
compareTo(Object o)833 compareTo(Object o) {
834 Name arg = (Name) o;
835
836 if (this == arg)
837 return (0);
838
839 int labels = labels();
840 int alabels = arg.labels();
841 int compares = labels > alabels ? alabels : labels;
842
843 for (int i = 1; i <= compares; i++) {
844 int start = offset(labels - i);
845 int astart = arg.offset(alabels - i);
846 int length = name[start];
847 int alength = arg.name[astart];
848 for (int j = 0; j < length && j < alength; j++) {
849 int n = lowercase[(name[j + start + 1]) & 0xFF] -
850 lowercase[(arg.name[j + astart + 1]) & 0xFF];
851 if (n != 0)
852 return (n);
853 }
854 if (length != alength)
855 return (length - alength);
856 }
857 return (labels - alabels);
858 }
859
860 }
861