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