|
David@0
|
1 |
/* |
|
David@0
|
2 |
* Project: Libezv |
|
David@0
|
3 |
* File: AWEzvRendezvousData.h |
|
David@0
|
4 |
* |
|
David@0
|
5 |
* Version: 1.0 |
|
David@0
|
6 |
* Author: Andrew Wellington <proton[at]wiretapped.net> |
|
David@0
|
7 |
* |
|
David@0
|
8 |
* License: |
|
David@0
|
9 |
* Copyright (C) 2004-2005 Andrew Wellington. |
|
David@0
|
10 |
* All rights reserved. |
|
David@0
|
11 |
* |
|
David@0
|
12 |
* Redistribution and use in source and binary forms, with or without |
|
David@0
|
13 |
* modification, are permitted provided that the following conditions are met: |
|
David@0
|
14 |
* |
|
David@0
|
15 |
* 1. Redistributions of source code must retain the above copyright notice, |
|
David@0
|
16 |
* this list of conditions and the following disclaimer. |
|
David@0
|
17 |
* 2. Redistributions in binary form must reproduce the above copyright notice, |
|
David@0
|
18 |
* this list of conditions and the following disclaimer in the documentation |
|
David@0
|
19 |
* and/or other materials provided with the distribution. |
|
David@0
|
20 |
* |
|
David@0
|
21 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED |
|
David@0
|
22 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
David@0
|
23 |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
|
David@0
|
24 |
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
David@0
|
25 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
David@0
|
26 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
|
David@0
|
27 |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
David@0
|
28 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
David@0
|
29 |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
David@0
|
30 |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
David@0
|
31 |
*/ |
|
David@0
|
32 |
|
|
David@0
|
33 |
#import "AWEzvRendezvousData.h" |
|
David@0
|
34 |
#import "AWEzvSupportRoutines.h" |
|
Evan@2930
|
35 |
#import <AIUtilities/AIStringAdditions.h> |
|
David@0
|
36 |
|
|
David@0
|
37 |
@implementation AWEzvRendezvousData |
|
David@0
|
38 |
|
|
David@0
|
39 |
/* subnegotiation that appears at start of rendezvous packet */ |
|
David@0
|
40 |
/* Reserved version? */ |
|
David@0
|
41 |
NSString *subn = @"subn\x00\x00\x00\x01"; |
|
David@0
|
42 |
|
|
David@0
|
43 |
/* end of subnegotation. significance of value unknown */ |
|
David@0
|
44 |
/* Reserved unknown */ |
|
David@0
|
45 |
NSString *endn = @"\x00\x00\x00\x00"; |
|
David@0
|
46 |
|
|
David@0
|
47 |
/* initialization, create our dictionary */ |
|
David@0
|
48 |
-(AWEzvRendezvousData *) init |
|
David@0
|
49 |
{ |
|
David@0
|
50 |
if ((self = [super init])) { |
|
David@0
|
51 |
keys = [[NSMutableDictionary dictionary] retain]; |
|
David@0
|
52 |
serial = 1; |
|
David@0
|
53 |
} |
|
David@0
|
54 |
|
|
David@0
|
55 |
return self; |
|
David@0
|
56 |
} |
|
David@0
|
57 |
|
|
David@0
|
58 |
/* intialise given an NSData containing an announcement */ |
|
David@0
|
59 |
-(AWEzvRendezvousData *) initWithData:(NSData *)data { |
|
David@0
|
60 |
UInt32 version; /* the version of the iChat announcement (?) */ |
|
David@0
|
61 |
UInt32 fieldCount; /* number of fields in the announcement */ |
|
David@0
|
62 |
UInt16 fieldLen; /* length of field being read */ |
|
David@0
|
63 |
UInt32 i; /* read index into data buffer */ |
|
David@0
|
64 |
NSString *fieldName; /* name of field */ |
|
David@0
|
65 |
NSString *fieldContent; /* contents of field */ |
|
David@0
|
66 |
NSData *tmpData; /* temporary data */ |
|
David@0
|
67 |
NSRange range; /* range for reading of data */ |
|
David@0
|
68 |
|
|
David@0
|
69 |
/* call the standard initialisation */ |
|
David@0
|
70 |
self = [self init]; |
|
David@0
|
71 |
|
|
David@0
|
72 |
/* check that the length is ok */ |
|
David@0
|
73 |
if ([data length] < ([subn length] + 4 + [endn length])) { |
|
David@0
|
74 |
AWEzvLog(@"Invalid rendezvous announcement: length %u", [data length]); |
|
David@0
|
75 |
[self autorelease]; |
|
David@0
|
76 |
return nil; |
|
David@0
|
77 |
} |
|
David@0
|
78 |
|
|
David@0
|
79 |
/* check version (?) of iChat announcement */ |
|
David@0
|
80 |
range.location = 4; |
|
David@0
|
81 |
range.length = 4; |
|
David@0
|
82 |
[data getBytes:&version range:range]; |
|
David@0
|
83 |
version = ntohl(version); |
|
David@0
|
84 |
if (version != 1) { |
|
David@0
|
85 |
AWEzvLog(@"Invalid rendezvous announcement: incorrect version: %u", version); |
|
David@0
|
86 |
[self autorelease]; |
|
David@0
|
87 |
return nil; |
|
David@0
|
88 |
} |
|
David@0
|
89 |
|
|
David@0
|
90 |
/* get serial of announcement */ |
|
David@0
|
91 |
range.location = 8; |
|
David@0
|
92 |
range.length = 4; |
|
David@0
|
93 |
[data getBytes:&serial range:range]; |
|
David@0
|
94 |
serial = ntohl(serial); |
|
David@0
|
95 |
|
|
David@0
|
96 |
/* get field count of data */ |
|
David@0
|
97 |
range.location = 16; |
|
David@0
|
98 |
range.length = 4; |
|
David@0
|
99 |
[data getBytes:&fieldCount range:range]; |
|
David@0
|
100 |
fieldCount = ntohl(fieldCount); |
|
David@0
|
101 |
|
|
David@0
|
102 |
/* read fields from data */ |
|
David@0
|
103 |
for (i = [subn length] + 4 + [endn length] + 4; i < [data length];) { |
|
David@0
|
104 |
int binFlag = 0; |
|
David@0
|
105 |
|
|
David@0
|
106 |
/* read length of field name */ |
|
David@0
|
107 |
if ([data length] < i + 2) { |
|
David@0
|
108 |
AWEzvLog(@"Invalid rendezvous announcement at field name length"); |
|
David@0
|
109 |
[self autorelease]; |
|
David@0
|
110 |
return nil; |
|
David@0
|
111 |
} |
|
David@0
|
112 |
range.location = i; |
|
David@0
|
113 |
range.length = 2; |
|
David@0
|
114 |
[data getBytes:&fieldLen range:range]; |
|
David@0
|
115 |
fieldLen = ntohs(fieldLen); |
|
David@0
|
116 |
fieldLen = fieldLen & 0x7FFF; |
|
David@0
|
117 |
i = i + 2; |
|
David@0
|
118 |
|
|
David@0
|
119 |
/* read field data */ |
|
David@0
|
120 |
if ([data length] < i + fieldLen) { |
|
David@0
|
121 |
AWEzvLog(@"Invalid rendezvous announcement at field name"); |
|
David@0
|
122 |
[self autorelease]; |
|
David@0
|
123 |
return nil; |
|
David@0
|
124 |
} |
|
David@0
|
125 |
tmpData = [NSData dataWithBytes:[data bytes] + i length:fieldLen]; |
|
David@0
|
126 |
fieldName = [[[NSString alloc] initWithData:tmpData encoding:NSUTF8StringEncoding] autorelease]; |
|
David@0
|
127 |
i = i + fieldLen; |
|
David@0
|
128 |
|
|
David@0
|
129 |
/* read length of field data */ |
|
David@0
|
130 |
if ([data length] < i + 2) { |
|
David@0
|
131 |
AWEzvLog(@"Invalid rendezvous announcement at field data length"); |
|
David@0
|
132 |
[self autorelease]; |
|
David@0
|
133 |
return nil; |
|
David@0
|
134 |
} |
|
David@0
|
135 |
range.location = i; |
|
David@0
|
136 |
range.length = 2; |
|
David@0
|
137 |
[data getBytes:&fieldLen range:range]; |
|
David@0
|
138 |
fieldLen = ntohs(fieldLen); |
|
David@0
|
139 |
/* most significant bit in fieldLen is a binary data flag */ |
|
David@0
|
140 |
if ((fieldLen & 0x7FFF) != fieldLen) |
|
David@0
|
141 |
binFlag = 1; |
|
David@0
|
142 |
fieldLen = fieldLen & 0x7FFF; |
|
David@0
|
143 |
i = i + 2; |
|
David@0
|
144 |
|
|
David@0
|
145 |
/* read field data */ |
|
David@0
|
146 |
if ([data length] < i + fieldLen) { |
|
David@0
|
147 |
AWEzvLog(@"Invalid rendezvous announcement at field data"); |
|
David@0
|
148 |
[self autorelease]; |
|
David@0
|
149 |
return nil; |
|
David@0
|
150 |
} |
|
David@0
|
151 |
if (!binFlag) { |
|
David@0
|
152 |
tmpData = [NSData dataWithBytes:[data bytes] + i length:fieldLen]; |
|
David@0
|
153 |
fieldContent = [[[NSString alloc] initWithData:tmpData encoding:NSUTF8StringEncoding] autorelease]; |
|
David@0
|
154 |
} else { |
|
sholt@2699
|
155 |
fieldContent = [[[NSString alloc] initWithBytes:[data bytes] + i length:fieldLen encoding:NSUTF8StringEncoding] autorelease]; |
|
David@0
|
156 |
} |
|
David@0
|
157 |
i = i + fieldLen; |
|
David@0
|
158 |
|
|
David@0
|
159 |
/* save field information */ |
|
David@0
|
160 |
[self setField:fieldName content:fieldContent]; |
|
David@0
|
161 |
} |
|
David@0
|
162 |
|
|
David@0
|
163 |
/* return initialised object */ |
|
David@0
|
164 |
return self; |
|
David@0
|
165 |
} |
|
David@0
|
166 |
|
|
David@0
|
167 |
/* intialise rendezvous data with a plist of the data */ |
|
David@0
|
168 |
-(AWEzvRendezvousData *) initWithPlist:(NSString *)plist { |
|
David@0
|
169 |
id extracted; /* extracted data from plist */ |
|
David@0
|
170 |
NSData *xmlData; /* XML data in NSData form */ |
|
David@0
|
171 |
NSString *error; /* error from conversion of plist */ |
|
David@0
|
172 |
NSPropertyListFormat format;/* something we can point at for the format pointer */ |
|
David@0
|
173 |
|
|
David@0
|
174 |
error = [NSString string]; |
|
David@0
|
175 |
|
|
David@0
|
176 |
/* create XML data */ |
|
David@0
|
177 |
xmlData = [NSData dataWithBytes:[plist UTF8String] length:[plist length]]; |
|
David@0
|
178 |
|
|
David@0
|
179 |
/* extract plist from XML data */ |
|
David@0
|
180 |
format = NSPropertyListXMLFormat_v1_0; |
|
David@0
|
181 |
extracted = [NSPropertyListSerialization |
|
David@0
|
182 |
propertyListFromData:xmlData |
|
David@0
|
183 |
mutabilityOption:NSPropertyListImmutable |
|
David@0
|
184 |
format:&format |
|
David@0
|
185 |
errorDescription:&error]; |
|
David@0
|
186 |
|
|
David@0
|
187 |
/* check if there was an error in extraction */ |
|
David@0
|
188 |
if (extracted == nil) { |
|
David@0
|
189 |
AWEzvLog(@"Unable to extract XML into plist"); |
|
David@0
|
190 |
[self autorelease]; |
|
David@0
|
191 |
return nil; |
|
David@0
|
192 |
} |
|
David@0
|
193 |
|
|
David@0
|
194 |
/* make sure it's an NSData, or reponds to getBytes:range: */ |
|
David@0
|
195 |
if (![extracted respondsToSelector:@selector(getBytes:range:)]) { |
|
David@0
|
196 |
AWEzvLog(@"Extracted object from XML is not an NSData"); |
|
David@0
|
197 |
[self autorelease]; |
|
David@0
|
198 |
return nil; |
|
David@0
|
199 |
} |
|
David@0
|
200 |
|
|
David@0
|
201 |
/* pass it to another initialiser */ |
|
David@0
|
202 |
return [self initWithData:extracted]; |
|
David@0
|
203 |
} |
|
David@0
|
204 |
|
|
David@0
|
205 |
/* initialise object with a dictionary */ |
|
David@0
|
206 |
- (AWEzvRendezvousData *)initWithDictionary:(NSDictionary *)dictionary |
|
David@0
|
207 |
{ |
|
David@0
|
208 |
if ((self = [super init])) { |
|
David@0
|
209 |
keys = [dictionary retain]; |
|
David@0
|
210 |
serial++; |
|
David@0
|
211 |
} |
|
David@0
|
212 |
|
|
David@0
|
213 |
return self; |
|
David@0
|
214 |
} |
|
David@0
|
215 |
|
|
David@0
|
216 |
/* initialise object with an AV TXT record */ |
|
David@0
|
217 |
- (AWEzvRendezvousData *)initWithAVTxt:(NSString *)txt { |
|
David@0
|
218 |
NSArray *attribs; |
|
David@0
|
219 |
NSString *key; |
|
David@0
|
220 |
|
|
David@0
|
221 |
self = [self init]; |
|
David@0
|
222 |
|
|
David@0
|
223 |
attribs = [txt componentsSeparatedByString:@"\001"]; |
|
David@0
|
224 |
|
|
David@75
|
225 |
for (key in attribs) { |
|
David@0
|
226 |
NSRange range; |
|
David@0
|
227 |
|
|
David@0
|
228 |
range = [key rangeOfString:@"="]; |
|
David@0
|
229 |
if (range.location != NSNotFound) { |
|
David@0
|
230 |
[self setField:[key substringToIndex:range.location] |
|
David@0
|
231 |
content:[key substringFromIndex:range.location+1]]; |
|
David@0
|
232 |
} |
|
David@0
|
233 |
} |
|
David@0
|
234 |
|
|
David@0
|
235 |
return self; |
|
David@0
|
236 |
} |
|
David@571
|
237 |
- (AWEzvRendezvousData *) initWithTXTRecordRef:(const unsigned char *) txtRecord length:(uint16_t)len{ |
|
David@0
|
238 |
|
|
David@0
|
239 |
self = [self init]; |
|
David@0
|
240 |
|
|
David@0
|
241 |
DNSServiceErrorType txtRecordError; |
|
David@0
|
242 |
|
|
David@0
|
243 |
int i, numKeys; |
|
David@0
|
244 |
numKeys = TXTRecordGetCount(len, txtRecord); |
|
David@0
|
245 |
for (i=0; i<numKeys; i++) { |
|
David@0
|
246 |
char key[256]; |
|
David@0
|
247 |
uint8_t valLen; |
|
David@0
|
248 |
const void *value; |
|
David@0
|
249 |
|
|
David@0
|
250 |
txtRecordError = TXTRecordGetItemAtIndex ( |
|
David@0
|
251 |
/* length */ len, |
|
David@0
|
252 |
/* txtrecord */ txtRecord, |
|
David@0
|
253 |
/* index */ i, |
|
David@0
|
254 |
/* keybuffer length */ sizeof(key), |
|
David@0
|
255 |
/* key buffer */ key, |
|
David@0
|
256 |
/* valueLength */ &valLen, |
|
David@0
|
257 |
/* value pointer */ &value ); |
|
David@0
|
258 |
if (txtRecordError == kDNSServiceErr_NoError) { |
|
David@0
|
259 |
NSString *keyString = NULL; |
|
David@0
|
260 |
NSString *data = NULL; |
|
David@0
|
261 |
keyString = [NSString stringWithUTF8String: key]; |
|
David@0
|
262 |
|
|
David@0
|
263 |
if (value) { |
|
David@0
|
264 |
data = [[[NSString alloc] initWithBytes: value length: valLen encoding: NSUTF8StringEncoding] autorelease]; |
|
David@0
|
265 |
} |
|
David@0
|
266 |
|
|
David@0
|
267 |
if (data != NULL && keyString != NULL) { |
|
David@0
|
268 |
[self setField:keyString content:data]; |
|
David@0
|
269 |
} else { |
|
David@0
|
270 |
AWEzvLog(@"Creating TXTRecord: No data and No key"); |
|
David@0
|
271 |
} |
|
David@0
|
272 |
|
|
David@0
|
273 |
/* AWEzvLog(@"key:%@ value=%@", keyString, data); */ |
|
David@0
|
274 |
} else { |
|
David@0
|
275 |
AWEzvLog(@"Error reading txt keys"); |
|
David@0
|
276 |
} |
|
David@0
|
277 |
|
|
David@0
|
278 |
|
|
David@0
|
279 |
|
|
David@0
|
280 |
} |
|
David@0
|
281 |
|
|
David@0
|
282 |
// // kind of a hack: munge txtRecord so it's human-readable |
|
David@0
|
283 |
// if ( len > 0) { |
|
David@0
|
284 |
// char *readableText = (char*) malloc( len); |
|
David@0
|
285 |
// if ( readableText != nil) { |
|
David@0
|
286 |
// ByteCount index, subStrLen; |
|
David@0
|
287 |
// memcpy( readableText, txtRecord, len); |
|
David@0
|
288 |
// for ( index=0; index < len - 1; index += subStrLen + 1) { |
|
David@0
|
289 |
// subStrLen = readableText[ index]; |
|
David@0
|
290 |
// readableText[ index] = '\n'; |
|
David@0
|
291 |
// } |
|
David@0
|
292 |
// //NSLog(@"%@\n\n",[NSString stringWithCString:&readableText[1] length:len - 1]); |
|
David@0
|
293 |
// free( readableText); |
|
David@0
|
294 |
// } |
|
David@0
|
295 |
// } |
|
David@0
|
296 |
|
|
David@0
|
297 |
return self; |
|
David@0
|
298 |
|
|
David@0
|
299 |
} |
|
David@0
|
300 |
|
|
David@0
|
301 |
/* deallocate, destroy our dictionary */ |
|
David@0
|
302 |
- (void)dealloc |
|
David@0
|
303 |
{ |
|
David@0
|
304 |
[keys release]; |
|
David@0
|
305 |
[super dealloc]; |
|
David@0
|
306 |
} |
|
David@0
|
307 |
|
|
David@0
|
308 |
/* sets a field in the rendezvous data structures */ |
|
David@0
|
309 |
-(void) setField:(NSString *)fieldName content:(NSObject *)content { |
|
David@0
|
310 |
if (content == nil || fieldName == nil) |
|
David@0
|
311 |
return; |
|
David@0
|
312 |
|
|
David@0
|
313 |
[keys setObject:content forKey:fieldName]; |
|
David@0
|
314 |
serial++; |
|
David@0
|
315 |
} |
|
David@0
|
316 |
|
|
David@0
|
317 |
/* delete a field in the rendezvous data structure */ |
|
David@0
|
318 |
-(void) deleteField:(NSString *)fieldName { |
|
David@0
|
319 |
if ([keys objectForKey:fieldName] != nil) |
|
David@0
|
320 |
[keys removeObjectForKey:fieldName]; |
|
David@0
|
321 |
} |
|
David@0
|
322 |
|
|
David@0
|
323 |
/* get a field from the rendezvous data structure */ |
|
David@0
|
324 |
-(NSString *) getField:(NSString *)fieldName { |
|
David@0
|
325 |
return [[[keys objectForKey:fieldName] copy] autorelease]; |
|
David@0
|
326 |
} |
|
David@0
|
327 |
|
|
David@0
|
328 |
/* return if a field exists */ |
|
David@0
|
329 |
-(BOOL) fieldExists:(NSString *)fieldName { |
|
David@0
|
330 |
return [keys objectForKey:fieldName] != nil; |
|
David@0
|
331 |
} |
|
David@0
|
332 |
|
|
David@0
|
333 |
/* return the serial number of the data */ |
|
David@0
|
334 |
-(UInt32) serial { |
|
David@0
|
335 |
return serial; |
|
David@0
|
336 |
} |
|
David@0
|
337 |
|
|
David@0
|
338 |
/* return the dictionary */ |
|
David@0
|
339 |
-(NSDictionary *)dictionary { |
|
David@0
|
340 |
return [[keys copy] autorelease]; |
|
David@0
|
341 |
} |
|
David@0
|
342 |
|
|
David@0
|
343 |
/* |
|
David@0
|
344 |
* Generate data to be placed in the protocolSpecificInformation (TXT record) |
|
David@0
|
345 |
* of the rendezvous announcement. This is data shouldn't be used directly, but |
|
David@0
|
346 |
* should be used indirectly via dataAsDNSTXT or dataAsPackedPString |
|
David@0
|
347 |
*/ |
|
David@0
|
348 |
-(NSString *) data { |
|
David@0
|
349 |
NSMutableData *data; /* binary representation of rendezvous data */ |
|
David@0
|
350 |
NSData *xmlData; /* data converted to an XML plist */ |
|
David@0
|
351 |
NSMutableString *infoData; /* XML plist as a string */ |
|
David@0
|
352 |
NSString *key; /* strings used when manipulating data */ |
|
David@0
|
353 |
id value; /* value for data field */ |
|
David@0
|
354 |
NSString *error; /* error from creation of plist */ |
|
David@0
|
355 |
UInt32 keycount; /* a 32-bit integer, count of keys in data */ |
|
David@0
|
356 |
UInt16 fieldlen; /* a 16-bit integer, length of field being added to data */ |
|
David@0
|
357 |
UInt16 fieldlenBE; /* fieldlen as converted to network byte order */ |
|
David@0
|
358 |
UInt32 serialBE = htonl(serial); /* serial as converted to network byte order */ |
|
David@0
|
359 |
|
|
David@0
|
360 |
/* allocate NSData to create data in */ |
|
David@0
|
361 |
data = [[NSMutableData alloc] init]; |
|
David@0
|
362 |
[data autorelease]; |
|
David@0
|
363 |
/* add the subnegotiation string */ |
|
David@0
|
364 |
[data appendBytes:[subn UTF8String] length:[subn length]]; |
|
David@0
|
365 |
[data appendBytes:&serialBE length:4]; |
|
David@0
|
366 |
[data appendBytes:[endn UTF8String] length:[endn length]]; |
|
David@0
|
367 |
/* add a field containing the number of fields for the rest of the data */ |
|
David@0
|
368 |
keycount = [keys count] + 1; /* +1 for slumming field */ |
|
David@0
|
369 |
keycount = htonl(keycount); |
|
David@0
|
370 |
[data appendBytes:&keycount length:4]; |
|
David@0
|
371 |
|
|
David@0
|
372 |
/* loop through fields to be added and add them to data */ |
|
David@1616
|
373 |
for (key in keys) { |
|
David@0
|
374 |
/* add length of field name, then field name */ |
|
David@0
|
375 |
const char *field; |
|
David@0
|
376 |
field = [key UTF8String]; |
|
David@0
|
377 |
fieldlen = strlen(field); |
|
David@0
|
378 |
fieldlenBE = htons(fieldlen); |
|
David@0
|
379 |
[data appendBytes:&fieldlenBE length:2]; |
|
David@0
|
380 |
[data appendBytes:field length:fieldlen]; |
|
David@0
|
381 |
|
|
David@0
|
382 |
/* add length of field data, then field data */ |
|
David@0
|
383 |
value = [keys objectForKey:key]; |
|
David@0
|
384 |
if ([value isKindOfClass: [NSData class]]) { |
|
David@0
|
385 |
field = [value bytes]; |
|
David@0
|
386 |
fieldlen = [(NSData *)value length]; |
|
David@0
|
387 |
fieldlen = fieldlen | ~0x7FFF; |
|
David@0
|
388 |
} else { |
|
David@0
|
389 |
field = [value UTF8String]; |
|
David@0
|
390 |
fieldlen = strlen(field); |
|
David@0
|
391 |
} |
|
David@0
|
392 |
fieldlenBE = htons(fieldlen); |
|
David@0
|
393 |
[data appendBytes:&fieldlenBE length:2]; |
|
David@0
|
394 |
if ([value isKindOfClass: [NSData class]]) { |
|
David@0
|
395 |
fieldlen = [(NSData *)value length]; |
|
David@0
|
396 |
} |
|
David@0
|
397 |
[data appendBytes:field length:fieldlen]; |
|
David@0
|
398 |
} |
|
David@0
|
399 |
|
|
David@0
|
400 |
/* we're slumming it in iChat-land */ |
|
David@0
|
401 |
key = @"slumming"; |
|
David@0
|
402 |
fieldlen = [key length]; |
|
David@0
|
403 |
fieldlenBE = htons(fieldlen); |
|
hg@2794
|
404 |
[data appendBytes:&fieldlenBE length:2]; |
|
David@0
|
405 |
[data appendBytes:[key UTF8String] length:[key length]]; |
|
David@0
|
406 |
value = @"1"; |
|
David@0
|
407 |
fieldlen = [(NSData *)value length]; |
|
David@0
|
408 |
fieldlenBE = htons(fieldlen); |
|
hg@2794
|
409 |
[data appendBytes:&fieldlenBE length:2]; |
|
David@0
|
410 |
[data appendBytes:[value UTF8String] length:[(NSData *)value length]]; |
|
David@0
|
411 |
|
|
David@0
|
412 |
/* create XML plist of data and convert to string */ |
|
David@0
|
413 |
xmlData = [NSPropertyListSerialization dataFromPropertyList:data |
|
David@0
|
414 |
format:NSPropertyListXMLFormat_v1_0 |
|
David@0
|
415 |
errorDescription:&error]; |
|
David@0
|
416 |
infoData = [[NSMutableString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding]; |
|
David@0
|
417 |
[infoData autorelease]; |
|
David@0
|
418 |
|
|
David@0
|
419 |
/* and now we have the rendezvous data to return to the caller, the copy |
|
David@0
|
420 |
converts it to immutable */ |
|
David@0
|
421 |
return [[infoData copy] autorelease]; |
|
David@0
|
422 |
} |
|
David@0
|
423 |
|
|
David@0
|
424 |
/* |
|
David@0
|
425 |
* Converts data: to a format appropriate for passing to service registration. |
|
David@0
|
426 |
* We add an ASCII 1 character every 255 characters for pascal string separation |
|
David@0
|
427 |
*/ |
|
David@0
|
428 |
-(NSString *)dataAsDNSTXT { |
|
David@0
|
429 |
NSMutableString *infoData = [[[self data] mutableCopy] autorelease]; /* data to be done */ |
|
David@0
|
430 |
unsigned long i; /* loop counter */ |
|
David@0
|
431 |
|
|
David@0
|
432 |
/* add the character \001 when we exceed 255 characters, required to allow announcement |
|
David@0
|
433 |
to be longer than 255 characters */ |
|
David@0
|
434 |
for (i = 255; i < [infoData length]; i += 255) |
|
David@0
|
435 |
{ |
|
David@0
|
436 |
[infoData insertString:@"\001" atIndex:i]; |
|
David@0
|
437 |
} |
|
David@0
|
438 |
|
|
David@0
|
439 |
/* return a copy so it is immutable */ |
|
David@0
|
440 |
return [[infoData copy] autorelease]; |
|
David@0
|
441 |
} |
|
David@0
|
442 |
|
|
David@0
|
443 |
/* ichat AV style TXT record */ |
|
David@0
|
444 |
-(NSString *)avDataAsDNSTXT { |
|
David@0
|
445 |
NSMutableString *infoData = [NSMutableString string]; |
|
David@0
|
446 |
id value; |
|
David@0
|
447 |
NSString *key; |
|
David@0
|
448 |
|
|
David@0
|
449 |
[infoData appendString:@"\001txtvers=1"]; |
|
David@0
|
450 |
[infoData appendString:@"\001version=1"]; |
|
David@0
|
451 |
|
|
David@1616
|
452 |
/* enumerate through fields for announcement */ |
|
David@1616
|
453 |
for (key in keys) { |
|
David@0
|
454 |
[infoData appendString:@"\001"]; |
|
David@0
|
455 |
[infoData appendString:key]; |
|
David@0
|
456 |
[infoData appendString:@"="]; |
|
David@0
|
457 |
value = [keys objectForKey:key]; |
|
David@0
|
458 |
|
|
David@0
|
459 |
if ([value isKindOfClass: [NSData class]]) { |
|
David@0
|
460 |
/* convert binary to hex */ |
|
David@0
|
461 |
char *hexdata = (char *)malloc([(NSData *)value length] * 2 + 1); |
|
David@0
|
462 |
int i; |
|
David@0
|
463 |
|
|
David@0
|
464 |
for (i = 0; i < 20; i++) { |
|
David@0
|
465 |
sprintf(hexdata + (i*2), "%.2x", ((unsigned char *)[(NSData *)value bytes])[i]); |
|
David@0
|
466 |
} |
|
David@0
|
467 |
hexdata[[(NSData *)value length] * 2] = '\0'; |
|
David@0
|
468 |
|
|
sholt@2699
|
469 |
[infoData appendString:[NSString stringWithUTF8String:hexdata]]; |
|
David@0
|
470 |
} else { |
|
David@0
|
471 |
[infoData appendString:value]; |
|
David@0
|
472 |
} |
|
David@0
|
473 |
} |
|
David@0
|
474 |
|
|
David@0
|
475 |
return infoData; |
|
David@0
|
476 |
} |
|
David@0
|
477 |
|
|
David@0
|
478 |
-(TXTRecordRef)dataAsTXTRecordRef { |
|
David@0
|
479 |
//AWEzvLog(@"dataAsTXTRecordRef called"); |
|
David@0
|
480 |
TXTRecordRef txtRecord; |
|
David@0
|
481 |
DNSServiceErrorType txtRecordError; |
|
David@0
|
482 |
id value; |
|
David@565
|
483 |
const char *valueToSet; |
|
David@0
|
484 |
uint8_t valueSize; |
|
David@0
|
485 |
TXTRecordCreate(/* TXTRecordRef */ &txtRecord, /* buffer length */ 0, /* buffer */ NULL); |
|
David@0
|
486 |
|
|
David@0
|
487 |
/* Enumerate through keys setting the txtrecordvalue */ |
|
David@559
|
488 |
for (NSString *key in [keys keyEnumerator]) { |
|
David@0
|
489 |
value = [keys objectForKey:key]; |
|
David@0
|
490 |
//AWEzvLog(@"key=%@ value=%@", key, value); |
|
David@0
|
491 |
|
|
David@0
|
492 |
if ([value isKindOfClass: [NSData class]]) { |
|
David@0
|
493 |
/* convert binary to hex */ |
|
David@0
|
494 |
char *hexdata = (char *)malloc([(NSData *)value length] * 2 + 1); |
|
David@0
|
495 |
int i; |
|
David@0
|
496 |
|
|
David@0
|
497 |
for (i = 0; i < 20; i++) { |
|
David@0
|
498 |
sprintf(hexdata + (i*2), "%.2x", ((unsigned char *)[(NSData *)value bytes])[i]); |
|
David@0
|
499 |
} |
|
David@0
|
500 |
hexdata[[(NSData *)value length] * 2] = '\0'; |
|
sholt@2699
|
501 |
valueToSet = [[NSString stringWithUTF8String:hexdata] UTF8String]; |
|
David@0
|
502 |
valueSize = strlen(valueToSet); |
|
David@0
|
503 |
free(hexdata); |
|
David@0
|
504 |
} else { |
|
David@0
|
505 |
valueToSet = [value UTF8String]; |
|
David@0
|
506 |
valueSize = strlen(valueToSet); |
|
David@0
|
507 |
} |
|
David@0
|
508 |
|
|
David@0
|
509 |
txtRecordError = TXTRecordSetValue ( |
|
David@0
|
510 |
/* TXTRecord */ &txtRecord, |
|
David@0
|
511 |
/* key */ [key UTF8String], |
|
David@0
|
512 |
/* size, may be zero */ valueSize, |
|
Evan@2930
|
513 |
/* value, may be null */ valueToSet); |
|
Evan@2930
|
514 |
|
|
Evan@2930
|
515 |
if ((txtRecordError == kDNSServiceErr_Invalid) && |
|
Evan@2930
|
516 |
[value isKindOfClass:[NSString class]]) { |
|
Evan@2930
|
517 |
/* kDNSServiceErr_Invalid may be returned if: |
|
Evan@2930
|
518 |
* 1. Invalid characters were included (per documentation) |
|
Evan@2930
|
519 |
* 2. The length of the value is >= 250 characters (at least for the msg key) |
|
Evan@2930
|
520 |
* |
|
Evan@2930
|
521 |
* So: First, try stripping out any non-ASCII characters, as I'm not sure what might consitute an |
|
Evan@2930
|
522 |
* "illegal character" in a field which requests UTF8. |
|
Evan@2930
|
523 |
* |
|
Evan@2930
|
524 |
* Then, if that still fails, truncate the string to 248 characters (248,249,250 are the ellipsis). |
|
Evan@2930
|
525 |
*/ |
|
Evan@2930
|
526 |
valueToSet = [[value dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES] bytes]; |
|
Evan@2930
|
527 |
valueSize = strlen(valueToSet); |
|
Evan@2930
|
528 |
|
|
Evan@2930
|
529 |
txtRecordError = TXTRecordSetValue ( |
|
Evan@2930
|
530 |
/* TXTRecord */ &txtRecord, |
|
Evan@2930
|
531 |
/* key */ [key UTF8String], |
|
Evan@2930
|
532 |
/* size, may be zero */ valueSize, |
|
Evan@2930
|
533 |
/* value, may be null */ valueToSet); |
|
Evan@2930
|
534 |
|
|
Evan@2930
|
535 |
if (txtRecordError == kDNSServiceErr_Invalid) { |
|
Evan@2930
|
536 |
valueToSet = [[value stringWithEllipsisByTruncatingToLength:248] UTF8String]; |
|
Evan@2930
|
537 |
valueSize = strlen(valueToSet); |
|
Evan@2930
|
538 |
txtRecordError = TXTRecordSetValue ( |
|
Evan@2930
|
539 |
/* TXTRecord */ &txtRecord, |
|
Evan@2930
|
540 |
/* key */ [key UTF8String], |
|
Evan@2930
|
541 |
/* size, may be zero */ valueSize, |
|
Evan@2930
|
542 |
/* value, may be null */ valueToSet); |
|
Evan@2930
|
543 |
} |
|
Evan@2930
|
544 |
} |
|
Evan@2930
|
545 |
|
|
David@0
|
546 |
if (txtRecordError != kDNSServiceErr_NoError) { |
|
Evan@2930
|
547 |
if (txtRecordError == kDNSServiceErr_Invalid) { |
|
Evan@2930
|
548 |
AWEzvLog(@"Error setting TXTRecord of key=%@ and value=%s: Value contains illegal characters", key, valueToSet); |
|
Evan@2930
|
549 |
|
|
Evan@2930
|
550 |
} else if (txtRecordError == kDNSServiceErr_NoMemory) { |
|
Evan@2930
|
551 |
AWEzvLog(@"Error setting TXTRecord of key=%@ and value=%s: Exceeded available storage", key, valueToSet); |
|
Evan@2930
|
552 |
} else { |
|
Evan@2930
|
553 |
AWEzvLog(@"Error setting TXTRecord of key=%@ and value=%s: Error is %i", key, valueToSet, txtRecordError); |
|
Evan@2930
|
554 |
} |
|
David@0
|
555 |
|
|
David@0
|
556 |
} |
|
David@0
|
557 |
|
|
David@0
|
558 |
} |
|
David@0
|
559 |
|
|
David@0
|
560 |
return txtRecord; |
|
David@0
|
561 |
} |
|
David@0
|
562 |
/* |
|
David@0
|
563 |
* Converts data: to packed PString format as required by the low level rendezvous |
|
David@0
|
564 |
* functions when passing an opaque RData structure |
|
David@0
|
565 |
*/ |
|
David@0
|
566 |
-(NSData *) dataAsPackedPString { |
|
David@0
|
567 |
NSString *origdata = [self data]; /* original data */ |
|
David@0
|
568 |
NSMutableData *data = [NSMutableData data]; /* modified data to return */ |
|
David@0
|
569 |
unsigned char pstring[256]; /* pascal string under construction */ |
|
David@0
|
570 |
unsigned long i; /* loop counter */ |
|
David@0
|
571 |
|
|
David@0
|
572 |
/* initialise pstring */ |
|
David@0
|
573 |
pstring[0] = 0; |
|
David@0
|
574 |
|
|
David@0
|
575 |
/* create strings */ |
|
David@0
|
576 |
for (i = 0; i < [origdata length]; i++) { |
|
David@0
|
577 |
pstring[0]++; |
|
David@0
|
578 |
pstring[pstring[0]] = [origdata characterAtIndex:i]; |
|
David@0
|
579 |
if (pstring[0] == 254 || i == [origdata length] - 1) { |
|
David@0
|
580 |
[data appendBytes:(char *)&pstring length:pstring[0] + 1]; |
|
David@0
|
581 |
pstring[0] = 0; |
|
David@0
|
582 |
} |
|
David@0
|
583 |
} |
|
David@0
|
584 |
|
|
David@0
|
585 |
/* return copy so it is immutable */ |
|
David@0
|
586 |
return [[data copy] autorelease]; |
|
David@0
|
587 |
} |
|
David@0
|
588 |
|
|
David@0
|
589 |
/* ichat AV style TXT record */ |
|
David@0
|
590 |
-(NSData *)avDataAsPackedPString { |
|
David@0
|
591 |
NSMutableString *infoData = [NSMutableString string]; |
|
David@0
|
592 |
id value; |
|
David@0
|
593 |
NSString *key; |
|
David@0
|
594 |
const char *data; |
|
David@0
|
595 |
|
|
David@0
|
596 |
[infoData appendString:@"\x09txtvers=1"]; |
|
David@0
|
597 |
[infoData appendString:@"\x09version=1"]; |
|
David@0
|
598 |
|
|
David@1616
|
599 |
/* enumerate through fields for announcement */ |
|
David@1616
|
600 |
for (key in keys) { |
|
David@0
|
601 |
/* convert binary to hex */ |
|
David@0
|
602 |
char *hexdata; |
|
David@0
|
603 |
int i; |
|
David@0
|
604 |
|
|
David@0
|
605 |
value = [keys objectForKey:key]; |
|
David@0
|
606 |
|
|
David@0
|
607 |
if ([value isKindOfClass:[NSData class]]) { |
|
David@0
|
608 |
hexdata = (char *)malloc([(NSData *)value length] * 2 + 1); |
|
David@0
|
609 |
|
|
David@0
|
610 |
for (i = 0; i < 20; i++) { |
|
David@0
|
611 |
sprintf(hexdata + (i*2), "%.2x", ((unsigned char *)[(NSData *)value bytes])[i]); |
|
David@0
|
612 |
} |
|
David@0
|
613 |
hexdata[[(NSData *)value length] * 2] = '\0'; |
|
David@0
|
614 |
|
|
David@0
|
615 |
[infoData appendFormat:@"%c", ([(NSData *)value length] * 2 + [key length] + 1)]; |
|
David@0
|
616 |
[infoData appendString:key]; |
|
David@0
|
617 |
[infoData appendString:@"="]; |
|
sholt@2699
|
618 |
[infoData appendString:[NSString stringWithUTF8String:hexdata]]; |
|
David@0
|
619 |
free(hexdata); |
|
David@0
|
620 |
} else { |
|
David@0
|
621 |
const char *val = [(NSString *)value UTF8String]; |
|
David@0
|
622 |
int len = strlen(val); |
|
David@0
|
623 |
[infoData appendFormat:@"%c", len + [key length] + 1]; |
|
David@0
|
624 |
[infoData appendString:key]; |
|
David@0
|
625 |
[infoData appendString:@"="]; |
|
David@0
|
626 |
[infoData appendString:value]; |
|
David@0
|
627 |
} |
|
David@0
|
628 |
} |
|
David@0
|
629 |
|
|
David@0
|
630 |
data = [infoData UTF8String]; |
|
David@0
|
631 |
|
|
David@0
|
632 |
return [NSData dataWithBytes:data length:strlen(data)]; |
|
David@0
|
633 |
} |
|
David@0
|
634 |
|
|
David@0
|
635 |
|
|
David@0
|
636 |
@end |