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