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