]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
update NEWS with even more features for v258
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-lease.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a6cc569e 2/***
810adae9 3 Copyright © 2013 Intel Corporation. All rights reserved.
a6cc569e
TG
4***/
5
07630cea 6#include <arpa/inet.h>
ca78ad1d 7#include <sys/stat.h>
07630cea
LP
8
9#include "sd-dhcp-lease.h"
a6cc569e 10
b5efdb8a 11#include "alloc-util.h"
07630cea 12#include "dhcp-lease-internal.h"
8664ded7 13#include "dhcp-option.h"
1e35e81b 14#include "dns-def.h"
07630cea 15#include "dns-domain.h"
1cf40697 16#include "dns-resolver-internal.h"
686d13b9 17#include "env-file.h"
3ffd4af2 18#include "fd-util.h"
fe8db0c5 19#include "fileio.h"
abcf60e7 20#include "fs-util.h"
b11d6a7b 21#include "hexdecoct.h"
958b66ea 22#include "hostname-util.h"
07630cea 23#include "in-addr-util.h"
8664ded7 24#include "network-common.h"
0339cd77 25#include "network-internal.h"
6bedfcbb 26#include "parse-util.h"
25c33e35 27#include "sort-util.h"
d054f0a4 28#include "stdio-util.h"
b11d6a7b 29#include "string-util.h"
51517f9e 30#include "strv.h"
a93538e8 31#include "time-util.h"
e4de7287 32#include "tmpfile-util.h"
07630cea 33#include "unaligned.h"
a6cc569e 34
905d0ea7
YW
35void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp) {
36 assert(lease);
37
38 if (timestamp && triple_timestamp_is_set(timestamp))
39 lease->timestamp = *timestamp;
40 else
fa5a0251 41 triple_timestamp_now(&lease->timestamp);
905d0ea7
YW
42}
43
a93538e8
YW
44int sd_dhcp_lease_get_timestamp(sd_dhcp_lease *lease, clockid_t clock, uint64_t *ret) {
45 assert_return(lease, -EINVAL);
46 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
47 assert_return(clock_supported(clock), -EOPNOTSUPP);
48 assert_return(ret, -EINVAL);
49
50 if (!triple_timestamp_is_set(&lease->timestamp))
51 return -ENODATA;
52
53 *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
54 return 0;
55}
56
041ea9f9 57int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
a6cc569e
TG
58 assert_return(lease, -EINVAL);
59 assert_return(addr, -EINVAL);
60
0339cd77
LP
61 if (lease->address == 0)
62 return -ENODATA;
63
a6cc569e 64 addr->s_addr = lease->address;
0339cd77
LP
65 return 0;
66}
a6cc569e 67
041ea9f9 68int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
0339cd77
LP
69 assert_return(lease, -EINVAL);
70 assert_return(addr, -EINVAL);
71
72 if (!lease->have_broadcast)
73 return -ENODATA;
74
75 addr->s_addr = lease->broadcast;
a6cc569e
TG
76 return 0;
77}
78
5fde4d37 79int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint64_t *ret) {
68ceb9df 80 assert_return(lease, -EINVAL);
5fde4d37 81 assert_return(ret, -EINVAL);
68ceb9df 82
0339cd77
LP
83 if (lease->lifetime <= 0)
84 return -ENODATA;
85
5fde4d37 86 *ret = lease->lifetime;
0339cd77
LP
87 return 0;
88}
68ceb9df 89
5fde4d37 90int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint64_t *ret) {
0339cd77 91 assert_return(lease, -EINVAL);
5fde4d37 92 assert_return(ret, -EINVAL);
0339cd77
LP
93
94 if (lease->t1 <= 0)
95 return -ENODATA;
96
5fde4d37 97 *ret = lease->t1;
0339cd77
LP
98 return 0;
99}
100
5fde4d37 101int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint64_t *ret) {
0339cd77 102 assert_return(lease, -EINVAL);
5fde4d37 103 assert_return(ret, -EINVAL);
0339cd77
LP
104
105 if (lease->t2 <= 0)
106 return -ENODATA;
107
5fde4d37 108 *ret = lease->t2;
68ceb9df
PF
109 return 0;
110}
111
a93538e8
YW
112#define DEFINE_GET_TIMESTAMP(name) \
113 int sd_dhcp_lease_get_##name##_timestamp( \
114 sd_dhcp_lease *lease, \
115 clockid_t clock, \
116 uint64_t *ret) { \
117 \
118 usec_t t, timestamp; \
119 int r; \
120 \
121 assert_return(ret, -EINVAL); \
122 \
123 r = sd_dhcp_lease_get_##name(lease, &t); \
124 if (r < 0) \
125 return r; \
126 \
127 r = sd_dhcp_lease_get_timestamp(lease, clock, &timestamp); \
128 if (r < 0) \
129 return r; \
130 \
131 *ret = usec_add(t, timestamp); \
132 return 0; \
133 }
134
135DEFINE_GET_TIMESTAMP(lifetime);
136DEFINE_GET_TIMESTAMP(t1);
137DEFINE_GET_TIMESTAMP(t2);
138
041ea9f9 139int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
a6cc569e
TG
140 assert_return(lease, -EINVAL);
141 assert_return(mtu, -EINVAL);
142
0339cd77
LP
143 if (lease->mtu <= 0)
144 return -ENODATA;
a6cc569e 145
0339cd77 146 *mtu = lease->mtu;
a6cc569e
TG
147 return 0;
148}
149
8541db8f 150int sd_dhcp_lease_get_servers(
041ea9f9 151 sd_dhcp_lease *lease,
2324fd3a 152 sd_dhcp_lease_server_type_t what,
8541db8f
ZJS
153 const struct in_addr **addr) {
154
a6cc569e 155 assert_return(lease, -EINVAL);
ddb82ec2
LP
156 assert_return(what >= 0, -EINVAL);
157 assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
a6cc569e 158
ddb82ec2
LP
159 if (lease->servers[what].size <= 0)
160 return -ENODATA;
50018bfa 161
ee5b4834
YW
162 if (addr)
163 *addr = lease->servers[what].addr;
164
ddb82ec2 165 return (int) lease->servers[what].size;
f678ac7e
SS
166}
167
041ea9f9 168int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
ddb82ec2 169 return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
8541db8f 170}
041ea9f9 171int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
ddb82ec2 172 return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
8541db8f 173}
041ea9f9 174int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
ddb82ec2 175 return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
8541db8f 176}
041ea9f9 177int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
ddb82ec2 178 return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
8541db8f 179}
041ea9f9 180int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
ddb82ec2 181 return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
2c649ca1 182}
041ea9f9 183int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
ddb82ec2 184 return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
50018bfa 185}
2c649ca1 186
041ea9f9 187int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
a6cc569e
TG
188 assert_return(lease, -EINVAL);
189 assert_return(domainname, -EINVAL);
190
0339cd77
LP
191 if (!lease->domainname)
192 return -ENODATA;
a6cc569e 193
0339cd77 194 *domainname = lease->domainname;
a6cc569e
TG
195 return 0;
196}
197
041ea9f9 198int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
a6cc569e
TG
199 assert_return(lease, -EINVAL);
200 assert_return(hostname, -EINVAL);
201
0339cd77
LP
202 if (!lease->hostname)
203 return -ENODATA;
a6cc569e 204
0339cd77 205 *hostname = lease->hostname;
a6cc569e
TG
206 return 0;
207}
208
041ea9f9 209int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
ce78df79
TG
210 assert_return(lease, -EINVAL);
211 assert_return(root_path, -EINVAL);
212
0339cd77
LP
213 if (!lease->root_path)
214 return -ENODATA;
ce78df79 215
0339cd77 216 *root_path = lease->root_path;
ce78df79
TG
217 return 0;
218}
219
7040fd38
RP
220int sd_dhcp_lease_get_captive_portal(sd_dhcp_lease *lease, const char **ret) {
221 assert_return(lease, -EINVAL);
222 assert_return(ret, -EINVAL);
223
224 if (!lease->captive_portal)
225 return -ENODATA;
226
227 *ret = lease->captive_portal;
228 return 0;
229}
230
25c33e35
RP
231int sd_dhcp_lease_get_dnr(sd_dhcp_lease *lease, sd_dns_resolver **ret_resolvers) {
232 assert_return(lease, -EINVAL);
233 assert_return(ret_resolvers, -EINVAL);
234
235 if (!lease->dnr)
236 return -ENODATA;
237
238 *ret_resolvers = lease->dnr;
239 return lease->n_dnr;
240}
241
041ea9f9 242int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) {
a6cc569e
TG
243 assert_return(lease, -EINVAL);
244 assert_return(addr, -EINVAL);
245
f8862395 246 if (lease->router_size <= 0)
0339cd77 247 return -ENODATA;
a6cc569e 248
f8862395
TH
249 *addr = lease->router;
250 return (int) lease->router_size;
a6cc569e
TG
251}
252
041ea9f9 253int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
a6cc569e
TG
254 assert_return(lease, -EINVAL);
255 assert_return(addr, -EINVAL);
256
0339cd77
LP
257 if (!lease->have_subnet_mask)
258 return -ENODATA;
a6cc569e 259
0339cd77 260 addr->s_addr = lease->subnet_mask;
a6cc569e
TG
261 return 0;
262}
263
33acdc51
YW
264int sd_dhcp_lease_get_prefix(sd_dhcp_lease *lease, struct in_addr *ret_prefix, uint8_t *ret_prefixlen) {
265 struct in_addr address, netmask;
266 uint8_t prefixlen;
267 int r;
268
269 assert_return(lease, -EINVAL);
270
271 r = sd_dhcp_lease_get_address(lease, &address);
272 if (r < 0)
273 return r;
274
275 r = sd_dhcp_lease_get_netmask(lease, &netmask);
276 if (r < 0)
277 return r;
278
279 prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
280
281 r = in4_addr_mask(&address, prefixlen);
282 if (r < 0)
283 return r;
284
285 if (ret_prefix)
286 *ret_prefix = address;
287 if (ret_prefixlen)
288 *ret_prefixlen = prefixlen;
289 return 0;
290}
291
041ea9f9 292int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
0ad853bc
TG
293 assert_return(lease, -EINVAL);
294 assert_return(addr, -EINVAL);
295
0339cd77
LP
296 if (lease->server_address == 0)
297 return -ENODATA;
0ad853bc 298
0339cd77 299 addr->s_addr = lease->server_address;
0ad853bc
TG
300 return 0;
301}
302
041ea9f9 303int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
8e34a618
TG
304 assert_return(lease, -EINVAL);
305 assert_return(addr, -EINVAL);
306
0339cd77
LP
307 if (lease->next_server == 0)
308 return -ENODATA;
8e34a618 309
0339cd77 310 addr->s_addr = lease->next_server;
8e34a618
TG
311 return 0;
312}
313
f8693fc7
BG
314/*
315 * The returned routes array must be freed by the caller.
316 * Route objects have the same lifetime of the lease and must not be freed.
317 */
32f8a613
YW
318static int dhcp_lease_get_routes(sd_dhcp_route *routes, size_t n_routes, sd_dhcp_route ***ret) {
319 assert(routes || n_routes == 0);
f8693fc7 320
32f8a613 321 if (n_routes <= 0)
0339cd77 322 return -ENODATA;
e1ea665e 323
32f8a613
YW
324 if (ret) {
325 sd_dhcp_route **buf;
f8693fc7 326
32f8a613
YW
327 buf = new(sd_dhcp_route*, n_routes);
328 if (!buf)
329 return -ENOMEM;
330
331 for (size_t i = 0; i < n_routes; i++)
332 buf[i] = &routes[i];
333
334 *ret = buf;
335 }
f8693fc7 336
32f8a613
YW
337 return (int) n_routes;
338}
339
340int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) {
341 assert_return(lease, -EINVAL);
342
343 return dhcp_lease_get_routes(lease->static_routes, lease->n_static_routes, ret);
344}
345
346int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) {
347 assert_return(lease, -EINVAL);
348
349 return dhcp_lease_get_routes(lease->classless_routes, lease->n_classless_routes, ret);
e1ea665e
EY
350}
351
041ea9f9 352int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
da6053d0 353 size_t r;
b85bc551
DW
354
355 assert_return(lease, -EINVAL);
356 assert_return(domains, -EINVAL);
357
358 r = strv_length(lease->search_domains);
359 if (r > 0) {
360 *domains = lease->search_domains;
361 return (int) r;
362 }
363
364 return -ENODATA;
365}
366
dbdcaca3
YW
367int sd_dhcp_lease_get_6rd(
368 sd_dhcp_lease *lease,
369 uint8_t *ret_ipv4masklen,
370 uint8_t *ret_prefixlen,
371 struct in6_addr *ret_prefix,
372 const struct in_addr **ret_br_addresses,
373 size_t *ret_n_br_addresses) {
374
375 assert_return(lease, -EINVAL);
376
377 if (lease->sixrd_n_br_addresses <= 0)
378 return -ENODATA;
379
380 if (ret_ipv4masklen)
381 *ret_ipv4masklen = lease->sixrd_ipv4masklen;
382 if (ret_prefixlen)
383 *ret_prefixlen = lease->sixrd_prefixlen;
384 if (ret_prefix)
385 *ret_prefix = lease->sixrd_prefix;
386 if (ret_br_addresses)
387 *ret_br_addresses = lease->sixrd_br_addresses;
388 if (ret_n_br_addresses)
389 *ret_n_br_addresses = lease->sixrd_n_br_addresses;
390
391 return 0;
392}
393
0ad8d953
YW
394int sd_dhcp_lease_has_6rd(sd_dhcp_lease *lease) {
395 return lease && lease->sixrd_n_br_addresses > 0;
396}
397
041ea9f9 398int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
e43a8393
BG
399 assert_return(lease, -EINVAL);
400 assert_return(data, -EINVAL);
401 assert_return(data_len, -EINVAL);
402
0339cd77
LP
403 if (lease->vendor_specific_len <= 0)
404 return -ENODATA;
e43a8393
BG
405
406 *data = lease->vendor_specific;
407 *data_len = lease->vendor_specific_len;
e43a8393
BG
408 return 0;
409}
410
8301aa0b 411static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
52e3671b 412 struct sd_dhcp_raw_option *option;
3733eec3 413
52e3671b 414 assert(lease);
3733eec3 415
52e3671b 416 while ((option = LIST_POP(options, lease->private_options))) {
3733eec3
LP
417 free(option->data);
418 free(option);
a6cc569e
TG
419 }
420
e2975f85 421 free(lease->root_path);
f8862395 422 free(lease->router);
e2975f85 423 free(lease->timezone);
3733eec3
LP
424 free(lease->hostname);
425 free(lease->domainname);
7040fd38 426 free(lease->captive_portal);
ddb82ec2 427
2324fd3a 428 for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
ddb82ec2
LP
429 free(lease->servers[i].addr);
430
25c33e35 431 dns_resolver_done_many(lease->dnr, lease->n_dnr);
32f8a613
YW
432 free(lease->static_routes);
433 free(lease->classless_routes);
3733eec3 434 free(lease->vendor_specific);
b85bc551 435 strv_free(lease->search_domains);
dbdcaca3 436 free(lease->sixrd_br_addresses);
6b430fdb 437 return mfree(lease);
a6cc569e
TG
438}
439
8301aa0b
YW
440DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_lease, sd_dhcp_lease, dhcp_lease_free);
441
511d96d3 442static int lease_parse_be32_seconds(const uint8_t *option, size_t len, bool max_as_infinity, usec_t *ret) {
e140ae58
TG
443 assert(option);
444 assert(ret);
445
0339cd77
LP
446 if (len != 4)
447 return -EINVAL;
e140ae58 448
511d96d3 449 *ret = unaligned_be32_sec_to_usec(option, max_as_infinity);
5fde4d37
YW
450 return 0;
451}
452
0339cd77 453static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
e140ae58
TG
454 assert(option);
455 assert(ret);
456
0339cd77
LP
457 if (len != 2)
458 return -EINVAL;
e140ae58 459
0339cd77
LP
460 *ret = unaligned_read_be16((be16_t*) option);
461 if (*ret < min)
462 *ret = min;
f5c0c00f 463
0339cd77 464 return 0;
f5c0c00f
TG
465}
466
0339cd77 467static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
f5c0c00f
TG
468 assert(option);
469 assert(ret);
470
0339cd77
LP
471 if (len != 4)
472 return -EINVAL;
f5c0c00f 473
0339cd77
LP
474 memcpy(ret, option, 4);
475 return 0;
f5c0c00f
TG
476}
477
978c6477
LP
478static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
479 _cleanup_free_ char *name = NULL, *normalized = NULL;
480 int r;
481
482 assert(option);
483 assert(ret);
484
930133d5 485 r = dhcp_option_parse_string(option, len, &name);
978c6477
LP
486 if (r < 0)
487 return r;
488 if (!name) {
489 *ret = mfree(*ret);
490 return 0;
491 }
492
7470cc4c 493 r = dns_name_normalize(name, 0, &normalized);
978c6477
LP
494 if (r < 0)
495 return r;
496
497 if (is_localhost(normalized))
498 return -EINVAL;
499
500 if (dns_name_is_root(normalized))
501 return -EINVAL;
502
f9ecfd3b 503 free_and_replace(*ret, normalized);
978c6477
LP
504
505 return 0;
506}
507
7040fd38
RP
508static int lease_parse_captive_portal(const uint8_t *option, size_t len, char **ret) {
509 _cleanup_free_ char *uri = NULL;
510 int r;
511
512 assert(option);
513 assert(ret);
514
515 r = dhcp_option_parse_string(option, len, &uri);
516 if (r < 0)
517 return r;
518 if (uri && !in_charset(uri, URI_VALID))
519 return -EINVAL;
520
521 return free_and_replace(*ret, uri);
522}
523
072320ea 524static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
ddb82ec2 525 assert(option || len == 0);
e140ae58 526 assert(ret);
0339cd77 527 assert(n_ret);
e140ae58 528
0339cd77
LP
529 if (len <= 0) {
530 *ret = mfree(*ret);
531 *n_ret = 0;
532 } else {
533 size_t n_addresses;
e140ae58
TG
534 struct in_addr *addresses;
535
0339cd77
LP
536 if (len % 4 != 0)
537 return -EINVAL;
e140ae58 538
0339cd77
LP
539 n_addresses = len / 4;
540
541 addresses = newdup(struct in_addr, option, n_addresses);
e140ae58
TG
542 if (!addresses)
543 return -ENOMEM;
544
d6f2cd67 545 free_and_replace(*ret, addresses);
0339cd77 546 *n_ret = n_addresses;
e140ae58
TG
547 }
548
549 return 0;
550}
551
299d578f 552static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
ddb82ec2 553 assert(option || len == 0);
299d578f
SS
554 assert(ret);
555 assert(n_ret);
556
ddb82ec2
LP
557 if (len <= 0)
558 return -EINVAL;
299d578f 559
ddb82ec2
LP
560 /* The SIP record is like the other, regular server records, but prefixed with a single "encoding"
561 * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like
562 * the other fields */
299d578f 563
ddb82ec2
LP
564 if (option[0] != 1) { /* We only support IP address encoding for now */
565 *ret = mfree(*ret);
566 *n_ret = 0;
567 return 0;
299d578f
SS
568 }
569
ddb82ec2 570 return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret);
299d578f
SS
571}
572
25c33e35
RP
573static int lease_parse_dns_name(const uint8_t *optval, size_t optlen, char **ret) {
574 _cleanup_free_ char *name = NULL;
575 int r;
576
577 assert(optval);
578 assert(ret);
579
580 r = dns_name_from_wire_format(&optval, &optlen, &name);
581 if (r < 0)
582 return r;
583 if (r == 0 || optlen != 0)
584 return -EBADMSG;
585
586 *ret = TAKE_PTR(name);
587 return r;
588}
589
590static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver **ret_dnr, size_t *ret_n_dnr) {
591 int r;
592 sd_dns_resolver *res_list = NULL;
593 size_t n_resolvers = 0;
594 CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_done_many);
595
596 assert(option || len == 0);
597 assert(ret_dnr);
598
599 _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
600
601 size_t offset = 0;
602 while (offset < len) {
603 /* Instance Data length */
604 if (offset + 2 > len)
605 return -EBADMSG;
606 size_t ilen = unaligned_read_be16(option + offset);
607 if (offset + ilen + 2 > len)
608 return -EBADMSG;
609 offset += 2;
610 size_t iend = offset + ilen;
611
612 /* priority */
613 if (offset + 2 > len)
614 return -EBADMSG;
615 res.priority = unaligned_read_be16(option + offset);
616 offset += 2;
617
618 /* Authenticated Domain Name */
619 if (offset + 1 > len)
620 return -EBADMSG;
621 ilen = option[offset++];
622 if (offset + ilen > iend)
623 return -EBADMSG;
624
625 r = lease_parse_dns_name(option + offset, ilen, &res.auth_name);
626 if (r < 0)
627 return r;
b31b99d7
RP
628 r = dns_name_is_valid_ldh(res.auth_name);
629 if (r < 0)
630 return r;
631 if (!r)
632 return -EBADMSG;
25c33e35
RP
633 if (dns_name_is_root(res.auth_name))
634 return -EBADMSG;
635 offset += ilen;
636
637 /* RFC9463 § 3.1.6: In ADN-only mode, server omits everything after the ADN.
638 * We don't support these, but they are not invalid. */
639 if (offset == iend) {
640 log_debug("Received ADN-only DNRv4 option, ignoring.");
641 sd_dns_resolver_done(&res);
642 continue;
643 }
644
645 /* IPv4 addrs */
646 if (offset + 1 > len)
647 return -EBADMSG;
648 ilen = option[offset++];
649 if (offset + ilen > iend)
650 return -EBADMSG;
651
652 size_t n_addrs;
653 _cleanup_free_ struct in_addr *addrs = NULL;
654 r = lease_parse_in_addrs(option + offset, ilen, &addrs, &n_addrs);
655 if (r < 0)
656 return r;
657 offset += ilen;
658
659 /* RFC9463 § 3.1.8: option MUST include at least one valid IP addr */
660 if (!n_addrs)
661 return -EBADMSG;
662
663 res.addrs = new(union in_addr_union, n_addrs);
664 if (!res.addrs)
665 return -ENOMEM;
666 for (size_t i = 0; i < n_addrs; i++) {
667 union in_addr_union addr = {.in = addrs[i]};
668 /* RFC9463 § 5.2 client MUST discard multicast and host loopback addresses */
669 if (in_addr_is_multicast(AF_INET, &addr) ||
670 in_addr_is_localhost(AF_INET, &addr))
671 return -EBADMSG;
672 res.addrs[i] = addr;
673 }
674 res.n_addrs = n_addrs;
675 res.family = AF_INET;
676
677 /* service params */
678 r = dnr_parse_svc_params(option + offset, iend-offset, &res);
679 if (r < 0)
680 return r;
681 if (r == 0) {
682 /* We can't use this record, but it was not invalid. */
683 log_debug("Received DNRv4 option with unsupported SvcParams, ignoring.");
684 sd_dns_resolver_done(&res);
685 continue;
686 }
687 offset = iend;
688
689 /* Append the latest resolver */
690 if (!GREEDY_REALLOC0(res_list, n_resolvers+1))
691 return -ENOMEM;
692
693 res_list[n_resolvers++] = TAKE_STRUCT(res);
694 }
695
696 typesafe_qsort(*ret_dnr, *ret_n_dnr, dns_resolver_prio_compare);
697
698 dns_resolver_done_many(*ret_dnr, *ret_n_dnr);
699 *ret_dnr = TAKE_PTR(res_list);
700 *ret_n_dnr = n_resolvers;
701
702 return n_resolvers;
703}
704
32f8a613 705static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
7b868543 706 int r;
e1ea665e 707
32f8a613 708 assert(lease);
0339cd77 709 assert(option || len <= 0);
e1ea665e 710
e1ea665e
EY
711 if (len % 8 != 0)
712 return -EINVAL;
713
e1ea665e 714 while (len >= 8) {
7b868543
YW
715 struct in_addr dst, gw;
716 uint8_t prefixlen;
e1ea665e 717
7b868543 718 assert_se(lease_parse_be32(option, 4, &dst.s_addr) >= 0);
e1ea665e
EY
719 option += 4;
720
7b868543 721 assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0);
e1ea665e
EY
722 option += 4;
723
724 len -= 8;
7b868543
YW
725
726 r = in4_addr_default_prefixlen(&dst, &prefixlen);
32f8a613
YW
727 if (r < 0) {
728 log_debug("sd-dhcp-lease: cannot determine class of received static route, ignoring.");
729 continue;
730 }
7b868543
YW
731
732 (void) in4_addr_mask(&dst, prefixlen);
733
32f8a613 734 if (!GREEDY_REALLOC(lease->static_routes, lease->n_static_routes + 1))
7b868543
YW
735 return -ENOMEM;
736
32f8a613 737 lease->static_routes[lease->n_static_routes++] = (struct sd_dhcp_route) {
7b868543
YW
738 .dst_addr = dst,
739 .gw_addr = gw,
740 .dst_prefixlen = prefixlen,
7b868543 741 };
e1ea665e
EY
742 }
743
744 return 0;
745}
746
747/* parses RFC3442 Classless Static Route Option */
32f8a613
YW
748static int lease_parse_classless_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
749 assert(lease);
0339cd77 750 assert(option || len <= 0);
e1ea665e 751
32f8a613 752 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip) */
e1ea665e
EY
753
754 while (len > 0) {
32f8a613
YW
755 uint8_t prefixlen, dst_octets;
756 struct in_addr dst = {}, gw;
e1ea665e 757
32f8a613 758 prefixlen = *option;
e1ea665e
EY
759 option++;
760 len--;
761
32f8a613
YW
762 dst_octets = DIV_ROUND_UP(prefixlen, 8);
763
e1ea665e
EY
764 /* can't have more than 4 octets in IPv4 */
765 if (dst_octets > 4 || len < dst_octets)
766 return -EINVAL;
767
32f8a613 768 memcpy(&dst, option, dst_octets);
e1ea665e
EY
769 option += dst_octets;
770 len -= dst_octets;
771
772 if (len < 4)
773 return -EINVAL;
774
32f8a613 775 assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0);
e1ea665e
EY
776 option += 4;
777 len -= 4;
778
32f8a613
YW
779 if (!GREEDY_REALLOC(lease->classless_routes, lease->n_classless_routes + 1))
780 return -ENOMEM;
781
782 lease->classless_routes[lease->n_classless_routes++] = (struct sd_dhcp_route) {
783 .dst_addr = dst,
784 .gw_addr = gw,
785 .dst_prefixlen = prefixlen,
786 };
e1ea665e
EY
787 }
788
789 return 0;
790}
791
dbdcaca3
YW
792static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
793 uint8_t ipv4masklen, prefixlen;
794 struct in6_addr prefix;
795 _cleanup_free_ struct in_addr *br_addresses = NULL;
796 size_t n_br_addresses;
797
798 assert(lease);
799 assert(option);
800
801 /* See RFC 5969 Section 7.1.1 */
802
803 if (lease->sixrd_n_br_addresses > 0)
804 /* Multiple 6rd option?? */
805 return -EINVAL;
806
807 /* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
808 if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
809 (len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
810 return -EINVAL;
811
812 /* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
813 * within a given 6rd domain. This may be any value between 0 and 32. Any value
814 * greater than 32 is invalid. */
815 ipv4masklen = option[0];
816 if (ipv4masklen > 32)
817 return -EINVAL;
818
819 /* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
820 * purpose of bounds checking by DHCP option processing, the sum of
821 * (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
822 prefixlen = option[1];
823 if (32 - ipv4masklen + prefixlen > 128)
824 return -EINVAL;
825
826 /* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
827 * The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
828 * MUST be initialized to zero by the sender and ignored by the receiver. */
829 memcpy(&prefix, option + 2, sizeof(struct in6_addr));
830 (void) in6_addr_mask(&prefix, prefixlen);
831
0923b425 832 /* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relays for a given 6rd domain. */
dbdcaca3
YW
833 n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
834 br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
835 if (!br_addresses)
836 return -ENOMEM;
837
838 lease->sixrd_ipv4masklen = ipv4masklen;
839 lease->sixrd_prefixlen = prefixlen;
840 lease->sixrd_prefix = prefix;
841 lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
842 lease->sixrd_n_br_addresses = n_br_addresses;
843
844 return 0;
845}
846
e4735228 847int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
99534007 848 sd_dhcp_lease *lease = ASSERT_PTR(userdata);
e140ae58
TG
849 int r;
850
79893116 851 switch (code) {
a6cc569e 852
22805d92 853 case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
511d96d3 854 r = lease_parse_be32_seconds(option, len, /* max_as_infinity = */ true, &lease->lifetime);
0339cd77
LP
855 if (r < 0)
856 log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
857
a6cc569e
TG
858 break;
859
22805d92 860 case SD_DHCP_OPTION_SERVER_IDENTIFIER:
0339cd77
LP
861 r = lease_parse_be32(option, len, &lease->server_address);
862 if (r < 0)
863 log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
864
a6cc569e
TG
865 break;
866
22805d92 867 case SD_DHCP_OPTION_SUBNET_MASK:
0339cd77
LP
868 r = lease_parse_be32(option, len, &lease->subnet_mask);
869 if (r < 0)
870 log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
871 else
872 lease->have_subnet_mask = true;
a6cc569e
TG
873 break;
874
22805d92 875 case SD_DHCP_OPTION_BROADCAST:
0339cd77
LP
876 r = lease_parse_be32(option, len, &lease->broadcast);
877 if (r < 0)
878 log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
879 else
880 lease->have_broadcast = true;
f5c0c00f
TG
881 break;
882
22805d92 883 case SD_DHCP_OPTION_ROUTER:
072320ea 884 r = lease_parse_in_addrs(option, len, &lease->router, &lease->router_size);
f8862395
TH
885 if (r < 0)
886 log_debug_errno(r, "Failed to parse router addresses, ignoring: %m");
a6cc569e
TG
887 break;
888
808b65a0
RP
889 case SD_DHCP_OPTION_RAPID_COMMIT:
890 if (len > 0)
d19434fb 891 log_debug("Invalid DHCP Rapid Commit option, ignoring.");
808b65a0
RP
892 lease->rapid_commit = true;
893 break;
894
22805d92 895 case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
ddb82ec2 896 r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
0339cd77
LP
897 if (r < 0)
898 log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
a6cc569e
TG
899 break;
900
22805d92 901 case SD_DHCP_OPTION_NTP_SERVER:
ddb82ec2 902 r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size);
0339cd77
LP
903 if (r < 0)
904 log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
f5c0c00f
TG
905 break;
906
299d578f 907 case SD_DHCP_OPTION_SIP_SERVER:
ddb82ec2 908 r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size);
299d578f
SS
909 if (r < 0)
910 log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
911 break;
912
f678ac7e 913 case SD_DHCP_OPTION_POP3_SERVER:
ddb82ec2 914 r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size);
f678ac7e
SS
915 if (r < 0)
916 log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m");
917 break;
918
2c649ca1 919 case SD_DHCP_OPTION_SMTP_SERVER:
ddb82ec2 920 r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size);
2c649ca1
SS
921 if (r < 0)
922 log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m");
923 break;
924
50018bfa 925 case SD_DHCP_OPTION_LPR_SERVER:
ddb82ec2 926 r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size);
50018bfa
SS
927 if (r < 0)
928 log_debug_errno(r, "Failed to parse LPR server, ignoring: %m");
929 break;
930
7040fd38
RP
931 case SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL:
932 r = lease_parse_captive_portal(option, len, &lease->captive_portal);
933 if (r < 0)
934 log_debug_errno(r, "Failed to parse captive portal, ignoring: %m");
935 break;
936
22805d92 937 case SD_DHCP_OPTION_STATIC_ROUTE:
32f8a613 938 r = lease_parse_static_routes(lease, option, len);
0339cd77
LP
939 if (r < 0)
940 log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
f5c0c00f
TG
941 break;
942
3cb62efe 943 case SD_DHCP_OPTION_MTU_INTERFACE:
0339cd77
LP
944 r = lease_parse_u16(option, len, &lease->mtu, 68);
945 if (r < 0)
946 log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
4473cd7f
YW
947 if (lease->mtu < DHCP_MIN_PACKET_SIZE) {
948 log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_MIN_PACKET_SIZE);
949 lease->mtu = DHCP_MIN_PACKET_SIZE;
955d99ed
MG
950 }
951
f5c0c00f
TG
952 break;
953
978c6477
LP
954 case SD_DHCP_OPTION_DOMAIN_NAME:
955 r = lease_parse_domain(option, len, &lease->domainname);
0339cd77
LP
956 if (r < 0) {
957 log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
958 return 0;
959 }
a6cc569e 960
784d9b9c 961 break;
784d9b9c 962
3cb62efe 963 case SD_DHCP_OPTION_DOMAIN_SEARCH:
b85bc551
DW
964 r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains);
965 if (r < 0)
966 log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m");
967 break;
968
978c6477
LP
969 case SD_DHCP_OPTION_HOST_NAME:
970 r = lease_parse_domain(option, len, &lease->hostname);
0339cd77 971 if (r < 0) {
38b38500 972 log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
0339cd77
LP
973 return 0;
974 }
a6cc569e 975
784d9b9c 976 break;
ce78df79 977
fa3357b9
YW
978 case SD_DHCP_OPTION_ROOT_PATH: {
979 _cleanup_free_ char *p = NULL;
980
981 r = dhcp_option_parse_string(option, len, &p);
0339cd77
LP
982 if (r < 0)
983 log_debug_errno(r, "Failed to parse root path, ignoring: %m");
ce78df79 984
fa3357b9
YW
985 free_and_replace(lease->root_path, p);
986 break;
987 }
3cb62efe 988 case SD_DHCP_OPTION_RENEWAL_TIME:
511d96d3 989 r = lease_parse_be32_seconds(option, len, /* max_as_infinity = */ true, &lease->t1);
0339cd77
LP
990 if (r < 0)
991 log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
a6cc569e
TG
992 break;
993
3cb62efe 994 case SD_DHCP_OPTION_REBINDING_TIME:
511d96d3 995 r = lease_parse_be32_seconds(option, len, /* max_as_infinity = */ true, &lease->t2);
0339cd77
LP
996 if (r < 0)
997 log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
a6cc569e 998 break;
e1ea665e 999
22805d92 1000 case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
32f8a613 1001 r = lease_parse_classless_routes(lease, option, len);
0339cd77
LP
1002 if (r < 0)
1003 log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
1004 break;
e43a8393 1005
3cb62efe 1006 case SD_DHCP_OPTION_TZDB_TIMEZONE: {
8eb9058d
LP
1007 _cleanup_free_ char *tz = NULL;
1008
930133d5 1009 r = dhcp_option_parse_string(option, len, &tz);
0339cd77
LP
1010 if (r < 0) {
1011 log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
1012 return 0;
1013 }
8eb9058d 1014
089fb865 1015 if (!timezone_is_valid(tz, LOG_DEBUG)) {
990d0aa9 1016 log_debug("Timezone is not valid, ignoring.");
0339cd77
LP
1017 return 0;
1018 }
8eb9058d 1019
f9ecfd3b 1020 free_and_replace(lease->timezone, tz);
0339cd77 1021
8eb9058d
LP
1022 break;
1023 }
1024
25c33e35
RP
1025 case SD_DHCP_OPTION_V4_DNR:
1026 r = lease_parse_dnr(option, len, &lease->dnr, &lease->n_dnr);
1027 if (r < 0) {
1028 log_debug_errno(r, "Failed to parse network-designated resolvers, ignoring: %m");
1029 return 0;
1030 }
1031
1032 break;
1033
22805d92 1034 case SD_DHCP_OPTION_VENDOR_SPECIFIC:
2d03c0b8 1035
0339cd77
LP
1036 if (len <= 0)
1037 lease->vendor_specific = mfree(lease->vendor_specific);
1038 else {
1039 void *p;
1040
1041 p = memdup(option, len);
1042 if (!p)
e43a8393 1043 return -ENOMEM;
e43a8393 1044
d6f2cd67 1045 free_and_replace(lease->vendor_specific, p);
0339cd77 1046 }
7e753d9d 1047
0339cd77
LP
1048 lease->vendor_specific_len = len;
1049 break;
7e753d9d 1050
dbdcaca3
YW
1051 case SD_DHCP_OPTION_6RD:
1052 r = lease_parse_6rd(lease, option, len);
1053 if (r < 0)
1054 log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
1055 break;
1056
a91b888f
YW
1057 case SD_DHCP_OPTION_IPV6_ONLY_PREFERRED:
1058 r = lease_parse_be32_seconds(option, len, /* max_as_infinity = */ false, &lease->ipv6_only_preferred_usec);
1059 if (r < 0)
1060 log_debug_errno(r, "Failed to parse IPv6 only preferred option, ignoring: %m");
1061
1062 else if (lease->ipv6_only_preferred_usec < MIN_V6ONLY_WAIT_USEC &&
1063 !network_test_mode_enabled())
1064 lease->ipv6_only_preferred_usec = MIN_V6ONLY_WAIT_USEC;
1065 break;
1066
22805d92 1067 case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
7e753d9d
AC
1068 r = dhcp_lease_insert_private_option(lease, code, option, len);
1069 if (r < 0)
1070 return r;
0339cd77
LP
1071
1072 break;
1073
1074 default:
3857d367 1075 log_debug("Ignoring DHCP option %"PRIu8" while parsing.", code);
a6cc569e
TG
1076 }
1077
1078 return 0;
1079}
1080
b85bc551
DW
1081/* Parses compressed domain names. */
1082int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) {
1083 _cleanup_strv_free_ char **names = NULL;
1084 size_t pos = 0, cnt = 0;
1085 int r;
1086
1087 assert(domains);
c0b927eb
YW
1088 assert(option || len == 0);
1089
1090 if (len == 0)
1091 return -EBADMSG;
b85bc551
DW
1092
1093 while (pos < len) {
1094 _cleanup_free_ char *name = NULL;
319a4f4b 1095 size_t n = 0;
b85bc551
DW
1096 size_t jump_barrier = pos, next_chunk = 0;
1097 bool first = true;
1098
1099 for (;;) {
1100 uint8_t c;
1101 c = option[pos++];
1102
1103 if (c == 0) {
1104 /* End of name */
1105 break;
1106 } else if (c <= 63) {
1107 const char *label;
1108
1109 /* Literal label */
1110 label = (const char*) (option + pos);
1111 pos += c;
1112 if (pos >= len)
1113 return -EBADMSG;
1114
319a4f4b 1115 if (!GREEDY_REALLOC(name, n + !first + DNS_LABEL_ESCAPED_MAX))
b85bc551
DW
1116 return -ENOMEM;
1117
1118 if (first)
1119 first = false;
1120 else
1121 name[n++] = '.';
1122
1123 r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
1124 if (r < 0)
1125 return r;
1126
1127 n += r;
1d6cc5d0 1128 } else if (FLAGS_SET(c, 0xc0)) {
b85bc551
DW
1129 /* Pointer */
1130
1131 uint8_t d;
1132 uint16_t ptr;
1133
1134 if (pos >= len)
1135 return -EBADMSG;
1136
1137 d = option[pos++];
1138 ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
1139
1140 /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
1141 if (ptr >= jump_barrier)
1142 return -EBADMSG;
1143 jump_barrier = ptr;
1144
1145 /* Save current location so we don't end up re-parsing what's parsed so far. */
1146 if (next_chunk == 0)
1147 next_chunk = pos;
1148
1149 pos = ptr;
1150 } else
1151 return -EBADMSG;
1152 }
1153
319a4f4b 1154 if (!GREEDY_REALLOC(name, n + 1))
b85bc551
DW
1155 return -ENOMEM;
1156 name[n] = 0;
1157
1158 r = strv_extend(&names, name);
1159 if (r < 0)
1160 return r;
1161
1162 cnt++;
1163
1164 if (next_chunk != 0)
1165 pos = next_chunk;
1166 }
1167
9591c0a8 1168 strv_free_and_replace(*domains, names);
b85bc551
DW
1169
1170 return cnt;
1171}
1172
0339cd77 1173int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
03677889 1174 struct sd_dhcp_raw_option *option, *before = NULL;
7e753d9d 1175
0339cd77
LP
1176 assert(lease);
1177
7e753d9d 1178 LIST_FOREACH(options, cur, lease->private_options) {
03677889
YW
1179 if (tag < cur->tag) {
1180 before = cur;
7e753d9d 1181 break;
03677889 1182 }
0339cd77
LP
1183 if (tag == cur->tag) {
1184 log_debug("Ignoring duplicate option, tagged %i.", tag);
7e753d9d
AC
1185 return 0;
1186 }
1187 }
1188
1189 option = new(struct sd_dhcp_raw_option, 1);
1190 if (!option)
1191 return -ENOMEM;
1192
1193 option->tag = tag;
1194 option->length = len;
1195 option->data = memdup(data, len);
1196 if (!option->data) {
1197 free(option);
1198 return -ENOMEM;
1199 }
1200
03677889 1201 LIST_INSERT_BEFORE(options, lease->private_options, before, option);
7e753d9d
AC
1202 return 0;
1203}
1204
a6cc569e 1205int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 1206 sd_dhcp_lease *lease;
a6cc569e
TG
1207
1208 lease = new0(sd_dhcp_lease, 1);
1209 if (!lease)
1210 return -ENOMEM;
1211
3733eec3 1212 lease->n_ref = 1;
a6cc569e
TG
1213
1214 *ret = lease;
a6cc569e
TG
1215 return 0;
1216}
fe8db0c5 1217
041ea9f9 1218int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
ab7153b3 1219 _cleanup_(unlink_and_freep) char *temp_path = NULL;
fe8db0c5 1220 _cleanup_fclose_ FILE *f = NULL;
fe8db0c5 1221 struct in_addr address;
a2ba62c7 1222 const struct in_addr *addresses;
d59a2b3b
YW
1223 const void *data;
1224 size_t data_len;
fe8db0c5
TG
1225 const char *string;
1226 uint16_t mtu;
f8693fc7 1227 _cleanup_free_ sd_dhcp_route **routes = NULL;
32f8a613 1228 char **search_domains;
5fde4d37 1229 usec_t t;
fe8db0c5
TG
1230 int r;
1231
1232 assert(lease);
1233 assert(lease_file);
1234
fe8db0c5
TG
1235 r = fopen_temporary(lease_file, &f, &temp_path);
1236 if (r < 0)
ab7153b3 1237 return r;
fe8db0c5 1238
0d536673 1239 (void) fchmod(fileno(f), 0644);
fe8db0c5 1240
fe8db0c5 1241 fprintf(f,
0339cd77 1242 "# This is private data. Do not parse.\n");
fe8db0c5 1243
0339cd77
LP
1244 r = sd_dhcp_lease_get_address(lease, &address);
1245 if (r >= 0)
071e522e 1246 fprintf(f, "ADDRESS=%s\n", IN4_ADDR_TO_STRING(&address));
fe8db0c5 1247
0339cd77
LP
1248 r = sd_dhcp_lease_get_netmask(lease, &address);
1249 if (r >= 0)
071e522e 1250 fprintf(f, "NETMASK=%s\n", IN4_ADDR_TO_STRING(&address));
fe8db0c5 1251
f8862395
TH
1252 r = sd_dhcp_lease_get_router(lease, &addresses);
1253 if (r > 0) {
1254 fputs("ROUTER=", f);
2cc697d7 1255 serialize_in_addrs(f, addresses, r, NULL, NULL);
f8862395
TH
1256 fputc('\n', f);
1257 }
8ddbeaa2 1258
0ad853bc 1259 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb 1260 if (r >= 0)
071e522e 1261 fprintf(f, "SERVER_ADDRESS=%s\n", IN4_ADDR_TO_STRING(&address));
0ad853bc 1262
8e34a618 1263 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb 1264 if (r >= 0)
071e522e 1265 fprintf(f, "NEXT_SERVER=%s\n", IN4_ADDR_TO_STRING(&address));
8e34a618 1266
0339cd77
LP
1267 r = sd_dhcp_lease_get_broadcast(lease, &address);
1268 if (r >= 0)
071e522e 1269 fprintf(f, "BROADCAST=%s\n", IN4_ADDR_TO_STRING(&address));
0339cd77 1270
fe8db0c5
TG
1271 r = sd_dhcp_lease_get_mtu(lease, &mtu);
1272 if (r >= 0)
1273 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
1274
5fde4d37 1275 r = sd_dhcp_lease_get_t1(lease, &t);
0339cd77 1276 if (r >= 0)
5fde4d37 1277 fprintf(f, "T1=%s\n", FORMAT_TIMESPAN(t, USEC_PER_SEC));
0339cd77 1278
5fde4d37 1279 r = sd_dhcp_lease_get_t2(lease, &t);
109731eb 1280 if (r >= 0)
5fde4d37 1281 fprintf(f, "T2=%s\n", FORMAT_TIMESPAN(t, USEC_PER_SEC));
0339cd77 1282
5fde4d37 1283 r = sd_dhcp_lease_get_lifetime(lease, &t);
0339cd77 1284 if (r >= 0)
5fde4d37 1285 fprintf(f, "LIFETIME=%s\n", FORMAT_TIMESPAN(t, USEC_PER_SEC));
0339cd77
LP
1286
1287 r = sd_dhcp_lease_get_dns(lease, &addresses);
1288 if (r > 0) {
0d536673 1289 fputs("DNS=", f);
2cc697d7 1290 serialize_in_addrs(f, addresses, r, NULL, NULL);
f8862395 1291 fputc('\n', f);
0339cd77 1292 }
109731eb 1293
3fd6708c
RP
1294 sd_dns_resolver *resolvers;
1295 r = sd_dhcp_lease_get_dnr(lease, &resolvers);
1296 if (r > 0) {
1297 fputs("DNR=", f);
1298 serialize_dnr(f, resolvers, r, NULL);
1299 fputc('\n', f);
1300 }
1301
a2ba62c7 1302 r = sd_dhcp_lease_get_ntp(lease, &addresses);
0339cd77 1303 if (r > 0) {
0d536673 1304 fputs("NTP=", f);
2cc697d7 1305 serialize_in_addrs(f, addresses, r, NULL, NULL);
f8862395 1306 fputc('\n', f);
0339cd77 1307 }
fe8db0c5 1308
299d578f
SS
1309 r = sd_dhcp_lease_get_sip(lease, &addresses);
1310 if (r > 0) {
1311 fputs("SIP=", f);
2cc697d7 1312 serialize_in_addrs(f, addresses, r, NULL, NULL);
299d578f
SS
1313 fputc('\n', f);
1314 }
1315
fe8db0c5
TG
1316 r = sd_dhcp_lease_get_domainname(lease, &string);
1317 if (r >= 0)
1318 fprintf(f, "DOMAINNAME=%s\n", string);
1319
b85bc551
DW
1320 r = sd_dhcp_lease_get_search_domains(lease, &search_domains);
1321 if (r > 0) {
0d536673 1322 fputs("DOMAIN_SEARCH_LIST=", f);
b85bc551 1323 fputstrv(f, search_domains, NULL, NULL);
f8862395 1324 fputc('\n', f);
b85bc551
DW
1325 }
1326
fe8db0c5
TG
1327 r = sd_dhcp_lease_get_hostname(lease, &string);
1328 if (r >= 0)
1329 fprintf(f, "HOSTNAME=%s\n", string);
1330
ce78df79
TG
1331 r = sd_dhcp_lease_get_root_path(lease, &string);
1332 if (r >= 0)
1333 fprintf(f, "ROOT_PATH=%s\n", string);
1334
32f8a613
YW
1335 r = sd_dhcp_lease_get_static_routes(lease, &routes);
1336 if (r > 0)
1337 serialize_dhcp_routes(f, "STATIC_ROUTES", routes, r);
1338
1339 routes = mfree(routes);
1340 r = sd_dhcp_lease_get_classless_routes(lease, &routes);
0339cd77 1341 if (r > 0)
32f8a613 1342 serialize_dhcp_routes(f, "CLASSLESS_ROUTES", routes, r);
e1ea665e 1343
8eb9058d
LP
1344 r = sd_dhcp_lease_get_timezone(lease, &string);
1345 if (r >= 0)
1346 fprintf(f, "TIMEZONE=%s\n", string);
1347
d59a2b3b 1348 if (sd_dhcp_client_id_is_set(&lease->client_id)) {
3587161a 1349 _cleanup_free_ char *client_id_hex = NULL;
e37f74a6 1350
d59a2b3b 1351 client_id_hex = hexmem(lease->client_id.raw, lease->client_id.size);
ab7153b3
YW
1352 if (!client_id_hex)
1353 return -ENOMEM;
e37f74a6
DW
1354 fprintf(f, "CLIENTID=%s\n", client_id_hex);
1355 }
1356
e43a8393
BG
1357 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
1358 if (r >= 0) {
1359 _cleanup_free_ char *option_hex = NULL;
1360
1361 option_hex = hexmem(data, data_len);
ab7153b3
YW
1362 if (!option_hex)
1363 return -ENOMEM;
e43a8393
BG
1364 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
1365 }
1366
a073309f 1367 LIST_FOREACH(options, option, lease->private_options) {
dbcb4a90 1368 char key[STRLEN("OPTION_000")+1];
0339cd77 1369
d054f0a4 1370 xsprintf(key, "OPTION_%" PRIu8, option->tag);
a073309f
AC
1371 r = serialize_dhcp_option(f, key, option->data, option->length);
1372 if (r < 0)
ab7153b3 1373 return r;
a073309f
AC
1374 }
1375
dacd6cee
LP
1376 r = fflush_and_check(f);
1377 if (r < 0)
ab7153b3 1378 return r;
fe8db0c5 1379
abcf60e7
YW
1380 r = conservative_rename(temp_path, lease_file);
1381 if (r < 0)
ab7153b3 1382 return r;
dacd6cee 1383
ab7153b3 1384 temp_path = mfree(temp_path);
fe8db0c5 1385
ab7153b3 1386 return 0;
fe8db0c5
TG
1387}
1388
06cf04df
YW
1389static char **private_options_free(char **options) {
1390 if (!options)
1391 return NULL;
1392
24ae45cb 1393 free_many_charp(options, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1);
06cf04df
YW
1394
1395 return mfree(options);
1396}
1397
1398DEFINE_TRIVIAL_CLEANUP_FUNC(char**, private_options_free);
1399
bd91b83e 1400int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
4afd3348 1401 _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
0339cd77
LP
1402 _cleanup_free_ char
1403 *address = NULL,
1404 *router = NULL,
1405 *netmask = NULL,
1406 *server_address = NULL,
1407 *next_server = NULL,
1408 *broadcast = NULL,
1409 *dns = NULL,
3fd6708c 1410 *dnr = NULL,
0339cd77 1411 *ntp = NULL,
299d578f 1412 *sip = NULL,
ddb82ec2
LP
1413 *pop3 = NULL,
1414 *smtp = NULL,
1415 *lpr = NULL,
0339cd77 1416 *mtu = NULL,
32f8a613
YW
1417 *static_routes = NULL,
1418 *classless_routes = NULL,
b85bc551 1419 *domains = NULL,
0339cd77
LP
1420 *client_id_hex = NULL,
1421 *vendor_specific_hex = NULL,
1422 *lifetime = NULL,
1423 *t1 = NULL,
06cf04df
YW
1424 *t2 = NULL;
1425 _cleanup_(private_options_freep) char **options = NULL;
0339cd77 1426
a073309f 1427 int r, i;
fe8db0c5
TG
1428
1429 assert(lease_file);
1430 assert(ret);
1431
1432 r = dhcp_lease_new(&lease);
1433 if (r < 0)
1434 return r;
1435
06cf04df
YW
1436 options = new0(char*, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1);
1437 if (!options)
1438 return -ENOMEM;
1439
aa8fbc74 1440 r = parse_env_file(NULL, lease_file,
fe8db0c5
TG
1441 "ADDRESS", &address,
1442 "ROUTER", &router,
1443 "NETMASK", &netmask,
fd1f3b3e 1444 "SERVER_ADDRESS", &server_address,
8e34a618 1445 "NEXT_SERVER", &next_server,
0339cd77 1446 "BROADCAST", &broadcast,
109731eb 1447 "DNS", &dns,
3fd6708c 1448 "DNR", &dnr,
109731eb 1449 "NTP", &ntp,
299d578f 1450 "SIP", &sip,
ddb82ec2
LP
1451 "POP3", &pop3,
1452 "SMTP", &smtp,
1453 "LPR", &lpr,
fe8db0c5
TG
1454 "MTU", &mtu,
1455 "DOMAINNAME", &lease->domainname,
1456 "HOSTNAME", &lease->hostname,
b85bc551 1457 "DOMAIN_SEARCH_LIST", &domains,
ce78df79 1458 "ROOT_PATH", &lease->root_path,
32f8a613
YW
1459 "STATIC_ROUTES", &static_routes,
1460 "CLASSLESS_ROUTES", &classless_routes,
e37f74a6 1461 "CLIENTID", &client_id_hex,
8eb9058d 1462 "TIMEZONE", &lease->timezone,
e43a8393 1463 "VENDOR_SPECIFIC", &vendor_specific_hex,
0339cd77
LP
1464 "LIFETIME", &lifetime,
1465 "T1", &t1,
1466 "T2", &t2,
a073309f
AC
1467 "OPTION_224", &options[0],
1468 "OPTION_225", &options[1],
1469 "OPTION_226", &options[2],
1470 "OPTION_227", &options[3],
1471 "OPTION_228", &options[4],
1472 "OPTION_229", &options[5],
1473 "OPTION_230", &options[6],
1474 "OPTION_231", &options[7],
1475 "OPTION_232", &options[8],
1476 "OPTION_233", &options[9],
1477 "OPTION_234", &options[10],
1478 "OPTION_235", &options[11],
1479 "OPTION_236", &options[12],
1480 "OPTION_237", &options[13],
1481 "OPTION_238", &options[14],
1482 "OPTION_239", &options[15],
1483 "OPTION_240", &options[16],
1484 "OPTION_241", &options[17],
1485 "OPTION_242", &options[18],
1486 "OPTION_243", &options[19],
1487 "OPTION_244", &options[20],
1488 "OPTION_245", &options[21],
1489 "OPTION_246", &options[22],
1490 "OPTION_247", &options[23],
1491 "OPTION_248", &options[24],
1492 "OPTION_249", &options[25],
1493 "OPTION_250", &options[26],
1494 "OPTION_251", &options[27],
1495 "OPTION_252", &options[28],
1496 "OPTION_253", &options[29],
13df9c39 1497 "OPTION_254", &options[30]);
fe8db0c5
TG
1498 if (r < 0)
1499 return r;
1500
0339cd77
LP
1501 if (address) {
1502 r = inet_pton(AF_INET, address, &lease->address);
1503 if (r <= 0)
e26ea7fc 1504 log_debug("Failed to parse address %s, ignoring.", address);
0339cd77 1505 }
fe8db0c5 1506
8ddbeaa2 1507 if (router) {
f8862395
TH
1508 r = deserialize_in_addrs(&lease->router, router);
1509 if (r < 0)
1510 log_debug_errno(r, "Failed to deserialize router addresses %s, ignoring: %m", router);
1511 else
1512 lease->router_size = r;
8ddbeaa2 1513 }
fe8db0c5 1514
0339cd77
LP
1515 if (netmask) {
1516 r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
1517 if (r <= 0)
e26ea7fc 1518 log_debug("Failed to parse netmask %s, ignoring.", netmask);
0339cd77
LP
1519 else
1520 lease->have_subnet_mask = true;
1521 }
fe8db0c5 1522
0ad853bc 1523 if (server_address) {
0339cd77
LP
1524 r = inet_pton(AF_INET, server_address, &lease->server_address);
1525 if (r <= 0)
e26ea7fc 1526 log_debug("Failed to parse server address %s, ignoring.", server_address);
0ad853bc
TG
1527 }
1528
8e34a618 1529 if (next_server) {
0339cd77
LP
1530 r = inet_pton(AF_INET, next_server, &lease->next_server);
1531 if (r <= 0)
e26ea7fc 1532 log_debug("Failed to parse next server %s, ignoring.", next_server);
0339cd77 1533 }
8e34a618 1534
0339cd77
LP
1535 if (broadcast) {
1536 r = inet_pton(AF_INET, broadcast, &lease->broadcast);
1537 if (r <= 0)
e26ea7fc 1538 log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
0339cd77
LP
1539 else
1540 lease->have_broadcast = true;
8e34a618
TG
1541 }
1542
109731eb 1543 if (dns) {
ddb82ec2 1544 r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns);
109731eb 1545 if (r < 0)
0339cd77
LP
1546 log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
1547 else
ddb82ec2 1548 lease->servers[SD_DHCP_LEASE_DNS].size = r;
109731eb
TG
1549 }
1550
3fd6708c
RP
1551 if (dnr) {
1552 r = deserialize_dnr(&lease->dnr, dnr);
1553 if (r < 0)
1554 log_debug_errno(r, "Failed to deserialize DNR servers %s, ignoring: %m", dnr);
f54f473b
RP
1555 else
1556 lease->n_dnr = r;
3fd6708c
RP
1557 }
1558
109731eb 1559 if (ntp) {
ddb82ec2 1560 r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
109731eb 1561 if (r < 0)
0339cd77
LP
1562 log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
1563 else
ddb82ec2 1564 lease->servers[SD_DHCP_LEASE_NTP].size = r;
109731eb
TG
1565 }
1566
299d578f 1567 if (sip) {
ddb82ec2 1568 r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip);
299d578f 1569 if (r < 0)
850fd9b8 1570 log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip);
299d578f 1571 else
ddb82ec2 1572 lease->servers[SD_DHCP_LEASE_SIP].size = r;
299d578f
SS
1573 }
1574
ddb82ec2
LP
1575 if (pop3) {
1576 r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3);
f678ac7e 1577 if (r < 0)
ddb82ec2 1578 log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3);
f678ac7e 1579 else
ddb82ec2 1580 lease->servers[SD_DHCP_LEASE_POP3].size = r;
f678ac7e
SS
1581 }
1582
ddb82ec2
LP
1583 if (smtp) {
1584 r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp);
2c649ca1 1585 if (r < 0)
ddb82ec2 1586 log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp);
2c649ca1 1587 else
ddb82ec2 1588 lease->servers[SD_DHCP_LEASE_SMTP].size = r;
2c649ca1
SS
1589 }
1590
ddb82ec2
LP
1591 if (lpr) {
1592 r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr);
50018bfa 1593 if (r < 0)
ddb82ec2 1594 log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr);
50018bfa 1595 else
ddb82ec2 1596 lease->servers[SD_DHCP_LEASE_LPR].size = r;
50018bfa
SS
1597 }
1598
fe8db0c5 1599 if (mtu) {
0339cd77
LP
1600 r = safe_atou16(mtu, &lease->mtu);
1601 if (r < 0)
1602 log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
fe8db0c5
TG
1603 }
1604
b85bc551
DW
1605 if (domains) {
1606 _cleanup_strv_free_ char **a = NULL;
1607 a = strv_split(domains, " ");
1608 if (!a)
1609 return -ENOMEM;
1610
d46b79bb 1611 if (!strv_isempty(a))
d7a0f1f4 1612 lease->search_domains = TAKE_PTR(a);
b85bc551
DW
1613 }
1614
32f8a613
YW
1615 if (static_routes) {
1616 r = deserialize_dhcp_routes(
1617 &lease->static_routes,
1618 &lease->n_static_routes,
1619 static_routes);
1620 if (r < 0)
1621 log_debug_errno(r, "Failed to parse DHCP static routes %s, ignoring: %m", static_routes);
1622 }
1623
1624 if (classless_routes) {
0339cd77 1625 r = deserialize_dhcp_routes(
32f8a613
YW
1626 &lease->classless_routes,
1627 &lease->n_classless_routes,
1628 classless_routes);
0339cd77 1629 if (r < 0)
32f8a613 1630 log_debug_errno(r, "Failed to parse DHCP classless routes %s, ignoring: %m", classless_routes);
0339cd77
LP
1631 }
1632
1633 if (lifetime) {
5fde4d37 1634 r = parse_sec(lifetime, &lease->lifetime);
0339cd77
LP
1635 if (r < 0)
1636 log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
1637 }
1638
1639 if (t1) {
5fde4d37 1640 r = parse_sec(t1, &lease->t1);
0339cd77
LP
1641 if (r < 0)
1642 log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
1643 }
1644
1645 if (t2) {
5fde4d37 1646 r = parse_sec(t2, &lease->t2);
e1ea665e 1647 if (r < 0)
0339cd77 1648 log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
e1ea665e
EY
1649 }
1650
e37f74a6 1651 if (client_id_hex) {
d59a2b3b
YW
1652 _cleanup_free_ void *data = NULL;
1653 size_t data_size;
1654
bdd2036e 1655 r = unhexmem(client_id_hex, &data, &data_size);
30494563 1656 if (r < 0)
0339cd77 1657 log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
d59a2b3b
YW
1658
1659 r = sd_dhcp_client_id_set_raw(&lease->client_id, data, data_size);
1660 if (r < 0)
1661 log_debug_errno(r, "Failed to assign client ID, ignoring: %m");
e37f74a6
DW
1662 }
1663
e43a8393 1664 if (vendor_specific_hex) {
bdd2036e 1665 r = unhexmem(vendor_specific_hex, &lease->vendor_specific, &lease->vendor_specific_len);
e43a8393 1666 if (r < 0)
0339cd77 1667 log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
e43a8393
BG
1668 }
1669
22805d92 1670 for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
e4735228 1671 _cleanup_free_ void *data = NULL;
a073309f
AC
1672 size_t len;
1673
1674 if (!options[i])
1675 continue;
1676
bdd2036e 1677 r = unhexmem(options[i], &data, &len);
0339cd77
LP
1678 if (r < 0) {
1679 log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
1680 continue;
1681 }
a073309f 1682
22805d92 1683 r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
626be147 1684 if (r < 0)
a073309f
AC
1685 return r;
1686 }
1687
1cc6c93a 1688 *ret = TAKE_PTR(lease);
fe8db0c5
TG
1689
1690 return 0;
1691}
9e64dd72
TG
1692
1693int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
0339cd77 1694 struct in_addr address, mask;
df40eee8 1695 int r;
9e64dd72
TG
1696
1697 assert(lease);
9e64dd72 1698
8fc72505
YW
1699 if (lease->have_subnet_mask)
1700 return 0;
1701
0339cd77
LP
1702 if (lease->address == 0)
1703 return -ENODATA;
1704
df40eee8 1705 address.s_addr = lease->address;
9e64dd72
TG
1706
1707 /* fall back to the default subnet masks based on address class */
5a941f5f 1708 r = in4_addr_default_subnet_mask(&address, &mask);
df40eee8
TG
1709 if (r < 0)
1710 return r;
9e64dd72 1711
df40eee8 1712 lease->subnet_mask = mask.s_addr;
0339cd77 1713 lease->have_subnet_mask = true;
9e64dd72
TG
1714
1715 return 0;
1716}
e37f74a6 1717
d59a2b3b 1718int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret) {
e37f74a6 1719 assert_return(lease, -EINVAL);
d59a2b3b 1720 assert_return(ret, -EINVAL);
e37f74a6 1721
d59a2b3b 1722 if (!sd_dhcp_client_id_is_set(&lease->client_id))
0339cd77
LP
1723 return -ENODATA;
1724
d59a2b3b 1725 *ret = &lease->client_id;
0339cd77 1726
e37f74a6
DW
1727 return 0;
1728}
1729
d59a2b3b 1730int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id) {
e37f74a6 1731 assert_return(lease, -EINVAL);
e37f74a6 1732
d59a2b3b
YW
1733 if (!sd_dhcp_client_id_is_set(client_id))
1734 return sd_dhcp_client_id_clear(&lease->client_id);
e37f74a6 1735
d59a2b3b 1736 lease->client_id = *client_id;
e37f74a6
DW
1737
1738 return 0;
1739}
8eb9058d 1740
041ea9f9 1741int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
8eb9058d 1742 assert_return(lease, -EINVAL);
64d6c229 1743 assert_return(tz, -EINVAL);
8eb9058d
LP
1744
1745 if (!lease->timezone)
0339cd77 1746 return -ENODATA;
8eb9058d 1747
64d6c229 1748 *tz = lease->timezone;
8eb9058d
LP
1749 return 0;
1750}
f8693fc7 1751
041ea9f9 1752int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
f8693fc7
BG
1753 assert_return(route, -EINVAL);
1754 assert_return(destination, -EINVAL);
1755
1756 *destination = route->dst_addr;
1757 return 0;
1758}
1759
041ea9f9 1760int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
f8693fc7
BG
1761 assert_return(route, -EINVAL);
1762 assert_return(length, -EINVAL);
1763
1764 *length = route->dst_prefixlen;
1765 return 0;
1766}
1767
041ea9f9 1768int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
f8693fc7
BG
1769 assert_return(route, -EINVAL);
1770 assert_return(gateway, -EINVAL);
1771
1772 *gateway = route->gw_addr;
1773 return 0;
1774}