1 /***
2 This file is part of avahi.
3
4 avahi is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 avahi is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with avahi; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <assert.h>
27 #include <string.h>
28
29 #include "avahi-common/avahi-malloc.h"
30
31 #include "dns_sd.h"
32 #include "warn.h"
33
34 typedef struct TXTRecordInternal {
35 uint8_t *buffer, *malloc_buffer;
36 size_t size, max_size;
37 } TXTRecordInternal;
38
39 #define INTERNAL_PTR(txtref) (* (TXTRecordInternal**) (txtref))
40 #define INTERNAL_PTR_CONST(txtref) (* (const TXTRecordInternal* const *) (txtref))
41
TXTRecordCreate(TXTRecordRef * txtref,uint16_t length,void * buffer)42 void DNSSD_API TXTRecordCreate(
43 TXTRecordRef *txtref,
44 uint16_t length,
45 void *buffer) {
46
47 TXTRecordInternal *t;
48
49 AVAHI_WARN_LINKAGE;
50
51 assert(txtref);
52
53 /* Apple's API design is flawed in so many ways, including the
54 * fact that it isn't compatible with 64 bit processors. To work
55 * around this we need some magic here which involves allocating
56 * our own memory. Please, Apple, do your homework next time
57 * before designing an API! */
58
59 if ((t = avahi_new(TXTRecordInternal, 1))) {
60 t->buffer = buffer;
61 t->max_size = buffer ? length : (size_t)0;
62 t->size = 0;
63 t->malloc_buffer = NULL;
64 }
65
66 /* If we were unable to allocate memory, we store a NULL pointer
67 * and return a NoMemory error later, is somewhat unclean, but
68 * should work. */
69 INTERNAL_PTR(txtref) = t;
70 }
71
TXTRecordDeallocate(TXTRecordRef * txtref)72 void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtref) {
73 TXTRecordInternal *t;
74
75 AVAHI_WARN_LINKAGE;
76
77 assert(txtref);
78 t = INTERNAL_PTR(txtref);
79 if (!t)
80 return;
81
82 avahi_free(t->malloc_buffer);
83 avahi_free(t);
84
85 /* Just in case ... */
86 INTERNAL_PTR(txtref) = NULL;
87 }
88
make_sure_fits_in(TXTRecordInternal * t,size_t size)89 static int make_sure_fits_in(TXTRecordInternal *t, size_t size) {
90 uint8_t *n;
91 size_t nsize;
92
93 assert(t);
94
95 if (t->size + size <= t->max_size)
96 return 0;
97
98 nsize = t->size + size + 100;
99
100 if (nsize > 0xFFFF)
101 return -1;
102
103 if (!(n = avahi_realloc(t->malloc_buffer, nsize)))
104 return -1;
105
106 if (!t->malloc_buffer && t->size)
107 memcpy(n, t->buffer, t->size);
108
109 t->buffer = t->malloc_buffer = n;
110 t->max_size = nsize;
111
112 return 0;
113 }
114
remove_key(TXTRecordInternal * t,const char * key)115 static int remove_key(TXTRecordInternal *t, const char *key) {
116 size_t i;
117 uint8_t *p;
118 size_t key_len;
119 int found = 0;
120
121 key_len = strlen(key);
122 assert(key_len <= 0xFF);
123
124 p = t->buffer;
125 i = 0;
126
127 while (i < t->size) {
128
129 /* Does the item fit in? */
130 assert(*p <= t->size - i - 1);
131
132 /* Key longer than buffer */
133 if (key_len > t->size - i - 1)
134 break;
135
136 if (key_len <= *p &&
137 strncmp(key, (char*) p+1, key_len) == 0 &&
138 (key_len == *p || p[1+key_len] == '=')) {
139
140 uint8_t s;
141
142 /* Key matches, so let's remove it */
143
144 s = *p;
145 memmove(p, p + 1 + *p, t->size - i - *p -1);
146 t->size -= s + 1;
147
148 found = 1;
149 } else {
150 /* Skip to next */
151
152 i += *p +1;
153 p += *p +1;
154 }
155 }
156
157 return found;
158 }
159
TXTRecordSetValue(TXTRecordRef * txtref,const char * key,uint8_t length,const void * value)160 DNSServiceErrorType DNSSD_API TXTRecordSetValue(
161 TXTRecordRef *txtref,
162 const char *key,
163 uint8_t length,
164 const void *value) {
165
166 TXTRecordInternal *t;
167 uint8_t *p;
168 size_t l, n;
169
170 AVAHI_WARN_LINKAGE;
171
172 assert(key);
173 assert(txtref);
174
175 l = strlen(key);
176
177 if (*key == 0 || strchr(key, '=') || l > 0xFF) /* Empty or invalid key */
178 return kDNSServiceErr_Invalid;
179
180 if (!(t = INTERNAL_PTR(txtref)))
181 return kDNSServiceErr_NoMemory;
182
183 n = l + (value ? length + 1 : 0);
184
185 if (n > 0xFF)
186 return kDNSServiceErr_Invalid;
187
188 if (make_sure_fits_in(t, 1 + n) < 0)
189 return kDNSServiceErr_NoMemory;
190
191 remove_key(t, key);
192
193 p = t->buffer + t->size;
194
195 *(p++) = (uint8_t) n;
196 t->size ++;
197
198 memcpy(p, key, l);
199 p += l;
200 t->size += l;
201
202 if (value) {
203 *(p++) = '=';
204 memcpy(p, value, length);
205 t->size += length + 1;
206 }
207
208 assert(t->size <= t->max_size);
209
210 return kDNSServiceErr_NoError;
211 }
212
TXTRecordRemoveValue(TXTRecordRef * txtref,const char * key)213 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue(TXTRecordRef *txtref, const char *key) {
214 TXTRecordInternal *t;
215 int found;
216
217 AVAHI_WARN_LINKAGE;
218
219 assert(key);
220 assert(txtref);
221
222 if (*key == 0 || strchr(key, '=') || strlen(key) > 0xFF) /* Empty or invalid key */
223 return kDNSServiceErr_Invalid;
224
225 if (!(t = INTERNAL_PTR(txtref)))
226 return kDNSServiceErr_NoError;
227
228 found = remove_key(t, key);
229
230 return found ? kDNSServiceErr_NoError : kDNSServiceErr_NoSuchKey;
231 }
232
TXTRecordGetLength(const TXTRecordRef * txtref)233 uint16_t DNSSD_API TXTRecordGetLength(const TXTRecordRef *txtref) {
234 const TXTRecordInternal *t;
235
236 AVAHI_WARN_LINKAGE;
237
238 assert(txtref);
239
240 if (!(t = INTERNAL_PTR_CONST(txtref)))
241 return 0;
242
243 assert(t->size <= 0xFFFF);
244 return (uint16_t) t->size;
245 }
246
TXTRecordGetBytesPtr(const TXTRecordRef * txtref)247 const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtref) {
248 const TXTRecordInternal *t;
249
250 AVAHI_WARN_LINKAGE;
251
252 assert(txtref);
253
254 if (!(t = INTERNAL_PTR_CONST(txtref)) || !t->buffer)
255 return "";
256
257 return t->buffer;
258 }
259
find_key(const uint8_t * buffer,size_t size,const char * key)260 static const uint8_t *find_key(const uint8_t *buffer, size_t size, const char *key) {
261 size_t i;
262 const uint8_t *p;
263 size_t key_len;
264
265 key_len = strlen(key);
266
267 assert(key_len <= 0xFF);
268
269 p = buffer;
270 i = 0;
271
272 while (i < size) {
273
274 /* Does the item fit in? */
275 if (*p > size - i - 1)
276 return NULL;
277
278 /* Key longer than buffer */
279 if (key_len > size - i - 1)
280 return NULL;
281
282 if (key_len <= *p &&
283 strncmp(key, (const char*) p+1, key_len) == 0 &&
284 (key_len == *p || p[1+key_len] == '=')) {
285
286 /* Key matches, so let's return it */
287
288 return p;
289 }
290
291 /* Skip to next */
292 i += *p +1;
293 p += *p +1;
294 }
295
296 return NULL;
297 }
298
TXTRecordContainsKey(uint16_t size,const void * buffer,const char * key)299 int DNSSD_API TXTRecordContainsKey (
300 uint16_t size,
301 const void *buffer,
302 const char *key) {
303
304 AVAHI_WARN_LINKAGE;
305
306 assert(key);
307
308 if (!size)
309 return 0;
310
311 assert(buffer);
312
313 if (!(find_key(buffer, size, key)))
314 return 0;
315
316 return 1;
317 }
318
TXTRecordGetValuePtr(uint16_t size,const void * buffer,const char * key,uint8_t * value_len)319 const void * DNSSD_API TXTRecordGetValuePtr(
320 uint16_t size,
321 const void *buffer,
322 const char *key,
323 uint8_t *value_len) {
324
325 const uint8_t *p;
326 size_t n, l;
327
328 AVAHI_WARN_LINKAGE;
329
330 assert(key);
331
332 if (!size)
333 goto fail;
334
335 if (*key == 0 || strchr(key, '=') || strlen(key) > 0xFF) /* Empty or invalid key */
336 return NULL;
337
338 assert(buffer);
339
340 if (!(p = find_key(buffer, size, key)))
341 goto fail;
342
343 n = *p;
344 l = strlen(key);
345
346 assert(n >= l);
347 p += 1 + l;
348 n -= l;
349
350 if (n <= 0)
351 goto fail;
352
353 assert(*p == '=');
354 p++;
355 n--;
356
357 if (value_len)
358 *value_len = n;
359
360 return p;
361
362 fail:
363 if (value_len)
364 *value_len = 0;
365
366 return NULL;
367 }
368
369
TXTRecordGetCount(uint16_t size,const void * buffer)370 uint16_t DNSSD_API TXTRecordGetCount(
371 uint16_t size,
372 const void *buffer) {
373
374 const uint8_t *p;
375 unsigned n = 0;
376 size_t i;
377
378 AVAHI_WARN_LINKAGE;
379
380 if (!size)
381 return 0;
382
383 assert(buffer);
384
385 p = buffer;
386 i = 0;
387
388 while (i < size) {
389
390 /* Does the item fit in? */
391 if (*p > size - i - 1)
392 break;
393
394 n++;
395
396 /* Skip to next */
397 i += *p +1;
398 p += *p +1;
399 }
400
401 assert(n <= 0xFFFF);
402
403 return (uint16_t) n;
404 }
405
TXTRecordGetItemAtIndex(uint16_t size,const void * buffer,uint16_t idx,uint16_t key_len,char * key,uint8_t * value_len,const void ** value)406 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex(
407 uint16_t size,
408 const void *buffer,
409 uint16_t idx,
410 uint16_t key_len,
411 char *key,
412 uint8_t *value_len,
413 const void **value) {
414
415 const uint8_t *p;
416 size_t i;
417 unsigned n = 0;
418 DNSServiceErrorType ret = kDNSServiceErr_Invalid;
419
420 AVAHI_WARN_LINKAGE;
421
422 if (!size)
423 goto fail;
424
425 assert(buffer);
426
427 p = buffer;
428 i = 0;
429
430 while (i < size) {
431
432 /* Does the item fit in? */
433 if (*p > size - i - 1)
434 goto fail;
435
436 if (n >= idx) {
437 size_t l;
438 const uint8_t *d;
439
440 d = memchr(p+1, '=', *p);
441
442 /* Length of key */
443 l = d ? d - p - 1 : *p;
444
445 if (key_len < l+1) {
446 ret = kDNSServiceErr_NoMemory;
447 goto fail;
448 }
449
450 strncpy(key, (const char*) p + 1, l);
451 key[l] = 0;
452
453 if (d) {
454 if (value_len)
455 *value_len = *p - l - 1;
456
457 if (value)
458 *value = d + 1;
459 } else {
460
461 if (value_len)
462 *value_len = 0;
463
464 if (value)
465 *value = NULL;
466 }
467
468 return kDNSServiceErr_NoError;
469 }
470
471 n++;
472
473 /* Skip to next */
474 i += *p +1;
475 p += *p +1;
476 }
477
478
479 fail:
480
481 if (value)
482 *value = NULL;
483
484 if (value_len)
485 *value_len = 0;
486
487 return ret;
488
489 }
490