Coverage Report

Created: 2025-12-10 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}