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