]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
dhcp: be more careful when parsing strings from DHCP packets
[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
a6cc569e
TG
426int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
427 void *user_data) {
428 sd_dhcp_lease *lease = user_data;
e140ae58
TG
429 int r;
430
431 assert(lease);
a6cc569e
TG
432
433 switch(code) {
434
f5c0c00f
TG
435 case DHCP_OPTION_TIME_OFFSET:
436 lease_parse_s32(option, len, &lease->time_offset);
437
438 break;
439
440 case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
441 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
442
443 break;
444
a6cc569e 445 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
e140ae58 446 lease_parse_u32(option, len, &lease->lifetime, 1);
a6cc569e
TG
447
448 break;
449
450 case DHCP_OPTION_SERVER_IDENTIFIER:
e140ae58 451 lease_parse_be32(option, len, &lease->server_address);
a6cc569e
TG
452
453 break;
454
455 case DHCP_OPTION_SUBNET_MASK:
e140ae58 456 lease_parse_be32(option, len, &lease->subnet_mask);
a6cc569e
TG
457
458 break;
459
f5c0c00f
TG
460 case DHCP_OPTION_BROADCAST:
461 lease_parse_be32(option, len, &lease->broadcast);
462
463 break;
464
a6cc569e 465 case DHCP_OPTION_ROUTER:
a0518527
NO
466 if(len >= 4)
467 lease_parse_be32(option, 4, &lease->router);
a6cc569e
TG
468
469 break;
470
471 case DHCP_OPTION_DOMAIN_NAME_SERVER:
e140ae58
TG
472 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
473 if (r < 0)
474 return r;
46844696
TG
475
476 break;
477
478 case DHCP_OPTION_NTP_SERVER:
e140ae58
TG
479 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
480 if (r < 0)
481 return r;
a6cc569e
TG
482
483 break;
484
f5c0c00f
TG
485 case DHCP_OPTION_POLICY_FILTER:
486 r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
487 if (r < 0)
488 return r;
489
490 break;
491
492 case DHCP_OPTION_STATIC_ROUTE:
e1ea665e
EY
493 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
494 &lease->static_route_allocated);
f5c0c00f
TG
495 if (r < 0)
496 return r;
497
498 break;
499
a6cc569e 500 case DHCP_OPTION_INTERFACE_MTU:
e140ae58 501 lease_parse_u16(option, len, &lease->mtu, 68);
a6cc569e
TG
502
503 break;
504
f5c0c00f
TG
505 case DHCP_OPTION_INTERFACE_MDR:
506 lease_parse_u16(option, len, &lease->mdr, 576);
507
508 break;
509
510 case DHCP_OPTION_INTERFACE_TTL:
511 lease_parse_u8(option, len, &lease->ttl, 1);
512
513 break;
514
515 case DHCP_OPTION_BOOT_FILE_SIZE:
516 lease_parse_u16(option, len, &lease->boot_file_size, 0);
517
518 break;
519
a6cc569e 520 case DHCP_OPTION_DOMAIN_NAME:
784d9b9c
TG
521 {
522 _cleanup_free_ char *domainname = NULL;
f50f01f4 523 char *e;
784d9b9c
TG
524
525 r = lease_parse_string(option, len, &domainname);
e140ae58
TG
526 if (r < 0)
527 return r;
a6cc569e 528
f50f01f4
LP
529 /* Chop off trailing dot of domain name that some DHCP
530 * servers send us back. Internally we want to store
531 * host names without trailing dots and
532 * host_name_is_valid() doesn't accept them. */
533 e = endswith(domainname, ".");
534 if (e)
535 *e = 0;
536
37de2509 537 if (is_localhost(domainname))
784d9b9c
TG
538 break;
539
37de2509
NO
540 r = dns_name_is_valid(domainname);
541 if (r <= 0) {
542 if (r < 0)
543 log_error_errno(r, "Failed to validate domain name: %s: %m", domainname);
544 if (r == 0)
545 log_warning("Domain name is not valid, ignoring: %s", domainname);
546 break;
547 }
548
784d9b9c
TG
549 free(lease->domainname);
550 lease->domainname = domainname;
551 domainname = NULL;
a6cc569e 552
784d9b9c
TG
553 break;
554 }
a6cc569e 555 case DHCP_OPTION_HOST_NAME:
784d9b9c
TG
556 {
557 _cleanup_free_ char *hostname = NULL;
f50f01f4 558 char *e;
784d9b9c
TG
559
560 r = lease_parse_string(option, len, &hostname);
e140ae58
TG
561 if (r < 0)
562 return r;
a6cc569e 563
f50f01f4
LP
564 e = endswith(hostname, ".");
565 if (e)
566 *e = 0;
567
8fb49443 568 if (!hostname_is_valid(hostname, false) || is_localhost(hostname))
784d9b9c
TG
569 break;
570
8fb49443 571 free_and_replace(&lease->hostname, hostname);
784d9b9c 572 hostname = NULL;
a6cc569e 573
784d9b9c
TG
574 break;
575 }
ce78df79 576 case DHCP_OPTION_ROOT_PATH:
e140ae58
TG
577 r = lease_parse_string(option, len, &lease->root_path);
578 if (r < 0)
579 return r;
ce78df79
TG
580
581 break;
582
a6cc569e 583 case DHCP_OPTION_RENEWAL_T1_TIME:
e140ae58 584 lease_parse_u32(option, len, &lease->t1, 1);
a6cc569e
TG
585
586 break;
587
588 case DHCP_OPTION_REBINDING_T2_TIME:
e140ae58 589 lease_parse_u32(option, len, &lease->t2, 1);
a6cc569e 590
f5c0c00f
TG
591 break;
592
593 case DHCP_OPTION_ENABLE_IP_FORWARDING:
594 lease_parse_bool(option, len, &lease->ip_forward);
595
596 break;
597
598 case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
599 lease_parse_bool(option, len, &lease->ip_forward_non_local);
600
a6cc569e 601 break;
e1ea665e
EY
602
603 case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
604 r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
605 &lease->static_route_allocated);
606 if (r < 0)
607 return r;
608
609 break;
e43a8393
BG
610
611 case DHCP_OPTION_VENDOR_SPECIFIC:
612 if (len >= 1) {
613 free(lease->vendor_specific);
614 lease->vendor_specific = memdup(option, len);
615 if (!lease->vendor_specific)
616 return -ENOMEM;
617 lease->vendor_specific_len = len;
618 }
619
620 break;
7e753d9d
AC
621
622 default:
623 if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST)
624 break;
625
626 r = dhcp_lease_insert_private_option(lease, code, option, len);
627 if (r < 0)
628 return r;
a6cc569e
TG
629 }
630
631 return 0;
632}
633
7e753d9d
AC
634int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
635 const uint8_t *data, uint8_t len) {
636 struct sd_dhcp_raw_option *cur, *option;
637
638 LIST_FOREACH(options, cur, lease->private_options) {
639 if (tag < cur->tag)
640 break;
641 else if (tag == cur->tag) {
642 log_error("Ignoring duplicate option, tagged %d.", tag);
643 return 0;
644 }
645 }
646
647 option = new(struct sd_dhcp_raw_option, 1);
648 if (!option)
649 return -ENOMEM;
650
651 option->tag = tag;
652 option->length = len;
653 option->data = memdup(data, len);
654 if (!option->data) {
655 free(option);
656 return -ENOMEM;
657 }
658
659 LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
660
661 return 0;
662}
663
a6cc569e 664int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 665 sd_dhcp_lease *lease;
a6cc569e
TG
666
667 lease = new0(sd_dhcp_lease, 1);
668 if (!lease)
669 return -ENOMEM;
670
8ddbeaa2 671 lease->router = INADDR_ANY;
a6cc569e 672 lease->n_ref = REFCNT_INIT;
7e753d9d 673 LIST_HEAD_INIT(lease->private_options);
a6cc569e
TG
674
675 *ret = lease;
a6cc569e
TG
676 return 0;
677}
fe8db0c5 678
1dc24d5f 679int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fe8db0c5
TG
680 _cleanup_free_ char *temp_path = NULL;
681 _cleanup_fclose_ FILE *f = NULL;
a073309f 682 struct sd_dhcp_raw_option *option;
fe8db0c5 683 struct in_addr address;
a2ba62c7 684 const struct in_addr *addresses;
e43a8393
BG
685 const uint8_t *client_id, *data;
686 size_t client_id_len, data_len;
fe8db0c5
TG
687 const char *string;
688 uint16_t mtu;
e1ea665e 689 struct sd_dhcp_route *routes;
fe8db0c5
TG
690 int r;
691
692 assert(lease);
693 assert(lease_file);
694
fe8db0c5
TG
695 r = fopen_temporary(lease_file, &f, &temp_path);
696 if (r < 0)
dacd6cee 697 goto fail;
fe8db0c5
TG
698
699 fchmod(fileno(f), 0644);
700
701 r = sd_dhcp_lease_get_address(lease, &address);
702 if (r < 0)
dacd6cee 703 goto fail;
fe8db0c5 704
fe8db0c5
TG
705 fprintf(f,
706 "# This is private data. Do not parse.\n"
109731eb 707 "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 708
fe8db0c5
TG
709 r = sd_dhcp_lease_get_netmask(lease, &address);
710 if (r < 0)
dacd6cee 711 goto fail;
fe8db0c5 712
109731eb 713 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 714
8ddbeaa2
UTL
715 r = sd_dhcp_lease_get_router(lease, &address);
716 if (r >= 0)
717 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
718
0ad853bc 719 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb
TG
720 if (r >= 0)
721 fprintf(f, "SERVER_ADDRESS=%s\n",
722 inet_ntoa(address));
0ad853bc 723
8e34a618 724 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
725 if (r >= 0)
726 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 727
fe8db0c5
TG
728 r = sd_dhcp_lease_get_mtu(lease, &mtu);
729 if (r >= 0)
730 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
731
b0e39c82 732 fputs("DNS=", f);
a2ba62c7 733 r = sd_dhcp_lease_get_dns(lease, &addresses);
109731eb 734 if (r >= 0)
b0e39c82
TG
735 serialize_in_addrs(f, addresses, r);
736 fputs("\n", f);
109731eb 737
b0e39c82 738 fputs("NTP=", f);
a2ba62c7 739 r = sd_dhcp_lease_get_ntp(lease, &addresses);
109731eb 740 if (r >= 0)
b0e39c82
TG
741 serialize_in_addrs(f, addresses, r);
742 fputs("\n", f);
fe8db0c5
TG
743
744 r = sd_dhcp_lease_get_domainname(lease, &string);
745 if (r >= 0)
746 fprintf(f, "DOMAINNAME=%s\n", string);
747
748 r = sd_dhcp_lease_get_hostname(lease, &string);
749 if (r >= 0)
750 fprintf(f, "HOSTNAME=%s\n", string);
751
ce78df79
TG
752 r = sd_dhcp_lease_get_root_path(lease, &string);
753 if (r >= 0)
754 fprintf(f, "ROOT_PATH=%s\n", string);
755
a2ba62c7 756 r = sd_dhcp_lease_get_routes(lease, &routes);
e1ea665e 757 if (r >= 0)
a2ba62c7 758 serialize_dhcp_routes(f, "ROUTES", routes, r);
e1ea665e 759
e37f74a6
DW
760 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
761 if (r >= 0) {
762 _cleanup_free_ char *client_id_hex;
763
dde8bb32 764 client_id_hex = hexmem(client_id, client_id_len);
e37f74a6
DW
765 if (!client_id_hex) {
766 r = -ENOMEM;
dacd6cee 767 goto fail;
e37f74a6
DW
768 }
769 fprintf(f, "CLIENTID=%s\n", client_id_hex);
770 }
771
e43a8393
BG
772 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
773 if (r >= 0) {
774 _cleanup_free_ char *option_hex = NULL;
775
776 option_hex = hexmem(data, data_len);
777 if (!option_hex) {
778 r = -ENOMEM;
dacd6cee 779 goto fail;
e43a8393
BG
780 }
781 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
782 }
783
a073309f 784 LIST_FOREACH(options, option, lease->private_options) {
30afef87 785 char key[strlen("OPTION_000")+1];
a073309f
AC
786 snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
787 r = serialize_dhcp_option(f, key, option->data, option->length);
788 if (r < 0)
789 goto fail;
790 }
791
dacd6cee
LP
792 r = fflush_and_check(f);
793 if (r < 0)
794 goto fail;
fe8db0c5 795
dacd6cee 796 if (rename(temp_path, lease_file) < 0) {
fe8db0c5 797 r = -errno;
dacd6cee 798 goto fail;
fe8db0c5
TG
799 }
800
dacd6cee
LP
801 return 0;
802
803fail:
804 if (temp_path)
805 (void) unlink(temp_path);
fe8db0c5 806
dacd6cee 807 return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
fe8db0c5
TG
808}
809
1dc24d5f 810int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
fe8db0c5
TG
811 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
812 _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
8e34a618 813 *server_address = NULL, *next_server = NULL,
e37f74a6 814 *dns = NULL, *ntp = NULL, *mtu = NULL,
e43a8393 815 *routes = NULL, *client_id_hex = NULL,
a073309f
AC
816 *vendor_specific_hex = NULL,
817 *options[DHCP_OPTION_PRIVATE_LAST -
818 DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };
fe8db0c5 819 struct in_addr addr;
a073309f 820 int r, i;
fe8db0c5
TG
821
822 assert(lease_file);
823 assert(ret);
824
825 r = dhcp_lease_new(&lease);
826 if (r < 0)
827 return r;
828
829 r = parse_env_file(lease_file, NEWLINE,
830 "ADDRESS", &address,
831 "ROUTER", &router,
832 "NETMASK", &netmask,
0ad853bc 833 "SERVER_IDENTIFIER", &server_address,
8e34a618 834 "NEXT_SERVER", &next_server,
109731eb
TG
835 "DNS", &dns,
836 "NTP", &ntp,
fe8db0c5
TG
837 "MTU", &mtu,
838 "DOMAINNAME", &lease->domainname,
839 "HOSTNAME", &lease->hostname,
ce78df79 840 "ROOT_PATH", &lease->root_path,
e1ea665e 841 "ROUTES", &routes,
e37f74a6 842 "CLIENTID", &client_id_hex,
e43a8393 843 "VENDOR_SPECIFIC", &vendor_specific_hex,
a073309f
AC
844 "OPTION_224", &options[0],
845 "OPTION_225", &options[1],
846 "OPTION_226", &options[2],
847 "OPTION_227", &options[3],
848 "OPTION_228", &options[4],
849 "OPTION_229", &options[5],
850 "OPTION_230", &options[6],
851 "OPTION_231", &options[7],
852 "OPTION_232", &options[8],
853 "OPTION_233", &options[9],
854 "OPTION_234", &options[10],
855 "OPTION_235", &options[11],
856 "OPTION_236", &options[12],
857 "OPTION_237", &options[13],
858 "OPTION_238", &options[14],
859 "OPTION_239", &options[15],
860 "OPTION_240", &options[16],
861 "OPTION_241", &options[17],
862 "OPTION_242", &options[18],
863 "OPTION_243", &options[19],
864 "OPTION_244", &options[20],
865 "OPTION_245", &options[21],
866 "OPTION_246", &options[22],
867 "OPTION_247", &options[23],
868 "OPTION_248", &options[24],
869 "OPTION_249", &options[25],
870 "OPTION_250", &options[26],
871 "OPTION_251", &options[27],
872 "OPTION_252", &options[28],
873 "OPTION_253", &options[29],
874 "OPTION_254", &options[30],
fe8db0c5
TG
875 NULL);
876 if (r < 0) {
877 if (r == -ENOENT)
878 return 0;
879
23bbb0de 880 return log_error_errno(r, "Failed to read %s: %m", lease_file);
fe8db0c5
TG
881 }
882
883 r = inet_pton(AF_INET, address, &addr);
884 if (r < 0)
885 return r;
886
887 lease->address = addr.s_addr;
888
8ddbeaa2
UTL
889 if (router) {
890 r = inet_pton(AF_INET, router, &addr);
891 if (r < 0)
892 return r;
fe8db0c5 893
8ddbeaa2
UTL
894 lease->router = addr.s_addr;
895 }
fe8db0c5
TG
896
897 r = inet_pton(AF_INET, netmask, &addr);
898 if (r < 0)
899 return r;
900
901 lease->subnet_mask = addr.s_addr;
902
0ad853bc
TG
903 if (server_address) {
904 r = inet_pton(AF_INET, server_address, &addr);
905 if (r < 0)
906 return r;
907
908 lease->server_address = addr.s_addr;
909 }
910
8e34a618
TG
911 if (next_server) {
912 r = inet_pton(AF_INET, next_server, &addr);
913 if (r < 0)
914 return r;
915
916 lease->next_server = addr.s_addr;
917 }
918
109731eb 919 if (dns) {
a2ba62c7 920 r = deserialize_in_addrs(&lease->dns, dns);
109731eb
TG
921 if (r < 0)
922 return r;
a2ba62c7
LP
923
924 lease->dns_size = r;
109731eb
TG
925 }
926
927 if (ntp) {
a2ba62c7 928 r = deserialize_in_addrs(&lease->ntp, ntp);
109731eb
TG
929 if (r < 0)
930 return r;
a2ba62c7
LP
931
932 lease->ntp_size = r;
109731eb
TG
933 }
934
fe8db0c5
TG
935 if (mtu) {
936 uint16_t u;
937 if (sscanf(mtu, "%" SCNu16, &u) > 0)
938 lease->mtu = u;
939 }
940
e1ea665e
EY
941 if (routes) {
942 r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
943 &lease->static_route_allocated, routes);
944 if (r < 0)
945 return r;
946 }
947
e37f74a6 948 if (client_id_hex) {
9b57d9ae 949 r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
30494563
TG
950 if (r < 0)
951 return r;
e37f74a6
DW
952 }
953
e43a8393 954 if (vendor_specific_hex) {
9b57d9ae 955 r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
e43a8393
BG
956 if (r < 0)
957 return r;
958 }
959
a073309f 960 for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
626be147 961 _cleanup_free_ uint8_t *data = NULL;
a073309f
AC
962 size_t len;
963
964 if (!options[i])
965 continue;
966
967 r = deserialize_dhcp_option(&data, &len, options[i]);
968 if (r < 0)
969 return r;
970
971 r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
626be147 972 if (r < 0)
a073309f
AC
973 return r;
974 }
975
fe8db0c5
TG
976 *ret = lease;
977 lease = NULL;
978
979 return 0;
980}
9e64dd72
TG
981
982int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
df40eee8
TG
983 struct in_addr address;
984 struct in_addr mask;
985 int r;
9e64dd72
TG
986
987 assert(lease);
9e64dd72 988
df40eee8 989 address.s_addr = lease->address;
9e64dd72
TG
990
991 /* fall back to the default subnet masks based on address class */
df40eee8
TG
992 r = in_addr_default_subnet_mask(&address, &mask);
993 if (r < 0)
994 return r;
9e64dd72 995
df40eee8 996 lease->subnet_mask = mask.s_addr;
9e64dd72
TG
997
998 return 0;
999}
e37f74a6
DW
1000
1001int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
1002 size_t *client_id_len) {
1003 assert_return(lease, -EINVAL);
1004 assert_return(client_id, -EINVAL);
1005 assert_return(client_id_len, -EINVAL);
1006
1007 *client_id = lease->client_id;
1008 *client_id_len = lease->client_id_len;
1009 return 0;
1010}
1011
1012int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
1013 size_t client_id_len) {
1014 assert_return(lease, -EINVAL);
1015 assert_return((!client_id && !client_id_len) ||
1016 (client_id && client_id_len), -EINVAL);
1017
1018 free (lease->client_id);
1019 lease->client_id = NULL;
1020 lease->client_id_len = 0;
1021
1022 if (client_id) {
1023 lease->client_id = memdup (client_id, client_id_len);
1024 lease->client_id_len = client_id_len;
1025 }
1026
1027 return 0;
1028}