]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
Merge pull request #9017 from keszybz/man-coredump
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-lease.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a6cc569e
TG
2/***
3 This file is part of systemd.
4
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 Copyright (C) 2014 Tom Gundersen
a6cc569e
TG
7***/
8
07630cea 9#include <arpa/inet.h>
a6cc569e 10#include <errno.h>
a6cc569e 11#include <stdio.h>
0d536673 12#include <stdio_ext.h>
07630cea
LP
13#include <stdlib.h>
14#include <string.h>
15
16#include "sd-dhcp-lease.h"
a6cc569e 17
b5efdb8a 18#include "alloc-util.h"
07630cea
LP
19#include "dhcp-lease-internal.h"
20#include "dhcp-protocol.h"
21#include "dns-domain.h"
3ffd4af2 22#include "fd-util.h"
fe8db0c5 23#include "fileio.h"
b11d6a7b 24#include "hexdecoct.h"
958b66ea 25#include "hostname-util.h"
07630cea 26#include "in-addr-util.h"
0339cd77 27#include "network-internal.h"
6bedfcbb 28#include "parse-util.h"
d054f0a4 29#include "stdio-util.h"
b11d6a7b 30#include "string-util.h"
07630cea 31#include "unaligned.h"
a6cc569e
TG
32
33int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
34 assert_return(lease, -EINVAL);
35 assert_return(addr, -EINVAL);
36
0339cd77
LP
37 if (lease->address == 0)
38 return -ENODATA;
39
a6cc569e 40 addr->s_addr = lease->address;
0339cd77
LP
41 return 0;
42}
a6cc569e 43
0339cd77
LP
44int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
45 assert_return(lease, -EINVAL);
46 assert_return(addr, -EINVAL);
47
48 if (!lease->have_broadcast)
49 return -ENODATA;
50
51 addr->s_addr = lease->broadcast;
a6cc569e
TG
52 return 0;
53}
54
68ceb9df
PF
55int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
56 assert_return(lease, -EINVAL);
1c6eb4e3 57 assert_return(lifetime, -EINVAL);
68ceb9df 58
0339cd77
LP
59 if (lease->lifetime <= 0)
60 return -ENODATA;
61
68ceb9df 62 *lifetime = lease->lifetime;
0339cd77
LP
63 return 0;
64}
68ceb9df 65
0339cd77
LP
66int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
67 assert_return(lease, -EINVAL);
68 assert_return(t1, -EINVAL);
69
70 if (lease->t1 <= 0)
71 return -ENODATA;
72
73 *t1 = lease->t1;
74 return 0;
75}
76
77int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
78 assert_return(lease, -EINVAL);
79 assert_return(t2, -EINVAL);
80
81 if (lease->t2 <= 0)
82 return -ENODATA;
83
84 *t2 = lease->t2;
68ceb9df
PF
85 return 0;
86}
87
a6cc569e
TG
88int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
89 assert_return(lease, -EINVAL);
90 assert_return(mtu, -EINVAL);
91
0339cd77
LP
92 if (lease->mtu <= 0)
93 return -ENODATA;
a6cc569e 94
0339cd77 95 *mtu = lease->mtu;
a6cc569e
TG
96 return 0;
97}
98
a2ba62c7 99int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
a6cc569e
TG
100 assert_return(lease, -EINVAL);
101 assert_return(addr, -EINVAL);
a6cc569e 102
0339cd77
LP
103 if (lease->dns_size <= 0)
104 return -ENODATA;
a6cc569e 105
0339cd77
LP
106 *addr = lease->dns;
107 return (int) lease->dns_size;
a6cc569e
TG
108}
109
a2ba62c7 110int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
46844696
TG
111 assert_return(lease, -EINVAL);
112 assert_return(addr, -EINVAL);
46844696 113
0339cd77
LP
114 if (lease->ntp_size <= 0)
115 return -ENODATA;
46844696 116
0339cd77
LP
117 *addr = lease->ntp;
118 return (int) lease->ntp_size;
46844696
TG
119}
120
a6cc569e
TG
121int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
122 assert_return(lease, -EINVAL);
123 assert_return(domainname, -EINVAL);
124
0339cd77
LP
125 if (!lease->domainname)
126 return -ENODATA;
a6cc569e 127
0339cd77 128 *domainname = lease->domainname;
a6cc569e
TG
129 return 0;
130}
131
132int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
133 assert_return(lease, -EINVAL);
134 assert_return(hostname, -EINVAL);
135
0339cd77
LP
136 if (!lease->hostname)
137 return -ENODATA;
a6cc569e 138
0339cd77 139 *hostname = lease->hostname;
a6cc569e
TG
140 return 0;
141}
142
ce78df79
TG
143int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
144 assert_return(lease, -EINVAL);
145 assert_return(root_path, -EINVAL);
146
0339cd77
LP
147 if (!lease->root_path)
148 return -ENODATA;
ce78df79 149
0339cd77 150 *root_path = lease->root_path;
ce78df79
TG
151 return 0;
152}
153
a6cc569e
TG
154int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
155 assert_return(lease, -EINVAL);
156 assert_return(addr, -EINVAL);
157
0339cd77
LP
158 if (lease->router == 0)
159 return -ENODATA;
a6cc569e 160
0339cd77 161 addr->s_addr = lease->router;
a6cc569e
TG
162 return 0;
163}
164
165int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
166 assert_return(lease, -EINVAL);
167 assert_return(addr, -EINVAL);
168
0339cd77
LP
169 if (!lease->have_subnet_mask)
170 return -ENODATA;
a6cc569e 171
0339cd77 172 addr->s_addr = lease->subnet_mask;
a6cc569e
TG
173 return 0;
174}
175
0ad853bc
TG
176int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
177 assert_return(lease, -EINVAL);
178 assert_return(addr, -EINVAL);
179
0339cd77
LP
180 if (lease->server_address == 0)
181 return -ENODATA;
0ad853bc 182
0339cd77 183 addr->s_addr = lease->server_address;
0ad853bc
TG
184 return 0;
185}
186
8e34a618
TG
187int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
188 assert_return(lease, -EINVAL);
189 assert_return(addr, -EINVAL);
190
0339cd77
LP
191 if (lease->next_server == 0)
192 return -ENODATA;
8e34a618 193
0339cd77 194 addr->s_addr = lease->next_server;
8e34a618
TG
195 return 0;
196}
197
f8693fc7
BG
198/*
199 * The returned routes array must be freed by the caller.
200 * Route objects have the same lifetime of the lease and must not be freed.
201 */
202int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
203 sd_dhcp_route **ret;
204 unsigned i;
205
e1ea665e
EY
206 assert_return(lease, -EINVAL);
207 assert_return(routes, -EINVAL);
e1ea665e 208
0339cd77
LP
209 if (lease->static_route_size <= 0)
210 return -ENODATA;
e1ea665e 211
f8693fc7
BG
212 ret = new(sd_dhcp_route *, lease->static_route_size);
213 if (!ret)
214 return -ENOMEM;
215
216 for (i = 0; i < lease->static_route_size; i++)
217 ret[i] = &lease->static_route[i];
218
219 *routes = ret;
0339cd77 220 return (int) lease->static_route_size;
e1ea665e
EY
221}
222
b85bc551 223int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
da6053d0 224 size_t r;
b85bc551
DW
225
226 assert_return(lease, -EINVAL);
227 assert_return(domains, -EINVAL);
228
229 r = strv_length(lease->search_domains);
230 if (r > 0) {
231 *domains = lease->search_domains;
232 return (int) r;
233 }
234
235 return -ENODATA;
236}
237
0339cd77 238int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
e43a8393
BG
239 assert_return(lease, -EINVAL);
240 assert_return(data, -EINVAL);
241 assert_return(data_len, -EINVAL);
242
0339cd77
LP
243 if (lease->vendor_specific_len <= 0)
244 return -ENODATA;
e43a8393
BG
245
246 *data = lease->vendor_specific;
247 *data_len = lease->vendor_specific_len;
e43a8393
BG
248 return 0;
249}
250
a6cc569e 251sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
3733eec3
LP
252
253 if (!lease)
254 return NULL;
255
256 assert(lease->n_ref >= 1);
257 lease->n_ref++;
a6cc569e
TG
258
259 return lease;
260}
261
262sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
7e753d9d 263
3733eec3
LP
264 if (!lease)
265 return NULL;
7e753d9d 266
3733eec3
LP
267 assert(lease->n_ref >= 1);
268 lease->n_ref--;
269
270 if (lease->n_ref > 0)
271 return NULL;
272
273 while (lease->private_options) {
274 struct sd_dhcp_raw_option *option = lease->private_options;
275
276 LIST_REMOVE(options, lease->private_options, option);
277
278 free(option->data);
279 free(option);
a6cc569e
TG
280 }
281
3733eec3
LP
282 free(lease->hostname);
283 free(lease->domainname);
284 free(lease->dns);
285 free(lease->ntp);
286 free(lease->static_route);
287 free(lease->client_id);
288 free(lease->vendor_specific);
b85bc551 289 strv_free(lease->search_domains);
6b430fdb 290 return mfree(lease);
a6cc569e
TG
291}
292
0339cd77 293static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
e140ae58
TG
294 assert(option);
295 assert(ret);
296
0339cd77
LP
297 if (len != 4)
298 return -EINVAL;
e140ae58 299
0339cd77
LP
300 *ret = unaligned_read_be32((be32_t*) option);
301 if (*ret < min)
302 *ret = min;
e140ae58 303
0339cd77 304 return 0;
e140ae58
TG
305}
306
0339cd77 307static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
e140ae58
TG
308 assert(option);
309 assert(ret);
310
0339cd77
LP
311 if (len != 2)
312 return -EINVAL;
e140ae58 313
0339cd77
LP
314 *ret = unaligned_read_be16((be16_t*) option);
315 if (*ret < min)
316 *ret = min;
f5c0c00f 317
0339cd77 318 return 0;
f5c0c00f
TG
319}
320
0339cd77 321static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
f5c0c00f
TG
322 assert(option);
323 assert(ret);
324
0339cd77
LP
325 if (len != 4)
326 return -EINVAL;
f5c0c00f 327
0339cd77
LP
328 memcpy(ret, option, 4);
329 return 0;
f5c0c00f
TG
330}
331
e140ae58
TG
332static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
333 assert(option);
334 assert(ret);
335
0339cd77
LP
336 if (len <= 0)
337 *ret = mfree(*ret);
338 else {
e140ae58
TG
339 char *string;
340
e989fd9b
LP
341 /*
342 * One trailing NUL byte is OK, we don't mind. See:
343 * https://github.com/systemd/systemd/issues/1337
344 */
345 if (memchr(option, 0, len - 1))
43f447b1
LP
346 return -EINVAL;
347
e989fd9b 348 string = strndup((const char *) option, len);
e140ae58 349 if (!string)
43f447b1 350 return -ENOMEM;
e140ae58
TG
351
352 free(*ret);
353 *ret = string;
0339cd77 354 }
e140ae58
TG
355
356 return 0;
357}
358
978c6477
LP
359static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
360 _cleanup_free_ char *name = NULL, *normalized = NULL;
361 int r;
362
363 assert(option);
364 assert(ret);
365
366 r = lease_parse_string(option, len, &name);
367 if (r < 0)
368 return r;
369 if (!name) {
370 *ret = mfree(*ret);
371 return 0;
372 }
373
374 r = dns_name_normalize(name, &normalized);
375 if (r < 0)
376 return r;
377
378 if (is_localhost(normalized))
379 return -EINVAL;
380
381 if (dns_name_is_root(normalized))
382 return -EINVAL;
383
f9ecfd3b 384 free_and_replace(*ret, normalized);
978c6477
LP
385
386 return 0;
387}
388
d9ec2e63
LP
389static void filter_bogus_addresses(struct in_addr *addresses, size_t *n) {
390 size_t i, j;
391
392 /* Silently filter DNS/NTP servers supplied to us that do not make outside of the local scope. */
393
394 for (i = 0, j = 0; i < *n; i ++) {
395
396 if (in4_addr_is_null(addresses+i) ||
397 in4_addr_is_localhost(addresses+i))
398 continue;
399
400 addresses[j++] = addresses[i];
401 }
402
403 *n = j;
404}
405
0339cd77 406static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
e140ae58
TG
407 assert(option);
408 assert(ret);
0339cd77 409 assert(n_ret);
e140ae58 410
0339cd77
LP
411 if (len <= 0) {
412 *ret = mfree(*ret);
413 *n_ret = 0;
414 } else {
415 size_t n_addresses;
e140ae58
TG
416 struct in_addr *addresses;
417
0339cd77
LP
418 if (len % 4 != 0)
419 return -EINVAL;
e140ae58 420
0339cd77
LP
421 n_addresses = len / 4;
422
423 addresses = newdup(struct in_addr, option, n_addresses);
e140ae58
TG
424 if (!addresses)
425 return -ENOMEM;
426
d9ec2e63
LP
427 filter_bogus_addresses(addresses, &n_addresses);
428
e140ae58
TG
429 free(*ret);
430 *ret = addresses;
0339cd77 431 *n_ret = n_addresses;
e140ae58
TG
432 }
433
434 return 0;
435}
436
0339cd77
LP
437static int lease_parse_routes(
438 const uint8_t *option, size_t len,
439 struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
e1ea665e
EY
440
441 struct in_addr addr;
442
0339cd77 443 assert(option || len <= 0);
e1ea665e
EY
444 assert(routes);
445 assert(routes_size);
446 assert(routes_allocated);
447
0339cd77 448 if (len <= 0)
e1ea665e
EY
449 return 0;
450
451 if (len % 8 != 0)
452 return -EINVAL;
453
454 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
455 return -ENOMEM;
456
457 while (len >= 8) {
458 struct sd_dhcp_route *route = *routes + *routes_size;
1caa12d0 459 int r;
e1ea665e 460
8cdc46e7 461 route->option = SD_DHCP_OPTION_STATIC_ROUTE;
5a941f5f 462 r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
1caa12d0 463 if (r < 0) {
0339cd77 464 log_debug("Failed to determine destination prefix length from class based IP, ignoring");
e1ea665e
EY
465 continue;
466 }
467
0339cd77 468 assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
e1ea665e
EY
469 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
470 option += 4;
471
0339cd77 472 assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
e1ea665e
EY
473 option += 4;
474
475 len -= 8;
476 (*routes_size)++;
477 }
478
479 return 0;
480}
481
482/* parses RFC3442 Classless Static Route Option */
0339cd77
LP
483static int lease_parse_classless_routes(
484 const uint8_t *option, size_t len,
485 struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
e1ea665e 486
0339cd77 487 assert(option || len <= 0);
e1ea665e
EY
488 assert(routes);
489 assert(routes_size);
490 assert(routes_allocated);
491
0339cd77
LP
492 if (len <= 0)
493 return 0;
494
e1ea665e
EY
495 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
496
497 while (len > 0) {
498 uint8_t dst_octets;
499 struct sd_dhcp_route *route;
500
501 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
0339cd77 502 return -ENOMEM;
e1ea665e
EY
503
504 route = *routes + *routes_size;
8cdc46e7 505 route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
e1ea665e
EY
506
507 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
508 route->dst_prefixlen = *option;
509 option++;
510 len--;
511
512 /* can't have more than 4 octets in IPv4 */
513 if (dst_octets > 4 || len < dst_octets)
514 return -EINVAL;
515
516 route->dst_addr.s_addr = 0;
517 memcpy(&route->dst_addr.s_addr, option, dst_octets);
518 option += dst_octets;
519 len -= dst_octets;
520
521 if (len < 4)
522 return -EINVAL;
523
b14fff6e 524 assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
e1ea665e
EY
525 option += 4;
526 len -= 4;
527
528 (*routes_size)++;
529 }
530
531 return 0;
532}
533
e4735228 534int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
2d03c0b8 535 sd_dhcp_lease *lease = userdata;
e140ae58
TG
536 int r;
537
538 assert(lease);
a6cc569e
TG
539
540 switch(code) {
541
22805d92 542 case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
0339cd77
LP
543 r = lease_parse_u32(option, len, &lease->lifetime, 1);
544 if (r < 0)
545 log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
546
a6cc569e
TG
547 break;
548
22805d92 549 case SD_DHCP_OPTION_SERVER_IDENTIFIER:
0339cd77
LP
550 r = lease_parse_be32(option, len, &lease->server_address);
551 if (r < 0)
552 log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
553
a6cc569e
TG
554 break;
555
22805d92 556 case SD_DHCP_OPTION_SUBNET_MASK:
0339cd77
LP
557 r = lease_parse_be32(option, len, &lease->subnet_mask);
558 if (r < 0)
559 log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
560 else
561 lease->have_subnet_mask = true;
a6cc569e
TG
562 break;
563
22805d92 564 case SD_DHCP_OPTION_BROADCAST:
0339cd77
LP
565 r = lease_parse_be32(option, len, &lease->broadcast);
566 if (r < 0)
567 log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
568 else
569 lease->have_broadcast = true;
f5c0c00f
TG
570 break;
571
22805d92 572 case SD_DHCP_OPTION_ROUTER:
0339cd77
LP
573 if (len >= 4) {
574 r = lease_parse_be32(option, 4, &lease->router);
575 if (r < 0)
576 log_debug_errno(r, "Failed to parse router address, ignoring: %m");
577 }
a6cc569e
TG
578 break;
579
22805d92 580 case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
0339cd77
LP
581 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
582 if (r < 0)
583 log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
a6cc569e
TG
584 break;
585
22805d92 586 case SD_DHCP_OPTION_NTP_SERVER:
0339cd77
LP
587 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
588 if (r < 0)
589 log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
f5c0c00f
TG
590 break;
591
22805d92 592 case SD_DHCP_OPTION_STATIC_ROUTE:
0339cd77
LP
593 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
594 if (r < 0)
595 log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
f5c0c00f
TG
596 break;
597
22805d92 598 case SD_DHCP_OPTION_INTERFACE_MTU:
0339cd77
LP
599 r = lease_parse_u16(option, len, &lease->mtu, 68);
600 if (r < 0)
601 log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
955d99ed 602 if (lease->mtu < DHCP_DEFAULT_MIN_SIZE) {
4dd53da9 603 log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE);
955d99ed
MG
604 lease->mtu = DHCP_DEFAULT_MIN_SIZE;
605 }
606
f5c0c00f
TG
607 break;
608
978c6477
LP
609 case SD_DHCP_OPTION_DOMAIN_NAME:
610 r = lease_parse_domain(option, len, &lease->domainname);
0339cd77
LP
611 if (r < 0) {
612 log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
613 return 0;
614 }
a6cc569e 615
784d9b9c 616 break;
784d9b9c 617
b85bc551
DW
618 case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST:
619 r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains);
620 if (r < 0)
621 log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m");
622 break;
623
978c6477
LP
624 case SD_DHCP_OPTION_HOST_NAME:
625 r = lease_parse_domain(option, len, &lease->hostname);
0339cd77
LP
626 if (r < 0) {
627 log_debug_errno(r, "Failed to parse host name, ignoring: %m");
628 return 0;
629 }
a6cc569e 630
784d9b9c 631 break;
ce78df79 632
22805d92 633 case SD_DHCP_OPTION_ROOT_PATH:
0339cd77
LP
634 r = lease_parse_string(option, len, &lease->root_path);
635 if (r < 0)
636 log_debug_errno(r, "Failed to parse root path, ignoring: %m");
637 break;
ce78df79 638
22805d92 639 case SD_DHCP_OPTION_RENEWAL_T1_TIME:
0339cd77
LP
640 r = lease_parse_u32(option, len, &lease->t1, 1);
641 if (r < 0)
642 log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
a6cc569e
TG
643 break;
644
22805d92 645 case SD_DHCP_OPTION_REBINDING_T2_TIME:
0339cd77
LP
646 r = lease_parse_u32(option, len, &lease->t2, 1);
647 if (r < 0)
648 log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
a6cc569e 649 break;
e1ea665e 650
22805d92 651 case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
0339cd77 652 r = lease_parse_classless_routes(
2d03c0b8
LP
653 option, len,
654 &lease->static_route,
655 &lease->static_route_size,
656 &lease->static_route_allocated);
0339cd77
LP
657 if (r < 0)
658 log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
659 break;
e43a8393 660
22805d92 661 case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
8eb9058d
LP
662 _cleanup_free_ char *tz = NULL;
663
664 r = lease_parse_string(option, len, &tz);
0339cd77
LP
665 if (r < 0) {
666 log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
667 return 0;
668 }
8eb9058d 669
089fb865 670 if (!timezone_is_valid(tz, LOG_DEBUG)) {
0339cd77
LP
671 log_debug_errno(r, "Timezone is not valid, ignoring: %m");
672 return 0;
673 }
8eb9058d 674
f9ecfd3b 675 free_and_replace(lease->timezone, tz);
0339cd77 676
8eb9058d
LP
677 break;
678 }
679
22805d92 680 case SD_DHCP_OPTION_VENDOR_SPECIFIC:
2d03c0b8 681
0339cd77
LP
682 if (len <= 0)
683 lease->vendor_specific = mfree(lease->vendor_specific);
684 else {
685 void *p;
686
687 p = memdup(option, len);
688 if (!p)
e43a8393 689 return -ENOMEM;
e43a8393 690
0339cd77
LP
691 free(lease->vendor_specific);
692 lease->vendor_specific = p;
693 }
7e753d9d 694
0339cd77
LP
695 lease->vendor_specific_len = len;
696 break;
7e753d9d 697
22805d92 698 case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
7e753d9d
AC
699 r = dhcp_lease_insert_private_option(lease, code, option, len);
700 if (r < 0)
701 return r;
0339cd77
LP
702
703 break;
704
705 default:
f693e9b3 706 log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
0339cd77 707 break;
a6cc569e
TG
708 }
709
710 return 0;
711}
712
b85bc551
DW
713/* Parses compressed domain names. */
714int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) {
715 _cleanup_strv_free_ char **names = NULL;
716 size_t pos = 0, cnt = 0;
717 int r;
718
719 assert(domains);
720 assert_return(option && len > 0, -ENODATA);
721
722 while (pos < len) {
723 _cleanup_free_ char *name = NULL;
724 size_t n = 0, allocated = 0;
725 size_t jump_barrier = pos, next_chunk = 0;
726 bool first = true;
727
728 for (;;) {
729 uint8_t c;
730 c = option[pos++];
731
732 if (c == 0) {
733 /* End of name */
734 break;
735 } else if (c <= 63) {
736 const char *label;
737
738 /* Literal label */
739 label = (const char*) (option + pos);
740 pos += c;
741 if (pos >= len)
742 return -EBADMSG;
743
744 if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
745 return -ENOMEM;
746
747 if (first)
748 first = false;
749 else
750 name[n++] = '.';
751
752 r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
753 if (r < 0)
754 return r;
755
756 n += r;
757 } else if ((c & 0xc0) == 0xc0) {
758 /* Pointer */
759
760 uint8_t d;
761 uint16_t ptr;
762
763 if (pos >= len)
764 return -EBADMSG;
765
766 d = option[pos++];
767 ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
768
769 /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
770 if (ptr >= jump_barrier)
771 return -EBADMSG;
772 jump_barrier = ptr;
773
774 /* Save current location so we don't end up re-parsing what's parsed so far. */
775 if (next_chunk == 0)
776 next_chunk = pos;
777
778 pos = ptr;
779 } else
780 return -EBADMSG;
781 }
782
783 if (!GREEDY_REALLOC(name, allocated, n + 1))
784 return -ENOMEM;
785 name[n] = 0;
786
787 r = strv_extend(&names, name);
788 if (r < 0)
789 return r;
790
791 cnt++;
792
793 if (next_chunk != 0)
794 pos = next_chunk;
795 }
796
ae2a15bc 797 *domains = TAKE_PTR(names);
b85bc551
DW
798
799 return cnt;
800}
801
0339cd77 802int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
7e753d9d
AC
803 struct sd_dhcp_raw_option *cur, *option;
804
0339cd77
LP
805 assert(lease);
806
7e753d9d
AC
807 LIST_FOREACH(options, cur, lease->private_options) {
808 if (tag < cur->tag)
809 break;
0339cd77
LP
810 if (tag == cur->tag) {
811 log_debug("Ignoring duplicate option, tagged %i.", tag);
7e753d9d
AC
812 return 0;
813 }
814 }
815
816 option = new(struct sd_dhcp_raw_option, 1);
817 if (!option)
818 return -ENOMEM;
819
820 option->tag = tag;
821 option->length = len;
822 option->data = memdup(data, len);
823 if (!option->data) {
824 free(option);
825 return -ENOMEM;
826 }
827
828 LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
7e753d9d
AC
829 return 0;
830}
831
a6cc569e 832int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 833 sd_dhcp_lease *lease;
a6cc569e
TG
834
835 lease = new0(sd_dhcp_lease, 1);
836 if (!lease)
837 return -ENOMEM;
838
8ddbeaa2 839 lease->router = INADDR_ANY;
3733eec3 840 lease->n_ref = 1;
a6cc569e
TG
841
842 *ret = lease;
a6cc569e
TG
843 return 0;
844}
fe8db0c5 845
bd91b83e 846int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fe8db0c5
TG
847 _cleanup_free_ char *temp_path = NULL;
848 _cleanup_fclose_ FILE *f = NULL;
a073309f 849 struct sd_dhcp_raw_option *option;
fe8db0c5 850 struct in_addr address;
a2ba62c7 851 const struct in_addr *addresses;
e4735228 852 const void *client_id, *data;
e43a8393 853 size_t client_id_len, data_len;
fe8db0c5
TG
854 const char *string;
855 uint16_t mtu;
f8693fc7 856 _cleanup_free_ sd_dhcp_route **routes = NULL;
b85bc551 857 char **search_domains = NULL;
0339cd77 858 uint32_t t1, t2, lifetime;
fe8db0c5
TG
859 int r;
860
861 assert(lease);
862 assert(lease_file);
863
fe8db0c5
TG
864 r = fopen_temporary(lease_file, &f, &temp_path);
865 if (r < 0)
dacd6cee 866 goto fail;
fe8db0c5 867
0d536673
LP
868 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
869 (void) fchmod(fileno(f), 0644);
fe8db0c5 870
fe8db0c5 871 fprintf(f,
0339cd77 872 "# This is private data. Do not parse.\n");
fe8db0c5 873
0339cd77
LP
874 r = sd_dhcp_lease_get_address(lease, &address);
875 if (r >= 0)
876 fprintf(f, "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 877
0339cd77
LP
878 r = sd_dhcp_lease_get_netmask(lease, &address);
879 if (r >= 0)
880 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 881
8ddbeaa2
UTL
882 r = sd_dhcp_lease_get_router(lease, &address);
883 if (r >= 0)
884 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
885
0ad853bc 886 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb 887 if (r >= 0)
0339cd77 888 fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address));
0ad853bc 889
8e34a618 890 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
891 if (r >= 0)
892 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 893
0339cd77
LP
894 r = sd_dhcp_lease_get_broadcast(lease, &address);
895 if (r >= 0)
896 fprintf(f, "BROADCAST=%s\n", inet_ntoa(address));
897
fe8db0c5
TG
898 r = sd_dhcp_lease_get_mtu(lease, &mtu);
899 if (r >= 0)
900 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
901
0339cd77
LP
902 r = sd_dhcp_lease_get_t1(lease, &t1);
903 if (r >= 0)
904 fprintf(f, "T1=%" PRIu32 "\n", t1);
905
906 r = sd_dhcp_lease_get_t2(lease, &t2);
109731eb 907 if (r >= 0)
0339cd77
LP
908 fprintf(f, "T2=%" PRIu32 "\n", t2);
909
910 r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
911 if (r >= 0)
912 fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime);
913
914 r = sd_dhcp_lease_get_dns(lease, &addresses);
915 if (r > 0) {
0d536673 916 fputs("DNS=", f);
b0e39c82 917 serialize_in_addrs(f, addresses, r);
0d536673 918 fputs("\n", f);
0339cd77 919 }
109731eb 920
a2ba62c7 921 r = sd_dhcp_lease_get_ntp(lease, &addresses);
0339cd77 922 if (r > 0) {
0d536673 923 fputs("NTP=", f);
b0e39c82 924 serialize_in_addrs(f, addresses, r);
0d536673 925 fputs("\n", f);
0339cd77 926 }
fe8db0c5
TG
927
928 r = sd_dhcp_lease_get_domainname(lease, &string);
929 if (r >= 0)
930 fprintf(f, "DOMAINNAME=%s\n", string);
931
b85bc551
DW
932 r = sd_dhcp_lease_get_search_domains(lease, &search_domains);
933 if (r > 0) {
0d536673 934 fputs("DOMAIN_SEARCH_LIST=", f);
b85bc551 935 fputstrv(f, search_domains, NULL, NULL);
0d536673 936 fputs("\n", f);
b85bc551
DW
937 }
938
fe8db0c5
TG
939 r = sd_dhcp_lease_get_hostname(lease, &string);
940 if (r >= 0)
941 fprintf(f, "HOSTNAME=%s\n", string);
942
ce78df79
TG
943 r = sd_dhcp_lease_get_root_path(lease, &string);
944 if (r >= 0)
945 fprintf(f, "ROOT_PATH=%s\n", string);
946
a2ba62c7 947 r = sd_dhcp_lease_get_routes(lease, &routes);
0339cd77 948 if (r > 0)
a2ba62c7 949 serialize_dhcp_routes(f, "ROUTES", routes, r);
e1ea665e 950
8eb9058d
LP
951 r = sd_dhcp_lease_get_timezone(lease, &string);
952 if (r >= 0)
953 fprintf(f, "TIMEZONE=%s\n", string);
954
e37f74a6
DW
955 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
956 if (r >= 0) {
3587161a 957 _cleanup_free_ char *client_id_hex = NULL;
e37f74a6 958
dde8bb32 959 client_id_hex = hexmem(client_id, client_id_len);
e37f74a6
DW
960 if (!client_id_hex) {
961 r = -ENOMEM;
dacd6cee 962 goto fail;
e37f74a6
DW
963 }
964 fprintf(f, "CLIENTID=%s\n", client_id_hex);
965 }
966
e43a8393
BG
967 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
968 if (r >= 0) {
969 _cleanup_free_ char *option_hex = NULL;
970
971 option_hex = hexmem(data, data_len);
972 if (!option_hex) {
973 r = -ENOMEM;
dacd6cee 974 goto fail;
e43a8393
BG
975 }
976 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
977 }
978
a073309f 979 LIST_FOREACH(options, option, lease->private_options) {
dbcb4a90 980 char key[STRLEN("OPTION_000")+1];
0339cd77 981
d054f0a4 982 xsprintf(key, "OPTION_%" PRIu8, option->tag);
a073309f
AC
983 r = serialize_dhcp_option(f, key, option->data, option->length);
984 if (r < 0)
985 goto fail;
986 }
987
dacd6cee
LP
988 r = fflush_and_check(f);
989 if (r < 0)
990 goto fail;
fe8db0c5 991
dacd6cee 992 if (rename(temp_path, lease_file) < 0) {
fe8db0c5 993 r = -errno;
dacd6cee 994 goto fail;
fe8db0c5
TG
995 }
996
dacd6cee
LP
997 return 0;
998
999fail:
1000 if (temp_path)
1001 (void) unlink(temp_path);
fe8db0c5 1002
dacd6cee 1003 return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
fe8db0c5
TG
1004}
1005
bd91b83e 1006int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
0339cd77 1007
4afd3348 1008 _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
0339cd77
LP
1009 _cleanup_free_ char
1010 *address = NULL,
1011 *router = NULL,
1012 *netmask = NULL,
1013 *server_address = NULL,
1014 *next_server = NULL,
1015 *broadcast = NULL,
1016 *dns = NULL,
1017 *ntp = NULL,
1018 *mtu = NULL,
1019 *routes = NULL,
b85bc551 1020 *domains = NULL,
0339cd77
LP
1021 *client_id_hex = NULL,
1022 *vendor_specific_hex = NULL,
1023 *lifetime = NULL,
1024 *t1 = NULL,
1025 *t2 = NULL,
22805d92 1026 *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
0339cd77 1027
a073309f 1028 int r, i;
fe8db0c5
TG
1029
1030 assert(lease_file);
1031 assert(ret);
1032
1033 r = dhcp_lease_new(&lease);
1034 if (r < 0)
1035 return r;
1036
1037 r = parse_env_file(lease_file, NEWLINE,
1038 "ADDRESS", &address,
1039 "ROUTER", &router,
1040 "NETMASK", &netmask,
0ad853bc 1041 "SERVER_IDENTIFIER", &server_address,
8e34a618 1042 "NEXT_SERVER", &next_server,
0339cd77 1043 "BROADCAST", &broadcast,
109731eb
TG
1044 "DNS", &dns,
1045 "NTP", &ntp,
fe8db0c5
TG
1046 "MTU", &mtu,
1047 "DOMAINNAME", &lease->domainname,
1048 "HOSTNAME", &lease->hostname,
b85bc551 1049 "DOMAIN_SEARCH_LIST", &domains,
ce78df79 1050 "ROOT_PATH", &lease->root_path,
e1ea665e 1051 "ROUTES", &routes,
e37f74a6 1052 "CLIENTID", &client_id_hex,
8eb9058d 1053 "TIMEZONE", &lease->timezone,
e43a8393 1054 "VENDOR_SPECIFIC", &vendor_specific_hex,
0339cd77
LP
1055 "LIFETIME", &lifetime,
1056 "T1", &t1,
1057 "T2", &t2,
a073309f
AC
1058 "OPTION_224", &options[0],
1059 "OPTION_225", &options[1],
1060 "OPTION_226", &options[2],
1061 "OPTION_227", &options[3],
1062 "OPTION_228", &options[4],
1063 "OPTION_229", &options[5],
1064 "OPTION_230", &options[6],
1065 "OPTION_231", &options[7],
1066 "OPTION_232", &options[8],
1067 "OPTION_233", &options[9],
1068 "OPTION_234", &options[10],
1069 "OPTION_235", &options[11],
1070 "OPTION_236", &options[12],
1071 "OPTION_237", &options[13],
1072 "OPTION_238", &options[14],
1073 "OPTION_239", &options[15],
1074 "OPTION_240", &options[16],
1075 "OPTION_241", &options[17],
1076 "OPTION_242", &options[18],
1077 "OPTION_243", &options[19],
1078 "OPTION_244", &options[20],
1079 "OPTION_245", &options[21],
1080 "OPTION_246", &options[22],
1081 "OPTION_247", &options[23],
1082 "OPTION_248", &options[24],
1083 "OPTION_249", &options[25],
1084 "OPTION_250", &options[26],
1085 "OPTION_251", &options[27],
1086 "OPTION_252", &options[28],
1087 "OPTION_253", &options[29],
1088 "OPTION_254", &options[30],
fe8db0c5 1089 NULL);
fe8db0c5
TG
1090 if (r < 0)
1091 return r;
1092
0339cd77
LP
1093 if (address) {
1094 r = inet_pton(AF_INET, address, &lease->address);
1095 if (r <= 0)
e26ea7fc 1096 log_debug("Failed to parse address %s, ignoring.", address);
0339cd77 1097 }
fe8db0c5 1098
8ddbeaa2 1099 if (router) {
0339cd77
LP
1100 r = inet_pton(AF_INET, router, &lease->router);
1101 if (r <= 0)
e26ea7fc 1102 log_debug("Failed to parse router %s, ignoring.", router);
8ddbeaa2 1103 }
fe8db0c5 1104
0339cd77
LP
1105 if (netmask) {
1106 r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
1107 if (r <= 0)
e26ea7fc 1108 log_debug("Failed to parse netmask %s, ignoring.", netmask);
0339cd77
LP
1109 else
1110 lease->have_subnet_mask = true;
1111 }
fe8db0c5 1112
0ad853bc 1113 if (server_address) {
0339cd77
LP
1114 r = inet_pton(AF_INET, server_address, &lease->server_address);
1115 if (r <= 0)
e26ea7fc 1116 log_debug("Failed to parse server address %s, ignoring.", server_address);
0ad853bc
TG
1117 }
1118
8e34a618 1119 if (next_server) {
0339cd77
LP
1120 r = inet_pton(AF_INET, next_server, &lease->next_server);
1121 if (r <= 0)
e26ea7fc 1122 log_debug("Failed to parse next server %s, ignoring.", next_server);
0339cd77 1123 }
8e34a618 1124
0339cd77
LP
1125 if (broadcast) {
1126 r = inet_pton(AF_INET, broadcast, &lease->broadcast);
1127 if (r <= 0)
e26ea7fc 1128 log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
0339cd77
LP
1129 else
1130 lease->have_broadcast = true;
8e34a618
TG
1131 }
1132
109731eb 1133 if (dns) {
a2ba62c7 1134 r = deserialize_in_addrs(&lease->dns, dns);
109731eb 1135 if (r < 0)
0339cd77
LP
1136 log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
1137 else
1138 lease->dns_size = r;
109731eb
TG
1139 }
1140
1141 if (ntp) {
a2ba62c7 1142 r = deserialize_in_addrs(&lease->ntp, ntp);
109731eb 1143 if (r < 0)
0339cd77
LP
1144 log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
1145 else
1146 lease->ntp_size = r;
109731eb
TG
1147 }
1148
fe8db0c5 1149 if (mtu) {
0339cd77
LP
1150 r = safe_atou16(mtu, &lease->mtu);
1151 if (r < 0)
1152 log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
fe8db0c5
TG
1153 }
1154
b85bc551
DW
1155 if (domains) {
1156 _cleanup_strv_free_ char **a = NULL;
1157 a = strv_split(domains, " ");
1158 if (!a)
1159 return -ENOMEM;
1160
1161 if (!strv_isempty(a)) {
1162 lease->search_domains = a;
1163 a = NULL;
1164 }
1165 }
1166
e1ea665e 1167 if (routes) {
0339cd77
LP
1168 r = deserialize_dhcp_routes(
1169 &lease->static_route,
1170 &lease->static_route_size,
1171 &lease->static_route_allocated,
1172 routes);
1173 if (r < 0)
1174 log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
1175 }
1176
1177 if (lifetime) {
1178 r = safe_atou32(lifetime, &lease->lifetime);
1179 if (r < 0)
1180 log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
1181 }
1182
1183 if (t1) {
1184 r = safe_atou32(t1, &lease->t1);
1185 if (r < 0)
1186 log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
1187 }
1188
1189 if (t2) {
1190 r = safe_atou32(t2, &lease->t2);
e1ea665e 1191 if (r < 0)
0339cd77 1192 log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
e1ea665e
EY
1193 }
1194
e37f74a6 1195 if (client_id_hex) {
ce088d6f 1196 r = unhexmem(client_id_hex, (size_t) -1, &lease->client_id, &lease->client_id_len);
30494563 1197 if (r < 0)
0339cd77 1198 log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
e37f74a6
DW
1199 }
1200
e43a8393 1201 if (vendor_specific_hex) {
ce088d6f 1202 r = unhexmem(vendor_specific_hex, (size_t) -1, &lease->vendor_specific, &lease->vendor_specific_len);
e43a8393 1203 if (r < 0)
0339cd77 1204 log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
e43a8393
BG
1205 }
1206
22805d92 1207 for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
e4735228 1208 _cleanup_free_ void *data = NULL;
a073309f
AC
1209 size_t len;
1210
1211 if (!options[i])
1212 continue;
1213
ce088d6f 1214 r = unhexmem(options[i], (size_t) -1, &data, &len);
0339cd77
LP
1215 if (r < 0) {
1216 log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
1217 continue;
1218 }
a073309f 1219
22805d92 1220 r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
626be147 1221 if (r < 0)
a073309f
AC
1222 return r;
1223 }
1224
1cc6c93a 1225 *ret = TAKE_PTR(lease);
fe8db0c5
TG
1226
1227 return 0;
1228}
9e64dd72
TG
1229
1230int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
0339cd77 1231 struct in_addr address, mask;
df40eee8 1232 int r;
9e64dd72
TG
1233
1234 assert(lease);
9e64dd72 1235
0339cd77
LP
1236 if (lease->address == 0)
1237 return -ENODATA;
1238
df40eee8 1239 address.s_addr = lease->address;
9e64dd72
TG
1240
1241 /* fall back to the default subnet masks based on address class */
5a941f5f 1242 r = in4_addr_default_subnet_mask(&address, &mask);
df40eee8
TG
1243 if (r < 0)
1244 return r;
9e64dd72 1245
df40eee8 1246 lease->subnet_mask = mask.s_addr;
0339cd77 1247 lease->have_subnet_mask = true;
9e64dd72
TG
1248
1249 return 0;
1250}
e37f74a6 1251
0339cd77 1252int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
e37f74a6
DW
1253 assert_return(lease, -EINVAL);
1254 assert_return(client_id, -EINVAL);
1255 assert_return(client_id_len, -EINVAL);
1256
0339cd77
LP
1257 if (!lease->client_id)
1258 return -ENODATA;
1259
e37f74a6
DW
1260 *client_id = lease->client_id;
1261 *client_id_len = lease->client_id_len;
0339cd77 1262
e37f74a6
DW
1263 return 0;
1264}
1265
0339cd77 1266int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
e37f74a6 1267 assert_return(lease, -EINVAL);
0339cd77
LP
1268 assert_return(client_id || client_id_len <= 0, -EINVAL);
1269
1270 if (client_id_len <= 0)
1271 lease->client_id = mfree(lease->client_id);
1272 else {
1273 void *p;
e37f74a6 1274
0339cd77
LP
1275 p = memdup(client_id, client_id_len);
1276 if (!p)
1277 return -ENOMEM;
e37f74a6 1278
0339cd77
LP
1279 free(lease->client_id);
1280 lease->client_id = p;
e37f74a6
DW
1281 lease->client_id_len = client_id_len;
1282 }
1283
1284 return 0;
1285}
8eb9058d 1286
64d6c229 1287int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
8eb9058d 1288 assert_return(lease, -EINVAL);
64d6c229 1289 assert_return(tz, -EINVAL);
8eb9058d
LP
1290
1291 if (!lease->timezone)
0339cd77 1292 return -ENODATA;
8eb9058d 1293
64d6c229 1294 *tz = lease->timezone;
8eb9058d
LP
1295 return 0;
1296}
f8693fc7
BG
1297
1298int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
1299 assert_return(route, -EINVAL);
1300 assert_return(destination, -EINVAL);
1301
1302 *destination = route->dst_addr;
1303 return 0;
1304}
1305
1306int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
1307 assert_return(route, -EINVAL);
1308 assert_return(length, -EINVAL);
1309
1310 *length = route->dst_prefixlen;
1311 return 0;
1312}
1313
1314int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
1315 assert_return(route, -EINVAL);
1316 assert_return(gateway, -EINVAL);
1317
1318 *gateway = route->gw_addr;
1319 return 0;
1320}