1// 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}
823