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