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