Add support dhcpcd NOCARRIER_ROAMING
[dhcpcd-ui] / src / libdhcpcd / dhcpcd.c
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
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
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
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
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
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
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 *
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 *
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
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
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
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 *
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
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
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
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 *
559 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
560 {
561
562         assert(con);
563         return con->interfaces;
564 }
565
566 char **
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
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
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 **
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 *
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
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
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 *
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 *
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
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
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 *
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
982 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
983 {
984
985         assert(con);
986         con->progname = progname;
987 }
988
989 const char *
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
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
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
1091 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
1092 {
1093
1094         assert(con);
1095         return con->listen_fd;
1096 }
1097
1098 bool
1099 dhcpcd_privileged(DHCPCD_CONNECTION *con)
1100 {
1101
1102         assert(con);
1103         return con->privileged;
1104 }
1105
1106 unsigned int
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
1117 dhcpcd_af_waiting(const DHCPCD_CONNECTION *con)
1118 {
1119
1120         assert(con);
1121         return con->af_waiting;
1122 }
1123
1124 const char *
1125 dhcpcd_version(DHCPCD_CONNECTION *con)
1126 {
1127
1128         assert(con);
1129         return con->version;
1130 }
1131
1132 const char *
1133 dhcpcd_cffile(DHCPCD_CONNECTION *con)
1134 {
1135
1136         assert(con);
1137         return con->cffile;
1138 }
1139
1140 void
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
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
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
1226 dhcpcd_free(DHCPCD_CONNECTION *con)
1227 {
1228
1229         assert(con);
1230         free(con);
1231 }
1232
1233 DHCPCD_CONNECTION *
1234 dhcpcd_if_connection(DHCPCD_IF *i)
1235 {
1236
1237         assert(i);
1238         return i->con;
1239 }
1240
1241 char *
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 }