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