1 /*
2 * libdhcpcd
3 * Copyright 2009-2016 Roy Marples <roy@marples.name>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 // For strverscmp(3)
28 #define _GNU_SOURCE
29
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/un.h>
33
34 #include <arpa/inet.h>
35
36 #include <assert.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #define IN_LIBDHCPCD
47
48 #include "config.h"
49 #include "dhcpcd.h"
50
51 #ifdef HAS_GETTEXT
52 #include <libintl.h>
53 #define _ gettext
54 #else
55 #define _(a) (a)
56 #endif
57
58 #ifdef HAVE_VIS_H
59 #include <vis.h>
60 #endif
61
62 #ifndef SUN_LEN
63 #define SUN_LEN(su) \
64 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
65 #endif
66
67 #ifndef iswhite
68 #define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
69 #endif
70
71 const char * const dhcpcd_cstates[DHC_MAX] = {
72 "unknown",
73 "down",
74 "opened",
75 "initialised",
76 "disconnected",
77 "connecting",
78 "waiting",
79 "connected",
80 };
81
82 struct dhcpcd_vs {
83 unsigned int val;
84 const char *str;
85 };
86
87 static const struct dhcpcd_vs dhcpcd_states[] = {
88 { DHS_DUMP, "DUMP" },
89 { DHS_TEST, "TEST" },
90 { DHS_STOPPED, "STOPPED" },
91 { DHS_FAIL, "FAIL" },
92 { DHS_STOP, "STOP" },
93 { DHS_PREINIT, "PREINIT" },
94 { DHS_DEPARTED, "DEPARTED" },
95 { DHS_NOCARRIER, "NOCARRIER" },
96 { DHS_NOCARRIER_ROAMING, "NOCARRIER_ROAMING " },
97 { DHS_NAK, "NAK" },
98 { DHS_EXPIRE, "EXPIRE" },
99 { DHS_RECONFIGURE, "RECONFIGURE" },
100 { DHS_CARRIER, "CARRIER" },
101 { DHS_STATIC, "STATIC" },
102 { DHS_3RDPARTY, "3RDPARTY" },
103 { DHS_IPV4LL, "IPV4LL" },
104 { DHS_INFORM, "INFORM" },
105 { DHS_BOUND, "BOUND" },
106 { DHS_RENEW, "RENEW" },
107 { DHS_REBIND, "REBIND" },
108 { DHS_REBOOT, "REBOOT" },
109 { DHS_ROUTERADVERT, "ROUTERADVERT" },
110 { DHS_BOUND, "DELEGATED" },
111 { DHS_UNKNOWN, NULL }
112 };
113
114 static ssize_t
dhcpcd_command_fd(DHCPCD_CONNECTION * con,int fd,bool progname,const char * cmd,char ** buffer)115 dhcpcd_command_fd(DHCPCD_CONNECTION *con,
116 int fd, bool progname, const char *cmd, char **buffer)
117 {
118 size_t pl, cl, len;
119 ssize_t bytes;
120 char buf[1024], *p;
121 char *nbuf;
122
123 assert(con);
124 assert(cmd);
125
126 /* Each command is \n terminated.
127 * Each argument is NULL seperated.
128 * We may need to send a space one day, so the API
129 * in this function may need to be improved */
130 cl = strlen(cmd);
131 if (progname) {
132 pl = strlen(con->progname);
133 len = pl + 1 + cl + 1;
134 } else {
135 pl = 0;
136 len = cl + 1;
137 }
138 if (con->terminate_commands)
139 len++;
140 if (len > sizeof(buf)) {
141 errno = ENOBUFS;
142 return -1;
143 }
144 p = buf;
145 if (progname) {
146 memcpy(buf, con->progname, pl);
147 buf[pl] = '\0';
148 p = buf + pl + 1;
149 }
150 memcpy(p, cmd, cl);
151 p[cl] = '\0';
152 while ((p = strchr(p, ' ')) != NULL)
153 *p++ = '\0';
154 if (con->terminate_commands) {
155 buf[len - 2] = '\n';
156 buf[len - 1] = '\0';
157 } else
158 buf[len - 1] = '\0';
159
160 if (write(fd, buf, len) == -1)
161 return -1;
162 if (buffer == NULL)
163 return 0;
164
165 bytes = read(fd, buf, sizeof(size_t));
166 if (bytes == 0 || bytes == -1)
167 return bytes;
168 memcpy(&len, buf, sizeof(size_t));
169 nbuf = realloc(*buffer, len + 1);
170 if (nbuf == NULL)
171 return -1;
172 *buffer = nbuf;
173 bytes = read(fd, *buffer, len);
174 if (bytes != -1 && (size_t)bytes < len)
175 *buffer[bytes] = '\0';
176 return bytes;
177 }
178
179 ssize_t
dhcpcd_command(DHCPCD_CONNECTION * con,const char * cmd,char ** buffer)180 dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
181 {
182
183 assert(con);
184 if (!con->privileged) {
185 errno = EACCES;
186 return -1;
187 }
188 return dhcpcd_command_fd(con, con->command_fd, true, cmd, buffer);
189 }
190
191 static ssize_t
dhcpcd_ctrl_command(DHCPCD_CONNECTION * con,const char * cmd,char ** buffer)192 dhcpcd_ctrl_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
193 {
194
195 return dhcpcd_command_fd(con, con->command_fd, false, cmd, buffer);
196 }
197
198 bool
dhcpcd_realloc(DHCPCD_CONNECTION * con,size_t len)199 dhcpcd_realloc(DHCPCD_CONNECTION *con, size_t len)
200 {
201
202 assert(con);
203 if (con->buflen < len) {
204 char *nbuf;
205
206 nbuf = realloc(con->buf, len);
207 if (nbuf == NULL)
208 return false;
209 con->buf = nbuf;
210 con->buflen = len;
211 }
212 return true;
213 }
214
215 ssize_t
dhcpcd_command_arg(DHCPCD_CONNECTION * con,const char * cmd,const char * arg,char ** buffer)216 dhcpcd_command_arg(DHCPCD_CONNECTION *con, const char *cmd, const char *arg,
217 char **buffer)
218 {
219 size_t cmdlen, len;
220
221 assert(con);
222 assert(cmd);
223
224 cmdlen = strlen(cmd);
225 if (arg)
226 len = cmdlen + strlen(arg) + 2;
227 else
228 len = cmdlen + 1;
229 if (!dhcpcd_realloc(con, len))
230 return -1;
231 strlcpy(con->buf, cmd, con->buflen);
232 if (arg) {
233 con->buf[cmdlen] = ' ';
234 strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
235 }
236
237 return dhcpcd_command_fd(con, con->command_fd, true, con->buf, buffer);
238 }
239
240
241 static int
dhcpcd_connect(const char * path,int opts)242 dhcpcd_connect(const char *path, int opts)
243 {
244 int fd;
245 socklen_t len;
246 struct sockaddr_un sun;
247
248 assert(path);
249 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | opts, 0);
250 if (fd == -1)
251 return -1;
252
253 memset(&sun, 0, sizeof(sun));
254 sun.sun_family = AF_UNIX;
255 strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
256 len = (socklen_t)SUN_LEN(&sun);
257 if (connect(fd, (struct sockaddr *)&sun, len) == 0)
258 return fd;
259 close(fd);
260 return -1;
261 }
262
263 static const char *
get_value(const char * data,size_t len,const char * var)264 get_value(const char *data, size_t len, const char *var)
265 {
266 const char *end, *p;
267 size_t vlen;
268
269 assert(var);
270 end = data + len;
271 vlen = strlen(var);
272 p = NULL;
273 while (data + vlen + 1 < end) {
274 /* Skip past NUL padding */
275 if (*data == '\0') {
276 data++;
277 continue;
278 }
279 if (strncmp(data, var, vlen) == 0 && data[vlen] == '=') {
280 p = data + vlen + 1;
281 break;
282 }
283 data += strlen(data) + 1;
284 }
285 if (p != NULL && *p != '\0')
286 return p;
287 return NULL;
288 }
289
290 const char *
dhcpcd_get_value(const DHCPCD_IF * i,const char * var)291 dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
292 {
293
294 assert(i);
295 assert(var);
296 return get_value(i->data, i->data_len, var);
297 }
298
299 ssize_t
dhcpcd_encode_string_escape(char * dst,size_t len,const char * src,size_t slen)300 dhcpcd_encode_string_escape(char *dst, size_t len, const char *src, size_t slen)
301 {
302 const char *end;
303 size_t bytes;
304 unsigned char c;
305
306 end = src + slen;
307 bytes = 0;
308 while (src < end) {
309 c = (unsigned char)*src++;
310 if ((c == '\\' || !isascii(c) || !isprint(c))) {
311 if (c == '\\') {
312 if (dst) {
313 if (len == 0 || len == 1) {
314 errno = ENOSPC;
315 return -1;
316 }
317 *dst++ = '\\'; *dst++ = '\\';
318 len -= 2;
319 }
320 bytes += 2;
321 continue;
322 }
323 if (dst) {
324 if (len < 5) {
325 errno = ENOSPC;
326 return -1;
327 }
328 *dst++ = '\\';
329 *dst++ = (char)(((c >> 6) & 03) + '0');
330 *dst++ = (char)(((c >> 3) & 07) + '0');
331 *dst++ = (char)(( c & 07) + '0');
332 len -= 4;
333 }
334 bytes += 4;
335 } else {
336 if (dst) {
337 if (len == 0) {
338 errno = ENOSPC;
339 return -1;
340 }
341 *dst++ = (char)c;
342 len--;
343 }
344 bytes++;
345 }
346 }
347
348 if (dst) {
349 if (len == 0) {
350 errno = ENOSPC;
351 return -1;
352 }
353 *dst = '\0';
354 }
355
356 return (ssize_t)bytes;
357 }
358
359 ssize_t
dhcpcd_decode_string_escape(char * dst,size_t dlen,const char * src)360 dhcpcd_decode_string_escape(char *dst, size_t dlen, const char *src)
361 {
362 char c, esc;
363 int oct;
364 ssize_t bytes;
365
366 bytes = 0;
367 for (;;) {
368 c = *src++;
369 if (c == '\0')
370 break;
371 if (dst && --dlen == 0) {
372 errno = ENOSPC;
373 return -1;
374 }
375 switch (c) {
376 case '\\':
377 if (*src == '\0') {
378 errno = EINVAL;
379 return -1;
380 }
381 esc = *src++;
382 switch (esc) {
383 case '\\':
384 if (dst)
385 *dst++ = esc;
386 break;
387 case '0':
388 case '1':
389 case '2':
390 case '3':
391 case '4':
392 case '5':
393 case '6':
394 case '7':
395 oct = esc - '0';
396 if (*src >= '0' && *src <='7')
397 oct = oct * 8 + (*src++ - '0');
398 else {
399 errno = EINVAL;
400 return -1;
401 }
402 if (*src >= '0' && *src <='7')
403 oct = oct * 8 + (*src++ - '0');
404 else {
405 errno = EINVAL;
406 return -1;
407 }
408 if (dst)
409 *dst++ = (char)oct;
410 break;
411 default:
412 errno = EINVAL;
413 return -1;
414 }
415 break;
416 default:
417 if (dst)
418 *dst++ = c;
419 }
420 bytes++;
421 }
422
423 if (dst) {
424 if (--dlen == 0) {
425 errno = ENOSPC;
426 return -1;
427 }
428 *dst = '\0';
429 }
430 return bytes;
431 }
432
433 ssize_t
dhcpcd_decode_hex(char * dst,size_t dlen,const char * src)434 dhcpcd_decode_hex(char *dst, size_t dlen, const char *src)
435 {
436 size_t bytes, i;
437 char c;
438 int val, n;
439
440 bytes = 0;
441 while (*src) {
442 if (dlen == 0 || dlen == 1) {
443 errno = ENOSPC;
444 return -1;
445 }
446 val = 0;
447 for (i = 0; i < 2; i++) {
448 c = *src++;
449 if (c >= '0' && c <= '9')
450 n = c - '0';
451 else if (c >= 'a' && c <= 'f')
452 n = 10 + c - 'a';
453 else if (c >= 'A' && c <= 'F')
454 n = 10 + c - 'A';
455 else {
456 errno = EINVAL;
457 return -1;
458 }
459 val = val * 16 + n;
460 }
461 *dst++ = (char)val;
462 bytes += 2;
463 dlen -= 2;
464 if (*src == ':')
465 src++;
466 }
467 return (ssize_t)bytes;
468 }
469
470 const char *
dhcpcd_get_prefix_value(const DHCPCD_IF * i,const char * prefix,const char * var)471 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
472 {
473 char pvar[128], *p;
474 size_t plen, l;
475
476 assert(i);
477 assert(prefix);
478 assert(var);
479
480 p = pvar;
481 plen = sizeof(pvar);
482 l = strlcpy(p, prefix, plen);
483 if (l >= sizeof(pvar)) {
484 errno = ENOBUFS;
485 return NULL;
486 }
487 p += l;
488 plen -= l;
489 if (strlcpy(p, var, plen) >= plen) {
490 errno = ENOBUFS;
491 return NULL;
492 }
493 return dhcpcd_get_value(i, pvar);
494 }
495
496 static bool
strtobool(const char * var)497 strtobool(const char *var)
498 {
499
500 if (var == NULL)
501 return false;
502
503 return (*var == '0' || *var == '\0' ||
504 strcmp(var, "false") == 0 ||
505 strcmp(var, "no") == 0) ? false : true;
506 }
507
508 static unsigned int
get_status(DHCPCD_CONNECTION * con)509 get_status(DHCPCD_CONNECTION *con)
510 {
511 DHCPCD_IF *i;
512 unsigned int status;
513
514 assert(con);
515 if (con->command_fd == -1)
516 return DHC_DOWN;
517
518 if (con->listen_fd == -1)
519 return DHC_OPENED;
520
521 if (con->interfaces == NULL)
522 return DHC_INITIALISED;
523
524 status = DHC_DISCONNECTED;
525 for (i = con->interfaces; i; i = i->next) {
526 if (i->up) {
527 if (i->type == DHT_LINK) {
528 if (status == DHC_DISCONNECTED)
529 status = DHC_CONNECTING;
530 } else {
531 if (con->af_waiting)
532 status = DHC_AF_WAITING;
533 else {
534 status = DHC_CONNECTED;
535 break;
536 }
537 }
538 }
539 }
540 return status;
541 }
542
543 static void
update_status(DHCPCD_CONNECTION * con,unsigned int nstatus)544 update_status(DHCPCD_CONNECTION *con, unsigned int nstatus)
545 {
546
547 assert(con);
548 if (nstatus == DHC_UNKNOWN)
549 nstatus = get_status(con);
550 if (con->status != nstatus) {
551 con->status = nstatus;
552 if (con->status_cb)
553 con->status_cb(con, con->status,
554 dhcpcd_cstates[con->status], con->status_context);
555 }
556 }
557
558 DHCPCD_IF *
dhcpcd_interfaces(DHCPCD_CONNECTION * con)559 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
560 {
561
562 assert(con);
563 return con->interfaces;
564 }
565
566 char **
dhcpcd_interface_names(DHCPCD_CONNECTION * con,size_t * nnames)567 dhcpcd_interface_names(DHCPCD_CONNECTION *con, size_t *nnames)
568 {
569 char **names;
570 size_t n;
571 DHCPCD_IF *i;
572
573 assert(con);
574 if (con->interfaces == NULL)
575 return NULL;
576
577 n = 0;
578 for (i = con->interfaces; i; i = i->next) {
579 if (i->type == DHT_LINK)
580 n++;
581 }
582 names = malloc(sizeof(char *) * (n + 1));
583 if (names == NULL)
584 return NULL;
585 n = 0;
586 for (i = con->interfaces; i; i = i->next) {
587 if (i->type == DHT_LINK) {
588 names[n] = strdup(i->ifname);
589 if (names[n] == NULL) {
590 dhcpcd_freev(names);
591 return NULL;
592 }
593 n++;
594 }
595 }
596 names[n] = NULL;
597 if (nnames)
598 *nnames = n;
599
600 return names;
601 }
602
603 void
dhcpcd_freev(char ** argv)604 dhcpcd_freev(char **argv)
605 {
606 char **v;
607
608 if (argv) {
609 for (v = argv; *v; v++)
610 free(*v);
611 free(argv);
612 }
613 }
614
615 static int
dhcpcd_cmpstring(const void * p1,const void * p2)616 dhcpcd_cmpstring(const void *p1, const void *p2)
617 {
618 const char *s1, *s2;
619 int cmp;
620
621 s1 = *(char * const *)p1;
622 s2 = *(char * const *)p2;
623 if ((cmp = strcasecmp(s1, s2)) == 0)
624 cmp = strcmp(s1, s2);
625 return cmp;
626 }
627
628 char **
dhcpcd_interface_names_sorted(DHCPCD_CONNECTION * con)629 dhcpcd_interface_names_sorted(DHCPCD_CONNECTION *con)
630 {
631 char **names;
632 size_t nnames;
633
634 names = dhcpcd_interface_names(con, &nnames);
635 if (names)
636 qsort(names, nnames, sizeof(char *), dhcpcd_cmpstring);
637 return names;
638 }
639
640 DHCPCD_IF *
dhcpcd_get_if(DHCPCD_CONNECTION * con,const char * ifname,unsigned int type)641 dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, unsigned int type)
642 {
643 DHCPCD_IF *i;
644
645 assert(con);
646 assert(ifname);
647 assert(type);
648
649 for (i = con->interfaces; i; i = i->next)
650 if (i->type == type && strcmp(i->ifname, ifname) == 0)
651 return i;
652 return NULL;
653 }
654
655 static unsigned int
dhcpcd_reason_to_state1(const char * reason,int * isdhcp6)656 dhcpcd_reason_to_state1(const char *reason, int *isdhcp6)
657 {
658 unsigned int i;
659 char rbuf[32], *p;
660
661 assert(reason);
662
663 /* Take a copy of reason and trim 6 from the end
664 * so our DHCPv6 state can match DHCP states */
665 if (strlcpy(rbuf, reason, sizeof(rbuf)) >= sizeof(rbuf))
666 return DHS_UNKNOWN;
667 p = rbuf + (strlen(rbuf) - 1);
668 if (*p == '6') {
669 if (isdhcp6)
670 *isdhcp6 = 1;
671 *p = '\0';
672 } else if (isdhcp6)
673 *isdhcp6 = 0;
674
675 for (i = 0; dhcpcd_states[i].val != DHS_UNKNOWN; i++) {
676 if (strcmp(rbuf, dhcpcd_states[i].str) == 0)
677 return dhcpcd_states[i].val;
678 }
679 return DHS_UNKNOWN;
680 }
681
682 static void
dhcpcd_reason_to_statetype(const char * reason,unsigned int * state,unsigned int * type)683 dhcpcd_reason_to_statetype(const char *reason,
684 unsigned int *state, unsigned int *type)
685 {
686 int isdhcp6;
687
688 assert(state);
689 assert(type);
690 *state = dhcpcd_reason_to_state1(reason, &isdhcp6);
691 switch (*state) {
692 case DHS_UNKNOWN:
693 case DHS_PREINIT:
694 case DHS_CARRIER:
695 case DHS_NOCARRIER:
696 case DHS_NOCARRIER_ROAMING:
697 case DHS_DEPARTED:
698 case DHS_STOPPED:
699 *type = DHT_LINK;
700 return;
701 case DHS_ROUTERADVERT:
702 *type = DHT_RA;
703 return;
704 case DHS_IPV4LL:
705 *type = DHT_IPV4LL;
706 return;
707 case DHS_STATIC:
708 if (isdhcp6) {
709 *type = DHT_IPV6;
710 return;
711 }
712 }
713
714 if (isdhcp6)
715 *type = DHT_DHCP6;
716 else
717 *type = DHT_IPV4;
718 }
719
720 static DHCPCD_IF *
dhcpcd_new_if(DHCPCD_CONNECTION * con,char * data,size_t len)721 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
722 {
723 const char *ifname, *ifclass, *reason, *order, *flags;
724 unsigned int state, type;
725 char *orderdup, *o, *p;
726 DHCPCD_IF *e, *i, *l, *n, *nl;
727 bool addedi;
728
729 #if 0
730 char *dp = data, *de = data + len;
731 while (dp < de) {
732 printf ("XX: %s\n", dp);
733 dp += strlen(dp) + 1;
734 }
735 #endif
736
737 ifname = get_value(data, len, "interface");
738 if (ifname == NULL || *ifname == '\0') {
739 errno = ESRCH;
740 return NULL;
741 }
742 reason = get_value(data, len, "reason");
743 if (reason == NULL || *reason == '\0') {
744 errno = ESRCH;
745 return NULL;
746 }
747 dhcpcd_reason_to_statetype(reason, &state, &type);
748
749 ifclass = get_value(data, len, "ifclass");
750 /* Skip pseudo interfaces */
751 if (ifclass && *ifclass != '\0') {
752 errno = ENOTSUP;
753 return NULL;
754 }
755 if (state == DHS_RECONFIGURE || state == DHS_INFORM) {
756 errno = ENOTSUP;
757 return NULL;
758 }
759 order = get_value(data, len, "interface_order");
760 if (order == NULL || *order == '\0') {
761 errno = ESRCH;
762 return NULL;
763 }
764
765 i = NULL;
766 /* Remove all instances on carrier drop */
767 if (state == DHS_NOCARRIER ||
768 state == DHS_DEPARTED || state == DHS_STOPPED)
769 {
770 l = NULL;
771 for (e = con->interfaces; e; e = n) {
772 n = e->next;
773 if (strcmp(e->ifname, ifname) == 0) {
774 if (e->type == type)
775 l = i = e;
776 else {
777 if (l)
778 l->next = e->next;
779 else
780 con->interfaces = e->next;
781 free(e);
782 }
783 } else
784 l = e;
785 }
786 } else if (type != DHT_LINK) {
787 /* If link is down, ignore it */
788 e = dhcpcd_get_if(con, ifname, DHT_LINK);
789 if (e && !e->up)
790 return NULL;
791 }
792
793 orderdup = strdup(order);
794 if (orderdup == NULL)
795 return NULL;
796
797 /* Find our pointer */
798 if (i == NULL) {
799 l = NULL;
800 for (e = con->interfaces; e; e = e->next) {
801 if (e->type == type && strcmp(e->ifname, ifname) == 0) {
802 i = e;
803 break;
804 }
805 l = e;
806 }
807 }
808 if (i == NULL) {
809 i = malloc(sizeof(*i));
810 if (i == NULL) {
811 free(orderdup);
812 return NULL;
813 }
814 if (l)
815 l->next = i;
816 else
817 con->interfaces = i;
818 i->next = NULL;
819 i->last_message = NULL;
820 } else
821 free(i->data);
822
823 /* Now fill out our interface structure */
824 i->con = con;
825 i->data = data;
826 i->data_len = len;
827 i->ifname = ifname;
828 i->type = type;
829 i->state = state;
830 i->reason = reason;
831 flags = dhcpcd_get_value(i, "ifflags");
832 if (flags)
833 i->ifflags = (unsigned int)strtoul(flags, NULL, 0);
834 else
835 i->ifflags = 0;
836 if (state == DHS_CARRIER || state == DHS_DELEGATED)
837 i->up = true;
838 else
839 i->up = strtobool(dhcpcd_get_value(i, "if_up"));
840 i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless"));
841 i->ssid = dhcpcd_get_value(i, "ifssid");
842 i->freq = 0; /* wpa_supplicant will set this when opened */
843 if (i->ssid == NULL && i->wireless)
844 i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid" : "old_ssid");
845
846 /* Work out if we're waiting for any other addresses */
847 if (dhcpcd_get_value(i, "af_waiting") == NULL)
848 con->af_waiting = false;
849 else
850 con->af_waiting = true;
851
852 /* Sort! */
853 n = nl = NULL;
854 p = orderdup;
855 addedi = false;
856 while ((o = strsep(&p, " ")) != NULL) {
857 for (type = 0; type < DHT_MAX; type++) {
858 l = NULL;
859 for (e = con->interfaces; e; e = e->next) {
860 if (e->type == type && strcmp(e->ifname, o) == 0)
861 break;
862 l = e;
863 }
864 if (e == NULL)
865 continue;
866 if (i == e)
867 addedi = true;
868 if (l)
869 l->next = e->next;
870 else
871 con->interfaces = e->next;
872 e->next = NULL;
873 if (nl == NULL)
874 n = nl = e;
875 else {
876 nl->next = e;
877 nl = e;
878 }
879 }
880 }
881 free(orderdup);
882 /* Free any stragglers */
883 while (con->interfaces) {
884 e = con->interfaces->next;
885 free(con->interfaces->data);
886 free(con->interfaces->last_message);
887 free(con->interfaces);
888 con->interfaces = e;
889 }
890 con->interfaces = n;
891
892 return addedi ? i : NULL;
893 }
894
895 static DHCPCD_IF *
dhcpcd_read_if(DHCPCD_CONNECTION * con,int fd)896 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
897 {
898 char *rbuf, *rbufp;
899 size_t len;
900 ssize_t bytes;
901 DHCPCD_IF *i;
902
903 bytes = read(fd, &len, sizeof(len));
904 if (bytes == 0 || bytes == -1) {
905 dhcpcd_close(con);
906 return NULL;
907 }
908 if (len >= SSIZE_MAX) {
909 /* Even this is probably too big! */
910 errno = ENOBUFS;
911 return NULL;
912 }
913 rbuf = malloc(len + 1);
914 if (rbuf == NULL)
915 return NULL;
916 rbufp = rbuf;
917 again:
918 bytes = read(fd, rbufp, len);
919 if (bytes == 0 || bytes == -1) {
920 free(rbuf);
921 dhcpcd_close(con);
922 return NULL;
923 }
924 if ((size_t)bytes < len) {
925 rbufp += bytes;
926 len -= (size_t)bytes;
927 goto again;
928 }
929 if ((size_t)bytes != len) {
930 free(rbuf);
931 errno = EINVAL;
932 return NULL;
933 }
934 rbufp[bytes] = '\0';
935
936 i = dhcpcd_new_if(con, rbuf, (size_t)((rbufp - rbuf) + bytes));
937 if (i == NULL)
938 free(rbuf);
939 return i;
940 }
941
942 static void
dhcpcd_dispatchif(DHCPCD_IF * i)943 dhcpcd_dispatchif(DHCPCD_IF *i)
944 {
945
946 assert(i);
947 if (i->con->if_cb)
948 i->con->if_cb(i, i->con->if_context);
949 dhcpcd_wpa_if_event(i);
950 }
951
952 void
dhcpcd_dispatch(DHCPCD_CONNECTION * con)953 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
954 {
955 DHCPCD_IF *i;
956
957 assert(con);
958 i = dhcpcd_read_if(con, con->listen_fd);
959
960 if (i)
961 dhcpcd_dispatchif(i);
962
963 /* Have to call update_status last as it could
964 * cause the interface to be destroyed. */
965 update_status(con, DHC_UNKNOWN);
966 }
967
968 DHCPCD_CONNECTION *
dhcpcd_new(void)969 dhcpcd_new(void)
970 {
971 DHCPCD_CONNECTION *con;
972
973 con = calloc(1, sizeof(*con));
974 con->command_fd = con->listen_fd = -1;
975 con->open = false;
976 con->progname = "libdhcpcd";
977 con->af_waiting = false;
978 return con;
979 }
980
981 void
dhcpcd_set_progname(DHCPCD_CONNECTION * con,const char * progname)982 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
983 {
984
985 assert(con);
986 con->progname = progname;
987 }
988
989 const char *
dhcpcd_get_progname(const DHCPCD_CONNECTION * con)990 dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
991 {
992
993 assert(con);
994 return con->progname;
995 }
996
997 #ifndef HAVE_STRVERSCMP
998 /* Good enough for our needs */
999 static int
strverscmp(const char * s1,const char * s2)1000 strverscmp(const char *s1, const char *s2)
1001 {
1002 int s1maj, s1min, s1part;
1003 int s2maj, s2min, s2part;
1004 int r;
1005
1006 s1min = s1part = 0;
1007 if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
1008 return -1;
1009 s2min = s2part = 0;
1010 if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
1011 return -1;
1012 r = s1maj - s2maj;
1013 if (r != 0)
1014 return r;
1015 r = s1min - s2min;
1016 if (r != 0)
1017 return r;
1018 return s1part - s2part;
1019 }
1020 #endif
1021
1022 int
dhcpcd_open(DHCPCD_CONNECTION * con,bool privileged)1023 dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
1024 {
1025 const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
1026 char cmd[128];
1027 ssize_t bytes;
1028 size_t nifs, n;
1029
1030 assert(con);
1031 if (con->open) {
1032 if (con->listen_fd != -1)
1033 return con->listen_fd;
1034 errno = EISCONN;
1035 return -1;
1036 }
1037
1038 /* We need to block the command fd */
1039 con->command_fd = dhcpcd_connect(path, 0);
1040 if (con->command_fd == -1) {
1041 if (errno == ENOENT) {
1042 path = privileged ?
1043 DHCPCD_OSOCKET : DHCPCD_UNPRIV_OSOCKET;
1044 con->command_fd = dhcpcd_connect(path, 0);
1045 }
1046 if (con->command_fd == -1)
1047 goto err_exit;
1048 }
1049
1050 con->terminate_commands = false;
1051 if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
1052 goto err_exit;
1053 con->terminate_commands =
1054 strverscmp(con->version, "6.4.1") >= 0 ? true : false;
1055
1056 if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
1057 goto err_exit;
1058
1059 con->open = true;
1060 con->privileged = privileged;
1061 update_status(con, DHC_UNKNOWN);
1062
1063 con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
1064 if (con->listen_fd == -1)
1065 goto err_exit;
1066
1067 dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
1068 dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
1069 bytes = read(con->command_fd, cmd, sizeof(nifs));
1070 if (bytes != sizeof(nifs))
1071 goto err_exit;
1072 memcpy(&nifs, cmd, sizeof(nifs));
1073 /* We don't dispatch each interface here as that
1074 * causes too much notification spam when the GUI starts */
1075 for (n = 0; n < nifs; n++) {
1076 /* Some interface states we do not create an interface for
1077 * such as DHS_INFORM. */
1078 dhcpcd_read_if(con, con->command_fd);
1079 }
1080
1081 update_status(con, DHC_UNKNOWN);
1082
1083 return con->listen_fd;
1084
1085 err_exit:
1086 dhcpcd_close(con);
1087 return -1;
1088 }
1089
1090 int
dhcpcd_get_fd(DHCPCD_CONNECTION * con)1091 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
1092 {
1093
1094 assert(con);
1095 return con->listen_fd;
1096 }
1097
1098 bool
dhcpcd_privileged(DHCPCD_CONNECTION * con)1099 dhcpcd_privileged(DHCPCD_CONNECTION *con)
1100 {
1101
1102 assert(con);
1103 return con->privileged;
1104 }
1105
1106 unsigned int
dhcpcd_status(DHCPCD_CONNECTION * con,const char ** status)1107 dhcpcd_status(DHCPCD_CONNECTION *con, const char **status)
1108 {
1109
1110 assert(con);
1111 if (status)
1112 *status = dhcpcd_cstates[con->status];
1113 return con->status;
1114 }
1115
1116 bool
dhcpcd_af_waiting(const DHCPCD_CONNECTION * con)1117 dhcpcd_af_waiting(const DHCPCD_CONNECTION *con)
1118 {
1119
1120 assert(con);
1121 return con->af_waiting;
1122 }
1123
1124 const char *
dhcpcd_version(DHCPCD_CONNECTION * con)1125 dhcpcd_version(DHCPCD_CONNECTION *con)
1126 {
1127
1128 assert(con);
1129 return con->version;
1130 }
1131
1132 const char *
dhcpcd_cffile(DHCPCD_CONNECTION * con)1133 dhcpcd_cffile(DHCPCD_CONNECTION *con)
1134 {
1135
1136 assert(con);
1137 return con->cffile;
1138 }
1139
1140 void
dhcpcd_set_if_callback(DHCPCD_CONNECTION * con,void (* cb)(DHCPCD_IF *,void *),void * ctx)1141 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
1142 void (*cb)(DHCPCD_IF *, void *), void *ctx)
1143 {
1144
1145 assert(con);
1146 con->if_cb = cb;
1147 con->if_context = ctx;
1148 }
1149
1150 void
dhcpcd_set_status_callback(DHCPCD_CONNECTION * con,void (* cb)(DHCPCD_CONNECTION *,unsigned int,const char *,void *),void * ctx)1151 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
1152 void (*cb)(DHCPCD_CONNECTION *, unsigned int, const char *, void *),
1153 void *ctx)
1154 {
1155
1156 assert(con);
1157 con->status_cb = cb;
1158 con->status_context = ctx;
1159 }
1160
1161 void
dhcpcd_close(DHCPCD_CONNECTION * con)1162 dhcpcd_close(DHCPCD_CONNECTION *con)
1163 {
1164 DHCPCD_IF *nif;
1165 DHCPCD_WPA *nwpa;
1166 DHCPCD_WI_HIST *nh;
1167
1168 assert(con);
1169
1170 if (con->open) {
1171 if (con->command_fd != -1)
1172 shutdown(con->command_fd, SHUT_RDWR);
1173 if (con->listen_fd != -1)
1174 shutdown(con->listen_fd, SHUT_RDWR);
1175 con->open = false;
1176 }
1177
1178 /* Shut down WPA listeners as they aren't much good without dhcpcd.
1179 * They'll be restarted anyway when dhcpcd comes back up. */
1180 while (con->wpa) {
1181 nwpa = con->wpa->next;
1182 dhcpcd_wpa_close(con->wpa);
1183 free(con->wpa);
1184 con->wpa = nwpa;
1185 }
1186 while (con->wi_history) {
1187 nh = con->wi_history->next;
1188 free(con->wi_history);
1189 con->wi_history = nh;
1190 }
1191 while (con->interfaces) {
1192 nif = con->interfaces->next;
1193 free(con->interfaces->data);
1194 free(con->interfaces->last_message);
1195 free(con->interfaces);
1196 con->interfaces = nif;
1197 }
1198
1199 update_status(con, DHC_DOWN);
1200
1201 if (con->command_fd != -1) {
1202 close(con->command_fd);
1203 con->command_fd = -1;
1204 }
1205 if (con->listen_fd != -1) {
1206 close(con->listen_fd);
1207 con->listen_fd = -1;
1208 }
1209
1210 if (con->cffile) {
1211 free(con->cffile);
1212 con->cffile = NULL;
1213 }
1214 if (con->version) {
1215 free(con->version);
1216 con->version = NULL;
1217 }
1218 if (con->buf) {
1219 free(con->buf);
1220 con->buf = NULL;
1221 con->buflen = 0;
1222 }
1223 }
1224
1225 void
dhcpcd_free(DHCPCD_CONNECTION * con)1226 dhcpcd_free(DHCPCD_CONNECTION *con)
1227 {
1228
1229 assert(con);
1230 free(con);
1231 }
1232
1233 DHCPCD_CONNECTION *
dhcpcd_if_connection(DHCPCD_IF * i)1234 dhcpcd_if_connection(DHCPCD_IF *i)
1235 {
1236
1237 assert(i);
1238 return i->con;
1239 }
1240
1241 char *
dhcpcd_if_message(DHCPCD_IF * i,bool * new_msg)1242 dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg)
1243 {
1244 const char *ip, *iplen, *pfx;
1245 char *msg, *p;
1246 const char *reason = NULL;
1247 size_t len;
1248 bool showssid;
1249
1250 assert(i);
1251 /* Don't report non SLAAC configurations */
1252 if (i->type == DHT_RA && i->up &&
1253 dhcpcd_get_value(i, "nd1_addr1") == NULL &&
1254 dhcpcd_get_value(i, "ra1_prefix") == NULL)
1255 return NULL;
1256
1257 showssid = false;
1258 switch (i->state) {
1259 case DHS_EXPIRE:
1260 reason = _("Expired");
1261 break;
1262 case DHS_CARRIER:
1263 if (i->wireless) {
1264 showssid = true;
1265 reason = _("Associated with");
1266 } else {
1267 /* Don't report able in if we have addresses */
1268 const DHCPCD_IF *ci;
1269
1270 for (ci = i->con->interfaces; ci; ci = ci->next) {
1271 if (ci != i &&
1272 strcmp(i->ifname, ci->ifname) == 0 &&
1273 ci->up)
1274 break;
1275 }
1276 if (ci)
1277 return NULL;
1278 reason = _("Link is up, configuring");
1279 }
1280 break;
1281 case DHS_NOCARRIER:
1282 if (i->wireless) {
1283 if (i->ssid) {
1284 reason = _("Disassociated from");
1285 showssid = true;
1286 } else
1287 reason = _("Not associated");
1288 } else
1289 reason = _("Link is down");
1290 break;
1291 case DHS_NOCARRIER_ROAMING:
1292 reason = _("Link is down, roaming");
1293 break;
1294 case DHS_DEPARTED:
1295 reason = _("Departed");
1296 break;
1297 case DHS_UNKNOWN:
1298 reason = _("Unknown link state");
1299 break;
1300 case DHS_FAIL:
1301 reason = _("Automatic configuration not possible");
1302 break;
1303 case DHS_3RDPARTY:
1304 reason = _("Waiting for 3rd Party configuration");
1305 break;
1306 }
1307
1308 if (reason == NULL) {
1309 if (i->up) {
1310 if (i->state == DHS_DELEGATED)
1311 reason = _("Delegated");
1312 else
1313 reason = _("Configured");
1314 } else if (i->type == DHT_RA)
1315 reason = "Expired RA";
1316 else if (i->type == DHT_IPV4LL)
1317 reason = "Expired IPv4LL";
1318 else
1319 reason = i->reason;
1320 }
1321
1322 pfx = i->up ? "new_" : "old_";
1323 if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address")))
1324 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr");
1325 else if ((ip = dhcpcd_get_value(i, "nd1_addr1")))
1326 iplen = NULL;
1327 else if ((ip = dhcpcd_get_value(i, "ra1_addr")))
1328 iplen = NULL;
1329 else if ((ip = dhcpcd_get_value(i, "ra1_prefix")))
1330 iplen = NULL;
1331 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1332 "dhcp6_ia_na1_ia_addr1")))
1333 iplen = "128";
1334 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1335 "delegated_dhcp6_prefix")))
1336 iplen = NULL;
1337 else if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip6_address")))
1338 iplen = NULL;
1339 else {
1340 ip = NULL;
1341 iplen = NULL;
1342 }
1343
1344 len = strlen(i->ifname) + strlen(reason) + 3;
1345 if (showssid && i->ssid)
1346 len += strlen(i->ssid) + 1;
1347 if (ip)
1348 len += strlen(ip) + 1;
1349 if (iplen)
1350 len += strlen(iplen) + 1;
1351 msg = p = malloc(len);
1352 if (msg == NULL)
1353 return NULL;
1354 p += snprintf(msg, len, "%s: %s", i->ifname, reason);
1355 if (showssid)
1356 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
1357 if (iplen)
1358 snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
1359 else if (ip)
1360 snprintf(p, len - (size_t)(p - msg), " %s", ip);
1361
1362 if (new_msg) {
1363 if (i->last_message == NULL || strcmp(i->last_message, msg))
1364 *new_msg = true;
1365 else
1366 *new_msg = false;
1367 }
1368 free(i->last_message);
1369 i->last_message = strdup(msg);
1370
1371 return msg;
1372 }
1373