• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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