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