Line data Source code
1 : /***
2 : This file is part of systemd.
3 :
4 : Copyright (C) 2013 Intel Corporation. All rights reserved.
5 : Copyright (C) 2014 Tom Gundersen
6 :
7 : systemd is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU Lesser General Public License as published by
9 : the Free Software Foundation; either version 2.1 of the License, or
10 : (at your option) any later version.
11 :
12 : systemd is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public License
18 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 : ***/
20 :
21 : #include <stdlib.h>
22 : #include <errno.h>
23 : #include <string.h>
24 : #include <stdio.h>
25 : #include <arpa/inet.h>
26 :
27 : #include "fileio.h"
28 : #include "unaligned.h"
29 : #include "in-addr-util.h"
30 : #include "hostname-util.h"
31 : #include "dhcp-protocol.h"
32 : #include "dhcp-lease-internal.h"
33 : #include "sd-dhcp-lease.h"
34 : #include "network-internal.h"
35 : #include "dns-domain.h"
36 :
37 1 : int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
38 1 : assert_return(lease, -EINVAL);
39 1 : assert_return(addr, -EINVAL);
40 :
41 1 : addr->s_addr = lease->address;
42 :
43 1 : return 0;
44 : }
45 :
46 0 : int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
47 0 : assert_return(lease, -EINVAL);
48 0 : assert_return(lifetime, -EINVAL);
49 :
50 0 : *lifetime = lease->lifetime;
51 :
52 0 : return 0;
53 : }
54 :
55 0 : int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
56 0 : assert_return(lease, -EINVAL);
57 0 : assert_return(mtu, -EINVAL);
58 :
59 0 : if (lease->mtu)
60 0 : *mtu = lease->mtu;
61 : else
62 0 : return -ENOENT;
63 :
64 0 : return 0;
65 : }
66 :
67 0 : int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
68 0 : assert_return(lease, -EINVAL);
69 0 : assert_return(addr, -EINVAL);
70 :
71 0 : if (lease->dns_size) {
72 0 : *addr = lease->dns;
73 0 : return lease->dns_size;
74 : } else
75 0 : return -ENOENT;
76 :
77 : return 0;
78 : }
79 :
80 0 : int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
81 0 : assert_return(lease, -EINVAL);
82 0 : assert_return(addr, -EINVAL);
83 :
84 0 : if (lease->ntp_size) {
85 0 : *addr = lease->ntp;
86 0 : return lease->ntp_size;
87 : } else
88 0 : return -ENOENT;
89 :
90 : return 0;
91 : }
92 :
93 0 : int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
94 0 : assert_return(lease, -EINVAL);
95 0 : assert_return(domainname, -EINVAL);
96 :
97 0 : if (lease->domainname)
98 0 : *domainname = lease->domainname;
99 : else
100 0 : return -ENOENT;
101 :
102 0 : return 0;
103 : }
104 :
105 0 : int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
106 0 : assert_return(lease, -EINVAL);
107 0 : assert_return(hostname, -EINVAL);
108 :
109 0 : if (lease->hostname)
110 0 : *hostname = lease->hostname;
111 : else
112 0 : return -ENOENT;
113 :
114 0 : return 0;
115 : }
116 :
117 0 : int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
118 0 : assert_return(lease, -EINVAL);
119 0 : assert_return(root_path, -EINVAL);
120 :
121 0 : if (lease->root_path)
122 0 : *root_path = lease->root_path;
123 : else
124 0 : return -ENOENT;
125 :
126 0 : return 0;
127 : }
128 :
129 1 : int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
130 1 : assert_return(lease, -EINVAL);
131 1 : assert_return(addr, -EINVAL);
132 :
133 1 : if (lease->router != INADDR_ANY)
134 1 : addr->s_addr = lease->router;
135 : else
136 0 : return -ENOENT;
137 :
138 1 : return 0;
139 : }
140 :
141 1 : int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
142 1 : assert_return(lease, -EINVAL);
143 1 : assert_return(addr, -EINVAL);
144 :
145 1 : addr->s_addr = lease->subnet_mask;
146 :
147 1 : return 0;
148 : }
149 :
150 0 : int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
151 0 : assert_return(lease, -EINVAL);
152 0 : assert_return(addr, -EINVAL);
153 :
154 0 : addr->s_addr = lease->server_address;
155 :
156 0 : return 0;
157 : }
158 :
159 0 : int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
160 0 : assert_return(lease, -EINVAL);
161 0 : assert_return(addr, -EINVAL);
162 :
163 0 : addr->s_addr = lease->next_server;
164 :
165 0 : return 0;
166 : }
167 :
168 0 : int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
169 :
170 0 : assert_return(lease, -EINVAL);
171 0 : assert_return(routes, -EINVAL);
172 :
173 0 : if (lease->static_route_size) {
174 0 : *routes = lease->static_route;
175 0 : return lease->static_route_size;
176 : } else
177 0 : return -ENOENT;
178 :
179 : return 0;
180 : }
181 :
182 0 : int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
183 : size_t *data_len) {
184 0 : assert_return(lease, -EINVAL);
185 0 : assert_return(data, -EINVAL);
186 0 : assert_return(data_len, -EINVAL);
187 :
188 0 : if (!lease->vendor_specific)
189 0 : return -ENOENT;
190 :
191 0 : *data = lease->vendor_specific;
192 0 : *data_len = lease->vendor_specific_len;
193 :
194 0 : return 0;
195 : }
196 :
197 1 : sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
198 1 : if (lease)
199 1 : assert_se(REFCNT_INC(lease->n_ref) >= 2);
200 :
201 1 : return lease;
202 : }
203 :
204 11 : sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
205 11 : if (lease && REFCNT_DEC(lease->n_ref) == 0) {
206 2 : free(lease->hostname);
207 2 : free(lease->domainname);
208 2 : free(lease->dns);
209 2 : free(lease->ntp);
210 2 : free(lease->static_route);
211 2 : free(lease->client_id);
212 2 : free(lease->vendor_specific);
213 2 : free(lease);
214 : }
215 :
216 11 : return NULL;
217 : }
218 :
219 2 : static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
220 2 : assert(option);
221 2 : assert(ret);
222 :
223 2 : if (len == 4) {
224 2 : *ret = unaligned_read_be32((be32_t*) option);
225 :
226 2 : if (*ret < min)
227 0 : *ret = min;
228 : }
229 2 : }
230 :
231 0 : static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
232 0 : lease_parse_u32(option, len, (uint32_t *)ret, 0);
233 0 : }
234 :
235 0 : static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
236 0 : assert(option);
237 0 : assert(ret);
238 :
239 0 : if (len == 2) {
240 0 : *ret = unaligned_read_be16((be16_t*) option);
241 :
242 0 : if (*ret < min)
243 0 : *ret = min;
244 : }
245 0 : }
246 :
247 6 : static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
248 6 : assert(option);
249 6 : assert(ret);
250 :
251 6 : if (len == 4)
252 6 : memcpy(ret, option, 4);
253 6 : }
254 :
255 0 : static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
256 0 : assert(option);
257 0 : assert(ret);
258 :
259 0 : if (len == 1)
260 0 : *ret = !!(*option);
261 0 : }
262 :
263 0 : static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
264 0 : assert(option);
265 0 : assert(ret);
266 :
267 0 : if (len == 1) {
268 0 : *ret = *option;
269 :
270 0 : if (*ret < min)
271 0 : *ret = min;
272 : }
273 0 : }
274 :
275 2 : static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
276 2 : assert(option);
277 2 : assert(ret);
278 :
279 2 : if (len >= 1) {
280 : char *string;
281 :
282 2 : string = strndup((const char *)option, len);
283 2 : if (!string)
284 0 : return -errno;
285 :
286 2 : free(*ret);
287 2 : *ret = string;
288 : }
289 :
290 2 : return 0;
291 : }
292 :
293 2 : static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
294 2 : assert(option);
295 2 : assert(ret);
296 2 : assert(ret_size);
297 :
298 2 : if (len && !(len % (4 * mult))) {
299 : size_t size;
300 : struct in_addr *addresses;
301 :
302 2 : size = len / 4;
303 :
304 2 : addresses = newdup(struct in_addr, option, size);
305 2 : if (!addresses)
306 0 : return -ENOMEM;
307 :
308 2 : free(*ret);
309 2 : *ret = addresses;
310 2 : *ret_size = size;
311 : }
312 :
313 2 : return 0;
314 : }
315 :
316 2 : static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
317 2 : return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
318 : }
319 :
320 0 : static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
321 0 : return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
322 : }
323 :
324 0 : static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
325 : size_t *routes_size, size_t *routes_allocated) {
326 :
327 : struct in_addr addr;
328 :
329 0 : assert(option);
330 0 : assert(routes);
331 0 : assert(routes_size);
332 0 : assert(routes_allocated);
333 :
334 0 : if (!len)
335 0 : return 0;
336 :
337 0 : if (len % 8 != 0)
338 0 : return -EINVAL;
339 :
340 0 : if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
341 0 : return -ENOMEM;
342 :
343 0 : while (len >= 8) {
344 0 : struct sd_dhcp_route *route = *routes + *routes_size;
345 : int r;
346 :
347 0 : r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
348 0 : if (r < 0) {
349 0 : log_error("Failed to determine destination prefix length from class based IP, ignoring");
350 0 : continue;
351 : }
352 :
353 0 : lease_parse_be32(option, 4, &addr.s_addr);
354 0 : route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
355 0 : option += 4;
356 :
357 0 : lease_parse_be32(option, 4, &route->gw_addr.s_addr);
358 0 : option += 4;
359 :
360 0 : len -= 8;
361 0 : (*routes_size)++;
362 : }
363 :
364 0 : return 0;
365 : }
366 :
367 : /* parses RFC3442 Classless Static Route Option */
368 0 : static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
369 : size_t *routes_size, size_t *routes_allocated) {
370 :
371 0 : assert(option);
372 0 : assert(routes);
373 0 : assert(routes_size);
374 0 : assert(routes_allocated);
375 :
376 : /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
377 :
378 0 : while (len > 0) {
379 : uint8_t dst_octets;
380 : struct sd_dhcp_route *route;
381 :
382 0 : if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
383 0 : return -ENOMEM;
384 :
385 0 : route = *routes + *routes_size;
386 :
387 0 : dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
388 0 : route->dst_prefixlen = *option;
389 0 : option++;
390 0 : len--;
391 :
392 : /* can't have more than 4 octets in IPv4 */
393 0 : if (dst_octets > 4 || len < dst_octets)
394 0 : return -EINVAL;
395 :
396 0 : route->dst_addr.s_addr = 0;
397 0 : memcpy(&route->dst_addr.s_addr, option, dst_octets);
398 0 : option += dst_octets;
399 0 : len -= dst_octets;
400 :
401 0 : if (len < 4)
402 0 : return -EINVAL;
403 :
404 0 : lease_parse_be32(option, 4, &route->gw_addr.s_addr);
405 0 : option += 4;
406 0 : len -= 4;
407 :
408 0 : (*routes_size)++;
409 : }
410 :
411 0 : return 0;
412 : }
413 :
414 12 : int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
415 : void *user_data) {
416 12 : sd_dhcp_lease *lease = user_data;
417 : int r;
418 :
419 12 : assert(lease);
420 :
421 12 : switch(code) {
422 :
423 : case DHCP_OPTION_TIME_OFFSET:
424 0 : lease_parse_s32(option, len, &lease->time_offset);
425 :
426 0 : break;
427 :
428 : case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
429 0 : lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
430 :
431 0 : break;
432 :
433 : case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
434 2 : lease_parse_u32(option, len, &lease->lifetime, 1);
435 :
436 2 : break;
437 :
438 : case DHCP_OPTION_SERVER_IDENTIFIER:
439 2 : lease_parse_be32(option, len, &lease->server_address);
440 :
441 2 : break;
442 :
443 : case DHCP_OPTION_SUBNET_MASK:
444 2 : lease_parse_be32(option, len, &lease->subnet_mask);
445 :
446 2 : break;
447 :
448 : case DHCP_OPTION_BROADCAST:
449 0 : lease_parse_be32(option, len, &lease->broadcast);
450 :
451 0 : break;
452 :
453 : case DHCP_OPTION_ROUTER:
454 2 : if(len >= 4)
455 2 : lease_parse_be32(option, 4, &lease->router);
456 :
457 2 : break;
458 :
459 : case DHCP_OPTION_DOMAIN_NAME_SERVER:
460 0 : r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
461 0 : if (r < 0)
462 0 : return r;
463 :
464 0 : break;
465 :
466 : case DHCP_OPTION_NTP_SERVER:
467 2 : r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
468 2 : if (r < 0)
469 0 : return r;
470 :
471 2 : break;
472 :
473 : case DHCP_OPTION_POLICY_FILTER:
474 0 : r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
475 0 : if (r < 0)
476 0 : return r;
477 :
478 0 : break;
479 :
480 : case DHCP_OPTION_STATIC_ROUTE:
481 0 : r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
482 : &lease->static_route_allocated);
483 0 : if (r < 0)
484 0 : return r;
485 :
486 0 : break;
487 :
488 : case DHCP_OPTION_INTERFACE_MTU:
489 0 : lease_parse_u16(option, len, &lease->mtu, 68);
490 :
491 0 : break;
492 :
493 : case DHCP_OPTION_INTERFACE_MDR:
494 0 : lease_parse_u16(option, len, &lease->mdr, 576);
495 :
496 0 : break;
497 :
498 : case DHCP_OPTION_INTERFACE_TTL:
499 0 : lease_parse_u8(option, len, &lease->ttl, 1);
500 :
501 0 : break;
502 :
503 : case DHCP_OPTION_BOOT_FILE_SIZE:
504 0 : lease_parse_u16(option, len, &lease->boot_file_size, 0);
505 :
506 0 : break;
507 :
508 : case DHCP_OPTION_DOMAIN_NAME:
509 : {
510 4 : _cleanup_free_ char *domainname = NULL;
511 : char *e;
512 :
513 2 : r = lease_parse_string(option, len, &domainname);
514 2 : if (r < 0)
515 0 : return r;
516 :
517 : /* Chop off trailing dot of domain name that some DHCP
518 : * servers send us back. Internally we want to store
519 : * host names without trailing dots and
520 : * host_name_is_valid() doesn't accept them. */
521 2 : e = endswith(domainname, ".");
522 2 : if (e)
523 0 : *e = 0;
524 :
525 2 : if (is_localhost(domainname))
526 0 : break;
527 :
528 2 : r = dns_name_is_valid(domainname);
529 2 : if (r <= 0) {
530 0 : if (r < 0)
531 0 : log_error_errno(r, "Failed to validate domain name: %s: %m", domainname);
532 0 : if (r == 0)
533 0 : log_warning("Domain name is not valid, ignoring: %s", domainname);
534 0 : break;
535 : }
536 :
537 2 : free(lease->domainname);
538 2 : lease->domainname = domainname;
539 2 : domainname = NULL;
540 :
541 2 : break;
542 : }
543 : case DHCP_OPTION_HOST_NAME:
544 : {
545 0 : _cleanup_free_ char *hostname = NULL;
546 : char *e;
547 :
548 0 : r = lease_parse_string(option, len, &hostname);
549 0 : if (r < 0)
550 0 : return r;
551 :
552 0 : e = endswith(hostname, ".");
553 0 : if (e)
554 0 : *e = 0;
555 :
556 0 : if (!hostname_is_valid(hostname) || is_localhost(hostname))
557 : break;
558 :
559 0 : free(lease->hostname);
560 0 : lease->hostname = hostname;
561 0 : hostname = NULL;
562 :
563 0 : break;
564 : }
565 : case DHCP_OPTION_ROOT_PATH:
566 0 : r = lease_parse_string(option, len, &lease->root_path);
567 0 : if (r < 0)
568 0 : return r;
569 :
570 0 : break;
571 :
572 : case DHCP_OPTION_RENEWAL_T1_TIME:
573 0 : lease_parse_u32(option, len, &lease->t1, 1);
574 :
575 0 : break;
576 :
577 : case DHCP_OPTION_REBINDING_T2_TIME:
578 0 : lease_parse_u32(option, len, &lease->t2, 1);
579 :
580 0 : break;
581 :
582 : case DHCP_OPTION_ENABLE_IP_FORWARDING:
583 0 : lease_parse_bool(option, len, &lease->ip_forward);
584 :
585 0 : break;
586 :
587 : case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
588 0 : lease_parse_bool(option, len, &lease->ip_forward_non_local);
589 :
590 0 : break;
591 :
592 : case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
593 0 : r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
594 : &lease->static_route_allocated);
595 0 : if (r < 0)
596 0 : return r;
597 :
598 0 : break;
599 :
600 : case DHCP_OPTION_VENDOR_SPECIFIC:
601 0 : if (len >= 1) {
602 0 : free(lease->vendor_specific);
603 0 : lease->vendor_specific = memdup(option, len);
604 0 : if (!lease->vendor_specific)
605 0 : return -ENOMEM;
606 0 : lease->vendor_specific_len = len;
607 : }
608 :
609 0 : break;
610 : }
611 :
612 12 : return 0;
613 : }
614 :
615 2 : int dhcp_lease_new(sd_dhcp_lease **ret) {
616 : sd_dhcp_lease *lease;
617 :
618 2 : lease = new0(sd_dhcp_lease, 1);
619 2 : if (!lease)
620 0 : return -ENOMEM;
621 :
622 2 : lease->router = INADDR_ANY;
623 2 : lease->n_ref = REFCNT_INIT;
624 :
625 2 : *ret = lease;
626 2 : return 0;
627 : }
628 :
629 0 : int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
630 0 : _cleanup_free_ char *temp_path = NULL;
631 0 : _cleanup_fclose_ FILE *f = NULL;
632 : struct in_addr address;
633 : const struct in_addr *addresses;
634 : const uint8_t *client_id, *data;
635 : size_t client_id_len, data_len;
636 : const char *string;
637 : uint16_t mtu;
638 : struct sd_dhcp_route *routes;
639 : int r;
640 :
641 0 : assert(lease);
642 0 : assert(lease_file);
643 :
644 0 : r = fopen_temporary(lease_file, &f, &temp_path);
645 0 : if (r < 0)
646 0 : goto finish;
647 :
648 0 : fchmod(fileno(f), 0644);
649 :
650 0 : r = sd_dhcp_lease_get_address(lease, &address);
651 0 : if (r < 0)
652 0 : goto finish;
653 :
654 0 : fprintf(f,
655 : "# This is private data. Do not parse.\n"
656 : "ADDRESS=%s\n", inet_ntoa(address));
657 :
658 0 : r = sd_dhcp_lease_get_netmask(lease, &address);
659 0 : if (r < 0)
660 0 : goto finish;
661 :
662 0 : fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
663 :
664 0 : r = sd_dhcp_lease_get_router(lease, &address);
665 0 : if (r >= 0)
666 0 : fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
667 :
668 0 : r = sd_dhcp_lease_get_server_identifier(lease, &address);
669 0 : if (r >= 0)
670 0 : fprintf(f, "SERVER_ADDRESS=%s\n",
671 : inet_ntoa(address));
672 :
673 0 : r = sd_dhcp_lease_get_next_server(lease, &address);
674 0 : if (r >= 0)
675 0 : fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
676 :
677 0 : r = sd_dhcp_lease_get_mtu(lease, &mtu);
678 0 : if (r >= 0)
679 0 : fprintf(f, "MTU=%" PRIu16 "\n", mtu);
680 :
681 0 : fputs("DNS=", f);
682 0 : r = sd_dhcp_lease_get_dns(lease, &addresses);
683 0 : if (r >= 0)
684 0 : serialize_in_addrs(f, addresses, r);
685 0 : fputs("\n", f);
686 :
687 0 : fputs("NTP=", f);
688 0 : r = sd_dhcp_lease_get_ntp(lease, &addresses);
689 0 : if (r >= 0)
690 0 : serialize_in_addrs(f, addresses, r);
691 0 : fputs("\n", f);
692 :
693 0 : r = sd_dhcp_lease_get_domainname(lease, &string);
694 0 : if (r >= 0)
695 0 : fprintf(f, "DOMAINNAME=%s\n", string);
696 :
697 0 : r = sd_dhcp_lease_get_hostname(lease, &string);
698 0 : if (r >= 0)
699 0 : fprintf(f, "HOSTNAME=%s\n", string);
700 :
701 0 : r = sd_dhcp_lease_get_root_path(lease, &string);
702 0 : if (r >= 0)
703 0 : fprintf(f, "ROOT_PATH=%s\n", string);
704 :
705 0 : r = sd_dhcp_lease_get_routes(lease, &routes);
706 0 : if (r >= 0)
707 0 : serialize_dhcp_routes(f, "ROUTES", routes, r);
708 :
709 0 : r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
710 0 : if (r >= 0) {
711 0 : _cleanup_free_ char *client_id_hex;
712 :
713 0 : client_id_hex = hexmem(client_id, client_id_len);
714 0 : if (!client_id_hex) {
715 0 : r = -ENOMEM;
716 0 : goto finish;
717 : }
718 0 : fprintf(f, "CLIENTID=%s\n", client_id_hex);
719 : }
720 :
721 0 : r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
722 0 : if (r >= 0) {
723 0 : _cleanup_free_ char *option_hex = NULL;
724 :
725 0 : option_hex = hexmem(data, data_len);
726 0 : if (!option_hex) {
727 0 : r = -ENOMEM;
728 0 : goto finish;
729 : }
730 0 : fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
731 : }
732 :
733 0 : r = 0;
734 :
735 0 : fflush(f);
736 :
737 0 : if (ferror(f) || rename(temp_path, lease_file) < 0) {
738 0 : r = -errno;
739 0 : unlink(lease_file);
740 0 : unlink(temp_path);
741 : }
742 :
743 : finish:
744 0 : if (r < 0)
745 0 : log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
746 :
747 0 : return r;
748 : }
749 :
750 0 : int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
751 0 : _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
752 0 : _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
753 0 : *server_address = NULL, *next_server = NULL,
754 0 : *dns = NULL, *ntp = NULL, *mtu = NULL,
755 0 : *routes = NULL, *client_id_hex = NULL,
756 0 : *vendor_specific_hex = NULL;
757 : struct in_addr addr;
758 : int r;
759 :
760 0 : assert(lease_file);
761 0 : assert(ret);
762 :
763 0 : r = dhcp_lease_new(&lease);
764 0 : if (r < 0)
765 0 : return r;
766 :
767 0 : r = parse_env_file(lease_file, NEWLINE,
768 : "ADDRESS", &address,
769 : "ROUTER", &router,
770 : "NETMASK", &netmask,
771 : "SERVER_IDENTIFIER", &server_address,
772 : "NEXT_SERVER", &next_server,
773 : "DNS", &dns,
774 : "NTP", &ntp,
775 : "MTU", &mtu,
776 0 : "DOMAINNAME", &lease->domainname,
777 0 : "HOSTNAME", &lease->hostname,
778 0 : "ROOT_PATH", &lease->root_path,
779 : "ROUTES", &routes,
780 : "CLIENTID", &client_id_hex,
781 : "VENDOR_SPECIFIC", &vendor_specific_hex,
782 : NULL);
783 0 : if (r < 0) {
784 0 : if (r == -ENOENT)
785 0 : return 0;
786 :
787 0 : return log_error_errno(r, "Failed to read %s: %m", lease_file);
788 : }
789 :
790 0 : r = inet_pton(AF_INET, address, &addr);
791 0 : if (r < 0)
792 0 : return r;
793 :
794 0 : lease->address = addr.s_addr;
795 :
796 0 : if (router) {
797 0 : r = inet_pton(AF_INET, router, &addr);
798 0 : if (r < 0)
799 0 : return r;
800 :
801 0 : lease->router = addr.s_addr;
802 : }
803 :
804 0 : r = inet_pton(AF_INET, netmask, &addr);
805 0 : if (r < 0)
806 0 : return r;
807 :
808 0 : lease->subnet_mask = addr.s_addr;
809 :
810 0 : if (server_address) {
811 0 : r = inet_pton(AF_INET, server_address, &addr);
812 0 : if (r < 0)
813 0 : return r;
814 :
815 0 : lease->server_address = addr.s_addr;
816 : }
817 :
818 0 : if (next_server) {
819 0 : r = inet_pton(AF_INET, next_server, &addr);
820 0 : if (r < 0)
821 0 : return r;
822 :
823 0 : lease->next_server = addr.s_addr;
824 : }
825 :
826 0 : if (dns) {
827 0 : r = deserialize_in_addrs(&lease->dns, dns);
828 0 : if (r < 0)
829 0 : return r;
830 :
831 0 : lease->dns_size = r;
832 : }
833 :
834 0 : if (ntp) {
835 0 : r = deserialize_in_addrs(&lease->ntp, ntp);
836 0 : if (r < 0)
837 0 : return r;
838 :
839 0 : lease->ntp_size = r;
840 : }
841 :
842 0 : if (mtu) {
843 : uint16_t u;
844 0 : if (sscanf(mtu, "%" SCNu16, &u) > 0)
845 0 : lease->mtu = u;
846 : }
847 :
848 0 : if (routes) {
849 0 : r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
850 0 : &lease->static_route_allocated, routes);
851 0 : if (r < 0)
852 0 : return r;
853 : }
854 :
855 0 : if (client_id_hex) {
856 0 : if (strlen(client_id_hex) % 2)
857 0 : return -EINVAL;
858 :
859 0 : r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len);
860 0 : if (r < 0)
861 0 : return r;
862 : }
863 :
864 0 : if (vendor_specific_hex) {
865 0 : if (strlen(vendor_specific_hex) % 2)
866 0 : return -EINVAL;
867 :
868 0 : r = unhexmem(vendor_specific_hex, strlen(vendor_specific_hex), (void**) &lease->vendor_specific, &lease->vendor_specific_len);
869 0 : if (r < 0)
870 0 : return r;
871 : }
872 :
873 0 : *ret = lease;
874 0 : lease = NULL;
875 :
876 0 : return 0;
877 : }
878 :
879 0 : int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
880 : struct in_addr address;
881 : struct in_addr mask;
882 : int r;
883 :
884 0 : assert(lease);
885 :
886 0 : address.s_addr = lease->address;
887 :
888 : /* fall back to the default subnet masks based on address class */
889 0 : r = in_addr_default_subnet_mask(&address, &mask);
890 0 : if (r < 0)
891 0 : return r;
892 :
893 0 : lease->subnet_mask = mask.s_addr;
894 :
895 0 : return 0;
896 : }
897 :
898 0 : int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
899 : size_t *client_id_len) {
900 0 : assert_return(lease, -EINVAL);
901 0 : assert_return(client_id, -EINVAL);
902 0 : assert_return(client_id_len, -EINVAL);
903 :
904 0 : *client_id = lease->client_id;
905 0 : *client_id_len = lease->client_id_len;
906 0 : return 0;
907 : }
908 :
909 2 : int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
910 : size_t client_id_len) {
911 2 : assert_return(lease, -EINVAL);
912 2 : assert_return((!client_id && !client_id_len) ||
913 : (client_id && client_id_len), -EINVAL);
914 :
915 2 : free (lease->client_id);
916 2 : lease->client_id = NULL;
917 2 : lease->client_id_len = 0;
918 :
919 2 : if (client_id) {
920 2 : lease->client_id = memdup (client_id, client_id_len);
921 2 : lease->client_id_len = client_id_len;
922 : }
923 :
924 2 : return 0;
925 : }
|