• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, 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  */
285 public
Name(String s)286 Name(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  */
299 public static Name
fromString(String s, Name origin)300 fromString(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  */
317 public static Name
fromString(String s)318 fromString(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  */
328 public static Name
fromConstantString(String s)329 fromConstantString(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  */
343 public
Name(DNSInput in)344 Name(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  */
396 public
Name(byte [] b)397 Name(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  */
406 public
Name(Name src, int n)407 Name(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  */
425 public static Name
concatenate(Name prefix, Name suffix)426 concatenate(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  */
441 public Name
relativize(Name origin)442 relativize(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  */
459 public Name
wild(int n)460 wild(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  */
482 public Name
fromDNAME(DNAMERecord dname)483 fromDNAME(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  */
515 public boolean
isWild()516 isWild() {
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  */
525 public boolean
isAbsolute()526 isAbsolute() {
527 	if (labels() == 0)
528 		return false;
529 	return (name[name.length - 1] == 0);
530 }
531 
532 /**
533  * The length of the name.
534  */
535 public short
length()536 length() {
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  */
545 public int
labels()546 labels() {
547 	return getlabels();
548 }
549 
550 /**
551  * Is the current Name a subdomain of the specified name?
552  */
553 public boolean
subdomain(Name domain)554 subdomain(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 
564 private String
byteString(byte [] array, int pos)565 byteString(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  */
590 public String
toString()591 toString() {
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  */
618 public byte []
getLabel(int n)619 getLabel(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  */
632 public String
getLabelString(int n)633 getLabelString(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  */
644 public void
toWire(DNSOutput out, Compression c)645 toWire(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  */
678 public byte []
toWire()679 toWire() {
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  */
689 public void
toWireCanonical(DNSOutput out)690 toWireCanonical(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  */
699 public byte []
toWireCanonical()700 toWireCanonical() {
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  */
724 public void
toWire(DNSOutput out, Compression c, boolean canonical)725 toWire(DNSOutput out, Compression c, boolean canonical) {
726 	if (canonical)
727 		toWireCanonical(out);
728 	else
729 		toWire(out, c);
730 }
731 
732 private final boolean
equals(byte [] b, int bpos)733 equals(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  */
753 public boolean
equals(Object arg)754 equals(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  */
774 public int
hashCode()775 hashCode() {
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  */
794 public int
compareTo(Object o)795 compareTo(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 }
823