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