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