/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexei Y. Zakharov */ package org.apache.harmony.jndi.provider.dns; import java.util.StringTokenizer; import org.apache.harmony.jndi.internal.nls.Messages; /** * Represents domain protocol Resource Record * * @see RFC 1035 */ public class ResourceRecord { /** a domain name */ private String name; /** resource record type */ private int rrType; /** resource record class */ private int rrClass; /** time to live */ private long ttl; /** resource data length */ // private int rdLength; /** resource data itself */ private Object rData; /** empty constructor */ public ResourceRecord() { } /** * Constructs new ResourceRecord object from given values. * * @param name * a domain name * @param rrType * resource record type * @param rrClass * resource record class * @param ttl * time to live * @param rdLength * resource data length * @param rData * resource data itself */ public ResourceRecord(String name, int rrType, int rrClass, long ttl, /* int rdLength, */ Object rData) { this.name = name; this.rrType = rrType; this.rrClass = rrClass; this.ttl = ttl; // this.rdLength = rdLength; this.rData = rData; } /** * Creates the sequence of bytes that represents the current resource * record. * * @param buffer * the buffer to write the bytes into * @param startIdx * starting index * @return updated index * @throws DomainProtocolException * if something went wrong * @throws ArrayIndexOutOfBoundsException * if the buffer border unpredictably encountered */ public int writeBytes(byte[] buffer, int startIdx) throws DomainProtocolException { int idx = startIdx; // basic checking if (buffer == null) { // jndi.32=buffer is null throw new DomainProtocolException(Messages.getString("jndi.32")); //$NON-NLS-1$ } // NAME idx = ProviderMgr.writeName(name, buffer, idx); // TYPE idx = ProviderMgr.write16Int(rrType, buffer, idx); // CLASS idx = ProviderMgr.write16Int(rrClass, buffer, idx); // TTL idx = ProviderMgr.write32Int(ttl, buffer, idx); // RDLENGTH & RDATA if (rrType == ProviderConstants.NS_TYPE || rrType == ProviderConstants.CNAME_TYPE || rrType == ProviderConstants.PTR_TYPE) { int idx0 = idx; idx += 2; // RDATA idx = ProviderMgr.writeName((String) rData, buffer, idx); // RDLENGTH ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0); } else if (rrType == ProviderConstants.A_TYPE) { byte[] ipBytes = ProviderMgr.parseIpStr((String) rData); // RDLENGTH idx = ProviderMgr.write16Int(ipBytes.length, buffer, idx); for (byte element : ipBytes) { buffer[idx++] = element; } } else if (rrType == ProviderConstants.SOA_TYPE) { StringTokenizer st = new StringTokenizer((String) rData, " "); //$NON-NLS-1$ String token; int idx0 = idx; // saving RDLENGTH position if (st.countTokens() != 7) { // jndi.35=Invalid number of fields while parsing SOA record throw new DomainProtocolException(Messages.getString("jndi.35")); //$NON-NLS-1$ } idx += 2; // skip RDLENGTH for now // RDATA // MNAME token = st.nextToken(); idx = ProviderMgr.writeName(token, buffer, idx); // RNAME token = st.nextToken(); idx = ProviderMgr.writeName(token, buffer, idx); // SERIAL // REFRESH // RETRY // EXPIRE // MINIMUM try { for (int i = 0; i < 5; i++) { token = st.nextToken(); idx = ProviderMgr.write32Int(Long.parseLong(token), buffer, idx); } } catch (NumberFormatException e) { // jndi.36=Error while parsing SOA record throw new DomainProtocolException( Messages.getString("jndi.36"), e); //$NON-NLS-1$ } // RDLENGTH ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0); } else if (rrType == ProviderConstants.MX_TYPE) { StringTokenizer st = new StringTokenizer((String) rData, " "); //$NON-NLS-1$ String token; int idx0 = idx; // saving RDLENGTH position if (st.countTokens() != 2) { // jndi.37=Invalid number of fields while parsing MX record throw new DomainProtocolException(Messages.getString("jndi.37")); //$NON-NLS-1$ } idx += 2; // skip RDLENGTH for now // PREFERENCE token = st.nextToken(); try { ProviderMgr.write16Int(Integer.parseInt(token), buffer, idx); } catch (NumberFormatException e) { // jndi.38=Error while parsing MX record throw new DomainProtocolException( Messages.getString("jndi.38"), e); //$NON-NLS-1$ } // EXCHANGE token = st.nextToken(); idx = ProviderMgr.writeName(token, buffer, idx); // RDLENGTH ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0); } else if (rrType == ProviderConstants.HINFO_TYPE) { StringTokenizer st = new StringTokenizer((String) rData, " "); //$NON-NLS-1$ String token; int idx0 = idx; // saving RDLENGTH position if (st.countTokens() != 2) { // jndi.39=Invalid number of fields while parsing HINFO record throw new DomainProtocolException(Messages.getString("jndi.39")); //$NON-NLS-1$ } idx += 2; // skip RDLENGTH for now // CPU // OS for (int i = 0; i < 2; i++) { token = st.nextToken(); idx = ProviderMgr.writeCharString(token, buffer, idx); } // RDLENGTH ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0); } else if (rrType == ProviderConstants.TXT_TYPE) { // character string with preceding length octet int idx0 = idx; StringTokenizer st = new StringTokenizer((String) rData, " "); //$NON-NLS-1$ idx += 2; // RDATA while (st.hasMoreTokens()) { String token = st.nextToken(); if (token.getBytes().length > 255) { // jndi.3A=The length of character string exceed 255 octets throw new DomainProtocolException(Messages .getString("jndi.3A")); //$NON-NLS-1$ } idx = ProviderMgr.writeCharString(token, buffer, idx); } if (idx - 2 - idx0 > 65535) { // jndi.3B=Length of TXT field exceed 65535 throw new DomainProtocolException(Messages.getString("jndi.3B")); //$NON-NLS-1$ } // RDLENGTH ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0); } else if (rrType == ProviderConstants.SRV_TYPE) { StringTokenizer st = new StringTokenizer((String) rData, " "); //$NON-NLS-1$ String token; int idx0 = idx; // saving RDLENGTH position idx += 2; if (st.countTokens() != 4) { // jndi.3C=Invalid number of fields while parsing SRV record throw new DomainProtocolException(Messages.getString("jndi.3C")); //$NON-NLS-1$ } // RDATA // PRIORITY // WEIGHT // PORT try { for (int i = 0; i < 3; i++) { token = st.nextToken(); idx = ProviderMgr.write16Int(Integer.parseInt(token), buffer, idx); } } catch (NumberFormatException e) { // jndi.3D=Error while parsing SRV record throw new DomainProtocolException( Messages.getString("jndi.3D"), e); //$NON-NLS-1$ } // TARGET token = st.nextToken(); idx = ProviderMgr.writeName(token, buffer, idx); // RDLENGTH ProviderMgr.write16Int(idx - 2 - idx0, buffer, idx0); } // TODO add more Resource Record types here else { byte[] bytes; if (!(rData instanceof byte[])) { // jndi.3E=RDATA for unknown record type {0} should have value // of byte[] type throw new DomainProtocolException(Messages.getString( "jndi.3E", rrType)); //$NON-NLS-1$ } bytes = (byte[]) rData; // RDLENGTH idx = ProviderMgr.write16Int(bytes.length, buffer, idx); for (byte element : bytes) { buffer[idx++] = element; } } return idx; } /** * Parses given sequence of bytes and constructs a resource record from it. * * @param mesBytes * the byte array that should be parsed * @param startIdx * an index of <code>mesBytes</code> array to start the parsing * at * @param resultRR * an object the result of the operation will be stored into * @return updated index of <code>mesBytes</code> array * @throws DomainProtocolException * if something went wrong * @throws ArrayIndexOutOfBoundsException * if the array border unpredictably encountered */ public static int parseRecord(byte[] mesBytes, int startIdx, ResourceRecord resultRR) throws DomainProtocolException { int idx = startIdx; StringBuffer nameSB = new StringBuffer(); int rrType; int rdLen; Object rDat = null; if (resultRR == null) { // jndi.3F=Given resultRR is null throw new NullPointerException(Messages.getString("jndi.3F")); //$NON-NLS-1$ } // NAME idx = ProviderMgr.parseName(mesBytes, idx, nameSB); resultRR.setName(ProviderMgr.normalizeName(nameSB.toString())); // TYPE rrType = ProviderMgr.parse16Int(mesBytes, idx); resultRR.setRRType(rrType); idx += 2; // CLASS resultRR.setRRClass(ProviderMgr.parse16Int(mesBytes, idx)); idx += 2; // TTL resultRR.setTtl(ProviderMgr.parse32Int(mesBytes, idx)); idx += 4; // RDLENGTH rdLen = ProviderMgr.parse16Int(mesBytes, idx); idx += 2; // RDATA if (rrType == ProviderConstants.NS_TYPE || rrType == ProviderConstants.CNAME_TYPE || rrType == ProviderConstants.PTR_TYPE) { // let's parse the domain name StringBuffer name = new StringBuffer(); idx = ProviderMgr.parseName(mesBytes, idx, name); rDat = ProviderMgr.normalizeName(name.toString()); } else if (rrType == ProviderConstants.A_TYPE) { // let's parse the 32 bit Internet address byte tmpArr[] = new byte[4]; for (int i = 0; i < 4; i++) { tmpArr[i] = mesBytes[idx + i]; } rDat = ProviderMgr.getIpStr(tmpArr); idx += 4; } else if (rrType == ProviderConstants.MX_TYPE) { // 16 bit integer (preference) followed by domain name int preference; StringBuffer name = new StringBuffer(); preference = ProviderMgr.parse16Int(mesBytes, idx); idx += 2; idx = ProviderMgr.parseName(mesBytes, idx, name); rDat = "" + preference + " " + //$NON-NLS-1$ //$NON-NLS-2$ ProviderMgr.normalizeName(name.toString()); } else if (rrType == ProviderConstants.SOA_TYPE) { StringBuffer mName = new StringBuffer(); StringBuffer rName = new StringBuffer(); long serial; long refresh; long retry; long expire; long minimum; idx = ProviderMgr.parseName(mesBytes, idx, mName); idx = ProviderMgr.parseName(mesBytes, idx, rName); serial = ProviderMgr.parse32Int(mesBytes, idx); idx += 4; refresh = ProviderMgr.parse32Int(mesBytes, idx); idx += 4; retry = ProviderMgr.parse32Int(mesBytes, idx); idx += 4; expire = ProviderMgr.parse32Int(mesBytes, idx); idx += 4; minimum = ProviderMgr.parse32Int(mesBytes, idx); idx += 4; rDat = ProviderMgr.normalizeName(mName.toString()) + " " + //$NON-NLS-1$ ProviderMgr.normalizeName(rName.toString()) + " " + //$NON-NLS-1$ serial + " " + refresh + " " + retry + " " + expire + " " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ minimum; } else if (rrType == ProviderConstants.TXT_TYPE) { StringBuilder sbuf = new StringBuilder(); int idx0 = idx; while (true) { int len11 = ProviderMgr.parse8Int(mesBytes, idx++); if (idx - idx0 + len11 > rdLen) { idx--; break; } if (sbuf.length() > 0) { sbuf.append(' '); } sbuf.append(new String(mesBytes, idx, len11)); idx += len11; } rDat = sbuf.toString(); } else if (rrType == ProviderConstants.HINFO_TYPE) { // two character strings with preceding length octets StringBuffer res = new StringBuffer(); idx = ProviderMgr.parseCharString(mesBytes, idx, res); res.append(" "); //$NON-NLS-1$ idx = ProviderMgr.parseCharString(mesBytes, idx, res); rDat = res.toString(); } else if (rrType == ProviderConstants.SRV_TYPE) { int priority; int weight; int port; StringBuffer name = new StringBuffer(); priority = ProviderMgr.parse16Int(mesBytes, idx); idx += 2; weight = ProviderMgr.parse16Int(mesBytes, idx); idx += 2; port = ProviderMgr.parse16Int(mesBytes, idx); idx += 2; idx = ProviderMgr.parseName(mesBytes, idx, name); rDat = "" + priority + " " + weight + " " + port + " " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ ProviderMgr.normalizeName(name.toString()); } // TODO add more Resource Record types here else { // copy bytes since the retrieved bytes // could contain unknown binary data rDat = new byte[rdLen]; for (int i = 0; i < rdLen; i++) { ((byte[]) rDat)[i] = mesBytes[idx++]; } } resultRR.setRData(rDat); return idx; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(name); sb.append(" "); //$NON-NLS-1$ sb.append(ProviderConstants.rrTypeNames[rrType]); sb.append(" "); //$NON-NLS-1$ sb.append(rrClass); sb.append(" "); //$NON-NLS-1$ sb.append("TTL=" + ttl); //$NON-NLS-1$ sb.append(" "); //$NON-NLS-1$ sb.append(rData.toString()); return sb.toString(); } // getters and setters /** * @return Returns the name. */ public String getName() { return name; } /** * @param name * The name to set. */ public void setName(String name) { this.name = name; } /** * @return Returns the rData. */ public Object getRData() { return rData; } /** * @param data * The rData to set. */ public void setRData(Object data) { rData = data; } /** * @return Returns the rdLength. */ // public int getRDLength() { // return rdLength; // } /** * @param rdLength * The rdLength to set. */ // public void setRDLength(int rdLength) { // this.rdLength = rdLength; // } /** * @return Returns the rrClass. */ public int getRRClass() { return rrClass; } /** * @param rrClass * The rrClass to set. */ public void setRRClass(int rrClass) { this.rrClass = rrClass; } /** * @return Returns the rrType. */ public int getRRType() { return rrType; } /** * @param rrType * The rrType to set. */ public void setRRType(int rrType) { this.rrType = rrType; } /** * @return Returns the TTL. */ public long getTtl() { return ttl; } /** * @param ttl * The TTL to set. */ public void setTtl(long ttl) { this.ttl = ttl; } }