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