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