Home | History | Annotate | Download | only in DNS
      1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) 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();
     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 
     81 private
     82 Name() {
     83 }
     84 
     85 private final void
     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
     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
    112 setlabels(int labels) {
    113 	offsets &= ~(0xFF);
    114 	offsets |= labels;
    115 }
    116 
    117 private final int
    118 getlabels() {
    119 	return (int)(offsets & 0xFF);
    120 }
    121 
    122 private static final void
    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
    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
    171 parseException(String str, String message) {
    172 	return new TextParseException("'" + str + "': " + message);
    173 }
    174 
    175 private final void
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    546 labels() {
    547 	return getlabels();
    548 }
    549 
    550 /**
    551  * Is the current Name a subdomain of the specified name?
    552  */
    553 public boolean
    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
    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
    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 []
    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
    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
    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 []
    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
    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 []
    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
    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
    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
    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
    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
    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