]> git.meshlink.io Git - catta/blob - avahi-compat-libdns_sd/txt.c
9d6c11635ac26860120298e29ba6538fe416d3c2
[catta] / avahi-compat-libdns_sd / txt.c
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/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
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
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
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
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
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
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
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
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
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
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
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
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
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 }