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: * Represents domain protocol Resource Record
030: *
031: * @see RFC 1035
032: */
033: public class ResourceRecord {
034:
035: /** a domain name */
036: private String name;
037:
038: /** resource record type */
039: private int rrType;
040:
041: /** resource record class */
042: private int rrClass;
043:
044: /** time to live */
045: private long ttl;
046:
047: /** resource data length */
048: // private int rdLength;
049: /** resource data itself */
050: private Object rData;
051:
052: /** empty constructor */
053: public ResourceRecord() {
054: }
055:
056: /**
057: * Constructs new ResourceRecord object from given values.
058: *
059: * @param name
060: * a domain name
061: * @param rrType
062: * resource record type
063: * @param rrClass
064: * resource record class
065: * @param ttl
066: * time to live
067: * @param rdLength
068: * resource data length
069: * @param rData
070: * resource data itself
071: */
072: public ResourceRecord(String name, int rrType, int rrClass,
073: long ttl,
074: /* int rdLength, */
075: Object rData) {
076: this .name = name;
077: this .rrType = rrType;
078: this .rrClass = rrClass;
079: this .ttl = ttl;
080: // this.rdLength = rdLength;
081: this .rData = rData;
082: }
083:
084: /**
085: * Creates the sequence of bytes that represents the current resource
086: * record.
087: *
088: * @param buffer
089: * the buffer to write the bytes into
090: * @param startIdx
091: * starting index
092: * @return updated index
093: * @throws DomainProtocolException
094: * if something went wrong
095: * @throws ArrayIndexOutOfBoundsException
096: * if the buffer border unpredictably encountered
097: */
098: public int writeBytes(byte[] buffer, int startIdx)
099: throws DomainProtocolException {
100: int idx = startIdx;
101:
102: // basic checking
103: if (buffer == null) {
104: // jndi.32=buffer is null
105: throw new DomainProtocolException(Messages
106: .getString("jndi.32")); //$NON-NLS-1$
107: }
108: // NAME
109: idx = ProviderMgr.writeName(name, buffer, idx);
110: // TYPE
111: idx = ProviderMgr.write16Int(rrType, buffer, idx);
112: // CLASS
113: idx = ProviderMgr.write16Int(rrClass, buffer, idx);
114: // TTL
115: idx = ProviderMgr.write32Int(ttl, buffer, idx);
116: // RDLENGTH & RDATA
117: if (rrType == ProviderConstants.NS_TYPE
118: || rrType == ProviderConstants.CNAME_TYPE
119: || rrType == ProviderConstants.PTR_TYPE) {
120: int idx0 = idx;
121:
122: idx += 2;
123: // RDATA
124: idx = ProviderMgr.writeName((String) rData, buffer, idx);
125: // RDLENGTH
126: ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0);
127: } else if (rrType == ProviderConstants.A_TYPE) {
128: byte[] ipBytes = ProviderMgr.parseIpStr((String) rData);
129:
130: // RDLENGTH
131: idx = ProviderMgr.write16Int(ipBytes.length, buffer, idx);
132: for (byte element : ipBytes) {
133: buffer[idx++] = element;
134: }
135: } else if (rrType == ProviderConstants.SOA_TYPE) {
136: StringTokenizer st = new StringTokenizer((String) rData,
137: " "); //$NON-NLS-1$
138: String token;
139: int idx0 = idx; // saving RDLENGTH position
140:
141: if (st.countTokens() != 7) {
142: // jndi.35=Invalid number of fields while parsing SOA record
143: throw new DomainProtocolException(Messages
144: .getString("jndi.35")); //$NON-NLS-1$
145: }
146: idx += 2; // skip RDLENGTH for now
147: // RDATA
148: // MNAME
149: token = st.nextToken();
150: idx = ProviderMgr.writeName(token, buffer, idx);
151: // RNAME
152: token = st.nextToken();
153: idx = ProviderMgr.writeName(token, buffer, idx);
154: // SERIAL
155: // REFRESH
156: // RETRY
157: // EXPIRE
158: // MINIMUM
159: try {
160: for (int i = 0; i < 5; i++) {
161: token = st.nextToken();
162: idx = ProviderMgr.write32Int(Long.parseLong(token),
163: buffer, idx);
164: }
165: } catch (NumberFormatException e) {
166: // jndi.36=Error while parsing SOA record
167: throw new DomainProtocolException(Messages
168: .getString("jndi.36"), e); //$NON-NLS-1$
169: }
170: // RDLENGTH
171: ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0);
172: } else if (rrType == ProviderConstants.MX_TYPE) {
173: StringTokenizer st = new StringTokenizer((String) rData,
174: " "); //$NON-NLS-1$
175: String token;
176: int idx0 = idx; // saving RDLENGTH position
177:
178: if (st.countTokens() != 2) {
179: // jndi.37=Invalid number of fields while parsing MX record
180: throw new DomainProtocolException(Messages
181: .getString("jndi.37")); //$NON-NLS-1$
182: }
183: idx += 2; // skip RDLENGTH for now
184: // PREFERENCE
185: token = st.nextToken();
186: try {
187: ProviderMgr.write16Int(Integer.parseInt(token), buffer,
188: idx);
189: } catch (NumberFormatException e) {
190: // jndi.38=Error while parsing MX record
191: throw new DomainProtocolException(Messages
192: .getString("jndi.38"), e); //$NON-NLS-1$
193: }
194: // EXCHANGE
195: token = st.nextToken();
196: idx = ProviderMgr.writeName(token, buffer, idx);
197: // RDLENGTH
198: ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0);
199: } else if (rrType == ProviderConstants.HINFO_TYPE) {
200: StringTokenizer st = new StringTokenizer((String) rData,
201: " "); //$NON-NLS-1$
202: String token;
203: int idx0 = idx; // saving RDLENGTH position
204:
205: if (st.countTokens() != 2) {
206: // jndi.39=Invalid number of fields while parsing HINFO record
207: throw new DomainProtocolException(Messages
208: .getString("jndi.39")); //$NON-NLS-1$
209: }
210: idx += 2; // skip RDLENGTH for now
211: // CPU
212: // OS
213: for (int i = 0; i < 2; i++) {
214: token = st.nextToken();
215: idx = ProviderMgr.writeCharString(token, buffer, idx);
216: }
217: // RDLENGTH
218: ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0);
219: } else if (rrType == ProviderConstants.TXT_TYPE) {
220: // character string with preceding length octet
221: int idx0 = idx;
222: StringTokenizer st = new StringTokenizer((String) rData,
223: " "); //$NON-NLS-1$
224:
225: idx += 2;
226: // RDATA
227: while (st.hasMoreTokens()) {
228: String token = st.nextToken();
229:
230: if (token.getBytes().length > 255) {
231: // jndi.3A=The length of character string exceed 255 octets
232: throw new DomainProtocolException(Messages
233: .getString("jndi.3A")); //$NON-NLS-1$
234: }
235: idx = ProviderMgr.writeCharString(token, buffer, idx);
236: }
237: if (idx - 2 - idx0 > 65535) {
238: // jndi.3B=Length of TXT field exceed 65535
239: throw new DomainProtocolException(Messages
240: .getString("jndi.3B")); //$NON-NLS-1$
241: }
242: // RDLENGTH
243: ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0);
244: } else if (rrType == ProviderConstants.SRV_TYPE) {
245: StringTokenizer st = new StringTokenizer((String) rData,
246: " "); //$NON-NLS-1$
247: String token;
248: int idx0 = idx; // saving RDLENGTH position
249:
250: idx += 2;
251: if (st.countTokens() != 4) {
252: // jndi.3C=Invalid number of fields while parsing SRV record
253: throw new DomainProtocolException(Messages
254: .getString("jndi.3C")); //$NON-NLS-1$
255: }
256: // RDATA
257:
258: // PRIORITY
259: // WEIGHT
260: // PORT
261: try {
262: for (int i = 0; i < 3; i++) {
263: token = st.nextToken();
264: idx = ProviderMgr.write16Int(Integer
265: .parseInt(token), buffer, idx);
266: }
267: } catch (NumberFormatException e) {
268: // jndi.3D=Error while parsing SRV record
269: throw new DomainProtocolException(Messages
270: .getString("jndi.3D"), e); //$NON-NLS-1$
271: }
272: // TARGET
273: token = st.nextToken();
274: idx = ProviderMgr.writeName(token, buffer, idx);
275: // RDLENGTH
276: ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0);
277: }
278: // TODO add more Resource Record types here
279: else {
280: byte[] bytes;
281:
282: if (!(rData instanceof byte[])) {
283: // jndi.3E=RDATA for unknown record type {0} should have value
284: // of byte[] type
285: throw new DomainProtocolException(Messages.getString(
286: "jndi.3E", rrType)); //$NON-NLS-1$
287: }
288: bytes = (byte[]) rData;
289: // RDLENGTH
290: idx = ProviderMgr.write16Int(bytes.length, buffer, idx);
291: for (byte element : bytes) {
292: buffer[idx++] = element;
293: }
294: }
295: return idx;
296: }
297:
298: /**
299: * Parses given sequence of bytes and constructs a resource record from it.
300: *
301: * @param mesBytes
302: * the byte array that should be parsed
303: * @param startIdx
304: * an index of <code>mesBytes</code> array to start the parsing
305: * at
306: * @param resultRR
307: * an object the result of the operation will be stored into
308: * @return updated index of <code>mesBytes</code> array
309: * @throws DomainProtocolException
310: * if something went wrong
311: * @throws ArrayIndexOutOfBoundsException
312: * if the array border unpredictably encountered
313: */
314: public static int parseRecord(byte[] mesBytes, int startIdx,
315: ResourceRecord resultRR) throws DomainProtocolException {
316: int idx = startIdx;
317: StringBuffer nameSB = new StringBuffer();
318: int rrType;
319: int rdLen;
320: Object rDat = null;
321:
322: if (resultRR == null) {
323: // jndi.3F=Given resultRR is null
324: throw new NullPointerException(Messages
325: .getString("jndi.3F")); //$NON-NLS-1$
326: }
327: // NAME
328: idx = ProviderMgr.parseName(mesBytes, idx, nameSB);
329: resultRR.setName(ProviderMgr.normalizeName(nameSB.toString()));
330: // TYPE
331: rrType = ProviderMgr.parse16Int(mesBytes, idx);
332: resultRR.setRRType(rrType);
333: idx += 2;
334: // CLASS
335: resultRR.setRRClass(ProviderMgr.parse16Int(mesBytes, idx));
336: idx += 2;
337: // TTL
338: resultRR.setTtl(ProviderMgr.parse32Int(mesBytes, idx));
339: idx += 4;
340: // RDLENGTH
341: rdLen = ProviderMgr.parse16Int(mesBytes, idx);
342: idx += 2;
343: // RDATA
344: if (rrType == ProviderConstants.NS_TYPE
345: || rrType == ProviderConstants.CNAME_TYPE
346: || rrType == ProviderConstants.PTR_TYPE) {
347: // let's parse the domain name
348: StringBuffer name = new StringBuffer();
349:
350: idx = ProviderMgr.parseName(mesBytes, idx, name);
351: rDat = ProviderMgr.normalizeName(name.toString());
352: } else if (rrType == ProviderConstants.A_TYPE) {
353: // let's parse the 32 bit Internet address
354: byte tmpArr[] = new byte[4];
355:
356: for (int i = 0; i < 4; i++) {
357: tmpArr[i] = mesBytes[idx + i];
358: }
359: rDat = ProviderMgr.getIpStr(tmpArr);
360: idx += 4;
361: } else if (rrType == ProviderConstants.MX_TYPE) {
362: // 16 bit integer (preference) followed by domain name
363: int preference;
364: StringBuffer name = new StringBuffer();
365:
366: preference = ProviderMgr.parse16Int(mesBytes, idx);
367: idx += 2;
368: idx = ProviderMgr.parseName(mesBytes, idx, name);
369: rDat = "" + preference + " " + //$NON-NLS-1$ //$NON-NLS-2$
370: ProviderMgr.normalizeName(name.toString());
371: } else if (rrType == ProviderConstants.SOA_TYPE) {
372: StringBuffer mName = new StringBuffer();
373: StringBuffer rName = new StringBuffer();
374: long serial;
375: long refresh;
376: long retry;
377: long expire;
378: long minimum;
379:
380: idx = ProviderMgr.parseName(mesBytes, idx, mName);
381: idx = ProviderMgr.parseName(mesBytes, idx, rName);
382: serial = ProviderMgr.parse32Int(mesBytes, idx);
383: idx += 4;
384: refresh = ProviderMgr.parse32Int(mesBytes, idx);
385: idx += 4;
386: retry = ProviderMgr.parse32Int(mesBytes, idx);
387: idx += 4;
388: expire = ProviderMgr.parse32Int(mesBytes, idx);
389: idx += 4;
390: minimum = ProviderMgr.parse32Int(mesBytes, idx);
391: idx += 4;
392: rDat = ProviderMgr.normalizeName(mName.toString())
393: + " " + //$NON-NLS-1$
394: ProviderMgr.normalizeName(rName.toString())
395: + " " + //$NON-NLS-1$
396: serial
397: + " " + refresh + " " + retry + " " + expire + " " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
398: minimum;
399: } else if (rrType == ProviderConstants.TXT_TYPE) {
400: StringBuffer sbuf = new StringBuffer();
401: int idx0 = idx;
402:
403: while (true) {
404: int len11 = ProviderMgr.parse8Int(mesBytes, idx++);
405:
406: if (idx - idx0 + len11 > rdLen) {
407: idx--;
408: break;
409: }
410: if (sbuf.length() > 0) {
411: sbuf.append(' ');
412: }
413: sbuf.append(new String(mesBytes, idx, len11));
414: idx += len11;
415: }
416: rDat = sbuf.toString();
417: } else if (rrType == ProviderConstants.HINFO_TYPE) {
418: // two character strings with preceding length octets
419: StringBuffer res = new StringBuffer();
420:
421: idx = ProviderMgr.parseCharString(mesBytes, idx, res);
422: res.append(" "); //$NON-NLS-1$
423: idx = ProviderMgr.parseCharString(mesBytes, idx, res);
424: rDat = res.toString();
425: } else if (rrType == ProviderConstants.SRV_TYPE) {
426: int priority;
427: int weight;
428: int port;
429: StringBuffer name = new StringBuffer();
430:
431: priority = ProviderMgr.parse16Int(mesBytes, idx);
432: idx += 2;
433: weight = ProviderMgr.parse16Int(mesBytes, idx);
434: idx += 2;
435: port = ProviderMgr.parse16Int(mesBytes, idx);
436: idx += 2;
437: idx = ProviderMgr.parseName(mesBytes, idx, name);
438: rDat = "" + priority + " " + weight + " " + port + " " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
439: ProviderMgr.normalizeName(name.toString());
440: }
441: // TODO add more Resource Record types here
442: else {
443: // copy bytes since the retrieved bytes
444: // could contain unknown binary data
445: rDat = new byte[rdLen];
446: for (int i = 0; i < rdLen; i++) {
447: ((byte[]) rDat)[i] = mesBytes[idx++];
448: }
449: }
450: resultRR.setRData(rDat);
451: return idx;
452: }
453:
454: @Override
455: public String toString() {
456: StringBuffer sb = new StringBuffer();
457: sb.append(name);
458: sb.append(" "); //$NON-NLS-1$
459: sb.append(ProviderConstants.rrTypeNames[rrType]);
460: sb.append(" "); //$NON-NLS-1$
461: sb.append(rrClass);
462: sb.append(" "); //$NON-NLS-1$
463: sb.append("TTL=" + ttl); //$NON-NLS-1$
464: sb.append(" "); //$NON-NLS-1$
465: sb.append(rData.toString());
466: return sb.toString();
467: }
468:
469: // getters and setters
470:
471: /**
472: * @return Returns the name.
473: */
474: public String getName() {
475: return name;
476: }
477:
478: /**
479: * @param name
480: * The name to set.
481: */
482: public void setName(String name) {
483: this .name = name;
484: }
485:
486: /**
487: * @return Returns the rData.
488: */
489: public Object getRData() {
490: return rData;
491: }
492:
493: /**
494: * @param data
495: * The rData to set.
496: */
497: public void setRData(Object data) {
498: rData = data;
499: }
500:
501: /**
502: * @return Returns the rdLength.
503: */
504: // public int getRDLength() {
505: // return rdLength;
506: // }
507: /**
508: * @param rdLength
509: * The rdLength to set.
510: */
511: // public void setRDLength(int rdLength) {
512: // this.rdLength = rdLength;
513: // }
514: /**
515: * @return Returns the rrClass.
516: */
517: public int getRRClass() {
518: return rrClass;
519: }
520:
521: /**
522: * @param rrClass
523: * The rrClass to set.
524: */
525: public void setRRClass(int rrClass) {
526: this .rrClass = rrClass;
527: }
528:
529: /**
530: * @return Returns the rrType.
531: */
532: public int getRRType() {
533: return rrType;
534: }
535:
536: /**
537: * @param rrType
538: * The rrType to set.
539: */
540: public void setRRType(int rrType) {
541: this .rrType = rrType;
542: }
543:
544: /**
545: * @return Returns the TTL.
546: */
547: public long getTtl() {
548: return ttl;
549: }
550:
551: /**
552: * @param ttl
553: * The TTL to set.
554: */
555: public void setTtl(long ttl) {
556: this.ttl = ttl;
557: }
558: }
|