return count;
}
-int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
- size_t pos = 0, idx = 0;
- _cleanup_strv_free_ char **names = NULL;
+static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
+ _cleanup_free_ char *ret = NULL;
+ size_t n = 0, allocated = 0;
+ const uint8_t *optval = *data;
+ uint16_t optlen = *len;
+ bool first = true;
int r;
if (optlen <= 1)
return -ENODATA;
- if (optval[optlen - 1] != '\0')
- return -EINVAL;
- while (pos < optlen) {
- _cleanup_free_ char *ret = NULL;
- size_t n = 0, allocated = 0;
- bool first = true;
-
- for (;;) {
- const char *label;
- uint8_t c;
+ for (;;) {
+ const char *label;
+ uint8_t c;
- c = optval[pos++];
+ if (optlen == 0)
+ break;
- if (c == 0)
- /* End of name */
- break;
- if (c > 63)
- return -EBADMSG;
+ c = *optval;
+ optval++;
+ optlen--;
- /* Literal label */
- label = (const char *)&optval[pos];
- pos += c;
- if (pos >= optlen)
- return -EMSGSIZE;
+ if (c == 0)
+ /* End label */
+ break;
+ if (c > 63)
+ return -EBADMSG;
+ if (c > optlen)
+ return -EMSGSIZE;
- if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
- return -ENOMEM;
+ /* Literal label */
+ label = (const char *)optval;
+ optval += c;
+ optlen -= c;
- if (first)
- first = false;
- else
- ret[n++] = '.';
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+ return -ENOMEM;
- r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
- if (r < 0)
- return r;
+ if (first)
+ first = false;
+ else
+ ret[n++] = '.';
- n += r;
- }
+ r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
- if (n == 0)
- continue;
+ n += r;
+ }
+ if (n) {
if (!GREEDY_REALLOC(ret, allocated, n + 1))
return -ENOMEM;
-
ret[n] = 0;
+ }
+
+ *out_domain = TAKE_PTR(ret);
+ *data = optval;
+ *len = optlen;
+
+ return n;
+}
+
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) {
+ _cleanup_free_ char *domain = NULL;
+ int r;
+
+ r = parse_domain(&optval, &optlen, &domain);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENODATA;
+ if (optlen != 0)
+ return -EINVAL;
+
+ *str = TAKE_PTR(domain);
+ return 0;
+}
+
+int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
+ size_t idx = 0;
+ _cleanup_strv_free_ char **names = NULL;
+ int r;
+
+ if (optlen <= 1)
+ return -ENODATA;
+ if (optval[optlen - 1] != '\0')
+ return -EINVAL;
+
+ while (optlen > 0) {
+ _cleanup_free_ char *ret = NULL;
+
+ r = parse_domain(&optval, &optlen, &ret);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
r = strv_extend(&names, ret);
if (r < 0)
if (!optlen)
return 0;
- r = dhcp6_option_parse_domainname(optval, optlen, &domains);
+ r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
if (r < 0)
return 0;
break;
case DHCP6_NTP_SUBOPTION_SRV_FQDN:
- r = dhcp6_option_parse_domainname(subval, sublen,
- &servers);
+ r = dhcp6_option_parse_domainname_list(subval, sublen,
+ &servers);
if (r < 0)
return 0;
return -ENOENT;
}
+int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
+ size_t optlen) {
+ int r;
+ char *fqdn;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (optlen < 2)
+ return -ENODATA;
+
+ /* Ignore the flags field, it doesn't carry any useful
+ information for clients. */
+ r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
+ if (r < 0)
+ return r;
+
+ return free_and_replace(lease->fqdn, fqdn);
+}
+
+int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) {
+ assert_return(lease, -EINVAL);
+ assert_return(fqdn, -EINVAL);
+
+ if (lease->fqdn) {
+ *fqdn = lease->fqdn;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
assert(lease);
dhcp6_lease_free_ia(&lease->pd);
free(lease->dns);
+ free(lease->fqdn);
lease->domains = strv_free(lease->domains);
#include "macro.h"
#include "memory-util.h"
#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
#include "tests.h"
#include "time-util.h"
#include "virt.h"
return 0;
}
+static int test_parse_domain(sd_event *e) {
+ uint8_t *data;
+ char *domain;
+ char **list;
+ int r;
+
+ log_debug("/* %s */", __func__);
+
+ data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0 };
+ r = dhcp6_option_parse_domainname(data, 13, &domain);
+ assert_se(r == 0);
+ assert_se(domain);
+ assert_se(streq(domain, "example.com"));
+ free(domain);
+
+ data = (uint8_t []) { 4, 't', 'e', 's', 't' };
+ r = dhcp6_option_parse_domainname(data, 5, &domain);
+ assert_se(r == 0);
+ assert_se(domain);
+ assert_se(streq(domain, "test"));
+ free(domain);
+
+ data = (uint8_t []) { 0 };
+ r = dhcp6_option_parse_domainname(data, 1, &domain);
+ assert_se(r < 0);
+
+ data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
+ 6, 'f', 'o', 'o', 'b', 'a', 'r', 0 };
+ r = dhcp6_option_parse_domainname_list(data, 21, &list);
+ assert_se(r == 2);
+ assert_se(list);
+ assert_se(streq(list[0], "example.com"));
+ assert_se(streq(list[1], "foobar"));
+ strv_free(list);
+
+ data = (uint8_t []) { 1, 'a', 0, 20, 'b', 'c' };
+ r = dhcp6_option_parse_domainname_list(data, 6, &list);
+ assert_se(r < 0);
+
+ data = (uint8_t []) { 0 , 0 };
+ r = dhcp6_option_parse_domainname_list(data, 2, &list);
+ assert_se(r < 0);
+
+ return 0;
+}
+
static int test_option(sd_event *e) {
uint8_t packet[] = {
'F', 'O', 'O',
0x53, 0x00, 0x07, 0x00, 0x01, 0x00
};
-static uint8_t msg_reply[173] = {
+static uint8_t msg_reply[191] = {
0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e,
0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53,
0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01,
0x61, 0x62, 0x05, 0x69, 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, 0x00, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00,
+ 0x0e, 0x01, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61
};
static uint8_t fqdn_wire[16] = {
const struct in6_addr *addrs;
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
char **domains;
+ const char *fqdn;
log_debug("/* %s */", __func__);
assert_se(!strcmp("lab.intra", domains[0]));
assert_se(domains[1] == NULL);
+ assert_se(sd_dhcp6_lease_get_fqdn(lease, &fqdn) >= 0);
+ assert_se(streq(fqdn, "client.intra"));
+
assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
assert_se(!memcmp(addrs, &msg_advertise[124], 16));
test_option_status(e);
test_advertise_option(e);
test_client_solicit(e);
+ test_parse_domain(e);
return 0;
}