#include <arpa/inet.h>
#include <assert.h>
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#define _(a) (a)
#endif
+#ifdef HAVE_VIS_H
+#include <vis.h>
+#endif
+
#ifndef SUN_LEN
#define SUN_LEN(su) \
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
+#ifndef iswhite
+#define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
+#endif
+
static const char * const dhcpcd_types[] =
{ "link", "ipv4", "ra", "dhcp6", NULL };
static ssize_t
dhcpcd_command_fd(DHCPCD_CONNECTION *con,
- int fd, const char *cmd, char **buffer)
+ int fd, bool progname, const char *cmd, char **buffer)
{
- size_t len;
+ size_t pl, cl, len;
ssize_t bytes;
char buf[1024], *p;
char *nbuf;
+ assert(con);
+ assert(cmd);
+
/* Each command is \n terminated.
* Each argument is NULL seperated.
* We may need to send a space one day, so the API
* in this function may need to be improved */
- len = strlen(cmd) + 1;
+ cl = strlen(cmd);
+ if (progname) {
+ pl = strlen(con->progname);
+ len = pl + 1 + cl + 1;
+ } else {
+ pl = 0;
+ len = cl + 1;
+ }
if (con->terminate_commands)
len++;
if (len > sizeof(buf)) {
errno = ENOBUFS;
return -1;
}
- strlcpy(buf, cmd, sizeof(buf));
p = buf;
+ if (progname) {
+ memcpy(buf, con->progname, pl);
+ buf[pl] = '\0';
+ p = buf + pl + 1;
+ }
+ memcpy(p, cmd, cl);
+ p[cl] = '\0';
while ((p = strchr(p, ' ')) != NULL)
*p++ = '\0';
if (con->terminate_commands) {
dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
{
- return dhcpcd_command_fd(con, con->command_fd, cmd, buffer);
+ assert(con);
+ if (!con->privileged) {
+ errno = EACCES;
+ return -1;
+ }
+ return dhcpcd_command_fd(con, con->command_fd, true, cmd, buffer);
+}
+
+static ssize_t
+dhcpcd_ctrl_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
+{
+
+ return dhcpcd_command_fd(con, con->command_fd, false, cmd, buffer);
}
bool
dhcpcd_realloc(DHCPCD_CONNECTION *con, size_t len)
{
+ assert(con);
if (con->buflen < len) {
char *nbuf;
{
size_t cmdlen, len;
+ assert(con);
+ assert(cmd);
+
cmdlen = strlen(cmd);
- len = cmdlen + strlen(arg) + 2;
+ if (arg)
+ len = cmdlen + strlen(arg) + 2;
+ else
+ len = cmdlen + 1;
if (!dhcpcd_realloc(con, len))
return -1;
strlcpy(con->buf, cmd, con->buflen);
- con->buf[cmdlen] = ' ';
- strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
+ if (arg) {
+ con->buf[cmdlen] = ' ';
+ strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
+ }
- return dhcpcd_command_fd(con, con->command_fd, con->buf, buffer);
+ return dhcpcd_command_fd(con, con->command_fd, true, con->buf, buffer);
}
static int
-dhcpcd_connect(int opts)
+dhcpcd_connect(const char *path, int opts)
{
int fd;
socklen_t len;
struct sockaddr_un sun;
+ assert(path);
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | opts, 0);
if (fd == -1)
return -1;
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
- strlcpy(sun.sun_path, DHCPCD_SOCKET, sizeof(sun.sun_path));
+ strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
len = (socklen_t)SUN_LEN(&sun);
if (connect(fd, (struct sockaddr *)&sun, len) == 0)
return fd;
vlen = strlen(var);
p = NULL;
while (data + vlen + 1 < end) {
- if (strncmp(data, var, vlen) == 0) {
- p = data + vlen;
+ /* Skip past NUL padding */
+ if (*data == '\0') {
+ data++;
+ continue;
+ }
+ if (strncmp(data, var, vlen) == 0 && data[vlen] == '=') {
+ p = data + vlen + 1;
break;
}
data += strlen(data) + 1;
{
assert(i);
+ assert(var);
return get_value(i->data, i->data_len, var);
}
+ssize_t
+dhcpcd_encode_string_escape(char *dst, size_t len, const char *src, size_t slen)
+{
+ const char *end;
+ size_t bytes;
+ char c;
+
+ end = src + slen;
+ bytes = 0;
+ while (src < end) {
+ c = *src++;
+ if ((c == '\\' || !isascii(c) || !isprint(c))) {
+ if (c == '\\') {
+ if (dst) {
+ if (len == 0 || len == 1) {
+ errno = ENOSPC;
+ return -1;
+ }
+ *dst++ = '\\'; *dst++ = '\\';
+ len -= 2;
+ }
+ bytes += 2;
+ continue;
+ }
+ if (dst) {
+ if (len < 5) {
+ errno = ENOSPC;
+ return -1;
+ }
+ *dst++ = '\\';
+ *dst++ = (((unsigned char)c >> 6) & 03) + '0';
+ *dst++ = (((unsigned char)c >> 3) & 07) + '0';
+ *dst++ = ( (unsigned char)c & 07) + '0';
+ len -= 4;
+ }
+ bytes += 4;
+ } else {
+ if (dst) {
+ if (len == 0) {
+ errno = ENOSPC;
+ return -1;
+ }
+ *dst++ = (char)c;
+ len--;
+ }
+ bytes++;
+ }
+ }
+
+ if (dst) {
+ if (len == 0) {
+ errno = ENOSPC;
+ return -1;
+ }
+ *dst = '\0';
+ }
+ bytes++;
+
+ return (ssize_t)bytes;
+}
+
+ssize_t
+dhcpcd_decode_string_escape(char *dst, size_t dlen, const char *src)
+{
+ char c, esc;
+ int oct;
+ ssize_t bytes;
+
+ bytes = 0;
+ for (;;) {
+ c = *src++;
+ if (c == '\0')
+ break;
+ if (dst && --dlen == 0) {
+ errno = ENOSPC;
+ return 0;
+ }
+ switch (c) {
+ case '\\':
+ if (*src == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ esc = *src++;
+ switch (esc) {
+ case '\\':
+ case '0':
+ case '1':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ oct = esc - '0';
+ if (*src >= '0' && *src <='7')
+ oct = oct * 8 + (*src++ - '0');
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+ if (*src >= '0' && *src <='7')
+ oct = oct * 8 + (*src++ - '0');
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+ if (dst)
+ *dst++ = (char)oct;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ default:
+ if (dst)
+ *dst++ = c;
+ }
+ bytes++;
+ }
+ return bytes;
+}
+
+ssize_t
+dhcpcd_decode_hex(char *dst, size_t dlen, const char *src)
+{
+ size_t bytes, i;
+ char c;
+ int val, n;
+
+ bytes = 0;
+ while (*src) {
+ if (dlen == 0 || dlen == 1) {
+ errno = ENOSPC;
+ return -1;
+ }
+ val = 0;
+ for (i = 0; i < 2; i++) {
+ c = *src++;
+ if (c >= '0' && c <= '9')
+ n = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ n = 10 + c - 'a';
+ else if (c >= 'A' && c <= 'F')
+ n = 10 + c - 'A';
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+ val = val * 16 + n;
+ }
+ *dst++ = (char)val;
+ bytes += 2;
+ dlen -= 2;
+ if (*src == ':')
+ src++;
+ }
+ return (ssize_t)bytes;
+}
+
const char *
dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
{
char pvar[128], *p;
size_t plen, l;
+ assert(i);
+ assert(prefix);
+ assert(var);
+
p = pvar;
plen = sizeof(pvar);
l = strlcpy(p, prefix, plen);
DHCPCD_IF *i;
assert(con);
+ assert(ifname);
+ assert(type);
+
for (i = con->interfaces; i; i = i->next)
if (strcmp(i->ifname, ifname) == 0 &&
strcmp(i->type, type) == 0)
int ti;
bool addedi;
- ifname = get_value(data, len, "interface=");
+ ifname = get_value(data, len, "interface");
if (ifname == NULL || *ifname == '\0') {
errno = ESRCH;
return NULL;
}
- reason = get_value(data, len, "reason=");
+ reason = get_value(data, len, "reason");
if (reason == NULL || *reason == '\0') {
errno = ESRCH;
return NULL;
}
- ifclass = get_value(data, len, "ifclass=");
+ ifclass = get_value(data, len, "ifclass");
/* Skip pseudo interfaces */
if (ifclass && *ifclass != '\0') {
errno = ENOTSUP;
errno = ENOTSUP;
return NULL;
}
- order = get_value(data, len, "interface_order=");
+ order = get_value(data, len, "interface_order");
if (order == NULL || *order == '\0') {
errno = ESRCH;
return NULL;
} else if (strcmp(type, "link")) {
/* If link is down, ignore it */
e = dhcpcd_get_if(con, ifname, "link");
- if (e && strcmp(e->reason, "NOCARRIER") == 0)
+ if (e && !e->up)
return NULL;
}
i->ifname = ifname;
i->type = type;
i->reason = reason;
- flags = dhcpcd_get_value(i, "ifflags=");
+ flags = dhcpcd_get_value(i, "ifflags");
if (flags)
i->flags = (unsigned int)strtoul(flags, NULL, 0);
else
if (strcmp(reason, "CARRIER") == 0)
i->up = true;
else
- i->up = strtobool(dhcpcd_get_value(i, "if_up="));
- i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless="));
- i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid=" : "old_ssid=");
+ i->up = strtobool(dhcpcd_get_value(i, "if_up"));
+ i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless"));
+ i->ssid = dhcpcd_get_value(i, "ifssid");
/* Sort! */
n = nl = NULL;
con = calloc(1, sizeof(*con));
con->command_fd = con->listen_fd = -1;
con->open = false;
+ con->progname = "libdhcpcd";
return con;
}
-#ifndef __GLIBC__
+void
+dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
+{
+
+ assert(con);
+ con->progname = progname;
+}
+
+const char *
+dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
+{
+
+ assert(con);
+ return con->progname;
+}
+
+#ifndef HAVE_STRVERSCMP
/* Good enough for our needs */
static int
strverscmp(const char *s1, const char *s2)
#endif
int
-dhcpcd_open(DHCPCD_CONNECTION *con)
+dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
{
+ const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
char cmd[128];
ssize_t bytes;
size_t nifs, n;
return -1;
}
/* We need to block the command fd */
- con->command_fd = dhcpcd_connect(0);
+ con->command_fd = dhcpcd_connect(path, 0);
if (con->command_fd == -1)
goto err_exit;
con->terminate_commands = false;
- if (dhcpcd_command(con, "--version", &con->version) <= 0)
+ if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
goto err_exit;
con->terminate_commands =
strverscmp(con->version, "6.4.1") >= 0 ? true : false;
- if (dhcpcd_command(con, "--getconfigfile", &con->cffile) <= 0)
+ if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
goto err_exit;
con->open = true;
+ con->privileged = privileged;
update_status(con, NULL);
- con->listen_fd = dhcpcd_connect(SOCK_NONBLOCK);
+ con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
if (con->listen_fd == -1)
goto err_exit;
- dhcpcd_command_fd(con, con->listen_fd, "--listen", NULL);
- dhcpcd_command_fd(con, con->command_fd, "--getinterfaces", NULL);
+ dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
+ dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
bytes = read(con->command_fd, cmd, sizeof(nifs));
if (bytes != sizeof(nifs))
goto err_exit;
return con->listen_fd;
}
+bool
+dhcpcd_privileged(DHCPCD_CONNECTION *con)
+{
+
+ assert(con);
+ return con->privileged;
+}
+
const char *
dhcpcd_status(DHCPCD_CONNECTION *con)
{
assert(i);
/* Don't report non SLAAC configurations */
if (strcmp(i->type, "ra") == 0 && i->up &&
- dhcpcd_get_value(i, "ra1_prefix=") == NULL)
+ dhcpcd_get_value(i, "ra1_prefix") == NULL)
return NULL;
showssid = false;
reason = _("Not associated");
} else
reason = _("Cable unplugged");
- } else if (strcmp(i->reason, "UNKNOWN") == 0)
+ } else if (strcmp(i->reason, "DEPARTED") == 0)
+ reason = _("Departed");
+ else if (strcmp(i->reason, "UNKNOWN") == 0)
reason = _("Unknown link state");
else if (strcmp(i->reason, "FAIL") == 0)
reason = _("Automatic configuration not possible");
}
pfx = i->up ? "new_" : "old_";
- if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address=")))
- iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr=");
- else if ((ip = dhcpcd_get_value(i, "ra1_prefix=")))
+ if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address")))
+ iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr");
+ else if ((ip = dhcpcd_get_value(i, "ra1_prefix")))
iplen = NULL;
else if ((ip = dhcpcd_get_prefix_value(i, pfx,
- "dhcp6_ia_na1_ia_addr1=")))
+ "dhcp6_ia_na1_ia_addr1")))
iplen = "128";
else {
ip = NULL;