2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2014-06-19 14:39:20 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
2015-04-02 09:35:30 +02:00
|
|
|
Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <netinet/in.h>
|
2014-06-19 14:39:20 +02:00
|
|
|
#include <string.h>
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
#include "sd-dhcp6-client.h"
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2014-06-19 14:39:20 +02:00
|
|
|
#include "dhcp6-internal.h"
|
|
|
|
#include "dhcp6-protocol.h"
|
2015-05-04 12:23:46 +02:00
|
|
|
#include "dns-domain.h"
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "sparse-endian.h"
|
|
|
|
#include "strv.h"
|
|
|
|
#include "unaligned.h"
|
|
|
|
#include "util.h"
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
#define DHCP6_OPTION_IA_NA_LEN 12
|
|
|
|
#define DHCP6_OPTION_IA_TA_LEN 4
|
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
typedef struct DHCP6Option {
|
|
|
|
be16_t code;
|
|
|
|
be16_t len;
|
|
|
|
uint8_t data[];
|
2014-11-04 20:19:07 +01:00
|
|
|
} _packed_ DHCP6Option;
|
2014-11-01 14:32:28 +01:00
|
|
|
|
2014-06-19 14:39:20 +02:00
|
|
|
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
|
|
|
|
size_t optlen) {
|
2014-11-04 20:19:07 +01:00
|
|
|
DHCP6Option *option = (DHCP6Option*) *buf;
|
2014-11-01 14:32:28 +01:00
|
|
|
|
2014-06-19 14:39:20 +02:00
|
|
|
assert_return(buf, -EINVAL);
|
|
|
|
assert_return(*buf, -EINVAL);
|
|
|
|
assert_return(buflen, -EINVAL);
|
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option))
|
2014-06-19 14:39:20 +02:00
|
|
|
return -ENOBUFS;
|
|
|
|
|
2014-11-04 20:19:07 +01:00
|
|
|
option->code = htobe16(optcode);
|
|
|
|
option->len = htobe16(optlen);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
*buf += sizeof(DHCP6Option);
|
|
|
|
*buflen -= sizeof(DHCP6Option);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
|
|
|
size_t optlen, const void *optval) {
|
|
|
|
int r;
|
|
|
|
|
2014-06-25 15:54:30 +02:00
|
|
|
assert_return(optval || optlen == 0, -EINVAL);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
r = option_append_hdr(buf, buflen, code, optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-02-02 03:57:41 +01:00
|
|
|
memcpy_safe(*buf, optval, optlen);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
*buf += optlen;
|
|
|
|
*buflen -= optlen;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
|
|
|
uint16_t len;
|
2018-01-04 14:11:40 +01:00
|
|
|
be32_t *iaid;
|
2014-06-19 14:39:20 +02:00
|
|
|
uint8_t *ia_hdr;
|
|
|
|
size_t ia_buflen, ia_addrlen = 0;
|
|
|
|
DHCP6Address *addr;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(buf && *buf && buflen && ia, -EINVAL);
|
|
|
|
|
|
|
|
switch (ia->type) {
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_IA_NA:
|
2014-06-19 14:39:20 +02:00
|
|
|
len = DHCP6_OPTION_IA_NA_LEN;
|
2018-01-04 14:11:40 +01:00
|
|
|
iaid = &ia->ia_na.id;
|
2014-06-19 14:39:20 +02:00
|
|
|
break;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_IA_TA:
|
2014-06-19 14:39:20 +02:00
|
|
|
len = DHCP6_OPTION_IA_TA_LEN;
|
2018-01-04 14:11:40 +01:00
|
|
|
iaid = &ia->ia_ta.id;
|
2014-06-19 14:39:20 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*buflen < len)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
ia_hdr = *buf;
|
|
|
|
ia_buflen = *buflen;
|
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
*buf += sizeof(DHCP6Option);
|
|
|
|
*buflen -= sizeof(DHCP6Option);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2018-01-04 14:11:40 +01:00
|
|
|
memcpy(*buf, iaid, len);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
*buf += len;
|
|
|
|
*buflen -= len;
|
|
|
|
|
|
|
|
LIST_FOREACH(addresses, addr, ia->addresses) {
|
2016-01-20 14:44:28 +01:00
|
|
|
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
|
2014-10-08 10:00:07 +02:00
|
|
|
sizeof(addr->iaaddr));
|
2014-06-19 14:39:20 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-10-08 10:00:07 +02:00
|
|
|
memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2014-10-08 10:00:07 +02:00
|
|
|
*buf += sizeof(addr->iaaddr);
|
|
|
|
*buflen -= sizeof(addr->iaaddr);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr);
|
2014-06-19 14:39:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-16 10:07:07 +01:00
|
|
|
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
|
|
|
uint8_t buffer[1 + DNS_WIRE_FOMAT_HOSTNAME_MAX];
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(buf && *buf && buflen && fqdn, -EINVAL);
|
|
|
|
|
|
|
|
buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
|
|
|
|
|
|
|
|
/* Store domain name after flags field */
|
|
|
|
r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false);
|
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to RFC 4704, chapter 4.2 only add terminating zero-length
|
|
|
|
* label in case a FQDN is provided. Since dns_name_to_wire_format
|
|
|
|
* always adds terminating zero-length label remove if only a hostname
|
|
|
|
* is provided.
|
|
|
|
*/
|
|
|
|
if (dns_name_is_single_label(fqdn))
|
|
|
|
r--;
|
|
|
|
|
|
|
|
r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:39 +02:00
|
|
|
|
2014-11-04 20:19:07 +01:00
|
|
|
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
|
|
|
|
DHCP6Option *option = (DHCP6Option*) *buf;
|
2014-06-19 14:39:39 +02:00
|
|
|
uint16_t len;
|
|
|
|
|
|
|
|
assert_return(buf, -EINVAL);
|
2014-11-01 14:32:28 +01:00
|
|
|
assert_return(optcode, -EINVAL);
|
2014-06-19 14:39:39 +02:00
|
|
|
assert_return(optlen, -EINVAL);
|
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
if (*buflen < sizeof(DHCP6Option))
|
2014-06-19 14:39:39 +02:00
|
|
|
return -ENOMSG;
|
|
|
|
|
2014-11-04 20:19:07 +01:00
|
|
|
len = be16toh(option->len);
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
if (len > *buflen)
|
|
|
|
return -ENOMSG;
|
|
|
|
|
2014-11-04 20:19:07 +01:00
|
|
|
*optcode = be16toh(option->code);
|
2014-06-19 14:39:39 +02:00
|
|
|
*optlen = len;
|
|
|
|
|
|
|
|
*buf += 4;
|
|
|
|
*buflen -= 4;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:20 +02:00
|
|
|
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
|
|
|
size_t *optlen, uint8_t **optvalue) {
|
2014-06-19 14:39:39 +02:00
|
|
|
int r;
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2014-06-19 14:39:39 +02:00
|
|
|
assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2014-06-19 14:39:39 +02:00
|
|
|
r = option_parse_hdr(buf, buflen, optcode, optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-06-19 14:39:20 +02:00
|
|
|
|
2014-06-19 14:39:39 +02:00
|
|
|
if (*optlen > *buflen)
|
2014-06-19 14:39:20 +02:00
|
|
|
return -ENOBUFS;
|
|
|
|
|
2014-06-19 14:39:39 +02:00
|
|
|
*optvalue = *buf;
|
|
|
|
*buflen -= *optlen;
|
|
|
|
*buf += *optlen;
|
2014-06-19 14:39:20 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|
|
|
DHCP6IA *ia) {
|
|
|
|
int r;
|
|
|
|
uint16_t opt, status;
|
|
|
|
size_t optlen;
|
|
|
|
size_t iaaddr_offset;
|
|
|
|
DHCP6Address *addr;
|
|
|
|
uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
|
|
|
|
|
|
|
|
assert_return(ia, -EINVAL);
|
|
|
|
assert_return(!ia->addresses, -EINVAL);
|
|
|
|
|
|
|
|
switch (iatype) {
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_IA_NA:
|
2014-06-19 14:39:39 +02:00
|
|
|
|
2014-11-01 14:32:28 +01:00
|
|
|
if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
|
2014-10-08 10:00:07 +02:00
|
|
|
sizeof(addr->iaaddr)) {
|
2014-06-19 14:39:39 +02:00
|
|
|
r = -ENOBUFS;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
|
2018-01-04 14:11:40 +01:00
|
|
|
memcpy(&ia->ia_na, *buf, sizeof(ia->ia_na));
|
2014-06-19 14:39:39 +02:00
|
|
|
|
2018-01-04 14:11:40 +01:00
|
|
|
lt_t1 = be32toh(ia->ia_na.lifetime_t1);
|
|
|
|
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
|
|
|
|
log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
|
|
|
|
lt_t1, lt_t2);
|
|
|
|
r = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_IA_TA:
|
2014-11-01 14:32:28 +01:00
|
|
|
if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
|
2014-10-08 10:00:07 +02:00
|
|
|
sizeof(addr->iaaddr)) {
|
2014-06-19 14:39:39 +02:00
|
|
|
r = -ENOBUFS;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
|
2018-01-04 14:11:40 +01:00
|
|
|
memcpy(&ia->ia_ta.id, *buf, sizeof(ia->ia_ta));
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
r = -ENOMSG;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ia->type = iatype;
|
|
|
|
|
|
|
|
*buflen -= iaaddr_offset;
|
|
|
|
*buf += iaaddr_offset;
|
|
|
|
|
|
|
|
while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
|
|
|
|
|
|
|
|
switch (opt) {
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_IAADDR:
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
addr = new0(DHCP6Address, 1);
|
|
|
|
if (!addr) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_INIT(addresses, addr);
|
|
|
|
|
2014-10-08 10:00:07 +02:00
|
|
|
memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
|
2014-06-19 14:39:39 +02:00
|
|
|
|
2014-10-08 10:00:07 +02:00
|
|
|
lt_valid = be32toh(addr->iaaddr.lifetime_valid);
|
|
|
|
lt_pref = be32toh(addr->iaaddr.lifetime_valid);
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
if (!lt_valid || lt_pref > lt_valid) {
|
|
|
|
log_dhcp6_client(client, "IA preferred %ds > valid %ds",
|
|
|
|
lt_pref, lt_valid);
|
|
|
|
free(addr);
|
|
|
|
} else {
|
|
|
|
LIST_PREPEND(addresses, ia->addresses, addr);
|
|
|
|
if (lt_valid < lt_min)
|
|
|
|
lt_min = lt_valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_STATUS_CODE:
|
2014-06-19 14:39:39 +02:00
|
|
|
if (optlen < sizeof(status))
|
|
|
|
break;
|
|
|
|
|
|
|
|
status = (*buf)[0] << 8 | (*buf)[1];
|
|
|
|
if (status) {
|
|
|
|
log_dhcp6_client(client, "IA status %d",
|
|
|
|
status);
|
|
|
|
r = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
log_dhcp6_client(client, "Unknown IA option %d", opt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*buflen -= optlen;
|
|
|
|
*buf += optlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == -ENOMSG)
|
|
|
|
r = 0;
|
|
|
|
|
2018-01-04 14:11:40 +01:00
|
|
|
if (*buflen)
|
|
|
|
r = -ENOMSG;
|
|
|
|
|
|
|
|
if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
|
2014-06-19 14:39:39 +02:00
|
|
|
lt_t1 = lt_min / 2;
|
|
|
|
lt_t2 = lt_min / 10 * 8;
|
2018-01-04 14:11:40 +01:00
|
|
|
ia->ia_na.lifetime_t1 = htobe32(lt_t1);
|
|
|
|
ia->ia_na.lifetime_t2 = htobe32(lt_t2);
|
2014-06-19 14:39:39 +02:00
|
|
|
|
|
|
|
log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
|
|
|
|
lt_t1, lt_t2);
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
*buf += *buflen;
|
|
|
|
*buflen = 0;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2015-04-02 09:35:30 +02:00
|
|
|
|
|
|
|
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
|
|
|
struct in6_addr **addrs, size_t count,
|
|
|
|
size_t *allocated) {
|
|
|
|
|
|
|
|
if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(*addrs, *allocated,
|
|
|
|
count * sizeof(struct in6_addr) + optlen))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memcpy(*addrs + count, optval, optlen);
|
|
|
|
|
|
|
|
count += optlen / sizeof(struct in6_addr);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
2015-05-04 12:23:46 +02:00
|
|
|
|
2015-08-27 17:48:24 +02:00
|
|
|
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
|
2015-05-04 12:23:46 +02:00
|
|
|
size_t pos = 0, idx = 0;
|
sd-network: fix memleak in dhcp6_option_parse_domainname (#5114)
The simplest way to reproduce:
```diff
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index bd289fa..7b0a5ef 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -168,7 +168,7 @@ static uint8_t msg_advertise[198] = {
0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
- 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
+ 0x01, 0x6c, 0x01, 0x62, 0x00, 0x0a, 0x6e, 0x74,
0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
@@ -338,9 +338,7 @@ static int test_advertise_option(sd_event *e) {
assert_se(!memcmp(addrs, &msg_advertise[124], r * 16));
r = sd_dhcp6_lease_get_domains(lease, &domains);
- assert_se(r == 1);
- assert_se(!strcmp("lab.intra", domains[0]));
- assert_se(domains[1] == NULL);
+ assert_se(r == -ENOENT);
r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs);
assert_se(r == 1);
```
Fixes:
```
=================================================================
==15043==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f13c8564160 in strdup (/lib64/libasan.so.3+0x5a160)
#1 0x7f13c7caaf69 in strv_extend src/basic/strv.c:552
#2 0x55f775787230 in dhcp6_option_parse_domainname src/libsystemd-network/dhcp6-option.c:399
#3 0x55f775788b96 in dhcp6_lease_set_domains src/libsystemd-network/sd-dhcp6-lease.c:225
#4 0x55f775774b95 in test_advertise_option src/libsystemd-network/test-dhcp6-client.c:287
#5 0x55f77577883e in main src/libsystemd-network/test-dhcp6-client.c:759
#6 0x7f13c7589400 in __libc_start_main (/lib64/libc.so.6+0x20400)
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f13c8564160 in strdup (/lib64/libasan.so.3+0x5a160)
#1 0x7f13c7caaf69 in strv_extend src/basic/strv.c:552
#2 0x55f775787230 in dhcp6_option_parse_domainname src/libsystemd-network/dhcp6-option.c:399
#3 0x55f775788b96 in dhcp6_lease_set_domains src/libsystemd-network/sd-dhcp6-lease.c:225
#4 0x55f775781348 in client_parse_message src/libsystemd-network/sd-dhcp6-client.c:807
#5 0x55f775781ba2 in client_receive_advertise src/libsystemd-network/sd-dhcp6-client.c:895
#6 0x55f775782453 in client_receive_message src/libsystemd-network/sd-dhcp6-client.c:994
#7 0x7f13c7e447f4 in source_dispatch src/libsystemd/sd-event/sd-event.c:2268
#8 0x7f13c7e471b0 in sd_event_dispatch src/libsystemd/sd-event/sd-event.c:2627
#9 0x7f13c7e47ab3 in sd_event_run src/libsystemd/sd-event/sd-event.c:2686
#10 0x7f13c7e47c21 in sd_event_loop src/libsystemd/sd-event/sd-event.c:2706
#11 0x55f77577863c in test_client_solicit src/libsystemd-network/test-dhcp6-client.c:737
#12 0x55f77577884b in main src/libsystemd-network/test-dhcp6-client.c:760
#13 0x7f13c7589400 in __libc_start_main (/lib64/libc.so.6+0x20400)
SUMMARY: AddressSanitizer: 8 byte(s) leaked in 2 allocation(s).
```
2017-01-24 03:11:59 +01:00
|
|
|
_cleanup_strv_free_ char **names = NULL;
|
2015-05-04 12:23:46 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(optlen > 1, -ENODATA);
|
2015-10-26 13:07:30 +01:00
|
|
|
assert_return(optval[optlen - 1] == '\0', -EINVAL);
|
2015-05-04 12:23:46 +02:00
|
|
|
|
|
|
|
while (pos < optlen) {
|
|
|
|
_cleanup_free_ char *ret = NULL;
|
|
|
|
size_t n = 0, allocated = 0;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
uint8_t c;
|
|
|
|
|
|
|
|
c = optval[pos++];
|
|
|
|
|
|
|
|
if (c == 0)
|
|
|
|
/* End of name */
|
|
|
|
break;
|
|
|
|
else if (c <= 63) {
|
|
|
|
const char *label;
|
|
|
|
|
|
|
|
/* Literal label */
|
|
|
|
label = (const char *)&optval[pos];
|
|
|
|
pos += c;
|
|
|
|
if (pos > optlen)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
2015-11-25 21:56:48 +01:00
|
|
|
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
|
2015-05-04 12:23:46 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-11-25 21:56:48 +01:00
|
|
|
if (first)
|
2015-05-04 12:23:46 +02:00
|
|
|
first = false;
|
2015-11-25 21:56:48 +01:00
|
|
|
else
|
|
|
|
ret[n++] = '.';
|
|
|
|
|
|
|
|
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
2015-05-04 12:23:46 +02:00
|
|
|
|
|
|
|
n += r;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
r = -EBADMSG;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret[n] = 0;
|
|
|
|
|
|
|
|
r = strv_extend(&names, ret);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*str_arr = names;
|
|
|
|
names = NULL;
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return r;
|
|
|
|
}
|