]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
basic: document that people shouldn't use refcnt.h without reason
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-lease.c
CommitLineData
a6cc569e
TG
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5 Copyright (C) 2014 Tom Gundersen
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <stdlib.h>
22#include <errno.h>
23#include <string.h>
24#include <stdio.h>
fe8db0c5 25#include <arpa/inet.h>
a6cc569e 26
fe8db0c5 27#include "fileio.h"
bab47929 28#include "unaligned.h"
df40eee8 29#include "in-addr-util.h"
958b66ea 30#include "hostname-util.h"
a6cc569e 31#include "dhcp-protocol.h"
fe8db0c5
TG
32#include "dhcp-lease-internal.h"
33#include "sd-dhcp-lease.h"
09bee74d 34#include "network-internal.h"
37de2509 35#include "dns-domain.h"
a6cc569e
TG
36
37int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
38 assert_return(lease, -EINVAL);
39 assert_return(addr, -EINVAL);
40
41 addr->s_addr = lease->address;
42
43 return 0;
44}
45
68ceb9df
PF
46int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
47 assert_return(lease, -EINVAL);
1c6eb4e3 48 assert_return(lifetime, -EINVAL);
68ceb9df
PF
49
50 *lifetime = lease->lifetime;
51
52 return 0;
53}
54
a6cc569e
TG
55int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
56 assert_return(lease, -EINVAL);
57 assert_return(mtu, -EINVAL);
58
59 if (lease->mtu)
60 *mtu = lease->mtu;
61 else
62 return -ENOENT;
63
64 return 0;
65}
66
a2ba62c7 67int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
a6cc569e
TG
68 assert_return(lease, -EINVAL);
69 assert_return(addr, -EINVAL);
a6cc569e
TG
70
71 if (lease->dns_size) {
a6cc569e 72 *addr = lease->dns;
a2ba62c7 73 return lease->dns_size;
a6cc569e
TG
74 } else
75 return -ENOENT;
76
77 return 0;
78}
79
a2ba62c7 80int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
46844696
TG
81 assert_return(lease, -EINVAL);
82 assert_return(addr, -EINVAL);
46844696
TG
83
84 if (lease->ntp_size) {
46844696 85 *addr = lease->ntp;
a2ba62c7 86 return lease->ntp_size;
46844696
TG
87 } else
88 return -ENOENT;
89
90 return 0;
91}
92
a6cc569e
TG
93int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
94 assert_return(lease, -EINVAL);
95 assert_return(domainname, -EINVAL);
96
97 if (lease->domainname)
98 *domainname = lease->domainname;
99 else
100 return -ENOENT;
101
102 return 0;
103}
104
105int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
106 assert_return(lease, -EINVAL);
107 assert_return(hostname, -EINVAL);
108
109 if (lease->hostname)
110 *hostname = lease->hostname;
111 else
112 return -ENOENT;
113
114 return 0;
115}
116
ce78df79
TG
117int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
118 assert_return(lease, -EINVAL);
119 assert_return(root_path, -EINVAL);
120
121 if (lease->root_path)
122 *root_path = lease->root_path;
123 else
124 return -ENOENT;
125
126 return 0;
127}
128
a6cc569e
TG
129int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
130 assert_return(lease, -EINVAL);
131 assert_return(addr, -EINVAL);
132
8ddbeaa2
UTL
133 if (lease->router != INADDR_ANY)
134 addr->s_addr = lease->router;
135 else
136 return -ENOENT;
a6cc569e
TG
137
138 return 0;
139}
140
141int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
142 assert_return(lease, -EINVAL);
143 assert_return(addr, -EINVAL);
144
145 addr->s_addr = lease->subnet_mask;
146
147 return 0;
148}
149
0ad853bc
TG
150int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
151 assert_return(lease, -EINVAL);
152 assert_return(addr, -EINVAL);
153
154 addr->s_addr = lease->server_address;
155
156 return 0;
157}
158
8e34a618
TG
159int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
160 assert_return(lease, -EINVAL);
161 assert_return(addr, -EINVAL);
162
163 addr->s_addr = lease->next_server;
164
165 return 0;
166}
167
a2ba62c7 168int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
e1ea665e
EY
169
170 assert_return(lease, -EINVAL);
171 assert_return(routes, -EINVAL);
e1ea665e
EY
172
173 if (lease->static_route_size) {
174 *routes = lease->static_route;
a2ba62c7 175 return lease->static_route_size;
e1ea665e
EY
176 } else
177 return -ENOENT;
178
179 return 0;
180}
181
e43a8393
BG
182int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
183 size_t *data_len) {
184 assert_return(lease, -EINVAL);
185 assert_return(data, -EINVAL);
186 assert_return(data_len, -EINVAL);
187
188 if (!lease->vendor_specific)
189 return -ENOENT;
190
191 *data = lease->vendor_specific;
192 *data_len = lease->vendor_specific_len;
193
194 return 0;
195}
196
a6cc569e
TG
197sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
198 if (lease)
199 assert_se(REFCNT_INC(lease->n_ref) >= 2);
200
201 return lease;
202}
203
204sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
f0c4b1c3 205 if (lease && REFCNT_DEC(lease->n_ref) == 0) {
7e753d9d
AC
206 while (lease->private_options) {
207 struct sd_dhcp_raw_option *option = lease->private_options;
208
209 LIST_REMOVE(options, lease->private_options, option);
210
211 free(option->data);
212 free(option);
213 }
a6cc569e
TG
214 free(lease->hostname);
215 free(lease->domainname);
216 free(lease->dns);
81d98a39 217 free(lease->ntp);
e1ea665e 218 free(lease->static_route);
e37f74a6 219 free(lease->client_id);
e43a8393 220 free(lease->vendor_specific);
a6cc569e
TG
221 free(lease);
222 }
223
224 return NULL;
225}
226
e140ae58 227static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
e140ae58
TG
228 assert(option);
229 assert(ret);
230
231 if (len == 4) {
bab47929 232 *ret = unaligned_read_be32((be32_t*) option);
e140ae58
TG
233
234 if (*ret < min)
235 *ret = min;
236 }
237}
238
f5c0c00f
TG
239static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
240 lease_parse_u32(option, len, (uint32_t *)ret, 0);
241}
242
e140ae58 243static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
e140ae58
TG
244 assert(option);
245 assert(ret);
246
247 if (len == 2) {
bab47929 248 *ret = unaligned_read_be16((be16_t*) option);
e140ae58
TG
249
250 if (*ret < min)
251 *ret = min;
252 }
253}
254
255static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
256 assert(option);
257 assert(ret);
258
259 if (len == 4)
260 memcpy(ret, option, 4);
261}
262
f5c0c00f
TG
263static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
264 assert(option);
265 assert(ret);
266
267 if (len == 1)
268 *ret = !!(*option);
269}
270
271static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
272 assert(option);
273 assert(ret);
274
275 if (len == 1) {
276 *ret = *option;
277
278 if (*ret < min)
279 *ret = min;
280 }
281}
282
e140ae58
TG
283static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
284 assert(option);
285 assert(ret);
286
287 if (len >= 1) {
288 char *string;
289
43f447b1
LP
290 if (memchr(option, 0, len))
291 return -EINVAL;
292
e140ae58
TG
293 string = strndup((const char *)option, len);
294 if (!string)
43f447b1 295 return -ENOMEM;
e140ae58
TG
296
297 free(*ret);
298 *ret = string;
43f447b1
LP
299 } else
300 *ret = mfree(*ret);
e140ae58
TG
301
302 return 0;
303}
304
f5c0c00f 305static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
e140ae58
TG
306 assert(option);
307 assert(ret);
308 assert(ret_size);
309
f5c0c00f 310 if (len && !(len % (4 * mult))) {
e140ae58
TG
311 size_t size;
312 struct in_addr *addresses;
313
314 size = len / 4;
315
316 addresses = newdup(struct in_addr, option, size);
317 if (!addresses)
318 return -ENOMEM;
319
320 free(*ret);
321 *ret = addresses;
322 *ret_size = size;
323 }
324
325 return 0;
326}
327
f5c0c00f
TG
328static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
329 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
330}
331
332static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
333 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
334}
335
e1ea665e
EY
336static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
337 size_t *routes_size, size_t *routes_allocated) {
338
339 struct in_addr addr;
340
341 assert(option);
342 assert(routes);
343 assert(routes_size);
344 assert(routes_allocated);
345
346 if (!len)
347 return 0;
348
349 if (len % 8 != 0)
350 return -EINVAL;
351
352 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
353 return -ENOMEM;
354
355 while (len >= 8) {
356 struct sd_dhcp_route *route = *routes + *routes_size;
1caa12d0 357 int r;
e1ea665e 358
1caa12d0
TG
359 r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
360 if (r < 0) {
e1ea665e
EY
361 log_error("Failed to determine destination prefix length from class based IP, ignoring");
362 continue;
363 }
364
365 lease_parse_be32(option, 4, &addr.s_addr);
366 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
367 option += 4;
368
369 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
370 option += 4;
371
372 len -= 8;
373 (*routes_size)++;
374 }
375
376 return 0;
377}
378
379/* parses RFC3442 Classless Static Route Option */
380static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
381 size_t *routes_size, size_t *routes_allocated) {
382
383 assert(option);
384 assert(routes);
385 assert(routes_size);
386 assert(routes_allocated);
387
388 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
389
390 while (len > 0) {
391 uint8_t dst_octets;
392 struct sd_dhcp_route *route;
393
394 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
395 return -ENOMEM;
396
397 route = *routes + *routes_size;
398
399 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
400 route->dst_prefixlen = *option;
401 option++;
402 len--;
403
404 /* can't have more than 4 octets in IPv4 */
405 if (dst_octets > 4 || len < dst_octets)
406 return -EINVAL;
407
408 route->dst_addr.s_addr = 0;
409 memcpy(&route->dst_addr.s_addr, option, dst_octets);
410 option += dst_octets;
411 len -= dst_octets;
412
413 if (len < 4)
414 return -EINVAL;
415
416 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
417 option += 4;
418 len -= 4;
419
420 (*routes_size)++;
421 }
422
423 return 0;
424}
425
2d03c0b8
LP
426int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, void *userdata) {
427 sd_dhcp_lease *lease = userdata;
e140ae58
TG
428 int r;
429
430 assert(lease);
a6cc569e
TG
431
432 switch(code) {
433
f5c0c00f
TG
434 case DHCP_OPTION_TIME_OFFSET:
435 lease_parse_s32(option, len, &lease->time_offset);
f5c0c00f
TG
436 break;
437
438 case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
439 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
f5c0c00f
TG
440 break;
441
a6cc569e 442 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
e140ae58 443 lease_parse_u32(option, len, &lease->lifetime, 1);
a6cc569e
TG
444 break;
445
446 case DHCP_OPTION_SERVER_IDENTIFIER:
e140ae58 447 lease_parse_be32(option, len, &lease->server_address);
a6cc569e
TG
448 break;
449
450 case DHCP_OPTION_SUBNET_MASK:
e140ae58 451 lease_parse_be32(option, len, &lease->subnet_mask);
a6cc569e
TG
452 break;
453
f5c0c00f
TG
454 case DHCP_OPTION_BROADCAST:
455 lease_parse_be32(option, len, &lease->broadcast);
f5c0c00f
TG
456 break;
457
a6cc569e 458 case DHCP_OPTION_ROUTER:
2d03c0b8 459 if (len >= 4)
a0518527 460 lease_parse_be32(option, 4, &lease->router);
a6cc569e
TG
461
462 break;
463
464 case DHCP_OPTION_DOMAIN_NAME_SERVER:
2d03c0b8 465 return lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
46844696
TG
466
467 case DHCP_OPTION_NTP_SERVER:
2d03c0b8 468 return lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
a6cc569e 469
f5c0c00f 470 case DHCP_OPTION_POLICY_FILTER:
2d03c0b8 471 return lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
f5c0c00f
TG
472
473 case DHCP_OPTION_STATIC_ROUTE:
2d03c0b8 474 return lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
f5c0c00f 475
a6cc569e 476 case DHCP_OPTION_INTERFACE_MTU:
e140ae58 477 lease_parse_u16(option, len, &lease->mtu, 68);
a6cc569e
TG
478 break;
479
f5c0c00f
TG
480 case DHCP_OPTION_INTERFACE_MDR:
481 lease_parse_u16(option, len, &lease->mdr, 576);
f5c0c00f
TG
482 break;
483
484 case DHCP_OPTION_INTERFACE_TTL:
485 lease_parse_u8(option, len, &lease->ttl, 1);
f5c0c00f
TG
486 break;
487
488 case DHCP_OPTION_BOOT_FILE_SIZE:
489 lease_parse_u16(option, len, &lease->boot_file_size, 0);
f5c0c00f
TG
490 break;
491
90981625
LP
492 case DHCP_OPTION_DOMAIN_NAME: {
493 _cleanup_free_ char *domainname = NULL, *normalized = NULL;
784d9b9c
TG
494
495 r = lease_parse_string(option, len, &domainname);
e140ae58
TG
496 if (r < 0)
497 return r;
a6cc569e 498
90981625
LP
499 r = dns_name_normalize(domainname, &normalized);
500 if (r < 0) {
501 log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
502 return 0;
503 }
784d9b9c 504
90981625
LP
505 if (is_localhost(normalized)) {
506 log_debug_errno(r, "Detected 'localhost' as suggested hostname, ignoring.");
37de2509
NO
507 break;
508 }
509
784d9b9c 510 free(lease->domainname);
90981625
LP
511 lease->domainname = normalized;
512 normalized = NULL;
a6cc569e 513
784d9b9c
TG
514 break;
515 }
90981625
LP
516
517 case DHCP_OPTION_HOST_NAME: {
518 _cleanup_free_ char *hostname = NULL, *normalized = NULL;
784d9b9c
TG
519
520 r = lease_parse_string(option, len, &hostname);
e140ae58
TG
521 if (r < 0)
522 return r;
a6cc569e 523
90981625
LP
524 r = dns_name_normalize(hostname, &normalized);
525 if (r < 0) {
526 log_debug_errno(r, "Failed to normalize host name '%s': %m", hostname);
527 return 0;
528 }
f50f01f4 529
90981625
LP
530 if (is_localhost(normalized)) {
531 log_debug_errno(r, "Detected 'localhost' as suggested hostname, ignoring.");
532 return 0;
533 }
784d9b9c 534
90981625
LP
535 free(lease->hostname);
536 lease->hostname = normalized;
537 normalized = NULL;
a6cc569e 538
784d9b9c
TG
539 break;
540 }
ce78df79 541
2d03c0b8
LP
542 case DHCP_OPTION_ROOT_PATH:
543 return lease_parse_string(option, len, &lease->root_path);
ce78df79 544
a6cc569e 545 case DHCP_OPTION_RENEWAL_T1_TIME:
e140ae58 546 lease_parse_u32(option, len, &lease->t1, 1);
a6cc569e
TG
547 break;
548
549 case DHCP_OPTION_REBINDING_T2_TIME:
e140ae58 550 lease_parse_u32(option, len, &lease->t2, 1);
f5c0c00f
TG
551 break;
552
553 case DHCP_OPTION_ENABLE_IP_FORWARDING:
554 lease_parse_bool(option, len, &lease->ip_forward);
f5c0c00f
TG
555 break;
556
557 case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
558 lease_parse_bool(option, len, &lease->ip_forward_non_local);
a6cc569e 559 break;
e1ea665e
EY
560
561 case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
2d03c0b8
LP
562 return lease_parse_classless_routes(
563 option, len,
564 &lease->static_route,
565 &lease->static_route_size,
566 &lease->static_route_allocated);
e43a8393 567
8eb9058d
LP
568 case DHCP_OPTION_NEW_TZDB_TIMEZONE: {
569 _cleanup_free_ char *tz = NULL;
570
571 r = lease_parse_string(option, len, &tz);
572 if (r < 0)
573 return r;
574
575 if (!timezone_is_valid(tz))
576 return -EINVAL;
577
578 free(lease->timezone);
579 lease->timezone = tz;
580 tz = NULL;
581 break;
582 }
583
e43a8393 584 case DHCP_OPTION_VENDOR_SPECIFIC:
2d03c0b8 585
e43a8393
BG
586 if (len >= 1) {
587 free(lease->vendor_specific);
588 lease->vendor_specific = memdup(option, len);
589 if (!lease->vendor_specific)
590 return -ENOMEM;
591 lease->vendor_specific_len = len;
592 }
593
594 break;
7e753d9d
AC
595
596 default:
597 if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST)
598 break;
599
600 r = dhcp_lease_insert_private_option(lease, code, option, len);
601 if (r < 0)
602 return r;
a6cc569e
TG
603 }
604
605 return 0;
606}
607
7e753d9d
AC
608int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
609 const uint8_t *data, uint8_t len) {
610 struct sd_dhcp_raw_option *cur, *option;
611
612 LIST_FOREACH(options, cur, lease->private_options) {
613 if (tag < cur->tag)
614 break;
615 else if (tag == cur->tag) {
616 log_error("Ignoring duplicate option, tagged %d.", tag);
617 return 0;
618 }
619 }
620
621 option = new(struct sd_dhcp_raw_option, 1);
622 if (!option)
623 return -ENOMEM;
624
625 option->tag = tag;
626 option->length = len;
627 option->data = memdup(data, len);
628 if (!option->data) {
629 free(option);
630 return -ENOMEM;
631 }
632
633 LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
634
635 return 0;
636}
637
a6cc569e 638int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 639 sd_dhcp_lease *lease;
a6cc569e
TG
640
641 lease = new0(sd_dhcp_lease, 1);
642 if (!lease)
643 return -ENOMEM;
644
8ddbeaa2 645 lease->router = INADDR_ANY;
a6cc569e 646 lease->n_ref = REFCNT_INIT;
7e753d9d 647 LIST_HEAD_INIT(lease->private_options);
a6cc569e
TG
648
649 *ret = lease;
a6cc569e
TG
650 return 0;
651}
fe8db0c5 652
bd91b83e 653int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fe8db0c5
TG
654 _cleanup_free_ char *temp_path = NULL;
655 _cleanup_fclose_ FILE *f = NULL;
a073309f 656 struct sd_dhcp_raw_option *option;
fe8db0c5 657 struct in_addr address;
a2ba62c7 658 const struct in_addr *addresses;
e43a8393
BG
659 const uint8_t *client_id, *data;
660 size_t client_id_len, data_len;
fe8db0c5
TG
661 const char *string;
662 uint16_t mtu;
e1ea665e 663 struct sd_dhcp_route *routes;
fe8db0c5
TG
664 int r;
665
666 assert(lease);
667 assert(lease_file);
668
fe8db0c5
TG
669 r = fopen_temporary(lease_file, &f, &temp_path);
670 if (r < 0)
dacd6cee 671 goto fail;
fe8db0c5
TG
672
673 fchmod(fileno(f), 0644);
674
675 r = sd_dhcp_lease_get_address(lease, &address);
676 if (r < 0)
dacd6cee 677 goto fail;
fe8db0c5 678
fe8db0c5
TG
679 fprintf(f,
680 "# This is private data. Do not parse.\n"
109731eb 681 "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 682
fe8db0c5
TG
683 r = sd_dhcp_lease_get_netmask(lease, &address);
684 if (r < 0)
dacd6cee 685 goto fail;
fe8db0c5 686
109731eb 687 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 688
8ddbeaa2
UTL
689 r = sd_dhcp_lease_get_router(lease, &address);
690 if (r >= 0)
691 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
692
0ad853bc 693 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb
TG
694 if (r >= 0)
695 fprintf(f, "SERVER_ADDRESS=%s\n",
696 inet_ntoa(address));
0ad853bc 697
8e34a618 698 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
699 if (r >= 0)
700 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 701
fe8db0c5
TG
702 r = sd_dhcp_lease_get_mtu(lease, &mtu);
703 if (r >= 0)
704 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
705
b0e39c82 706 fputs("DNS=", f);
a2ba62c7 707 r = sd_dhcp_lease_get_dns(lease, &addresses);
109731eb 708 if (r >= 0)
b0e39c82
TG
709 serialize_in_addrs(f, addresses, r);
710 fputs("\n", f);
109731eb 711
b0e39c82 712 fputs("NTP=", f);
a2ba62c7 713 r = sd_dhcp_lease_get_ntp(lease, &addresses);
109731eb 714 if (r >= 0)
b0e39c82
TG
715 serialize_in_addrs(f, addresses, r);
716 fputs("\n", f);
fe8db0c5
TG
717
718 r = sd_dhcp_lease_get_domainname(lease, &string);
719 if (r >= 0)
720 fprintf(f, "DOMAINNAME=%s\n", string);
721
722 r = sd_dhcp_lease_get_hostname(lease, &string);
723 if (r >= 0)
724 fprintf(f, "HOSTNAME=%s\n", string);
725
ce78df79
TG
726 r = sd_dhcp_lease_get_root_path(lease, &string);
727 if (r >= 0)
728 fprintf(f, "ROOT_PATH=%s\n", string);
729
a2ba62c7 730 r = sd_dhcp_lease_get_routes(lease, &routes);
e1ea665e 731 if (r >= 0)
a2ba62c7 732 serialize_dhcp_routes(f, "ROUTES", routes, r);
e1ea665e 733
8eb9058d
LP
734 r = sd_dhcp_lease_get_timezone(lease, &string);
735 if (r >= 0)
736 fprintf(f, "TIMEZONE=%s\n", string);
737
e37f74a6
DW
738 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
739 if (r >= 0) {
740 _cleanup_free_ char *client_id_hex;
741
dde8bb32 742 client_id_hex = hexmem(client_id, client_id_len);
e37f74a6
DW
743 if (!client_id_hex) {
744 r = -ENOMEM;
dacd6cee 745 goto fail;
e37f74a6
DW
746 }
747 fprintf(f, "CLIENTID=%s\n", client_id_hex);
748 }
749
e43a8393
BG
750 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
751 if (r >= 0) {
752 _cleanup_free_ char *option_hex = NULL;
753
754 option_hex = hexmem(data, data_len);
755 if (!option_hex) {
756 r = -ENOMEM;
dacd6cee 757 goto fail;
e43a8393
BG
758 }
759 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
760 }
761
a073309f 762 LIST_FOREACH(options, option, lease->private_options) {
30afef87 763 char key[strlen("OPTION_000")+1];
a073309f
AC
764 snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
765 r = serialize_dhcp_option(f, key, option->data, option->length);
766 if (r < 0)
767 goto fail;
768 }
769
dacd6cee
LP
770 r = fflush_and_check(f);
771 if (r < 0)
772 goto fail;
fe8db0c5 773
dacd6cee 774 if (rename(temp_path, lease_file) < 0) {
fe8db0c5 775 r = -errno;
dacd6cee 776 goto fail;
fe8db0c5
TG
777 }
778
dacd6cee
LP
779 return 0;
780
781fail:
782 if (temp_path)
783 (void) unlink(temp_path);
fe8db0c5 784
dacd6cee 785 return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
fe8db0c5
TG
786}
787
bd91b83e 788int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
fe8db0c5
TG
789 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
790 _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
8e34a618 791 *server_address = NULL, *next_server = NULL,
e37f74a6 792 *dns = NULL, *ntp = NULL, *mtu = NULL,
e43a8393 793 *routes = NULL, *client_id_hex = NULL,
a073309f
AC
794 *vendor_specific_hex = NULL,
795 *options[DHCP_OPTION_PRIVATE_LAST -
796 DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };
fe8db0c5 797 struct in_addr addr;
a073309f 798 int r, i;
fe8db0c5
TG
799
800 assert(lease_file);
801 assert(ret);
802
803 r = dhcp_lease_new(&lease);
804 if (r < 0)
805 return r;
806
807 r = parse_env_file(lease_file, NEWLINE,
808 "ADDRESS", &address,
809 "ROUTER", &router,
810 "NETMASK", &netmask,
0ad853bc 811 "SERVER_IDENTIFIER", &server_address,
8e34a618 812 "NEXT_SERVER", &next_server,
109731eb
TG
813 "DNS", &dns,
814 "NTP", &ntp,
fe8db0c5
TG
815 "MTU", &mtu,
816 "DOMAINNAME", &lease->domainname,
817 "HOSTNAME", &lease->hostname,
ce78df79 818 "ROOT_PATH", &lease->root_path,
e1ea665e 819 "ROUTES", &routes,
e37f74a6 820 "CLIENTID", &client_id_hex,
8eb9058d 821 "TIMEZONE", &lease->timezone,
e43a8393 822 "VENDOR_SPECIFIC", &vendor_specific_hex,
a073309f
AC
823 "OPTION_224", &options[0],
824 "OPTION_225", &options[1],
825 "OPTION_226", &options[2],
826 "OPTION_227", &options[3],
827 "OPTION_228", &options[4],
828 "OPTION_229", &options[5],
829 "OPTION_230", &options[6],
830 "OPTION_231", &options[7],
831 "OPTION_232", &options[8],
832 "OPTION_233", &options[9],
833 "OPTION_234", &options[10],
834 "OPTION_235", &options[11],
835 "OPTION_236", &options[12],
836 "OPTION_237", &options[13],
837 "OPTION_238", &options[14],
838 "OPTION_239", &options[15],
839 "OPTION_240", &options[16],
840 "OPTION_241", &options[17],
841 "OPTION_242", &options[18],
842 "OPTION_243", &options[19],
843 "OPTION_244", &options[20],
844 "OPTION_245", &options[21],
845 "OPTION_246", &options[22],
846 "OPTION_247", &options[23],
847 "OPTION_248", &options[24],
848 "OPTION_249", &options[25],
849 "OPTION_250", &options[26],
850 "OPTION_251", &options[27],
851 "OPTION_252", &options[28],
852 "OPTION_253", &options[29],
853 "OPTION_254", &options[30],
fe8db0c5
TG
854 NULL);
855 if (r < 0) {
856 if (r == -ENOENT)
857 return 0;
858
23bbb0de 859 return log_error_errno(r, "Failed to read %s: %m", lease_file);
fe8db0c5
TG
860 }
861
862 r = inet_pton(AF_INET, address, &addr);
863 if (r < 0)
864 return r;
865
866 lease->address = addr.s_addr;
867
8ddbeaa2
UTL
868 if (router) {
869 r = inet_pton(AF_INET, router, &addr);
870 if (r < 0)
871 return r;
fe8db0c5 872
8ddbeaa2
UTL
873 lease->router = addr.s_addr;
874 }
fe8db0c5
TG
875
876 r = inet_pton(AF_INET, netmask, &addr);
877 if (r < 0)
878 return r;
879
880 lease->subnet_mask = addr.s_addr;
881
0ad853bc
TG
882 if (server_address) {
883 r = inet_pton(AF_INET, server_address, &addr);
884 if (r < 0)
885 return r;
886
887 lease->server_address = addr.s_addr;
888 }
889
8e34a618
TG
890 if (next_server) {
891 r = inet_pton(AF_INET, next_server, &addr);
892 if (r < 0)
893 return r;
894
895 lease->next_server = addr.s_addr;
896 }
897
109731eb 898 if (dns) {
a2ba62c7 899 r = deserialize_in_addrs(&lease->dns, dns);
109731eb
TG
900 if (r < 0)
901 return r;
a2ba62c7
LP
902
903 lease->dns_size = r;
109731eb
TG
904 }
905
906 if (ntp) {
a2ba62c7 907 r = deserialize_in_addrs(&lease->ntp, ntp);
109731eb
TG
908 if (r < 0)
909 return r;
a2ba62c7
LP
910
911 lease->ntp_size = r;
109731eb
TG
912 }
913
fe8db0c5
TG
914 if (mtu) {
915 uint16_t u;
916 if (sscanf(mtu, "%" SCNu16, &u) > 0)
917 lease->mtu = u;
918 }
919
e1ea665e
EY
920 if (routes) {
921 r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
922 &lease->static_route_allocated, routes);
923 if (r < 0)
924 return r;
925 }
926
e37f74a6 927 if (client_id_hex) {
9b57d9ae 928 r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
30494563
TG
929 if (r < 0)
930 return r;
e37f74a6
DW
931 }
932
e43a8393 933 if (vendor_specific_hex) {
9b57d9ae 934 r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
e43a8393
BG
935 if (r < 0)
936 return r;
937 }
938
a073309f 939 for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
626be147 940 _cleanup_free_ uint8_t *data = NULL;
a073309f
AC
941 size_t len;
942
943 if (!options[i])
944 continue;
945
946 r = deserialize_dhcp_option(&data, &len, options[i]);
947 if (r < 0)
948 return r;
949
950 r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
626be147 951 if (r < 0)
a073309f
AC
952 return r;
953 }
954
fe8db0c5
TG
955 *ret = lease;
956 lease = NULL;
957
958 return 0;
959}
9e64dd72
TG
960
961int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
df40eee8
TG
962 struct in_addr address;
963 struct in_addr mask;
964 int r;
9e64dd72
TG
965
966 assert(lease);
9e64dd72 967
df40eee8 968 address.s_addr = lease->address;
9e64dd72
TG
969
970 /* fall back to the default subnet masks based on address class */
df40eee8
TG
971 r = in_addr_default_subnet_mask(&address, &mask);
972 if (r < 0)
973 return r;
9e64dd72 974
df40eee8 975 lease->subnet_mask = mask.s_addr;
9e64dd72
TG
976
977 return 0;
978}
e37f74a6
DW
979
980int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
981 size_t *client_id_len) {
982 assert_return(lease, -EINVAL);
983 assert_return(client_id, -EINVAL);
984 assert_return(client_id_len, -EINVAL);
985
986 *client_id = lease->client_id;
987 *client_id_len = lease->client_id_len;
988 return 0;
989}
990
991int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
992 size_t client_id_len) {
993 assert_return(lease, -EINVAL);
994 assert_return((!client_id && !client_id_len) ||
995 (client_id && client_id_len), -EINVAL);
996
997 free (lease->client_id);
998 lease->client_id = NULL;
999 lease->client_id_len = 0;
1000
1001 if (client_id) {
1002 lease->client_id = memdup (client_id, client_id_len);
1003 lease->client_id_len = client_id_len;
1004 }
1005
1006 return 0;
1007}
8eb9058d
LP
1008
1009int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone) {
1010 assert_return(lease, -EINVAL);
1011 assert_return(timezone, -EINVAL);
1012
1013 if (!lease->timezone)
1014 return -ENXIO;
1015
1016 *timezone = lease->timezone;
1017 return 0;
1018}