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 *
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.
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.
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;
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())) {
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);
|
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 *
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.
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
33 import javax.naming.*;
34
35
36 /**
37 * <tt>DnsName</tt> implements compound names for DNS as specified by
38 * RFCs 1034 and 1035, and as updated and clarified by RFCs 1123 and 2181.
39 *
40 * <p> The labels in a domain name correspond to JNDI atomic names.
41 * Each label must be less than 64 octets in length, and only the
42 * optional root label at the end of the name may be 0 octets long.
43 * The sum of the lengths of all labels in a name, plus the number of
44 * non-root labels plus 1, must be less than 256. The textual
45 * representation of a domain name consists of the labels, escaped as
46 * needed, dot-separated, and ordered right-to-left.
47 *
48 * <p> A label consists of a sequence of octets, each of which may
49 * have any value from 0 to 255.
50 *
51 * <p> <em>Host names</em> are a subset of domain names.
93 * are case-insensitive. When comparing names containing non-ASCII
94 * octets, <tt>DnsName</tt> uses case-insensitive comparison
95 * between pairs of ASCII values, and exact binary comparison
96 * otherwise.
97
98 * <p> A <tt>DnsName</tt> instance is not synchronized against
99 * concurrent access by multiple threads.
100 *
101 * @author Scott Seligman
102 */
103
104
105 public final class DnsName implements Name {
106
107 // If non-null, the domain name represented by this DnsName.
108 private String domain = "";
109
110 // The labels of this domain name, as a list of strings. Index 0
111 // corresponds to the leftmost (least significant) label: note that
112 // this is the reverse of the ordering used by the Name interface.
113 private ArrayList<String> labels = new ArrayList<>();
114
115 // The number of octets needed to carry this domain name in a DNS
116 // packet. Equal to the sum of the lengths of each label, plus the
117 // number of non-root labels, plus 1. Must remain less than 256.
118 private short octets = 1;
119
120
121 /**
122 * Constructs a <tt>DnsName</tt> representing the empty domain name.
123 */
124 public DnsName() {
125 }
126
127 /**
128 * Constructs a <tt>DnsName</tt> representing a given domain name.
129 *
130 * @param name the domain name to parse
131 * @throws InvalidNameException if <tt>name</tt> does not conform
132 * to DNS syntax.
133 */
134 public DnsName(String name) throws InvalidNameException {
135 parse(name);
136 }
137
138 /*
139 * Returns a new DnsName with its name components initialized to
140 * the components of "n" in the range [beg,end). Indexing is as
141 * for the Name interface, with 0 being the most significant.
142 */
143 private DnsName(DnsName n, int beg, int end) {
144 // Compute indexes into "labels", which has least-significant label
145 // at index 0 (opposite to the convention used for "beg" and "end").
146 int b = n.size() - end;
147 int e = n.size() - beg;
148 labels.addAll(n.labels.subList(b, e));
149
150 if (size() == n.size()) {
151 domain = n.domain;
152 octets = n.octets;
153 } else {
154 for (String label: labels) {
155 if (label.length() > 0) {
156 octets += (short) (label.length() + 1);
157 }
158 }
159 }
160 }
161
162
163 public String toString() {
164 if (domain == null) {
165 StringBuilder buf = new StringBuilder();
166 for (String label: labels) {
167 if (buf.length() > 0 || label.length() == 0) {
168 buf.append('.');
169 }
170 escape(buf, label);
171 }
172 domain = buf.toString();
173 }
174 return domain;
175 }
176
177 /**
178 * Does this domain name follow <em>host name</em> syntax?
179 */
180 public boolean isHostName() {
181 for (String label: labels) {
182 if (!isHostNameLabel(label)) {
183 return false;
184 }
185 }
186 return true;
187 }
188
189 public short getOctets() {
190 return octets;
191 }
192
193 public int size() {
194 return labels.size();
195 }
196
197 public boolean isEmpty() {
198 return (size() == 0);
199 }
200
201 public int hashCode() {
202 int h = 0;
218 public int compareTo(Object obj) {
219 Name n = (Name) obj;
220 return compareRange(0, size(), n); // never 0 if sizes differ
221 }
222
223 public boolean startsWith(Name n) {
224 return ((size() >= n.size()) &&
225 (compareRange(0, n.size(), n) == 0));
226 }
227
228 public boolean endsWith(Name n) {
229 return ((size() >= n.size()) &&
230 (compareRange(size() - n.size(), size(), n) == 0));
231 }
232
233 public String get(int pos) {
234 if (pos < 0 || pos >= size()) {
235 throw new ArrayIndexOutOfBoundsException();
236 }
237 int i = size() - pos - 1; // index of "pos" component in "labels"
238 return labels.get(i);
239 }
240
241 public Enumeration<String> getAll() {
242 return new Enumeration<String>() {
243 int pos = 0;
244 public boolean hasMoreElements() {
245 return (pos < size());
246 }
247 public String nextElement() {
248 if (pos < size()) {
249 return get(pos++);
250 }
251 throw new java.util.NoSuchElementException();
252 }
253 };
254 }
255
256 public Name getPrefix(int pos) {
257 return new DnsName(this, 0, pos);
258 }
259
260 public Name getSuffix(int pos) {
261 return new DnsName(this, pos, size());
262 }
263
264 public Object clone() {
265 return new DnsName(this, 0, size());
266 }
267
268 public Object remove(int pos) {
269 if (pos < 0 || pos >= size()) {
270 throw new ArrayIndexOutOfBoundsException();
271 }
272 int i = size() - pos - 1; // index of element to remove in "labels"
273 String label = labels.remove(i);
274 int len = label.length();
275 if (len > 0) {
276 octets -= (short) (len + 1);
277 }
278 domain = null; // invalidate "domain"
279 return label;
280 }
281
282 public Name add(String comp) throws InvalidNameException {
283 return add(size(), comp);
284 }
285
286 public Name add(int pos, String comp) throws InvalidNameException {
287 if (pos < 0 || pos > size()) {
288 throw new ArrayIndexOutOfBoundsException();
289 }
290 // Check for empty labels: may have only one, and only at end.
291 int len = comp.length();
292 if ((pos > 0 && len == 0) ||
293 (pos == 0 && hasRootLabel())) {
507 return false;
508 }
509 }
510 return !(label.startsWith("-") || label.endsWith("-"));
511 }
512
513 private static boolean isHostNameChar(char c) {
514 return (c == '-' ||
515 c >= 'a' && c <= 'z' ||
516 c >= 'A' && c <= 'Z' ||
517 c >= '0' && c <= '9');
518 }
519
520 private static boolean isDigit(char c) {
521 return (c >= '0' && c <= '9');
522 }
523
524 /*
525 * Append a label to buf, escaping as needed.
526 */
527 private static void escape(StringBuilder buf, String label) {
528 for (int i = 0; i < label.length(); i++) {
529 char c = label.charAt(i);
530 if (c == '.' || c == '\\') {
531 buf.append('\\');
532 }
533 buf.append(c);
534 }
535 }
536
537 /*
538 * Compares two labels, ignoring case for ASCII values.
539 * Returns negative, zero, or positive as the first label
540 * is less than, equal to, or greater than the second.
541 * See keyForLabel().
542 */
543 private static int compareLabels(String label1, String label2) {
544 int min = Math.min(label1.length(), label2.length());
545 for (int i = 0; i < min; i++) {
546 char c1 = label1.charAt(i);
547 char c2 = label2.charAt(i);
|