]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp-lease.c
sd-dhcp-lease: fix double realloc
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-lease.c
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 <net/ethernet.h>
26 #include <arpa/inet.h>
27 #include <sys/param.h>
28
29 #include "util.h"
30 #include "list.h"
31 #include "mkdir.h"
32 #include "fileio.h"
33
34 #include "dhcp-protocol.h"
35 #include "dhcp-internal.h"
36 #include "dhcp-lease-internal.h"
37 #include "sd-dhcp-lease.h"
38 #include "sd-dhcp-client.h"
39
40 int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
41 assert_return(lease, -EINVAL);
42 assert_return(addr, -EINVAL);
43
44 addr->s_addr = lease->address;
45
46 return 0;
47 }
48
49 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
50 assert_return(lease, -EINVAL);
51 assert_return(mtu, -EINVAL);
52
53 if (lease->mtu)
54 *mtu = lease->mtu;
55 else
56 return -ENOENT;
57
58 return 0;
59 }
60
61 int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
62 assert_return(lease, -EINVAL);
63 assert_return(addr, -EINVAL);
64 assert_return(addr_size, -EINVAL);
65
66 if (lease->dns_size) {
67 *addr_size = lease->dns_size;
68 *addr = lease->dns;
69 } else
70 return -ENOENT;
71
72 return 0;
73 }
74
75 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
76 assert_return(lease, -EINVAL);
77 assert_return(addr, -EINVAL);
78 assert_return(addr_size, -EINVAL);
79
80 if (lease->ntp_size) {
81 *addr_size = lease->ntp_size;
82 *addr = lease->ntp;
83 } else
84 return -ENOENT;
85
86 return 0;
87 }
88
89 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
90 assert_return(lease, -EINVAL);
91 assert_return(domainname, -EINVAL);
92
93 if (lease->domainname)
94 *domainname = lease->domainname;
95 else
96 return -ENOENT;
97
98 return 0;
99 }
100
101 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
102 assert_return(lease, -EINVAL);
103 assert_return(hostname, -EINVAL);
104
105 if (lease->hostname)
106 *hostname = lease->hostname;
107 else
108 return -ENOENT;
109
110 return 0;
111 }
112
113 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
114 assert_return(lease, -EINVAL);
115 assert_return(root_path, -EINVAL);
116
117 if (lease->root_path)
118 *root_path = lease->root_path;
119 else
120 return -ENOENT;
121
122 return 0;
123 }
124
125 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
126 assert_return(lease, -EINVAL);
127 assert_return(addr, -EINVAL);
128
129 addr->s_addr = lease->router;
130
131 return 0;
132 }
133
134 int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
135 assert_return(lease, -EINVAL);
136 assert_return(addr, -EINVAL);
137
138 addr->s_addr = lease->subnet_mask;
139
140 return 0;
141 }
142
143 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
144 assert_return(lease, -EINVAL);
145 assert_return(addr, -EINVAL);
146
147 addr->s_addr = lease->server_address;
148
149 return 0;
150 }
151
152 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
153 assert_return(lease, -EINVAL);
154 assert_return(addr, -EINVAL);
155
156 addr->s_addr = lease->next_server;
157
158 return 0;
159 }
160
161 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
162 if (lease)
163 assert_se(REFCNT_INC(lease->n_ref) >= 2);
164
165 return lease;
166 }
167
168 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
169 if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
170 free(lease->hostname);
171 free(lease->domainname);
172 free(lease->dns);
173 free(lease);
174 }
175
176 return NULL;
177 }
178
179 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
180 void *user_data) {
181 sd_dhcp_lease *lease = user_data;
182 be32_t val;
183
184 switch(code) {
185
186 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
187 if (len == 4) {
188 memcpy(&val, option, 4);
189 lease->lifetime = be32toh(val);
190 }
191
192 break;
193
194 case DHCP_OPTION_SERVER_IDENTIFIER:
195 if (len >= 4)
196 memcpy(&lease->server_address, option, 4);
197
198 break;
199
200 case DHCP_OPTION_SUBNET_MASK:
201 if (len >= 4)
202 memcpy(&lease->subnet_mask, option, 4);
203
204 break;
205
206 case DHCP_OPTION_ROUTER:
207 if (len >= 4)
208 memcpy(&lease->router, option, 4);
209
210 break;
211
212 case DHCP_OPTION_DOMAIN_NAME_SERVER:
213 if (len && !(len % 4)) {
214 lease->dns_size = len / 4;
215
216 free(lease->dns);
217 lease->dns = newdup(struct in_addr, option, lease->dns_size);
218 if (!lease->dns)
219 return -ENOMEM;
220 }
221
222 break;
223
224 case DHCP_OPTION_NTP_SERVER:
225 if (len && !(len % 4)) {
226 lease->ntp_size = len / 4;
227
228 free(lease->ntp);
229 lease->ntp = newdup(struct in_addr, option, lease->ntp_size);
230 if (!lease->ntp)
231 return -ENOMEM;
232 }
233
234 break;
235
236 case DHCP_OPTION_INTERFACE_MTU:
237 if (len >= 2) {
238 be16_t mtu;
239
240 memcpy(&mtu, option, 2);
241 lease->mtu = be16toh(mtu);
242
243 if (lease->mtu < 68)
244 lease->mtu = 0;
245 }
246
247 break;
248
249 case DHCP_OPTION_DOMAIN_NAME:
250 if (len >= 1) {
251 free(lease->domainname);
252 lease->domainname = strndup((const char *)option, len);
253 }
254
255 break;
256
257 case DHCP_OPTION_HOST_NAME:
258 if (len >= 1) {
259 free(lease->hostname);
260 lease->hostname = strndup((const char *)option, len);
261 }
262
263 break;
264
265 case DHCP_OPTION_ROOT_PATH:
266 if (len >= 1) {
267 free(lease->root_path);
268 lease->root_path = strndup((const char *)option, len);
269 }
270
271 break;
272
273 case DHCP_OPTION_RENEWAL_T1_TIME:
274 if (len == 4) {
275 memcpy(&val, option, 4);
276 lease->t1 = be32toh(val);
277 }
278
279 break;
280
281 case DHCP_OPTION_REBINDING_T2_TIME:
282 if (len == 4) {
283 memcpy(&val, option, 4);
284 lease->t2 = be32toh(val);
285 }
286
287 break;
288 }
289
290 return 0;
291 }
292
293 int dhcp_lease_new(sd_dhcp_lease **ret) {
294 sd_dhcp_lease *lease;
295
296 lease = new0(sd_dhcp_lease, 1);
297 if (!lease)
298 return -ENOMEM;
299
300 lease->n_ref = REFCNT_INIT;
301
302 *ret = lease;
303 return 0;
304 }
305
306 static void serialize_addresses(FILE *f, const char *key, struct in_addr *addresses, size_t size) {
307 unsigned i;
308
309 assert(key);
310 assert(addresses);
311 assert(size);
312
313 fputs("DNS=", f);
314
315 for (i = 0; i < size; i++)
316 fprintf(f, "%s%s", inet_ntoa(addresses[i]),
317 (i < (size - 1)) ? " ": "");
318
319 fputs("\n", f);
320 }
321
322 static int deserialize_addresses(struct in_addr **addresses, size_t *size, const char *string) {
323 char *word, *state;
324 size_t len;
325
326 FOREACH_WORD(word, len, string, state) {
327 struct in_addr *new_addresses;
328 int r;
329
330 new_addresses = realloc(*addresses, (*size + 1) * sizeof(struct in_addr));
331 if (!new_addresses)
332 return -ENOMEM;
333 else
334 *addresses = new_addresses;
335
336 r = inet_aton(word, &(new_addresses[*size]));
337 if (r < 0)
338 continue;
339
340 (*size)++;
341 }
342
343 return 0;
344 }
345
346 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
347 _cleanup_free_ char *temp_path = NULL;
348 _cleanup_fclose_ FILE *f = NULL;
349 struct in_addr address;
350 struct in_addr *addresses;
351 size_t addresses_size;
352 const char *string;
353 uint16_t mtu;
354 int r;
355
356 assert(lease);
357 assert(lease_file);
358
359 r = fopen_temporary(lease_file, &f, &temp_path);
360 if (r < 0)
361 goto finish;
362
363 fchmod(fileno(f), 0644);
364
365 r = sd_dhcp_lease_get_address(lease, &address);
366 if (r < 0)
367 goto finish;
368
369 fprintf(f,
370 "# This is private data. Do not parse.\n"
371 "ADDRESS=%s\n", inet_ntoa(address));
372
373 r = sd_dhcp_lease_get_router(lease, &address);
374 if (r < 0)
375 goto finish;
376
377 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
378
379 r = sd_dhcp_lease_get_netmask(lease, &address);
380 if (r < 0)
381 goto finish;
382
383 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
384
385 r = sd_dhcp_lease_get_server_identifier(lease, &address);
386 if (r >= 0)
387 fprintf(f, "SERVER_ADDRESS=%s\n",
388 inet_ntoa(address));
389
390 r = sd_dhcp_lease_get_next_server(lease, &address);
391 if (r >= 0)
392 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
393
394 r = sd_dhcp_lease_get_mtu(lease, &mtu);
395 if (r >= 0)
396 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
397
398 r = sd_dhcp_lease_get_dns(lease, &addresses, &addresses_size);
399 if (r >= 0)
400 serialize_addresses(f, "DNS", addresses, addresses_size);
401
402 r = sd_dhcp_lease_get_ntp(lease, &addresses, &addresses_size);
403 if (r >= 0)
404 serialize_addresses(f, "NTP", addresses, addresses_size);
405
406 r = sd_dhcp_lease_get_domainname(lease, &string);
407 if (r >= 0)
408 fprintf(f, "DOMAINNAME=%s\n", string);
409
410 r = sd_dhcp_lease_get_hostname(lease, &string);
411 if (r >= 0)
412 fprintf(f, "HOSTNAME=%s\n", string);
413
414 r = sd_dhcp_lease_get_root_path(lease, &string);
415 if (r >= 0)
416 fprintf(f, "ROOT_PATH=%s\n", string);
417
418 r = 0;
419
420 fflush(f);
421
422 if (ferror(f) || rename(temp_path, lease_file) < 0) {
423 r = -errno;
424 unlink(lease_file);
425 unlink(temp_path);
426 }
427
428 finish:
429 if (r < 0)
430 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
431
432 return r;
433 }
434
435 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
436 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
437 _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
438 *server_address = NULL, *next_server = NULL,
439 *dns = NULL, *ntp = NULL, *mtu = NULL;
440 struct in_addr addr;
441 int r;
442
443 assert(lease_file);
444 assert(ret);
445
446 r = dhcp_lease_new(&lease);
447 if (r < 0)
448 return r;
449
450 r = parse_env_file(lease_file, NEWLINE,
451 "ADDRESS", &address,
452 "ROUTER", &router,
453 "NETMASK", &netmask,
454 "SERVER_IDENTIFIER", &server_address,
455 "NEXT_SERVER", &next_server,
456 "DNS", &dns,
457 "NTP", &ntp,
458 "MTU", &mtu,
459 "DOMAINNAME", &lease->domainname,
460 "HOSTNAME", &lease->hostname,
461 "ROOT_PATH", &lease->root_path,
462 NULL);
463 if (r < 0) {
464 if (r == -ENOENT)
465 return 0;
466
467 log_error("Failed to read %s: %s", lease_file, strerror(-r));
468 return r;
469 }
470
471 r = inet_pton(AF_INET, address, &addr);
472 if (r < 0)
473 return r;
474
475 lease->address = addr.s_addr;
476
477 r = inet_pton(AF_INET, router, &addr);
478 if (r < 0)
479 return r;
480
481 lease->router = addr.s_addr;
482
483 r = inet_pton(AF_INET, netmask, &addr);
484 if (r < 0)
485 return r;
486
487 lease->subnet_mask = addr.s_addr;
488
489 if (server_address) {
490 r = inet_pton(AF_INET, server_address, &addr);
491 if (r < 0)
492 return r;
493
494 lease->server_address = addr.s_addr;
495 }
496
497 if (next_server) {
498 r = inet_pton(AF_INET, next_server, &addr);
499 if (r < 0)
500 return r;
501
502 lease->next_server = addr.s_addr;
503 }
504
505 if (dns) {
506 r = deserialize_addresses(&lease->dns, &lease->dns_size, dns);
507 if (r < 0)
508 return r;
509 }
510
511 if (ntp) {
512 r = deserialize_addresses(&lease->ntp, &lease->ntp_size, dns);
513 if (r < 0)
514 return r;
515 }
516
517 if (mtu) {
518 uint16_t u;
519 if (sscanf(mtu, "%" SCNu16, &u) > 0)
520 lease->mtu = u;
521 }
522
523 *ret = lease;
524 lease = NULL;
525
526 return 0;
527 }
528
529 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
530 uint32_t address;
531
532 assert(lease);
533 assert(lease->address != INADDR_ANY);
534
535 address = be32toh(lease->address);
536
537 /* fall back to the default subnet masks based on address class */
538
539 if ((address >> 31) == 0x0)
540 /* class A, leading bits: 0 */
541 lease->subnet_mask = htobe32(0xff000000);
542 else if ((address >> 30) == 0x2)
543 /* class B, leading bits 10 */
544 lease->subnet_mask = htobe32(0xffff0000);
545 else if ((address >> 29) == 0x6)
546 /* class C, leading bits 110 */
547 lease->subnet_mask = htobe32(0xffffff00);
548 else
549 /* class D or E, no default mask. give up */
550 return -ERANGE;
551
552 return 0;
553 }