Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/libsystemd-network/dhcp6-option.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
/***
3
  Copyright © 2014-2015 Intel Corporation. All rights reserved.
4
***/
5
6
#include <errno.h>
7
#include <netinet/in.h>
8
#include <string.h>
9
10
#include "sd-dhcp6-client.h"
11
12
#include "alloc-util.h"
13
#include "dhcp6-internal.h"
14
#include "dhcp6-lease-internal.h"
15
#include "dhcp6-protocol.h"
16
#include "dns-domain.h"
17
#include "memory-util.h"
18
#include "sparse-endian.h"
19
#include "strv.h"
20
#include "unaligned.h"
21
22
typedef struct DHCP6StatusOption {
23
        struct DHCP6Option option;
24
        be16_t status;
25
        char msg[];
26
} _packed_ DHCP6StatusOption;
27
28
typedef struct DHCP6AddressOption {
29
        struct DHCP6Option option;
30
        struct iaaddr iaaddr;
31
        uint8_t options[];
32
} _packed_ DHCP6AddressOption;
33
34
typedef struct DHCP6PDPrefixOption {
35
        struct DHCP6Option option;
36
        struct iapdprefix iapdprefix;
37
        uint8_t options[];
38
} _packed_ DHCP6PDPrefixOption;
39
40
5.16k
#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
41
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
42
0
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
43
44
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
45
0
                             size_t optlen) {
46
0
        DHCP6Option *option = (DHCP6Option*) *buf;
47
0
48
0
        assert_return(buf, -EINVAL);
49
0
        assert_return(*buf, -EINVAL);
50
0
        assert_return(buflen, -EINVAL);
51
0
52
0
        if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
53
0
                return -ENOBUFS;
54
0
55
0
        option->code = htobe16(optcode);
56
0
        option->len = htobe16(optlen);
57
0
58
0
        *buf += offsetof(DHCP6Option, data);
59
0
        *buflen -= offsetof(DHCP6Option, data);
60
0
61
0
        return 0;
62
0
}
63
64
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
65
0
                        size_t optlen, const void *optval) {
66
0
        int r;
67
0
68
0
        assert_return(optval || optlen == 0, -EINVAL);
69
0
70
0
        r = option_append_hdr(buf, buflen, code, optlen);
71
0
        if (r < 0)
72
0
                return r;
73
0
74
0
        memcpy_safe(*buf, optval, optlen);
75
0
76
0
        *buf += optlen;
77
0
        *buflen -= optlen;
78
0
79
0
        return 0;
80
0
}
81
82
0
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
83
0
        uint16_t len;
84
0
        uint8_t *ia_hdr;
85
0
        size_t iaid_offset, ia_buflen, ia_addrlen = 0;
86
0
        DHCP6Address *addr;
87
0
        int r;
88
0
89
0
        assert_return(buf, -EINVAL);
90
0
        assert_return(*buf, -EINVAL);
91
0
        assert_return(buflen, -EINVAL);
92
0
        assert_return(ia, -EINVAL);
93
0
94
0
        switch (ia->type) {
95
0
        case SD_DHCP6_OPTION_IA_NA:
96
0
                len = DHCP6_OPTION_IA_NA_LEN;
97
0
                iaid_offset = offsetof(DHCP6IA, ia_na);
98
0
                break;
99
0
100
0
        case SD_DHCP6_OPTION_IA_TA:
101
0
                len = DHCP6_OPTION_IA_TA_LEN;
102
0
                iaid_offset = offsetof(DHCP6IA, ia_ta);
103
0
                break;
104
0
105
0
        default:
106
0
                return -EINVAL;
107
0
        }
108
0
109
0
        if (*buflen < offsetof(DHCP6Option, data) + len)
110
0
                return -ENOBUFS;
111
0
112
0
        ia_hdr = *buf;
113
0
        ia_buflen = *buflen;
114
0
115
0
        *buf += offsetof(DHCP6Option, data);
116
0
        *buflen -= offsetof(DHCP6Option, data);
117
0
118
0
        memcpy(*buf, (char*) ia + iaid_offset, len);
119
0
120
0
        *buf += len;
121
0
        *buflen -= len;
122
0
123
0
        LIST_FOREACH(addresses, addr, ia->addresses) {
124
0
                r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
125
0
                                      sizeof(addr->iaaddr));
126
0
                if (r < 0)
127
0
                        return r;
128
0
129
0
                memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
130
0
131
0
                *buf += sizeof(addr->iaaddr);
132
0
                *buflen -= sizeof(addr->iaaddr);
133
0
134
0
                ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr);
135
0
        }
136
0
137
0
        r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
138
0
        if (r < 0)
139
0
                return r;
140
0
141
0
        return 0;
142
0
}
143
144
0
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
145
0
        uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX];
146
0
        int r;
147
0
148
0
        assert_return(buf && *buf && buflen && fqdn, -EINVAL);
149
0
150
0
        buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
151
0
152
0
        /* Store domain name after flags field */
153
0
        r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1,  false);
154
0
        if (r <= 0)
155
0
                return r;
156
0
157
0
        /*
158
0
         * According to RFC 4704, chapter 4.2 only add terminating zero-length
159
0
         * label in case a FQDN is provided. Since dns_name_to_wire_format
160
0
         * always adds terminating zero-length label remove if only a hostname
161
0
         * is provided.
162
0
         */
163
0
        if (dns_name_is_single_label(fqdn))
164
0
                r--;
165
0
166
0
        r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
167
0
168
0
        return r;
169
0
}
170
171
0
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
172
0
        DHCP6Option *option = (DHCP6Option *)buf;
173
0
        size_t i = sizeof(*option) + sizeof(pd->ia_pd);
174
0
        DHCP6Address *prefix;
175
0
176
0
        assert_return(buf, -EINVAL);
177
0
        assert_return(pd, -EINVAL);
178
0
        assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
179
0
180
0
        if (len < i)
181
0
                return -ENOBUFS;
182
0
183
0
        option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
184
0
185
0
        memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
186
0
187
0
        LIST_FOREACH(addresses, prefix, pd->addresses) {
188
0
                DHCP6PDPrefixOption *prefix_opt;
189
0
190
0
                if (len < i + sizeof(*prefix_opt))
191
0
                        return -ENOBUFS;
192
0
193
0
                prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
194
0
                prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
195
0
                prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
196
0
197
0
                memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
198
0
                       sizeof(struct iapdprefix));
199
0
200
0
                i += sizeof(*prefix_opt);
201
0
        }
202
0
203
0
        option->len = htobe16(i - sizeof(*option));
204
0
205
0
        return i;
206
0
}
207
208
5.56k
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
209
5.56k
        DHCP6Option *option = (DHCP6Option*) *buf;
210
5.56k
        uint16_t len;
211
5.56k
212
5.56k
        assert_return(buf, -EINVAL);
213
5.56k
        assert_return(optcode, -EINVAL);
214
5.56k
        assert_return(optlen, -EINVAL);
215
5.56k
216
5.56k
        if (*buflen < offsetof(DHCP6Option, data))
217
5.56k
                return -ENOMSG;
218
4.42k
219
4.42k
        len = be16toh(option->len);
220
4.42k
221
4.42k
        if (len > *buflen)
222
293
                return -ENOMSG;
223
4.13k
224
4.13k
        *optcode = be16toh(option->code);
225
4.13k
        *optlen = len;
226
4.13k
227
4.13k
        *buf += 4;
228
4.13k
        *buflen -= 4;
229
4.13k
230
4.13k
        return 0;
231
4.13k
}
232
233
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
234
5.56k
                       size_t *optlen, uint8_t **optvalue) {
235
5.56k
        int r;
236
5.56k
237
5.56k
        assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
238
5.56k
239
5.56k
        r = option_parse_hdr(buf, buflen, optcode, optlen);
240
5.56k
        if (r < 0)
241
1.43k
                return r;
242
4.13k
243
4.13k
        if (*optlen > *buflen)
244
18
                return -ENOBUFS;
245
4.11k
246
4.11k
        *optvalue = *buf;
247
4.11k
        *buflen -= *optlen;
248
4.11k
        *buf += *optlen;
249
4.11k
250
4.11k
        return 0;
251
4.11k
}
252
253
1.32k
int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
254
1.32k
        DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
255
1.32k
256
1.32k
        if (len < sizeof(DHCP6StatusOption) ||
257
1.32k
            be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
258
26
                return -ENOBUFS;
259
1.30k
260
1.30k
        return be16toh(statusopt->status);
261
1.30k
}
262
263
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
264
1.53k
                                      uint32_t *lifetime_valid) {
265
1.53k
        DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
266
1.53k
        DHCP6Address *addr;
267
1.53k
        uint32_t lt_valid, lt_pref;
268
1.53k
        int r;
269
1.53k
270
1.53k
        if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
271
13
                return -ENOBUFS;
272
1.51k
273
1.51k
        lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
274
1.51k
        lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
275
1.51k
276
1.51k
        if (lt_valid == 0 || lt_pref > lt_valid) {
277
841
                log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
278
841
                                 lt_pref, lt_valid);
279
841
280
841
                return 0;
281
841
        }
282
678
283
678
        if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
284
335
                r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
285
335
                if (r != 0)
286
243
                        return r < 0 ? r: 0;
287
435
        }
288
435
289
435
        addr = new0(DHCP6Address, 1);
290
435
        if (!addr)
291
0
                return -ENOMEM;
292
435
293
435
        LIST_INIT(addresses, addr);
294
435
        memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
295
435
296
435
        LIST_PREPEND(addresses, ia->addresses, addr);
297
435
298
435
        *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
299
435
300
435
        return 0;
301
435
}
302
303
static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
304
1.59k
                                       uint32_t *lifetime_valid) {
305
1.59k
        DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
306
1.59k
        DHCP6Address *prefix;
307
1.59k
        uint32_t lt_valid, lt_pref;
308
1.59k
        int r;
309
1.59k
310
1.59k
        if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
311
11
                return -ENOBUFS;
312
1.58k
313
1.58k
        lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
314
1.58k
        lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
315
1.58k
316
1.58k
        if (lt_valid == 0 || lt_pref > lt_valid) {
317
817
                log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
318
817
                                 lt_pref, lt_valid);
319
817
320
817
                return 0;
321
817
        }
322
765
323
765
        if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
324
468
                r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
325
468
                if (r != 0)
326
232
                        return r < 0 ? r: 0;
327
533
        }
328
533
329
533
        prefix = new0(DHCP6Address, 1);
330
533
        if (!prefix)
331
0
                return -ENOMEM;
332
533
333
533
        LIST_INIT(addresses, prefix);
334
533
        memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
335
533
336
533
        LIST_PREPEND(addresses, ia->addresses, prefix);
337
533
338
533
        *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
339
533
340
533
        return 0;
341
533
}
342
343
5.02k
int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
344
5.02k
        uint16_t iatype, optlen;
345
5.02k
        size_t i, len;
346
5.02k
        int r = 0, status;
347
5.02k
        uint16_t opt;
348
5.02k
        size_t iaaddr_offset;
349
5.02k
        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
350
5.02k
351
5.02k
        assert_return(ia, -EINVAL);
352
5.02k
        assert_return(!ia->addresses, -EINVAL);
353
5.02k
354
5.02k
        iatype = be16toh(iaoption->code);
355
5.02k
        len = be16toh(iaoption->len);
356
5.02k
357
5.02k
        switch (iatype) {
358
5.02k
        case SD_DHCP6_OPTION_IA_NA:
359
2.60k
360
2.60k
                if (len < DHCP6_OPTION_IA_NA_LEN)
361
2.60k
                        return -ENOBUFS;
362
2.55k
363
2.55k
                iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
364
2.55k
                memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
365
2.55k
366
2.55k
                lt_t1 = be32toh(ia->ia_na.lifetime_t1);
367
2.55k
                lt_t2 = be32toh(ia->ia_na.lifetime_t2);
368
2.55k
369
2.55k
                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
370
31
                        log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
371
31
                                         lt_t1, lt_t2);
372
31
                        return -EINVAL;
373
31
                }
374
2.52k
375
2.52k
                break;
376
2.52k
377
2.52k
        case SD_DHCP6_OPTION_IA_PD:
378
2.41k
379
2.41k
                if (len < sizeof(ia->ia_pd))
380
26
                        return -ENOBUFS;
381
2.39k
382
2.39k
                iaaddr_offset = sizeof(ia->ia_pd);
383
2.39k
                memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
384
2.39k
385
2.39k
                lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
386
2.39k
                lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
387
2.39k
388
2.39k
                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
389
35
                        log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
390
35
                                         lt_t1, lt_t2);
391
35
                        return -EINVAL;
392
35
                }
393
2.35k
394
2.35k
                break;
395
2.35k
396
2.35k
        case SD_DHCP6_OPTION_IA_TA:
397
0
                if (len < DHCP6_OPTION_IA_TA_LEN)
398
0
                        return -ENOBUFS;
399
0
400
0
                iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
401
0
                memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
402
0
403
0
                break;
404
0
405
0
        default:
406
0
                return -ENOMSG;
407
4.88k
        }
408
4.88k
409
4.88k
        ia->type = iatype;
410
4.88k
        i = iaaddr_offset;
411
4.88k
412
41.7k
        while (i < len) {
413
37.0k
                DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
414
37.0k
415
37.0k
                if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
416
125
                        return -ENOBUFS;
417
36.8k
418
36.8k
                opt = be16toh(option->code);
419
36.8k
                optlen = be16toh(option->len);
420
36.8k
421
36.8k
                switch (opt) {
422
36.8k
                case SD_DHCP6_OPTION_IAADDR:
423
1.53k
424
1.53k
                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
425
1
                                log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
426
1
                                return -EINVAL;
427
1
                        }
428
1.53k
429
1.53k
                        r = dhcp6_option_parse_address(option, ia, &lt_valid);
430
1.53k
                        if (r < 0)
431
16
                                return r;
432
1.51k
433
1.51k
                        if (lt_valid < lt_min)
434
1.10k
                                lt_min = lt_valid;
435
1.51k
436
1.51k
                        break;
437
1.51k
438
1.59k
                case SD_DHCP6_OPTION_IA_PD_PREFIX:
439
1.59k
440
1.59k
                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
441
1
                                log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
442
1
                                return -EINVAL;
443
1
                        }
444
1.59k
445
1.59k
                        r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
446
1.59k
                        if (r < 0)
447
21
                                return r;
448
1.57k
449
1.57k
                        if (lt_valid < lt_min)
450
920
                                lt_min = lt_valid;
451
1.57k
452
1.57k
                        break;
453
1.57k
454
1.57k
                case SD_DHCP6_OPTION_STATUS_CODE:
455
208
456
208
                        status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
457
208
                        if (status < 0)
458
1
                                return status;
459
207
                        if (status > 0) {
460
13
                                log_dhcp6_client(client, "IA status %d",
461
13
                                                 status);
462
13
463
13
                                return -EINVAL;
464
13
                        }
465
194
466
194
                        break;
467
194
468
33.5k
                default:
469
33.5k
                        log_dhcp6_client(client, "Unknown IA option %d", opt);
470
33.5k
                        break;
471
36.8k
                }
472
36.8k
473
36.8k
                i += sizeof(*option) + optlen;
474
36.8k
        }
475
4.88k
476
4.88k
        switch(iatype) {
477
4.70k
        case SD_DHCP6_OPTION_IA_NA:
478
2.43k
                if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
479
326
                        lt_t1 = lt_min / 2;
480
326
                        lt_t2 = lt_min / 10 * 8;
481
326
                        ia->ia_na.lifetime_t1 = htobe32(lt_t1);
482
326
                        ia->ia_na.lifetime_t2 = htobe32(lt_t2);
483
326
484
326
                        log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
485
326
                                         lt_t1, lt_t2);
486
326
                }
487
2.43k
488
2.43k
                break;
489
4.70k
490
4.70k
        case SD_DHCP6_OPTION_IA_PD:
491
2.27k
                if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
492
420
                        lt_t1 = lt_min / 2;
493
420
                        lt_t2 = lt_min / 10 * 8;
494
420
                        ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
495
420
                        ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
496
420
497
420
                        log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
498
420
                                         lt_t1, lt_t2);
499
420
                }
500
2.27k
501
2.27k
                break;
502
4.70k
503
4.70k
        default:
504
0
                break;
505
4.70k
        }
506
4.70k
507
4.70k
        return 0;
508
4.70k
}
509
510
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
511
                                struct in6_addr **addrs, size_t count,
512
716
                                size_t *allocated) {
513
716
514
716
        if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
515
65
                return -EINVAL;
516
651
517
651
        if (!GREEDY_REALLOC(*addrs, *allocated,
518
651
                            count * sizeof(struct in6_addr) + optlen))
519
651
                return -ENOMEM;
520
651
521
651
        memcpy(*addrs + count, optval, optlen);
522
651
523
651
        count += optlen / sizeof(struct in6_addr);
524
651
525
651
        return count;
526
651
}
527
528
3.06k
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
529
3.06k
        size_t pos = 0, idx = 0;
530
3.06k
        _cleanup_strv_free_ char **names = NULL;
531
3.06k
        int r;
532
3.06k
533
3.06k
        assert_return(optlen > 1, -ENODATA);
534
3.06k
        assert_return(optval[optlen - 1] == '\0', -EINVAL);
535
2.56k
536
188k
        while (pos < optlen) {
537
187k
                _cleanup_free_ char *ret = NULL;
538
187k
                size_t n = 0, allocated = 0;
539
187k
                bool first = true;
540
187k
541
247k
                for (;;) {
542
247k
                        const char *label;
543
247k
                        uint8_t c;
544
247k
545
247k
                        c = optval[pos++];
546
247k
547
247k
                        if (c == 0)
548
186k
                                /* End of name */
549
186k
                                break;
550
61.3k
                        if (c > 63)
551
391
                                return -EBADMSG;
552
60.9k
553
60.9k
                        /* Literal label */
554
60.9k
                        label = (const char *)&optval[pos];
555
60.9k
                        pos += c;
556
60.9k
                        if (pos >= optlen)
557
515
                                return -EMSGSIZE;
558
60.4k
559
60.4k
                        if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
560
60.4k
                                return -ENOMEM;
561
60.4k
562
60.4k
                        if (first)
563
41.0k
                                first = false;
564
60.4k
                        else
565
60.4k
                                ret[n++] = '.';
566
60.4k
567
60.4k
                        r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
568
60.4k
                        if (r < 0)
569
0
                                return r;
570
60.4k
571
60.4k
                        n += r;
572
60.4k
                }
573
187k
574
187k
                if (n == 0)
575
145k
                        continue;
576
40.8k
577
40.8k
                if (!GREEDY_REALLOC(ret, allocated, n + 1))
578
40.8k
                        return -ENOMEM;
579
40.8k
580
40.8k
                ret[n] = 0;
581
40.8k
582
40.8k
                r = strv_extend(&names, ret);
583
40.8k
                if (r < 0)
584
0
                        return r;
585
40.8k
586
40.8k
                idx++;
587
40.8k
        }
588
2.31k
589
2.31k
        *str_arr = TAKE_PTR(names);
590
1.40k
591
1.40k
        return idx;
592
2.31k
}