3 * Copyright 2009-2014 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/socket.h>
34 #include <arpa/inet.h>
65 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
69 #define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
72 static const char * const dhcpcd_types[] =
73 { "link", "ipv4", "ra", "dhcp6", NULL };
76 dhcpcd_command_fd(DHCPCD_CONNECTION *con,
77 int fd, bool progname, const char *cmd, char **buffer)
87 /* Each command is \n terminated.
88 * Each argument is NULL seperated.
89 * We may need to send a space one day, so the API
90 * in this function may need to be improved */
93 pl = strlen(con->progname);
94 len = pl + 1 + cl + 1;
99 if (con->terminate_commands)
101 if (len > sizeof(buf)) {
107 memcpy(buf, con->progname, pl);
113 while ((p = strchr(p, ' ')) != NULL)
115 if (con->terminate_commands) {
120 if (write(fd, buf, len) == -1)
125 bytes = read(fd, buf, sizeof(size_t));
126 if (bytes == 0 || bytes == -1)
128 memcpy(&len, buf, sizeof(size_t));
129 nbuf = realloc(*buffer, len + 1);
133 bytes = read(fd, *buffer, len);
134 if (bytes != -1 && (size_t)bytes < len)
135 *buffer[bytes] = '\0';
140 dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
144 if (!con->privileged) {
148 return dhcpcd_command_fd(con, con->command_fd, true, cmd, buffer);
152 dhcpcd_ctrl_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
155 return dhcpcd_command_fd(con, con->command_fd, false, cmd, buffer);
159 dhcpcd_realloc(DHCPCD_CONNECTION *con, size_t len)
163 if (con->buflen < len) {
166 nbuf = realloc(con->buf, len);
176 dhcpcd_command_arg(DHCPCD_CONNECTION *con, const char *cmd, const char *arg,
184 cmdlen = strlen(cmd);
186 len = cmdlen + strlen(arg) + 2;
189 if (!dhcpcd_realloc(con, len))
191 strlcpy(con->buf, cmd, con->buflen);
193 con->buf[cmdlen] = ' ';
194 strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
197 return dhcpcd_command_fd(con, con->command_fd, true, con->buf, buffer);
202 dhcpcd_connect(const char *path, int opts)
206 struct sockaddr_un sun;
209 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | opts, 0);
213 memset(&sun, 0, sizeof(sun));
214 sun.sun_family = AF_UNIX;
215 strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
216 len = (socklen_t)SUN_LEN(&sun);
217 if (connect(fd, (struct sockaddr *)&sun, len) == 0)
224 get_value(const char *data, size_t len, const char *var)
233 while (data + vlen + 1 < end) {
234 /* Skip past NUL padding */
239 if (strncmp(data, var, vlen) == 0 && data[vlen] == '=') {
243 data += strlen(data) + 1;
245 if (p != NULL && *p != '\0')
251 dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
256 return get_value(i->data, i->data_len, var);
260 dhcpcd_encode_string_escape(char *dst, size_t len, const char *src, size_t slen)
270 if ((c == '\\' || !isascii(c) || !isprint(c))) {
273 if (len == 0 || len == 1) {
277 *dst++ = '\\'; *dst++ = '\\';
289 *dst++ = (((unsigned char)c >> 6) & 03) + '0';
290 *dst++ = (((unsigned char)c >> 3) & 07) + '0';
291 *dst++ = ( (unsigned char)c & 07) + '0';
316 return (ssize_t)bytes;
320 dhcpcd_decode_string_escape(char *dst, size_t dlen, const char *src)
331 if (dst && --dlen == 0) {
352 if (*src >= '0' && *src <='7')
353 oct = oct * 8 + (*src++ - '0');
358 if (*src >= '0' && *src <='7')
359 oct = oct * 8 + (*src++ - '0');
377 if (dst && --dlen == 0) {
386 dhcpcd_decode_hex(char *dst, size_t dlen, const char *src)
394 if (dlen == 0 || dlen == 1) {
399 for (i = 0; i < 2; i++) {
401 if (c >= '0' && c <= '9')
403 else if (c >= 'a' && c <= 'f')
405 else if (c >= 'A' && c <= 'F')
419 return (ssize_t)bytes;
423 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
434 l = strlcpy(p, prefix, plen);
435 if (l >= sizeof(pvar)) {
441 if (strlcpy(p, var, plen) >= plen) {
445 return dhcpcd_get_value(i, pvar);
449 strtobool(const char *var)
455 return (*var == '0' || *var == '\0' ||
456 strcmp(var, "false") == 0 ||
457 strcmp(var, "no") == 0) ? false : true;
461 get_status(DHCPCD_CONNECTION *con)
467 if (con->command_fd == -1)
470 if (con->listen_fd == -1)
473 if (con->interfaces == NULL)
474 return "initialised";
476 status = "disconnected";
477 for (i = con->interfaces; i; i = i->next) {
479 if (strcmp(i->type, "link")) {
480 status = "connected";
483 status = "connecting";
490 update_status(DHCPCD_CONNECTION *con, const char *nstatus)
495 nstatus = get_status(con);
496 if (con->status == NULL || strcmp(nstatus, con->status)) {
497 con->status = nstatus;
499 con->status_cb(con, con->status, con->status_context);
504 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
508 return con->interfaces;
512 dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, const char *type)
520 for (i = con->interfaces; i; i = i->next)
521 if (strcmp(i->ifname, ifname) == 0 &&
522 strcmp(i->type, type) == 0)
528 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
530 const char *ifname, *ifclass, *reason, *type, *order, *flags;
531 char *orderdup, *o, *p;
532 DHCPCD_IF *e, *i, *l, *n, *nl;
537 char *dp = data, *de = data + len;
539 printf ("XX: %s\n", dp);
540 dp += strlen(dp) + 1;
544 ifname = get_value(data, len, "interface");
545 if (ifname == NULL || *ifname == '\0') {
549 reason = get_value(data, len, "reason");
550 if (reason == NULL || *reason == '\0') {
554 ifclass = get_value(data, len, "ifclass");
555 /* Skip pseudo interfaces */
556 if (ifclass && *ifclass != '\0') {
560 if (strcmp(reason, "RECONFIGURE") == 0 ||
561 strcmp(reason, "INFORM") == 0 || strcmp(reason, "INFORM6") == 0)
566 order = get_value(data, len, "interface_order");
567 if (order == NULL || *order == '\0') {
572 if (strcmp(reason, "PREINIT") == 0 ||
573 strcmp(reason, "UNKNOWN") == 0 ||
574 strcmp(reason, "CARRIER") == 0 ||
575 strcmp(reason, "NOCARRIER") == 0 ||
576 strcmp(reason, "DEPARTED") == 0 ||
577 strcmp(reason, "STOPPED") == 0)
579 else if (strcmp(reason, "ROUTERADVERT") == 0)
581 else if (reason[strlen(reason) - 1] == '6')
587 /* Remove all instances on carrier drop */
588 if (strcmp(reason, "NOCARRIER") == 0 ||
589 strcmp(reason, "DEPARTED") == 0 ||
590 strcmp(reason, "STOPPED") == 0)
593 for (e = con->interfaces; e; e = n) {
595 if (strcmp(e->ifname, ifname) == 0) {
596 if (strcmp(e->type, type) == 0)
602 con->interfaces = e->next;
608 } else if (strcmp(type, "link")) {
609 /* If link is down, ignore it */
610 e = dhcpcd_get_if(con, ifname, "link");
615 orderdup = strdup(order);
616 if (orderdup == NULL)
619 /* Find our pointer */
622 for (e = con->interfaces; e; e = e->next) {
623 if (strcmp(e->ifname, ifname) == 0 &&
624 strcmp(e->type, type) == 0)
633 i = malloc(sizeof(*i));
643 i->last_message = NULL;
647 /* Now fill out our interface structure */
654 flags = dhcpcd_get_value(i, "ifflags");
656 i->flags = (unsigned int)strtoul(flags, NULL, 0);
659 if (strcmp(reason, "CARRIER") == 0 ||
660 strcmp(reason, "DELEGATED6") == 0)
663 i->up = strtobool(dhcpcd_get_value(i, "if_up"));
664 i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless"));
665 i->ssid = dhcpcd_get_value(i, "ifssid");
666 if (i->ssid == NULL && i->wireless)
667 i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid" : "old_ssid");
673 while ((o = strsep(&p, " ")) != NULL) {
674 for (ti = 0; dhcpcd_types[ti]; ti++) {
676 for (e = con->interfaces; e; e = e->next) {
677 if (strcmp(e->ifname, o) == 0 &&
678 strcmp(e->type, dhcpcd_types[ti]) == 0)
689 con->interfaces = e->next;
700 /* Free any stragglers */
701 while (con->interfaces) {
702 e = con->interfaces->next;
703 free(con->interfaces->data);
704 free(con->interfaces->last_message);
705 free(con->interfaces);
710 return addedi ? i : NULL;
714 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
716 char sbuf[sizeof(size_t)], *rbuf;
721 bytes = read(fd, sbuf, sizeof(sbuf));
722 if (bytes == 0 || bytes == -1) {
726 memcpy(&len, sbuf, sizeof(len));
727 rbuf = malloc(len + 1);
730 bytes = read(fd, rbuf, len);
731 if (bytes == 0 || bytes == -1) {
736 if ((size_t)bytes != len) {
743 i = dhcpcd_new_if(con, rbuf, len);
750 dhcpcd_dispatchif(DHCPCD_IF *i)
755 i->con->if_cb(i, i->con->if_context);
756 dhcpcd_wpa_if_event(i);
760 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
765 i = dhcpcd_read_if(con, con->listen_fd);
767 dhcpcd_dispatchif(i);
769 /* Have to call update_status last as it could
770 * cause the interface to be destroyed. */
771 update_status(con, NULL);
777 DHCPCD_CONNECTION *con;
779 con = calloc(1, sizeof(*con));
780 con->command_fd = con->listen_fd = -1;
782 con->progname = "libdhcpcd";
787 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
791 con->progname = progname;
795 dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
799 return con->progname;
802 #ifndef HAVE_STRVERSCMP
803 /* Good enough for our needs */
805 strverscmp(const char *s1, const char *s2)
807 int s1maj, s1min, s1part;
808 int s2maj, s2min, s2part;
812 if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
815 if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
823 return s1part - s2part;
828 dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
830 const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
837 if (con->listen_fd != -1)
838 return con->listen_fd;
842 /* We need to block the command fd */
843 con->command_fd = dhcpcd_connect(path, 0);
844 if (con->command_fd == -1)
847 con->terminate_commands = false;
848 if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
850 con->terminate_commands =
851 strverscmp(con->version, "6.4.1") >= 0 ? true : false;
853 if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
857 con->privileged = privileged;
858 update_status(con, NULL);
860 con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
861 if (con->listen_fd == -1)
864 dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
865 dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
866 bytes = read(con->command_fd, cmd, sizeof(nifs));
867 if (bytes != sizeof(nifs))
869 memcpy(&nifs, cmd, sizeof(nifs));
870 /* We don't dispatch each interface here as that
871 * causes too much notification spam when the GUI starts */
872 for (n = 0; n < nifs; n++)
873 dhcpcd_read_if(con, con->command_fd);
875 update_status(con, NULL);
877 return con->listen_fd;
885 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
889 return con->listen_fd;
893 dhcpcd_privileged(DHCPCD_CONNECTION *con)
897 return con->privileged;
901 dhcpcd_status(DHCPCD_CONNECTION *con)
909 dhcpcd_version(DHCPCD_CONNECTION *con)
917 dhcpcd_cffile(DHCPCD_CONNECTION *con)
925 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
926 void (*cb)(DHCPCD_IF *, void *), void *ctx)
931 con->if_context = ctx;
935 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
936 void (*cb)(DHCPCD_CONNECTION *, const char *, void *), void *ctx)
941 con->status_context = ctx;
945 dhcpcd_close(DHCPCD_CONNECTION *con)
955 /* Shut down WPA listeners as they aren't much good without dhcpcd.
956 * They'll be restarted anyway when dhcpcd comes back up. */
958 nwpa = con->wpa->next;
959 dhcpcd_wpa_close(con->wpa);
963 while (con->wi_history) {
964 nh = con->wi_history->next;
965 free(con->wi_history);
966 con->wi_history = nh;
968 while (con->interfaces) {
969 nif = con->interfaces->next;
970 free(con->interfaces->data);
971 free(con->interfaces->last_message);
972 free(con->interfaces);
973 con->interfaces = nif;
976 if (con->command_fd != -1)
977 shutdown(con->command_fd, SHUT_RDWR);
978 if (con->listen_fd != -1)
979 shutdown(con->listen_fd, SHUT_RDWR);
981 update_status(con, "down");
983 if (con->command_fd != -1) {
984 close(con->command_fd);
985 con->command_fd = -1;
987 if (con->listen_fd != -1) {
988 close(con->listen_fd);
1008 dhcpcd_free(DHCPCD_CONNECTION *con)
1016 dhcpcd_if_connection(DHCPCD_IF *i)
1024 dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg)
1026 const char *ip, *iplen, *pfx;
1028 const char *reason = NULL;
1033 /* Don't report non SLAAC configurations */
1034 if (strcmp(i->type, "ra") == 0 && i->up &&
1035 dhcpcd_get_value(i, "ra1_prefix") == NULL)
1039 if (strcmp(i->reason, "EXPIRE") == 0)
1040 reason = _("Expired");
1041 else if (strcmp(i->reason, "CARRIER") == 0) {
1044 reason = _("Associated with");
1046 /* Don't report able in if we have addresses */
1047 const DHCPCD_IF *ci;
1049 for (ci = i->con->interfaces; ci; ci = ci->next) {
1051 strcmp(i->ifname, ci->ifname) == 0 &&
1057 reason = _("Link is up, configuring");
1059 } else if (strcmp(i->reason, "NOCARRIER") == 0) {
1062 reason = _("Disassociated from");
1065 reason = _("Not associated");
1067 reason = _("Link is down");
1068 } else if (strcmp(i->reason, "DEPARTED") == 0)
1069 reason = _("Departed");
1070 else if (strcmp(i->reason, "UNKNOWN") == 0)
1071 reason = _("Unknown link state");
1072 else if (strcmp(i->reason, "FAIL") == 0)
1073 reason = _("Automatic configuration not possible");
1074 else if (strcmp(i->reason, "3RDPARTY") == 0)
1075 reason = _("Waiting for 3rd Party configuration");
1077 if (reason == NULL) {
1079 if (strcmp(i->reason, "DELEGATED6") == 0)
1080 reason = _("Delegated");
1082 reason = _("Configured");
1083 } else if (strcmp(i->type, "ra") == 0)
1084 reason = "Expired RA";
1089 pfx = i->up ? "new_" : "old_";
1090 if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address")))
1091 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr");
1092 else if ((ip = dhcpcd_get_value(i, "ra1_prefix")))
1094 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1095 "dhcp6_ia_na1_ia_addr1")))
1097 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1098 "delegated_dhcp6_prefix")))
1105 len = strlen(i->ifname) + strlen(reason) + 3;
1106 if (showssid && i->ssid)
1107 len += strlen(i->ssid) + 1;
1109 len += strlen(ip) + 1;
1111 len += strlen(iplen) + 1;
1112 msg = p = malloc(len);
1115 p += snprintf(msg, len, "%s: %s", i->ifname, reason);
1117 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
1119 snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
1121 snprintf(p, len - (size_t)(p - msg), " %s", ip);
1124 if (i->last_message == NULL || strcmp(i->last_message, msg))
1129 free(i->last_message);
1130 i->last_message = strdup(msg);