2
|
1 |
/*
|
5506
|
2 |
* Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
|
2
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
5506
|
7 |
* published by the Free Software Foundation. Oracle designates this
|
2
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
5506
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
2
|
10 |
*
|
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
15 |
* accompanied this code).
|
|
16 |
*
|
|
17 |
* You should have received a copy of the GNU General Public License version
|
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
20 |
*
|
5506
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
22 |
* or visit www.oracle.com if you need additional information or have any
|
|
23 |
* questions.
|
2
|
24 |
*/
|
|
25 |
|
|
26 |
package com.sun.jndi.dns;
|
|
27 |
|
|
28 |
|
|
29 |
import java.util.ArrayList;
|
|
30 |
import java.util.Comparator;
|
|
31 |
import java.util.Enumeration;
|
|
32 |
import java.util.Iterator;
|
|
33 |
|
|
34 |
import javax.naming.*;
|
|
35 |
|
|
36 |
|
|
37 |
/**
|
|
38 |
* <tt>DnsName</tt> implements compound names for DNS as specified by
|
|
39 |
* RFCs 1034 and 1035, and as updated and clarified by RFCs 1123 and 2181.
|
|
40 |
*
|
|
41 |
* <p> The labels in a domain name correspond to JNDI atomic names.
|
|
42 |
* Each label must be less than 64 octets in length, and only the
|
|
43 |
* optional root label at the end of the name may be 0 octets long.
|
|
44 |
* The sum of the lengths of all labels in a name, plus the number of
|
|
45 |
* non-root labels plus 1, must be less than 256. The textual
|
|
46 |
* representation of a domain name consists of the labels, escaped as
|
|
47 |
* needed, dot-separated, and ordered right-to-left.
|
|
48 |
*
|
|
49 |
* <p> A label consists of a sequence of octets, each of which may
|
|
50 |
* have any value from 0 to 255.
|
|
51 |
*
|
|
52 |
* <p> <em>Host names</em> are a subset of domain names.
|
|
53 |
* Their labels contain only ASCII letters, digits, and hyphens, and
|
|
54 |
* none may begin or end with a hyphen. While names not conforming to
|
|
55 |
* these rules may be valid domain names, they will not be usable by a
|
|
56 |
* number of DNS applications, and should in most cases be avoided.
|
|
57 |
*
|
|
58 |
* <p> DNS does not specify an encoding (such as UTF-8) to use for
|
|
59 |
* octets with non-ASCII values. As of this writing there is some
|
|
60 |
* work going on in this area, but it is not yet finalized.
|
|
61 |
* <tt>DnsName</tt> currently converts any non-ASCII octets into
|
|
62 |
* characters using ISO-LATIN-1 encoding, in effect taking the
|
|
63 |
* value of each octet and storing it directly into the low-order byte
|
|
64 |
* of a Java character and <i>vice versa</i>. As a consequence, no
|
|
65 |
* character in a DNS name will ever have a non-zero high-order byte.
|
|
66 |
* When the work on internationalizing domain names has stabilized
|
|
67 |
* (see for example <i>draft-ietf-idn-idna-10.txt</i>), <tt>DnsName</tt>
|
|
68 |
* may be updated to conform to that work.
|
|
69 |
*
|
|
70 |
* <p> Backslash (<tt>\</tt>) is used as the escape character in the
|
|
71 |
* textual representation of a domain name. The character sequence
|
|
72 |
* `<tt>\DDD</tt>', where <tt>DDD</tt> is a 3-digit decimal number
|
|
73 |
* (with leading zeros if needed), represents the octet whose value
|
|
74 |
* is <tt>DDD</tt>. The character sequence `<tt>\C</tt>', where
|
|
75 |
* <tt>C</tt> is a character other than <tt>'0'</tt> through
|
|
76 |
* <tt>'9'</tt>, represents the octet whose value is that of
|
|
77 |
* <tt>C</tt> (again using ISO-LATIN-1 encoding); this is particularly
|
|
78 |
* useful for escaping <tt>'.'</tt> or backslash itself. Backslash is
|
|
79 |
* otherwise not allowed in a domain name. Note that escape characters
|
|
80 |
* are interpreted when a name is parsed. So, for example, the character
|
|
81 |
* sequences `<tt>S</tt>', `<tt>\S</tt>', and `<tt>\083</tt>' each
|
|
82 |
* represent the same one-octet name. The <tt>toString()</tt> method
|
|
83 |
* does not generally insert escape sequences except where necessary.
|
|
84 |
* If, however, the <tt>DnsName</tt> was constructed using unneeded
|
|
85 |
* escapes, those escapes may appear in the <tt>toString</tt> result.
|
|
86 |
*
|
|
87 |
* <p> Atomic names passed as parameters to methods of
|
|
88 |
* <tt>DnsName</tt>, and those returned by them, are unescaped. So,
|
|
89 |
* for example, <tt>(new DnsName()).add("a.b")</tt> creates an
|
|
90 |
* object representing the one-label domain name <tt>a\.b</tt>, and
|
|
91 |
* calling <tt>get(0)</tt> on this object returns <tt>"a.b"</tt>.
|
|
92 |
*
|
|
93 |
* <p> While DNS names are case-preserving, comparisons between them
|
|
94 |
* are case-insensitive. When comparing names containing non-ASCII
|
|
95 |
* octets, <tt>DnsName</tt> uses case-insensitive comparison
|
|
96 |
* between pairs of ASCII values, and exact binary comparison
|
|
97 |
* otherwise.
|
|
98 |
|
|
99 |
* <p> A <tt>DnsName</tt> instance is not synchronized against
|
|
100 |
* concurrent access by multiple threads.
|
|
101 |
*
|
|
102 |
* @author Scott Seligman
|
|
103 |
*/
|
|
104 |
|
|
105 |
|
|
106 |
public final class DnsName implements Name {
|
|
107 |
|
|
108 |
// If non-null, the domain name represented by this DnsName.
|
|
109 |
private String domain = "";
|
|
110 |
|
|
111 |
// The labels of this domain name, as a list of strings. Index 0
|
|
112 |
// corresponds to the leftmost (least significant) label: note that
|
|
113 |
// this is the reverse of the ordering used by the Name interface.
|
|
114 |
private ArrayList labels = new ArrayList();
|
|
115 |
|
|
116 |
// The number of octets needed to carry this domain name in a DNS
|
|
117 |
// packet. Equal to the sum of the lengths of each label, plus the
|
|
118 |
// number of non-root labels, plus 1. Must remain less than 256.
|
|
119 |
private short octets = 1;
|
|
120 |
|
|
121 |
|
|
122 |
/**
|
|
123 |
* Constructs a <tt>DnsName</tt> representing the empty domain name.
|
|
124 |
*/
|
|
125 |
public DnsName() {
|
|
126 |
}
|
|
127 |
|
|
128 |
/**
|
|
129 |
* Constructs a <tt>DnsName</tt> representing a given domain name.
|
|
130 |
*
|
|
131 |
* @param name the domain name to parse
|
|
132 |
* @throws InvalidNameException if <tt>name</tt> does not conform
|
|
133 |
* to DNS syntax.
|
|
134 |
*/
|
|
135 |
public DnsName(String name) throws InvalidNameException {
|
|
136 |
parse(name);
|
|
137 |
}
|
|
138 |
|
|
139 |
/*
|
|
140 |
* Returns a new DnsName with its name components initialized to
|
|
141 |
* the components of "n" in the range [beg,end). Indexing is as
|
|
142 |
* for the Name interface, with 0 being the most significant.
|
|
143 |
*/
|
|
144 |
private DnsName(DnsName n, int beg, int end) {
|
|
145 |
// Compute indexes into "labels", which has least-significant label
|
|
146 |
// at index 0 (opposite to the convention used for "beg" and "end").
|
|
147 |
int b = n.size() - end;
|
|
148 |
int e = n.size() - beg;
|
|
149 |
labels.addAll(n.labels.subList(b, e));
|
|
150 |
|
|
151 |
if (size() == n.size()) {
|
|
152 |
domain = n.domain;
|
|
153 |
octets = n.octets;
|
|
154 |
} else {
|
|
155 |
Iterator iter = labels.iterator();
|
|
156 |
while (iter.hasNext()) {
|
|
157 |
String label = (String) iter.next();
|
|
158 |
if (label.length() > 0) {
|
|
159 |
octets += (short) (label.length() + 1);
|
|
160 |
}
|
|
161 |
}
|
|
162 |
}
|
|
163 |
}
|
|
164 |
|
|
165 |
|
|
166 |
public String toString() {
|
|
167 |
if (domain == null) {
|
|
168 |
StringBuffer buf = new StringBuffer();
|
|
169 |
Iterator iter = labels.iterator();
|
|
170 |
while (iter.hasNext()) {
|
|
171 |
String label = (String) iter.next();
|
|
172 |
if (buf.length() > 0 || label.length() == 0) {
|
|
173 |
buf.append('.');
|
|
174 |
}
|
|
175 |
escape(buf, label);
|
|
176 |
}
|
|
177 |
domain = buf.toString();
|
|
178 |
}
|
|
179 |
return domain;
|
|
180 |
}
|
|
181 |
|
|
182 |
/**
|
|
183 |
* Does this domain name follow <em>host name</em> syntax?
|
|
184 |
*/
|
|
185 |
public boolean isHostName() {
|
|
186 |
Iterator iter = labels.iterator();
|
|
187 |
while (iter.hasNext()) {
|
|
188 |
if (!isHostNameLabel((String) iter.next())) {
|
|
189 |
return false;
|
|
190 |
}
|
|
191 |
}
|
|
192 |
return true;
|
|
193 |
}
|
|
194 |
|
|
195 |
public short getOctets() {
|
|
196 |
return octets;
|
|
197 |
}
|
|
198 |
|
|
199 |
public int size() {
|
|
200 |
return labels.size();
|
|
201 |
}
|
|
202 |
|
|
203 |
public boolean isEmpty() {
|
|
204 |
return (size() == 0);
|
|
205 |
}
|
|
206 |
|
|
207 |
public int hashCode() {
|
|
208 |
int h = 0;
|
|
209 |
for (int i = 0; i < size(); i++) {
|
|
210 |
h = 31 * h + getKey(i).hashCode();
|
|
211 |
}
|
|
212 |
return h;
|
|
213 |
}
|
|
214 |
|
|
215 |
public boolean equals(Object obj) {
|
|
216 |
if (!(obj instanceof Name) || (obj instanceof CompositeName)) {
|
|
217 |
return false;
|
|
218 |
}
|
|
219 |
Name n = (Name) obj;
|
|
220 |
return ((size() == n.size()) && // shortcut: do sizes differ?
|
|
221 |
(compareTo(obj) == 0));
|
|
222 |
}
|
|
223 |
|
|
224 |
public int compareTo(Object obj) {
|
|
225 |
Name n = (Name) obj;
|
|
226 |
return compareRange(0, size(), n); // never 0 if sizes differ
|
|
227 |
}
|
|
228 |
|
|
229 |
public boolean startsWith(Name n) {
|
|
230 |
return ((size() >= n.size()) &&
|
|
231 |
(compareRange(0, n.size(), n) == 0));
|
|
232 |
}
|
|
233 |
|
|
234 |
public boolean endsWith(Name n) {
|
|
235 |
return ((size() >= n.size()) &&
|
|
236 |
(compareRange(size() - n.size(), size(), n) == 0));
|
|
237 |
}
|
|
238 |
|
|
239 |
public String get(int pos) {
|
|
240 |
if (pos < 0 || pos >= size()) {
|
|
241 |
throw new ArrayIndexOutOfBoundsException();
|
|
242 |
}
|
|
243 |
int i = size() - pos - 1; // index of "pos" component in "labels"
|
|
244 |
return (String) labels.get(i);
|
|
245 |
}
|
|
246 |
|
|
247 |
public Enumeration getAll() {
|
|
248 |
return new Enumeration() {
|
|
249 |
int pos = 0;
|
|
250 |
public boolean hasMoreElements() {
|
|
251 |
return (pos < size());
|
|
252 |
}
|
|
253 |
public Object nextElement() {
|
|
254 |
if (pos < size()) {
|
|
255 |
return get(pos++);
|
|
256 |
}
|
|
257 |
throw new java.util.NoSuchElementException();
|
|
258 |
}
|
|
259 |
};
|
|
260 |
}
|
|
261 |
|
|
262 |
public Name getPrefix(int pos) {
|
|
263 |
return new DnsName(this, 0, pos);
|
|
264 |
}
|
|
265 |
|
|
266 |
public Name getSuffix(int pos) {
|
|
267 |
return new DnsName(this, pos, size());
|
|
268 |
}
|
|
269 |
|
|
270 |
public Object clone() {
|
|
271 |
return new DnsName(this, 0, size());
|
|
272 |
}
|
|
273 |
|
|
274 |
public Object remove(int pos) {
|
|
275 |
if (pos < 0 || pos >= size()) {
|
|
276 |
throw new ArrayIndexOutOfBoundsException();
|
|
277 |
}
|
|
278 |
int i = size() - pos - 1; // index of element to remove in "labels"
|
|
279 |
String label = (String) labels.remove(i);
|
|
280 |
int len = label.length();
|
|
281 |
if (len > 0) {
|
|
282 |
octets -= (short) (len + 1);
|
|
283 |
}
|
|
284 |
domain = null; // invalidate "domain"
|
|
285 |
return label;
|
|
286 |
}
|
|
287 |
|
|
288 |
public Name add(String comp) throws InvalidNameException {
|
|
289 |
return add(size(), comp);
|
|
290 |
}
|
|
291 |
|
|
292 |
public Name add(int pos, String comp) throws InvalidNameException {
|
|
293 |
if (pos < 0 || pos > size()) {
|
|
294 |
throw new ArrayIndexOutOfBoundsException();
|
|
295 |
}
|
|
296 |
// Check for empty labels: may have only one, and only at end.
|
|
297 |
int len = comp.length();
|
|
298 |
if ((pos > 0 && len == 0) ||
|
|
299 |
(pos == 0 && hasRootLabel())) {
|
|
300 |
throw new InvalidNameException(
|
|
301 |
"Empty label must be the last label in a domain name");
|
|
302 |
}
|
|
303 |
// Check total name length.
|
|
304 |
if (len > 0) {
|
|
305 |
if (octets + len + 1 >= 256) {
|
|
306 |
throw new InvalidNameException("Name too long");
|
|
307 |
}
|
|
308 |
octets += (short) (len + 1);
|
|
309 |
}
|
|
310 |
|
|
311 |
int i = size() - pos; // index for insertion into "labels"
|
|
312 |
verifyLabel(comp);
|
|
313 |
labels.add(i, comp);
|
|
314 |
|
|
315 |
domain = null; // invalidate "domain"
|
|
316 |
return this;
|
|
317 |
}
|
|
318 |
|
|
319 |
public Name addAll(Name suffix) throws InvalidNameException {
|
|
320 |
return addAll(size(), suffix);
|
|
321 |
}
|
|
322 |
|
|
323 |
public Name addAll(int pos, Name n) throws InvalidNameException {
|
|
324 |
if (n instanceof DnsName) {
|
|
325 |
// "n" is a DnsName so we can insert it as a whole, rather than
|
|
326 |
// verifying and inserting it component-by-component.
|
|
327 |
// More code, but less work.
|
|
328 |
DnsName dn = (DnsName) n;
|
|
329 |
|
|
330 |
if (dn.isEmpty()) {
|
|
331 |
return this;
|
|
332 |
}
|
|
333 |
// Check for empty labels: may have only one, and only at end.
|
|
334 |
if ((pos > 0 && dn.hasRootLabel()) ||
|
|
335 |
(pos == 0 && hasRootLabel())) {
|
|
336 |
throw new InvalidNameException(
|
|
337 |
"Empty label must be the last label in a domain name");
|
|
338 |
}
|
|
339 |
|
|
340 |
short newOctets = (short) (octets + dn.octets - 1);
|
|
341 |
if (newOctets > 255) {
|
|
342 |
throw new InvalidNameException("Name too long");
|
|
343 |
}
|
|
344 |
octets = newOctets;
|
|
345 |
int i = size() - pos; // index for insertion into "labels"
|
|
346 |
labels.addAll(i, dn.labels);
|
|
347 |
|
|
348 |
// Preserve "domain" if we're appending or prepending,
|
|
349 |
// otherwise invalidate it.
|
|
350 |
if (isEmpty()) {
|
|
351 |
domain = dn.domain;
|
|
352 |
} else if (domain == null || dn.domain == null) {
|
|
353 |
domain = null;
|
|
354 |
} else if (pos == 0) {
|
|
355 |
domain += (dn.domain.equals(".") ? "" : ".") + dn.domain;
|
|
356 |
} else if (pos == size()) {
|
|
357 |
domain = dn.domain + (domain.equals(".") ? "" : ".") + domain;
|
|
358 |
} else {
|
|
359 |
domain = null;
|
|
360 |
}
|
|
361 |
|
|
362 |
} else if (n instanceof CompositeName) {
|
|
363 |
n = (DnsName) n; // force ClassCastException
|
|
364 |
|
|
365 |
} else { // "n" is a compound name, but not a DnsName.
|
|
366 |
// Add labels least-significant first: sometimes more efficient.
|
|
367 |
for (int i = n.size() - 1; i >= 0; i--) {
|
|
368 |
add(pos, n.get(i));
|
|
369 |
}
|
|
370 |
}
|
|
371 |
return this;
|
|
372 |
}
|
|
373 |
|
|
374 |
|
|
375 |
boolean hasRootLabel() {
|
|
376 |
return (!isEmpty() &&
|
|
377 |
get(0).equals(""));
|
|
378 |
}
|
|
379 |
|
|
380 |
/*
|
|
381 |
* Helper method for public comparison methods. Lexicographically
|
|
382 |
* compares components of this name in the range [beg,end) with
|
|
383 |
* all components of "n". Indexing is as for the Name interface,
|
|
384 |
* with 0 being the most significant. Returns negative, zero, or
|
|
385 |
* positive as these name components are less than, equal to, or
|
|
386 |
* greater than those of "n".
|
|
387 |
*/
|
|
388 |
private int compareRange(int beg, int end, Name n) {
|
|
389 |
if (n instanceof CompositeName) {
|
|
390 |
n = (DnsName) n; // force ClassCastException
|
|
391 |
}
|
|
392 |
// Loop through labels, starting with most significant.
|
|
393 |
int minSize = Math.min(end - beg, n.size());
|
|
394 |
for (int i = 0; i < minSize; i++) {
|
|
395 |
String label1 = get(i + beg);
|
|
396 |
String label2 = n.get(i);
|
|
397 |
|
|
398 |
int j = size() - (i + beg) - 1; // index of label1 in "labels"
|
|
399 |
// assert (label1 == labels.get(j));
|
|
400 |
|
|
401 |
int c = compareLabels(label1, label2);
|
|
402 |
if (c != 0) {
|
|
403 |
return c;
|
|
404 |
}
|
|
405 |
}
|
|
406 |
return ((end - beg) - n.size()); // longer range wins
|
|
407 |
}
|
|
408 |
|
|
409 |
/*
|
|
410 |
* Returns a key suitable for hashing the label at index i.
|
|
411 |
* Indexing is as for the Name interface, with 0 being the most
|
|
412 |
* significant.
|
|
413 |
*/
|
|
414 |
String getKey(int i) {
|
|
415 |
return keyForLabel(get(i));
|
|
416 |
}
|
|
417 |
|
|
418 |
|
|
419 |
/*
|
|
420 |
* Parses a domain name, setting the values of instance vars accordingly.
|
|
421 |
*/
|
|
422 |
private void parse(String name) throws InvalidNameException {
|
|
423 |
|
|
424 |
StringBuffer label = new StringBuffer(); // label being parsed
|
|
425 |
|
|
426 |
for (int i = 0; i < name.length(); i++) {
|
|
427 |
char c = name.charAt(i);
|
|
428 |
|
|
429 |
if (c == '\\') { // found an escape sequence
|
|
430 |
c = getEscapedOctet(name, i++);
|
|
431 |
if (isDigit(name.charAt(i))) { // sequence is \DDD
|
|
432 |
i += 2; // consume remaining digits
|
|
433 |
}
|
|
434 |
label.append(c);
|
|
435 |
|
|
436 |
} else if (c != '.') { // an unescaped octet
|
|
437 |
label.append(c);
|
|
438 |
|
|
439 |
} else { // found '.' separator
|
|
440 |
add(0, label.toString()); // check syntax, then add label
|
|
441 |
// to end of name
|
|
442 |
label.delete(0, i); // clear buffer for next label
|
|
443 |
}
|
|
444 |
}
|
|
445 |
|
|
446 |
// If name is neither "." nor "", the octets (zero or more)
|
|
447 |
// from the rightmost dot onward are now added as the final
|
|
448 |
// label of the name. Those two are special cases in that for
|
|
449 |
// all other domain names, the number of labels is one greater
|
|
450 |
// than the number of dot separators.
|
|
451 |
if (!name.equals("") && !name.equals(".")) {
|
|
452 |
add(0, label.toString());
|
|
453 |
}
|
|
454 |
|
|
455 |
domain = name; // do this last, since add() sets it to null
|
|
456 |
}
|
|
457 |
|
|
458 |
/*
|
|
459 |
* Returns (as a char) the octet indicated by the escape sequence
|
|
460 |
* at a given position within a domain name.
|
|
461 |
* @throws InvalidNameException if a valid escape sequence is not found.
|
|
462 |
*/
|
|
463 |
private static char getEscapedOctet(String name, int pos)
|
|
464 |
throws InvalidNameException {
|
|
465 |
try {
|
|
466 |
// assert (name.charAt(pos) == '\\');
|
|
467 |
char c1 = name.charAt(++pos);
|
|
468 |
if (isDigit(c1)) { // sequence is `\DDD'
|
|
469 |
char c2 = name.charAt(++pos);
|
|
470 |
char c3 = name.charAt(++pos);
|
|
471 |
if (isDigit(c2) && isDigit(c3)) {
|
|
472 |
return (char)
|
|
473 |
((c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0'));
|
|
474 |
} else {
|
|
475 |
throw new InvalidNameException(
|
|
476 |
"Invalid escape sequence in " + name);
|
|
477 |
}
|
|
478 |
} else { // sequence is `\C'
|
|
479 |
return c1;
|
|
480 |
}
|
|
481 |
} catch (IndexOutOfBoundsException e) {
|
|
482 |
throw new InvalidNameException(
|
|
483 |
"Invalid escape sequence in " + name);
|
|
484 |
}
|
|
485 |
}
|
|
486 |
|
|
487 |
/*
|
|
488 |
* Checks that this label is valid.
|
|
489 |
* @throws InvalidNameException if label is not valid.
|
|
490 |
*/
|
|
491 |
private static void verifyLabel(String label) throws InvalidNameException {
|
|
492 |
if (label.length() > 63) {
|
|
493 |
throw new InvalidNameException(
|
|
494 |
"Label exceeds 63 octets: " + label);
|
|
495 |
}
|
|
496 |
// Check for two-byte characters.
|
|
497 |
for (int i = 0; i < label.length(); i++) {
|
|
498 |
char c = label.charAt(i);
|
|
499 |
if ((c & 0xFF00) != 0) {
|
|
500 |
throw new InvalidNameException(
|
|
501 |
"Label has two-byte char: " + label);
|
|
502 |
}
|
|
503 |
}
|
|
504 |
}
|
|
505 |
|
|
506 |
/*
|
|
507 |
* Does this label conform to host name syntax?
|
|
508 |
*/
|
|
509 |
private static boolean isHostNameLabel(String label) {
|
|
510 |
for (int i = 0; i < label.length(); i++) {
|
|
511 |
char c = label.charAt(i);
|
|
512 |
if (!isHostNameChar(c)) {
|
|
513 |
return false;
|
|
514 |
}
|
|
515 |
}
|
|
516 |
return !(label.startsWith("-") || label.endsWith("-"));
|
|
517 |
}
|
|
518 |
|
|
519 |
private static boolean isHostNameChar(char c) {
|
|
520 |
return (c == '-' ||
|
|
521 |
c >= 'a' && c <= 'z' ||
|
|
522 |
c >= 'A' && c <= 'Z' ||
|
|
523 |
c >= '0' && c <= '9');
|
|
524 |
}
|
|
525 |
|
|
526 |
private static boolean isDigit(char c) {
|
|
527 |
return (c >= '0' && c <= '9');
|
|
528 |
}
|
|
529 |
|
|
530 |
/*
|
|
531 |
* Append a label to buf, escaping as needed.
|
|
532 |
*/
|
|
533 |
private static void escape(StringBuffer buf, String label) {
|
|
534 |
for (int i = 0; i < label.length(); i++) {
|
|
535 |
char c = label.charAt(i);
|
|
536 |
if (c == '.' || c == '\\') {
|
|
537 |
buf.append('\\');
|
|
538 |
}
|
|
539 |
buf.append(c);
|
|
540 |
}
|
|
541 |
}
|
|
542 |
|
|
543 |
/*
|
|
544 |
* Compares two labels, ignoring case for ASCII values.
|
|
545 |
* Returns negative, zero, or positive as the first label
|
|
546 |
* is less than, equal to, or greater than the second.
|
|
547 |
* See keyForLabel().
|
|
548 |
*/
|
|
549 |
private static int compareLabels(String label1, String label2) {
|
|
550 |
int min = Math.min(label1.length(), label2.length());
|
|
551 |
for (int i = 0; i < min; i++) {
|
|
552 |
char c1 = label1.charAt(i);
|
|
553 |
char c2 = label2.charAt(i);
|
|
554 |
if (c1 >= 'A' && c1 <= 'Z') {
|
|
555 |
c1 += 'a' - 'A'; // to lower case
|
|
556 |
}
|
|
557 |
if (c2 >= 'A' && c2 <= 'Z') {
|
|
558 |
c2 += 'a' - 'A'; // to lower case
|
|
559 |
}
|
|
560 |
if (c1 != c2) {
|
|
561 |
return (c1 - c2);
|
|
562 |
}
|
|
563 |
}
|
|
564 |
return (label1.length() - label2.length()); // the longer one wins
|
|
565 |
}
|
|
566 |
|
|
567 |
/*
|
|
568 |
* Returns a key suitable for hashing a label. Two labels map to
|
|
569 |
* the same key iff they are equal, taking possible case-folding
|
|
570 |
* into account. See compareLabels().
|
|
571 |
*/
|
|
572 |
private static String keyForLabel(String label) {
|
|
573 |
StringBuffer buf = new StringBuffer(label.length());
|
|
574 |
for (int i = 0; i < label.length(); i++) {
|
|
575 |
char c = label.charAt(i);
|
|
576 |
if (c >= 'A' && c <= 'Z') {
|
|
577 |
c += 'a' - 'A'; // to lower case
|
|
578 |
}
|
|
579 |
buf.append(c);
|
|
580 |
}
|
|
581 |
return buf.toString();
|
|
582 |
}
|
|
583 |
|
|
584 |
|
|
585 |
/**
|
|
586 |
* Serializes only the domain name string, for compactness and to avoid
|
|
587 |
* any implementation dependency.
|
|
588 |
*
|
|
589 |
* @serialdata The domain name string.
|
|
590 |
*/
|
|
591 |
private void writeObject(java.io.ObjectOutputStream s)
|
|
592 |
throws java.io.IOException {
|
|
593 |
s.writeObject(toString());
|
|
594 |
}
|
|
595 |
|
|
596 |
private void readObject(java.io.ObjectInputStream s)
|
|
597 |
throws java.io.IOException, ClassNotFoundException {
|
|
598 |
try {
|
|
599 |
parse((String) s.readObject());
|
|
600 |
} catch (InvalidNameException e) {
|
|
601 |
// shouldn't happen
|
|
602 |
throw new java.io.StreamCorruptedException(
|
|
603 |
"Invalid name: " + domain);
|
|
604 |
}
|
|
605 |
}
|
|
606 |
|
|
607 |
private static final long serialVersionUID = 7040187611324710271L;
|
|
608 |
}
|