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