]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSShared/Java/TXTRecord.java
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / mDNSShared / Java / TXTRecord.java
1 /*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: TXTRecord.java,v $
26 Revision 1.5 2004/08/25 21:54:36 rpantos
27 <rdar://problem/3773973> Fix getValue() for values containing '='.
28
29 Revision 1.4 2004/08/04 01:04:50 rpantos
30 <rdar://problems/3731579&3731582> Fix set(); add remove() & toString().
31
32 Revision 1.3 2004/07/13 21:24:25 rpantos
33 Fix for <rdar://problem/3701120>.
34
35 Revision 1.2 2004/04/30 21:48:27 rpantos
36 Change line endings for CVS.
37
38 Revision 1.1 2004/04/30 16:29:35 rpantos
39 First checked in.
40
41 To do:
42 - implement remove()
43 - fix set() to replace existing values
44 */
45
46
47 package com.apple.dnssd;
48
49
50 /**
51 Object used to construct and parse DNS-SD format TXT records.
52 For more info see <a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS-Based Service Discovery</a>, section 6.
53 */
54
55 public class TXTRecord
56 {
57 /*
58 DNS-SD specifies that a TXT record corresponding to an SRV record consist of
59 a packed array of bytes, each preceded by a length byte. Each string
60 is an attribute-value pair.
61
62 The TXTRecord object stores the entire TXT data as a single byte array, traversing it
63 as need be to implement its various methods.
64 */
65
66 static final protected byte kAttrSep = '=';
67
68 protected byte[] fBytes;
69
70 /** Constructs a new, empty TXT record. */
71 public TXTRecord()
72 { fBytes = new byte[0]; }
73
74 /** Constructs a new TXT record from a byte array in the standard format. */
75 public TXTRecord( byte[] initBytes)
76 { fBytes = (byte[]) initBytes.clone(); }
77
78 /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
79 @param key
80 The key name. Must be ASCII, with no '=' characters.
81 <P>
82 @param value
83 Value to be encoded into bytes using the default platform character set.
84 */
85 public void set( String key, String value)
86 {
87 byte[] valBytes = (value != null) ? value.getBytes() : null;
88 this.set( key, valBytes);
89 }
90
91 /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
92 @param key
93 The key name. Must be ASCII, with no '=' characters.
94 <P>
95 @param value
96 Binary representation of the value.
97 */
98 public void set( String key, byte[] value)
99 {
100 byte[] keyBytes;
101 int valLen = (value != null) ? value.length : 0;
102
103 try {
104 keyBytes = key.getBytes( "US-ASCII");
105 }
106 catch ( java.io.UnsupportedEncodingException uee) {
107 throw new IllegalArgumentException();
108 }
109
110 for ( int i=0; i < keyBytes.length; i++)
111 if ( keyBytes[i] == '=')
112 throw new IllegalArgumentException();
113
114 if ( keyBytes.length + valLen >= 255)
115 throw new ArrayIndexOutOfBoundsException();
116
117 int prevLoc = this.remove( key);
118 if ( prevLoc == -1)
119 prevLoc = this.size();
120
121 this.insert( keyBytes, value, prevLoc);
122 }
123
124 protected void insert( byte[] keyBytes, byte[] value, int index)
125 // Insert a key-value pair at index
126 {
127 byte[] oldBytes = fBytes;
128 int valLen = (value != null) ? value.length : 0;
129 int insertion = 0;
130 byte newLen, avLen;
131
132 // locate the insertion point
133 for ( int i=0; i < index && insertion < fBytes.length; i++)
134 insertion += fBytes[ insertion] + 1;
135
136 avLen = (byte) ( keyBytes.length + valLen + (value != null ? 1 : 0));
137 newLen = (byte) ( avLen + oldBytes.length + 1);
138
139 fBytes = new byte[ newLen];
140 System.arraycopy( oldBytes, 0, fBytes, 0, insertion);
141 int secondHalfLen = oldBytes.length - insertion;
142 System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen);
143 fBytes[ insertion] = avLen;
144 System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length);
145 if ( value != null)
146 {
147 fBytes[ insertion + 1 + keyBytes.length] = kAttrSep;
148 System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen);
149 }
150 }
151
152 /** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */
153 public int remove( String key)
154 {
155 int avStart = 0;
156
157 for ( int i=0; avStart < fBytes.length; i++)
158 {
159 int avLen = fBytes[ avStart];
160 if ( key.length() <= avLen &&
161 ( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep))
162 {
163 String s = new String( fBytes, avStart + 1, key.length());
164 if ( 0 == key.compareToIgnoreCase( s))
165 {
166 byte[] oldBytes = fBytes;
167 fBytes = new byte[ oldBytes.length - avLen - 1];
168 System.arraycopy( oldBytes, 0, fBytes, 0, avStart);
169 System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1);
170 return i;
171 }
172 }
173 avStart += avLen + 1;
174 }
175 return -1;
176 }
177
178 /** Return the number of keys in the TXT record. */
179 public int size()
180 {
181 int i, avStart;
182
183 for ( i=0, avStart=0; avStart < fBytes.length; i++)
184 avStart += fBytes[ avStart] + 1;
185 return i;
186 }
187
188 /** Return true if key is present in the TXT record, false if not. */
189 public boolean contains( String key)
190 {
191 String s = null;
192
193 for ( int i=0; null != ( s = this.getKey( i)); i++)
194 if ( 0 == key.compareToIgnoreCase( s))
195 return true;
196 return false;
197 }
198
199 /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
200 public String getKey( int index)
201 {
202 int avStart = 0;
203
204 for ( int i=0; i < index && avStart < fBytes.length; i++)
205 avStart += fBytes[ avStart] + 1;
206
207 if ( avStart < fBytes.length)
208 {
209 int avLen = fBytes[ avStart];
210 int aLen = 0;
211
212 for ( aLen=0; aLen < avLen; aLen++)
213 if ( fBytes[ avStart + aLen + 1] == kAttrSep)
214 break;
215 return new String( fBytes, avStart + 1, aLen);
216 }
217 return null;
218 }
219
220 /**
221 Look up a key in the TXT record by zero-based index and return its value. <P>
222 Returns null if index exceeds the total number of keys.
223 Returns null if the key is present with no value.
224 */
225 public byte[] getValue( int index)
226 {
227 int avStart = 0;
228 byte[] value = null;
229
230 for ( int i=0; i < index && avStart < fBytes.length; i++)
231 avStart += fBytes[ avStart] + 1;
232
233 if ( avStart < fBytes.length)
234 {
235 int avLen = fBytes[ avStart];
236 int aLen = 0;
237
238 for ( aLen=0; aLen < avLen; aLen++)
239 {
240 if ( fBytes[ avStart + aLen + 1] == kAttrSep)
241 {
242 value = new byte[ avLen - aLen - 1];
243 System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1);
244 break;
245 }
246 }
247 }
248 return value;
249 }
250
251 /** Converts the result of getValue() to a string in the platform default character set. */
252 public String getValueAsString( int index)
253 {
254 byte[] value = this.getValue( index);
255 return value != null ? new String( value) : null;
256 }
257
258 /** Get the value associated with a key. Will be null if the key is not defined.
259 Array will have length 0 if the key is defined with an = but no value.<P>
260
261 @param forKey
262 The left-hand side of the key-value pair.
263 <P>
264 @return The binary representation of the value.
265 */
266 public byte[] getValue( String forKey)
267 {
268 String s = null;
269 int i;
270
271 for ( i=0; null != ( s = this.getKey( i)); i++)
272 if ( 0 == forKey.compareToIgnoreCase( s))
273 return this.getValue( i);
274 return null;
275 }
276
277 /** Converts the result of getValue() to a string in the platform default character set.<P>
278
279 @param forKey
280 The left-hand side of the key-value pair.
281 <P>
282 @return The value represented in the default platform character set.
283 */
284 public String getValueAsString( String forKey)
285 {
286 byte[] val = this.getValue( forKey);
287 return val != null ? new String( val) : null;
288 }
289
290 /** Return the contents of the TXT record as raw bytes. */
291 public byte[] getRawBytes() { return (byte[]) fBytes.clone(); }
292
293 /** Return a string representation of the object. */
294 public String toString()
295 {
296 String a, result = null;
297
298 for ( int i=0; null != ( a = this.getKey( i)); i++)
299 {
300 String av = String.valueOf( i) + "={" + a;
301 String val = this.getValueAsString( i);
302 if ( val != null)
303 av += "=" + val + "}";
304 else
305 av += "}";
306 if ( result == null)
307 result = av;
308 else
309 result = result + ", " + av;
310 }
311 return result != null ? result : "";
312 }
313 }
314