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 "avahi-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 
avahi_string_list_to_string(AvahiStringList * l)131 char* avahi_string_list_to_string(AvahiStringList *l) {
132     AvahiStringList *n;
133     size_t s = 0;
134     char *t, *e;
135 
136     for (n = l; n; n = n->next) {
137         if (n != l)
138             s ++;
139 
140         s += n->size+2;
141     }
142 
143     if (!(t = e = avahi_new(char, s+1)))
144         return NULL;
145 
146     l = avahi_string_list_reverse(l);
147 
148     for (n = l; n; n = n->next) {
149         if (n != l)
150             *(e++) = ' ';
151 
152         *(e++) = '"';
153         strncpy(e, (char*) n->text, n->size);
154         e[n->size] = 0;
155         e = strchr(e, 0);
156         *(e++) = '"';
157 
158         assert(e);
159     }
160 
161     l = avahi_string_list_reverse(l);
162 
163     *e = 0;
164 
165     return t;
166 }
167 
avahi_string_list_serialize(AvahiStringList * l,void * data,size_t size)168 size_t avahi_string_list_serialize(AvahiStringList *l, void *data, size_t size) {
169     size_t used = 0;
170 
171     if (data) {
172         AvahiStringList *n;
173         uint8_t *c;
174 
175         l = avahi_string_list_reverse(l);
176         c = data;
177 
178         for (n = l; size > 1 && n; n = n->next) {
179             size_t k;
180 
181             if ((k = n->size) == 0)
182                 /* Skip empty strings */
183                 continue;
184 
185             if (k > 255)
186                 /* Truncate strings at 255 characters */
187                 k = 255;
188 
189             if (k > size-1)
190                 /* Make sure this string fits in */
191                 k = size-1;
192 
193             *(c++) = (uint8_t) k;
194             memcpy(c, n->text, k);
195             c += k;
196 
197             used += 1 + k;
198             size -= 1 + k;
199         }
200 
201         l = avahi_string_list_reverse(l);
202 
203         if (used == 0 && size > 0) {
204 
205             /* Empty lists are treated specially. To comply with
206              * section 6.1 of the DNS-SD spec, we return a single
207              * empty string (i.e. a NUL byte)*/
208 
209             *(uint8_t*) data = 0;
210             used = 1;
211         }
212 
213     } else {
214         AvahiStringList *n;
215 
216         for (n = l; n; n = n->next) {
217             size_t k;
218 
219             if ((k = n->size) == 0)
220                 continue;
221 
222             if (k > 255)
223                 k = 255;
224 
225             used += 1+k;
226         }
227 
228         if (used == 0)
229             used = 1;
230     }
231 
232     return used;
233 }
234 
avahi_string_list_equal(const AvahiStringList * a,const AvahiStringList * b)235 int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b) {
236 
237     for (;;) {
238         if (!a && !b)
239             return 1;
240 
241         if (!a || !b)
242             return 0;
243 
244         if (a->size != b->size)
245             return 0;
246 
247         if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0)
248             return 0;
249 
250         a = a->next;
251         b = b->next;
252     }
253 }
254 
avahi_string_list_add_many(AvahiStringList * r,...)255 AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) {
256     va_list va;
257 
258     va_start(va, r);
259     r = avahi_string_list_add_many_va(r, va);
260     va_end(va);
261 
262     return r;
263 }
264 
avahi_string_list_add_many_va(AvahiStringList * r,va_list va)265 AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) {
266     const char *txt;
267 
268     while ((txt = va_arg(va, const char*)))
269         r = avahi_string_list_add(r, txt);
270 
271     return r;
272 }
273 
avahi_string_list_new(const char * txt,...)274 AvahiStringList *avahi_string_list_new(const char *txt, ...) {
275     va_list va;
276     AvahiStringList *r = NULL;
277 
278     if (txt) {
279         r = avahi_string_list_add(r, txt);
280 
281         va_start(va, txt);
282         r = avahi_string_list_add_many_va(r, va);
283         va_end(va);
284     }
285 
286     return r;
287 }
288 
avahi_string_list_new_va(va_list va)289 AvahiStringList *avahi_string_list_new_va(va_list va) {
290     return avahi_string_list_add_many_va(NULL, va);
291 }
292 
avahi_string_list_copy(const AvahiStringList * l)293 AvahiStringList *avahi_string_list_copy(const AvahiStringList *l) {
294     AvahiStringList *r = NULL;
295 
296     for (; l; l = l->next)
297         if (!(r = avahi_string_list_add_arbitrary(r, l->text, l->size))) {
298             avahi_string_list_free(r);
299             return NULL;
300         }
301 
302     return avahi_string_list_reverse(r);
303 }
304 
avahi_string_list_new_from_array(const char * array[],int length)305 AvahiStringList *avahi_string_list_new_from_array(const char *array[], int length) {
306     AvahiStringList *r = NULL;
307     int i;
308 
309     assert(array);
310 
311     for (i = 0; length >= 0 ? i < length : !!array[i]; i++)
312         r = avahi_string_list_add(r, array[i]);
313 
314     return r;
315 }
316 
avahi_string_list_length(const AvahiStringList * l)317 unsigned avahi_string_list_length(const AvahiStringList *l) {
318     unsigned n = 0;
319 
320     for (; l; l = l->next)
321         n++;
322 
323     return n;
324 }
325 
avahi_string_list_add_vprintf(AvahiStringList * l,const char * format,va_list va)326 AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va) {
327     size_t len = 80;
328     AvahiStringList *r;
329 
330     assert(format);
331 
332     if (!(r = avahi_malloc(sizeof(AvahiStringList) + len)))
333         return NULL;
334 
335     for (;;) {
336         int n;
337         AvahiStringList *nr;
338         va_list va2;
339 
340         va_copy(va2, va);
341         n = vsnprintf((char*) r->text, len, format, va2);
342         va_end(va2);
343 
344         if (n >= 0 && n < (int) len)
345             break;
346 
347         if (n >= 0)
348             len = n+1;
349         else
350             len *= 2;
351 
352         if (!(nr = avahi_realloc(r, sizeof(AvahiStringList) + len))) {
353             avahi_free(r);
354             return NULL;
355         }
356 
357         r = nr;
358     }
359 
360     r->next = l;
361     r->size = strlen((char*) r->text);
362 
363     return r;
364 }
365 
avahi_string_list_add_printf(AvahiStringList * l,const char * format,...)366 AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) {
367     va_list va;
368 
369     assert(format);
370 
371     va_start(va, format);
372     l  = avahi_string_list_add_vprintf(l, format, va);
373     va_end(va);
374 
375     return l;
376 }
377 
avahi_string_list_find(AvahiStringList * l,const char * key)378 AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key) {
379     size_t n;
380 
381     assert(key);
382     n = strlen(key);
383 
384     for (; l; l = l->next) {
385         if (strcasecmp((char*) l->text, key) == 0)
386             return l;
387 
388         if (strncasecmp((char*) l->text, key, n) == 0 && l->text[n] == '=')
389             return l;
390     }
391 
392     return NULL;
393 }
394 
avahi_string_list_add_pair(AvahiStringList * l,const char * key,const char * value)395 AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value) {
396     assert(key);
397 
398     if (value)
399         return avahi_string_list_add_printf(l, "%s=%s", key, value);
400     else
401         return avahi_string_list_add(l, key);
402 }
403 
avahi_string_list_add_pair_arbitrary(AvahiStringList * l,const char * key,const uint8_t * value,size_t size)404 AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size) {
405     size_t n;
406     assert(key);
407 
408     if (!value)
409         return avahi_string_list_add(l, key);
410 
411     n = strlen(key);
412 
413     if (!(l = avahi_string_list_add_anonymous(l, n + 1 + size)))
414         return NULL;
415 
416     memcpy(l->text, key, n);
417     l->text[n] = '=';
418     memcpy(l->text + n + 1, value, size);
419 
420     return l;
421 }
422 
avahi_string_list_get_pair(AvahiStringList * l,char ** key,char ** value,size_t * size)423 int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size) {
424     char *e;
425 
426     assert(l);
427 
428     if (!(e = memchr(l->text, '=', l->size))) {
429 
430         if (key)
431             if (!(*key = avahi_strdup((char*) l->text)))
432                 return -1;
433 
434         if (value)
435             *value = NULL;
436 
437         if (size)
438             *size = 0;
439 
440     } else {
441         size_t n;
442 
443         if (key)
444             if (!(*key = avahi_strndup((char*) l->text, e - (char *) l->text)))
445                 return -1;
446 
447         e++; /* Advance after '=' */
448 
449         n = l->size - (e - (char*) l->text);
450 
451         if (value) {
452 
453             if (!(*value = avahi_memdup(e, n+1))) {
454                 if (key)
455                     avahi_free(*key);
456                 return -1;
457             }
458 
459             (*value)[n] = 0;
460         }
461 
462         if (size)
463             *size = n;
464     }
465 
466     return 0;
467 }
468 
avahi_string_list_get_next(AvahiStringList * l)469 AvahiStringList *avahi_string_list_get_next(AvahiStringList *l) {
470     assert(l);
471     return l->next;
472 }
473 
avahi_string_list_get_text(AvahiStringList * l)474 uint8_t *avahi_string_list_get_text(AvahiStringList *l) {
475     assert(l);
476     return l->text;
477 }
478 
avahi_string_list_get_size(AvahiStringList * l)479 size_t avahi_string_list_get_size(AvahiStringList *l) {
480     assert(l);
481     return l->size;
482 }
483 
avahi_string_list_get_service_cookie(AvahiStringList * l)484 uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l) {
485     AvahiStringList *f;
486     char *value = NULL, *end = NULL;
487     uint32_t ret;
488 
489     if (!(f = avahi_string_list_find(l, AVAHI_SERVICE_COOKIE)))
490         return AVAHI_SERVICE_COOKIE_INVALID;
491 
492     if (avahi_string_list_get_pair(f, NULL, &value, NULL) < 0 || !value)
493         return AVAHI_SERVICE_COOKIE_INVALID;
494 
495     ret = (uint32_t) strtoll(value, &end, 0);
496 
497     if (*value && end && *end != 0) {
498         avahi_free(value);
499         return AVAHI_SERVICE_COOKIE_INVALID;
500     }
501 
502     avahi_free(value);
503 
504     return ret;
505 }
506