Coverage Report

Created: 2024-09-18 06:34

/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
}