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