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