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