001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Alexei Y. Zakharov
021: * @version $Revision: 1.1.2.4 $
022: */package org.apache.harmony.jndi.provider.dns;
023:
024: import java.util.StringTokenizer;
025:
026: import org.apache.harmony.jndi.internal.nls.Messages;
027:
028: /**
029: * Contains some useful routines that are used in other classes.
030: */
031: public class ProviderMgr {
032:
033: static final int LOG_NONE = 0;
034:
035: static final int LOG_ERROR = 1;
036:
037: static final int LOG_WARNING = 2;
038:
039: static final int LOG_DEBUG = 3;
040:
041: static final boolean CHECK_NAMES = false;
042:
043: /**
044: * Parses the given domain name and converts it into
045: * <code>length label length label ... length label</code> sequence of
046: * bytes.
047: *
048: * @param name
049: * a domain name, a dot-separated list of labels
050: * @param buffer
051: * target buffer in which the result will be written
052: * @param startIdx
053: * the index to start at while writing to the buffer array
054: * @return updated index of the buffer array
055: */
056: public static int writeName(String name, byte[] buffer, int startIdx)
057: throws DomainProtocolException {
058: StringTokenizer st;
059: int idx = startIdx;
060:
061: if (name != null) {
062: // initial check
063: if (buffer == null) {
064: // jndi.32=buffer is null
065: throw new NullPointerException(Messages
066: .getString("jndi.32")); //$NON-NLS-1$
067: }
068: if (startIdx > buffer.length || startIdx < 0) {
069: throw new ArrayIndexOutOfBoundsException();
070: }
071: // parsing the name
072: // if (CHECK_NAMES && !checkName(name)) {
073: // throw new DomainProtocolException(
074: // "The syntax of the domain name " +
075: // name + " does not conform to RFC 1035");
076: // }
077: st = new StringTokenizer(name, "."); //$NON-NLS-1$
078: while (st.hasMoreTokens()) {
079: String token = st.nextToken();
080: byte[] tokenBytes;
081: int tokenBytesLen;
082:
083: if (token == null || token.length() == 0) {
084: break;
085: }
086: tokenBytes = token.getBytes();
087: tokenBytesLen = tokenBytes.length;
088: if (tokenBytesLen > ProviderConstants.LABEL_MAX_CHARS) {
089: // jndi.64=The domain label is too long: {0}
090: throw new DomainProtocolException(Messages
091: .getString("jndi.64", token)); //$NON-NLS-1$
092: }
093: if (idx + tokenBytesLen + 1 > buffer.length) {
094: throw new ArrayIndexOutOfBoundsException();
095: }
096: buffer[idx++] = (byte) tokenBytesLen;
097: for (int i = 0; i < tokenBytesLen; i++) {
098: buffer[idx++] = tokenBytes[i];
099: }
100: if (idx - startIdx + 1 > ProviderConstants.NAME_MAX_CHARS) {
101: // jndi.5A=The domain name is more than {0} octets long: {1}
102: throw new DomainProtocolException(
103: Messages
104: .getString(
105: "jndi.5A", ProviderConstants.NAME_MAX_CHARS, name)); //$NON-NLS-1$
106: }
107: }
108: // every domain name should end with an zero octet
109: buffer[idx++] = (byte) 0;
110: }
111: return idx;
112: }
113:
114: /**
115: * Parses the domain name from the sequence of bytes.
116: *
117: * @param mesBytes
118: * byte representation of the message
119: * @param startIdx
120: * the position to start the parsing at
121: * @param result
122: * the string buffer to store parsed strings into
123: * @return updated index of <code>mesBytes</code> array
124: * @throws DomainProtocolException
125: * if something went wrong
126: */
127: public static int parseName(byte[] mesBytes, int startIdx,
128: StringBuffer result) throws DomainProtocolException {
129: int idx = startIdx;
130: boolean firstTime = true;
131:
132: if (mesBytes == null) {
133: // jndi.5B=Input byte array is null
134: throw new NullPointerException(Messages
135: .getString("jndi.5B")); //$NON-NLS-1$
136: }
137: if (result == null) {
138: // jndi.5C=The result string buffer is null
139: throw new NullPointerException(Messages
140: .getString("jndi.5C")); //$NON-NLS-1$
141: }
142: while (true) {
143: int n = parse8Int(mesBytes, idx++);
144:
145: if (n == 0) {
146: // end of the domain name reached
147: break;
148: }
149: if ((n & 0xc0) == 0xc0) {
150: // compressed label
151: int namePtr = parse16Int(mesBytes, --idx) & 0x3fff;
152:
153: idx += 2;
154: if (!firstTime) {
155: result.append('.');
156: }
157: parseName(mesBytes, namePtr, result);
158: break;
159: }
160: // plain label
161: if (n > ProviderConstants.LABEL_MAX_CHARS) {
162: // jndi.59=Domain label is too long.
163: throw new DomainProtocolException(Messages
164: .getString("jndi.59")); //$NON-NLS-1$
165: }
166: if (idx + n > mesBytes.length) {
167: // jndi.5D=Truncated data while parsing the domain name
168: throw new DomainProtocolException(Messages
169: .getString("jndi.5D")); //$NON-NLS-1$
170: }
171: // append parsed label
172: if (firstTime) {
173: firstTime = false;
174: } else {
175: result.append('.');
176: }
177: result.append(new String(mesBytes, idx, n));
178: idx += n;
179: }
180: return idx;
181: }
182:
183: /**
184: * Compares two labels and returns the matching count (number of the
185: * matching labels down from the root).
186: *
187: * @param name1
188: * first name
189: * @param name2
190: * second name
191: * @return number of equal labels from the root to leaves
192: */
193: public static int getMatchingCount(String name1, String name2) {
194: StringTokenizer st1, st2;
195: int k = 0;
196:
197: if (name1 == null || name2 == null) {
198: return 0;
199: }
200: st1 = new StringTokenizer(name1, "."); //$NON-NLS-1$
201: st2 = new StringTokenizer(name2, "."); //$NON-NLS-1$
202: while (st1.hasMoreTokens() && st2.hasMoreTokens()) {
203: if (st1.nextToken().equalsIgnoreCase(st2.nextToken())) {
204: k++;
205: } else {
206: break;
207: }
208: }
209: return k;
210: }
211:
212: /**
213: * Returns the name of parent DNS zone for given zone.
214: *
215: * @param name
216: * the current DNS zone name
217: * @return the name of the parent
218: */
219: public static String getParentName(String name) {
220: int n;
221:
222: if (name == null) {
223: return null;
224: }
225: if (name.trim().equals(".") || name.trim().length() == 0) { //$NON-NLS-1$
226: return "."; //$NON-NLS-1$
227: }
228: n = name.indexOf('.');
229: if (n != -1 && name.length() > n + 1) {
230: return name.substring(n + 1, name.length());
231: }
232: return "."; //$NON-NLS-1$
233: }
234:
235: /**
236: * Writes a 16-bit integer value into the buffer, high byte first
237: *
238: * @param value
239: * the value to write, first 16 bits will be taken
240: * @param buffer
241: * the buffer to write into
242: * @param startIdx
243: * a starting index
244: * @return updated index
245: */
246: public static int write16Int(int value, byte[] buffer, int startIdx) {
247: int idx = startIdx;
248:
249: buffer[idx++] = (byte) ((value & 0xff00) >> 8);
250: buffer[idx++] = (byte) (value & 0xff);
251: return idx;
252: }
253:
254: /**
255: * Writes a 32-bit integer value into the buffer, highest byte first
256: *
257: * @param value
258: * the value to write, first 32 bits will be taken
259: * @param buffer
260: * the buffer to write into
261: * @param startIdx
262: * a starting index
263: * @return updated index
264: */
265: public static int write32Int(long value, byte[] buffer, int startIdx) {
266: int idx = startIdx;
267:
268: buffer[idx++] = (byte) ((value & 0xff000000l) >> 24);
269: buffer[idx++] = (byte) ((value & 0xff0000) >> 16);
270: buffer[idx++] = (byte) ((value & 0xff00) >> 8);
271: buffer[idx++] = (byte) (value & 0xff);
272: return idx;
273: }
274:
275: /**
276: * Parses 8 bit integer. Buffer index should be updated manually after call
277: * to this method.
278: *
279: * @param buffer
280: * sequence of bytes
281: * @param startIdx
282: * the index to start at
283: * @return parsed integer value
284: */
285: public static int parse8Int(byte[] buffer, int idx) {
286: return (buffer[idx]) & 0xff;
287: }
288:
289: /**
290: * Parses 16 bit integer. Buffer index should be updated manually after call
291: * to this method.
292: *
293: * @param buffer
294: * sequence of bytes
295: * @param startIdx
296: * the index to start at
297: * @return parsed integer value
298: */
299: public static int parse16Int(byte[] buffer, int idx) {
300: int a = ((buffer[idx]) & 0xff) << 8;
301: int b = (buffer[idx + 1]) & 0xff;
302:
303: return (a | b);
304: }
305:
306: /**
307: * Parses 32 bit integer. Buffer index should be updated manually after call
308: * to this method.
309: *
310: * @param buffer
311: * sequence of bytes
312: * @param startIdx
313: * the index to start at
314: * @return parsed integer value
315: */
316: public static long parse32Int(byte[] buffer, int idx) {
317: long a = (((long) buffer[idx]) & 0xff) << 24;
318: long b = (((long) buffer[idx + 1]) & 0xff) << 16;
319: long c = (((long) buffer[idx + 2]) & 0xff) << 8;
320: long d = ((long) buffer[idx + 3]) & 0xff;
321:
322: return (a | b | c | d);
323: }
324:
325: /**
326: * Writes character string preceded with length octet.
327: *
328: * @param value
329: * string value to write
330: * @param buffer
331: * buffer to write to
332: * @param startIdx
333: * index in buffer to start from
334: * @return updated index
335: * @throws NullPointerException
336: * if some argument is null
337: * @throws DomainProtocolException
338: * if string is too long
339: */
340: public static int writeCharString(String value, byte[] buffer,
341: int startIdx) throws DomainProtocolException {
342: byte[] bytes;
343: int idx = startIdx;
344:
345: if (value == null || buffer == null) {
346: // jndi.5E=value or buffer is null
347: throw new NullPointerException(Messages
348: .getString("jndi.5E")); //$NON-NLS-1$
349: }
350: if (value.length() > 255) {
351: // jndi.5F=Character string is too long
352: throw new DomainProtocolException(Messages
353: .getString("jndi.5F")); //$NON-NLS-1$
354: }
355: bytes = value.getBytes();
356: buffer[idx++] = (byte) bytes.length;
357: for (byte element : bytes) {
358: buffer[idx++] = element;
359: }
360: return idx;
361: }
362:
363: /**
364: * Parses the string of characters preceded with length octet.
365: *
366: * @param mesBytes
367: * message bytes
368: * @param startIdx
369: * the index to start parsing from
370: * @param result
371: * string buffer to write the result too
372: * @return updated index
373: */
374: public static int parseCharString(byte[] mesBytes, int startIdx,
375: StringBuffer result) {
376: int len;
377:
378: if (mesBytes == null || result == null) {
379: // jndi.60=mesBytes or result is null
380: throw new NullPointerException(Messages
381: .getString("jndi.60")); //$NON-NLS-1$
382: }
383: len = mesBytes[startIdx];
384: result.append(new String(mesBytes, startIdx + 1, len));
385: return startIdx + 1 + len;
386: }
387:
388: /**
389: * Sets or drops specific bit(s) of the given number.
390: *
391: * @param value
392: * target integer value
393: * @param mask
394: * specifies bit(s) position(s)
395: * @param bit
396: * set if <code>true</code>, drop if <code>false</code>
397: * @return updated <code>value</code>
398: */
399: public static int setBit(int value, int mask, boolean bit) {
400: if (bit) {
401: value |= mask;
402: } else {
403: value &= ~mask;
404: }
405: return value;
406: }
407:
408: /**
409: * Checks if any of specified bits is set.
410: *
411: * @param value
412: * the number to look at
413: * @param mask
414: * a bit mask
415: * @return <code>true</code> of <code>false</code>
416: */
417: public static boolean checkBit(int value, int mask) {
418: if ((value & mask) == 0) {
419: return false;
420: }
421: return true;
422: }
423:
424: /**
425: * Compares two DNS names.
426: * <ol>
427: * <li>Case insensitive</li>
428: * <li>Appends "." to the end of the name if necessary before comparison</li>
429: * </ol>
430: *
431: * @param name1
432: * name1
433: * @param name2
434: * name2
435: * @return <code>true</code> if names are equal; <code>false</code>
436: * otherwise
437: */
438: public static boolean namesAreEqual(String name1, String name2) {
439: if (!name1.endsWith(".")) { //$NON-NLS-1$
440: name1 += "."; //$NON-NLS-1$
441: }
442: if (!name2.endsWith(".")) { //$NON-NLS-1$
443: name2 += "."; //$NON-NLS-1$
444: }
445: return name1.equalsIgnoreCase(name2);
446: }
447:
448: /**
449: * Removes _Service._Proto fields from SRV-style qName if any. Adds final
450: * dot to the end of name
451: *
452: * @param name
453: * name to process
454: * @return converted name
455: */
456: /*
457: * public static String removeSRVExtra(String name) { StringTokenizer st;
458: * int k = 0; StringBuffer res = new StringBuffer();
459: *
460: * if (name == null) { return name; } st = new StringTokenizer(name, ".",
461: * false); while (st.hasMoreTokens()) { String token = st.nextToken();
462: *
463: * if ((k != 0 && k != 1) || !token.startsWith("_")) { res.append(token); }
464: * k++; } return res.toString(); }
465: */
466:
467: /**
468: * Converts all letters to lower case and adds "." to the end of zone name
469: * if necessary.
470: *
471: * @param zone
472: * zone name
473: * @return expanded zone name
474: */
475: public static String normalizeName(String zone) {
476: if (zone == null) {
477: return zone;
478: }
479: return zone.endsWith(".") ? zone.toLowerCase() : //$NON-NLS-1$
480: zone.toLowerCase() + "."; //$NON-NLS-1$
481: }
482:
483: /**
484: * Creates the text representation of IPv4 address.
485: *
486: * @param ip
487: * the first four bytes should contain an IPv4 address
488: * @return string in <code>n.n.n.n</code> format
489: * @throws java.lang.IllegalArgumentException
490: * if given array has the length less than four
491: */
492: public static String getIpStr(byte[] ip) {
493: StringBuffer sb = new StringBuffer();
494:
495: if (ip == null || ip.length < 4) {
496: // jndi.61=Given array is null or has the length less than four
497: throw new IllegalArgumentException(Messages
498: .getString("jndi.61")); //$NON-NLS-1$
499: }
500: for (int i = 0; i < 4; i++) {
501: if (i > 0) {
502: sb.append("."); //$NON-NLS-1$
503: }
504: sb.append("" + ((ip[i]) & 0xff)); //$NON-NLS-1$
505: }
506: return sb.toString();
507: }
508:
509: /**
510: * Parses the text representation of IPv4 address.
511: *
512: * @param ipStr
513: * string in <code>n.n.n.n</code> format.
514: * @return four bytes with parsed IP address
515: * @throws java.lang.NullPointerException
516: * if <code>ipStr</code> is null
517: * @throws java.lang.IllegalArgumentException
518: * if given string is not in appropriate format
519: */
520: public static byte[] parseIpStr(String ipStr) {
521: StringTokenizer st;
522: byte[] b = new byte[4];
523: // jndi.62=Given string is not in appropriate format
524: final String errMsg1 = Messages.getString("jndi.62"); //$NON-NLS-1$
525:
526: if (ipStr != null) {
527: int k = 0;
528:
529: st = new StringTokenizer(ipStr, "."); //$NON-NLS-1$
530: while (st.hasMoreTokens()) {
531: String token = st.nextToken();
532: int n;
533:
534: try {
535: n = Integer.parseInt(token);
536: } catch (NumberFormatException e) {
537: throw new IllegalArgumentException(errMsg1);
538: }
539: b[k++] = (byte) n;
540: }
541: if (k != 4) {
542: throw new IllegalArgumentException(errMsg1);
543: }
544: } else {
545: // jndi.63=Given string representation is null
546: throw new NullPointerException(Messages
547: .getString("jndi.63")); //$NON-NLS-1$
548: }
549: return b;
550: }
551:
552: /**
553: * @param str
554: * string name of the DNS record class
555: * @return integer number for the class; <code>-1</code> if not found
556: */
557: public static int getRecordClassNumber(String str) {
558: for (int i = 0; i < ProviderConstants.rrClassNames.length; i++) {
559: if (ProviderConstants.rrClassNames[i] != null) {
560: if (str
561: .equalsIgnoreCase(ProviderConstants.rrClassNames[i])) {
562: return i;
563: }
564: }
565: }
566: return -1;
567: }
568:
569: /**
570: * @param str
571: * string name of the DNS record type
572: * @return integer number for the type; <code>-1</code> if not found
573: */
574: public static int getRecordTypeNumber(String str) {
575: for (int i = 0; i < ProviderConstants.rrTypeNames.length; i++) {
576: if (ProviderConstants.rrTypeNames[i] != null) {
577: if (str
578: .equalsIgnoreCase(ProviderConstants.rrTypeNames[i])) {
579: return i;
580: }
581: }
582: }
583: return -1;
584: }
585:
586: }
|