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 <string.h>
25 #include <stdarg.h>
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include "strlst.h"
31 #include "malloc.h"
32 #include "defs.h"
33 
avahi_string_list_add_anonymous(AvahiStringList * l,size_t size)34 AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size) {
35     AvahiStringList *n;
36 
37     if (!(n = avahi_malloc(sizeof(AvahiStringList) + size)))
38         return NULL;
39 
40     n->next = l;
41     n->size = size;
42 
43     /* NUL terminate strings, just to make sure */
44     n->text[size] = 0;
45 
46     return n;
47 }
48 
avahi_string_list_add_arbitrary(AvahiStringList * l,const uint8_t * text,size_t size)49 AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t*text, size_t size) {
50     AvahiStringList *n;
51 
52     assert(size == 0 || text);
53 
54     if (!(n = avahi_string_list_add_anonymous(l, size)))
55         return NULL;
56 
57     if (size > 0)
58         memcpy(n->text, text, size);
59 
60     return n;
61 }
62 
avahi_string_list_add(AvahiStringList * l,const char * text)63 AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text) {
64     assert(text);
65 
66     return avahi_string_list_add_arbitrary(l, (const uint8_t*) text, strlen(text));
67 }
68 
avahi_string_list_parse(const void * data,size_t size,AvahiStringList ** ret)69 int avahi_string_list_parse(const void* data, size_t size, AvahiStringList **ret) {
70     const uint8_t *c;
71     AvahiStringList *r = NULL;
72 
73     assert(data);
74     assert(ret);
75 
76     c = data;
77     while (size > 0) {
78         size_t k;
79 
80         k = *(c++);
81         size--;
82 
83         if (k > size)
84             goto fail; /* Overflow */
85 
86         if (k > 0) { /* Ignore empty strings */
87             AvahiStringList *n;
88 
89             if (!(n = avahi_string_list_add_arbitrary(r, c, k)))
90                 goto fail; /* OOM */
91 
92             r = n;
93         }
94 
95         c += k;
96         size -= k;
97     }
98 
99     *ret = r;
100 
101     return 0;
102 
103 fail:
104     avahi_string_list_free(r);
105     return -1;
106 }
107 
avahi_string_list_free(AvahiStringList * l)108 void avahi_string_list_free(AvahiStringList *l) {
109     AvahiStringList *n;
110 
111     while (l) {
112         n = l->next;
113         avahi_free(l);
114         l = n;
115     }
116 }
117 
avahi_string_list_reverse(AvahiStringList * l)118 AvahiStringList* avahi_string_list_reverse(AvahiStringList *l) {
119     AvahiStringList *r = NULL, *n;
120 
121     while (l) {
122         n = l->next;
123         l->next = r;
124         r = l;
125         l = n;
126     }
127 
128     return r;
129 }
130 
131 /**
132  * This routine is used for both human- and machine-readable output of
133  * TXT records. As such it must cope with escaping, in order to allow
134  * machines to reconstruct the original data.
135  *
136  * AFAIK no RFC specifies syntax for TXT data other than raw binary,
137  * though presumably zonefile syntax would make sense:
138  *
139  *   - RFC 1035 says that TXT records contain `<character-string>`s, and section
140  *     5 says:
141  *
142  *       <character-string> is expressed in one or two ways: as a contiguous set
143  *       of characters without interior spaces, or as a string beginning with a "
144  *       and ending with a ".  Inside a " delimited string any character can
145  *       occur, except for a " itself, which must be quoted using \ (back slash).
146  *
147  *     This omits escaping of backslashes (!).
148  *
149  *   - RFC 1034 doesn't say anything relevant.
150  *
151  *   - RFC 1464 suggests a specific encoding of information within a TXT
152  *     record but does not discuss formatting of TXT records in
153  *     general.
154  *
155  * In order to also escape newlines, which interfere with line-by-line
156  * machine processing of records, this routine:
157  *
158  *   - escapes >>> " <<< to >>> \" <<<
159  *   - escapes >>> \ <<< to >>> \\ <<<
160  *   - escapes bytes less than 32 to backslash-prefixed 3-digit DECIMAL form
161  */
avahi_string_list_to_string(AvahiStringList * l)162 char* avahi_string_list_to_string(AvahiStringList *l) {
163     AvahiStringList *n;
164     size_t s = 0;
165     char *p, *t, *e;
166 
167     for (n = l; n; n = n->next) {
168         if (n != l)
169             s ++; /* for the inter-string separating space */
170 
171         for (p = (char*) n->text; ((size_t) (p - (char*) n->text) < n->size); p++) {
172             switch (*p) {
173               case '"':
174               case '\\':
175                   s += 2;
176                   break;
177               default:
178                   if (*p < 32) {
179                       s += 4;
180                   } else {
181                       s ++;
182                       break;
183                   }
184             }
185         }
186         s += 2; /* for the leading and trailing double-quotes */
187     }
188 
189     if (!(t = e = avahi_new(char, s+1))) /* plus one for the trailing NUL */
190         return NULL;
191 
192     l = avahi_string_list_reverse(l);
193 
194     for (n = l; n; n = n->next) {
195         if (n != l)
196             *(e++) = ' ';
197 
198         *(e++) = '"';
199         for (p = (char*) n->text; ((size_t) (p - (char*) n->text) < n->size); p++) {
200             switch (*p) {
201               case '"':
202               case '\\':
203                   *(e++) = '\\';
204                   /* FALL THROUGH */
205               default:
206                   if (*p < 32) {
207                       *(e++) = '\\';
208                       *(e++) = '0' + (char)  ((uint8_t) *p / 100);
209                       *(e++) = '0' + (char) (((uint8_t) *p / 10) % 10);
210                       *(e++) = '0' + (char)  ((uint8_t) *p % 10);
211                   } else {
212                       *(e++) = *p;
213                   }
214             }
215         }
216         *(e++) = '"';
217 
218         assert(e);
219     }
220 
221     l = avahi_string_list_reverse(l);
222 
223     *e = 0;
224 
225     return t;
226 }
227 
avahi_string_list_serialize(AvahiStringList * l,void * data,size_t size)228 size_t avahi_string_list_serialize(AvahiStringList *l, void *data, size_t size) {
229     size_t used = 0;
230 
231     if (data) {
232         AvahiStringList *n;
233         uint8_t *c;
234 
235         l = avahi_string_list_reverse(l);
236         c = data;
237 
238         for (n = l; size > 1 && n; n = n->next) {
239             size_t k;
240 
241             if ((k = n->size) == 0)
242                 /* Skip empty strings */
243                 continue;
244 
245             if (k > 255)
246                 /* Truncate strings at 255 characters */
247                 k = 255;
248 
249             if (k > size-1)
250                 /* Make sure this string fits in */
251                 k = size-1;
252 
253             *(c++) = (uint8_t) k;
254             memcpy(c, n->text, k);
255             c += k;
256 
257             used += 1 + k;
258             size -= 1 + k;
259         }
260 
261         l = avahi_string_list_reverse(l);
262 
263         if (used == 0 && size > 0) {
264 
265             /* Empty lists are treated specially. To comply with
266              * section 6.1 of the DNS-SD spec, we return a single
267              * empty string (i.e. a NUL byte)*/
268 
269             *(uint8_t*) data = 0;
270             used = 1;
271         }
272 
273     } else {
274         AvahiStringList *n;
275 
276         for (n = l; n; n = n->next) {
277             size_t k;
278 
279             if ((k = n->size) == 0)
280                 continue;
281 
282             if (k > 255)
283                 k = 255;
284 
285             used += 1+k;
286         }
287 
288         if (used == 0)
289             used = 1;
290     }
291 
292     return used;
293 }
294 
avahi_string_list_equal(const AvahiStringList * a,const AvahiStringList * b)295 int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b) {
296 
297     for (;;) {
298         if (!a && !b)
299             return 1;
300 
301         if (!a || !b)
302             return 0;
303 
304         if (a->size != b->size)
305             return 0;
306 
307         if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0)
308             return 0;
309 
310         a = a->next;
311         b = b->next;
312     }
313 }
314 
avahi_string_list_add_many(AvahiStringList * r,...)315 AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) {
316     va_list va;
317 
318     va_start(va, r);
319     r = avahi_string_list_add_many_va(r, va);
320     va_end(va);
321 
322     return r;
323 }
324 
avahi_string_list_add_many_va(AvahiStringList * r,va_list va)325 AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) {
326     const char *txt;
327 
328     while ((txt = va_arg(va, const char*)))
329         r = avahi_string_list_add(r, txt);
330 
331     return r;
332 }
333 
avahi_string_list_new(const char * txt,...)334 AvahiStringList *avahi_string_list_new(const char *txt, ...) {
335     va_list va;
336     AvahiStringList *r = NULL;
337 
338     if (txt) {
339         r = avahi_string_list_add(r, txt);
340 
341         va_start(va, txt);
342         r = avahi_string_list_add_many_va(r, va);
343         va_end(va);
344     }
345 
346     return r;
347 }
348 
avahi_string_list_new_va(va_list va)349 AvahiStringList *avahi_string_list_new_va(va_list va) {
350     return avahi_string_list_add_many_va(NULL, va);
351 }
352 
avahi_string_list_copy(const AvahiStringList * l)353 AvahiStringList *avahi_string_list_copy(const AvahiStringList *l) {
354     AvahiStringList *r = NULL;
355 
356     for (; l; l = l->next)
357         if (!(r = avahi_string_list_add_arbitrary(r, l->text, l->size))) {
358             avahi_string_list_free(r);
359             return NULL;
360         }
361 
362     return avahi_string_list_reverse(r);
363 }
364 
avahi_string_list_new_from_array(const char * array[],int length)365 AvahiStringList *avahi_string_list_new_from_array(const char *array[], int length) {
366     AvahiStringList *r = NULL;
367     int i;
368 
369     assert(array);
370 
371     for (i = 0; length >= 0 ? i < length : !!array[i]; i++)
372         r = avahi_string_list_add(r, array[i]);
373 
374     return r;
375 }
376 
avahi_string_list_length(const AvahiStringList * l)377 unsigned avahi_string_list_length(const AvahiStringList *l) {
378     unsigned n = 0;
379 
380     for (; l; l = l->next)
381         n++;
382 
383     return n;
384 }
385 
avahi_string_list_add_vprintf(AvahiStringList * l,const char * format,va_list va)386 AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va) {
387     size_t len = 80;
388     AvahiStringList *r;
389 
390     assert(format);
391 
392     if (!(r = avahi_malloc(sizeof(AvahiStringList) + len)))
393         return NULL;
394 
395     for (;;) {
396         int n;
397         AvahiStringList *nr;
398         va_list va2;
399 
400         va_copy(va2, va);
401         n = vsnprintf((char*) r->text, len, format, va2);
402         va_end(va2);
403 
404         if (n >= 0 && n < (int) len)
405             break;
406 
407         if (n >= 0)
408             len = n+1;
409         else
410             len *= 2;
411 
412         if (!(nr = avahi_realloc(r, sizeof(AvahiStringList) + len))) {
413             avahi_free(r);
414             return NULL;
415         }
416 
417         r = nr;
418     }
419 
420     r->next = l;
421     r->size = strlen((char*) r->text);
422 
423     return r;
424 }
425 
avahi_string_list_add_printf(AvahiStringList * l,const char * format,...)426 AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) {
427     va_list va;
428 
429     assert(format);
430 
431     va_start(va, format);
432     l  = avahi_string_list_add_vprintf(l, format, va);
433     va_end(va);
434 
435     return l;
436 }
437 
avahi_string_list_find(AvahiStringList * l,const char * key)438 AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key) {
439     size_t n;
440 
441     assert(key);
442     n = strlen(key);
443 
444     for (; l; l = l->next) {
445         if (strcasecmp((char*) l->text, key) == 0)
446             return l;
447 
448         if (strncasecmp((char*) l->text, key, n) == 0 && l->text[n] == '=')
449             return l;
450     }
451 
452     return NULL;
453 }
454 
avahi_string_list_add_pair(AvahiStringList * l,const char * key,const char * value)455 AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value) {
456     assert(key);
457 
458     if (value)
459         return avahi_string_list_add_printf(l, "%s=%s", key, value);
460     else
461         return avahi_string_list_add(l, key);
462 }
463 
avahi_string_list_add_pair_arbitrary(AvahiStringList * l,const char * key,const uint8_t * value,size_t size)464 AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size) {
465     size_t n;
466     assert(key);
467 
468     if (!value)
469         return avahi_string_list_add(l, key);
470 
471     n = strlen(key);
472 
473     if (!(l = avahi_string_list_add_anonymous(l, n + 1 + size)))
474         return NULL;
475 
476     memcpy(l->text, key, n);
477     l->text[n] = '=';
478     memcpy(l->text + n + 1, value, size);
479 
480     return l;
481 }
482 
avahi_string_list_get_pair(AvahiStringList * l,char ** key,char ** value,size_t * size)483 int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size) {
484     char *e;
485 
486     assert(l);
487 
488     if (!(e = memchr(l->text, '=', l->size))) {
489 
490         if (key)
491             if (!(*key = avahi_strdup((char*) l->text)))
492                 return -1;
493 
494         if (value)
495             *value = NULL;
496 
497         if (size)
498             *size = 0;
499 
500     } else {
501         size_t n;
502 
503         if (key)
504             if (!(*key = avahi_strndup((char*) l->text, e - (char *) l->text)))
505                 return -1;
506 
507         e++; /* Advance after '=' */
508 
509         n = l->size - (e - (char*) l->text);
510 
511         if (value) {
512 
513             if (!(*value = avahi_memdup(e, n+1))) {
514                 if (key)
515                     avahi_free(*key);
516                 return -1;
517             }
518 
519             (*value)[n] = 0;
520         }
521 
522         if (size)
523             *size = n;
524     }
525 
526     return 0;
527 }
528 
avahi_string_list_get_next(AvahiStringList * l)529 AvahiStringList *avahi_string_list_get_next(AvahiStringList *l) {
530     assert(l);
531     return l->next;
532 }
533 
avahi_string_list_get_text(AvahiStringList * l)534 uint8_t *avahi_string_list_get_text(AvahiStringList *l) {
535     assert(l);
536     return l->text;
537 }
538 
avahi_string_list_get_size(AvahiStringList * l)539 size_t avahi_string_list_get_size(AvahiStringList *l) {
540     assert(l);
541     return l->size;
542 }
543 
avahi_string_list_get_service_cookie(AvahiStringList * l)544 uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l) {
545     AvahiStringList *f;
546     char *value = NULL, *end = NULL;
547     uint32_t ret;
548 
549     if (!(f = avahi_string_list_find(l, AVAHI_SERVICE_COOKIE)))
550         return AVAHI_SERVICE_COOKIE_INVALID;
551 
552     if (avahi_string_list_get_pair(f, NULL, &value, NULL) < 0 || !value)
553         return AVAHI_SERVICE_COOKIE_INVALID;
554 
555     ret = (uint32_t) strtoll(value, &end, 0);
556 
557     if (*value && end && *end != 0) {
558         avahi_free(value);
559         return AVAHI_SERVICE_COOKIE_INVALID;
560     }
561 
562     avahi_free(value);
563 
564     return ret;
565 }
566