xref: /dflybsd-src/contrib/dhcpcd/src/control.c (revision 49ecb1a08dc8bbbd8cc8cec29fd6e3a0a12651c2)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
480aa9461SRoy Marples  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57827cba2SAaron LI  * All rights reserved
67827cba2SAaron LI 
77827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI  * modification, are permitted provided that the following conditions
97827cba2SAaron LI  * are met:
107827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
157827cba2SAaron LI  *
167827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI  * SUCH DAMAGE.
277827cba2SAaron LI  */
287827cba2SAaron LI 
297827cba2SAaron LI #include <sys/socket.h>
307827cba2SAaron LI #include <sys/stat.h>
317827cba2SAaron LI #include <sys/uio.h>
327827cba2SAaron LI #include <sys/un.h>
337827cba2SAaron LI 
347827cba2SAaron LI #include <errno.h>
357827cba2SAaron LI #include <fcntl.h>
367827cba2SAaron LI #include <stdio.h>
377827cba2SAaron LI #include <stdlib.h>
387827cba2SAaron LI #include <string.h>
397827cba2SAaron LI #include <time.h>
407827cba2SAaron LI #include <unistd.h>
417827cba2SAaron LI 
427827cba2SAaron LI #include "config.h"
437827cba2SAaron LI #include "common.h"
447827cba2SAaron LI #include "dhcpcd.h"
457827cba2SAaron LI #include "control.h"
467827cba2SAaron LI #include "eloop.h"
477827cba2SAaron LI #include "if.h"
487827cba2SAaron LI #include "logerr.h"
497f8103cdSRoy Marples #include "privsep.h"
507827cba2SAaron LI 
517827cba2SAaron LI #ifndef SUN_LEN
527827cba2SAaron LI #define SUN_LEN(su) \
537827cba2SAaron LI 	    (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
547827cba2SAaron LI #endif
557827cba2SAaron LI 
5680aa9461SRoy Marples static void control_handle_data(void *, unsigned short);
5780aa9461SRoy Marples 
587827cba2SAaron LI static void
control_queue_free(struct fd_list * fd)597827cba2SAaron LI control_queue_free(struct fd_list *fd)
607827cba2SAaron LI {
617827cba2SAaron LI 	struct fd_data *fdp;
627827cba2SAaron LI 
637827cba2SAaron LI 	while ((fdp = TAILQ_FIRST(&fd->queue))) {
647827cba2SAaron LI 		TAILQ_REMOVE(&fd->queue, fdp, next);
658d36e1dfSRoy Marples 		if (fdp->data_size != 0)
668d36e1dfSRoy Marples 			free(fdp->data);
677827cba2SAaron LI 		free(fdp);
687827cba2SAaron LI 	}
698d36e1dfSRoy Marples 
708d36e1dfSRoy Marples #ifdef CTL_FREE_LIST
717827cba2SAaron LI 	while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
727827cba2SAaron LI 		TAILQ_REMOVE(&fd->free_queue, fdp, next);
738d36e1dfSRoy Marples 		if (fdp->data_size != 0)
748d36e1dfSRoy Marples 			free(fdp->data);
757827cba2SAaron LI 		free(fdp);
767827cba2SAaron LI 	}
778d36e1dfSRoy Marples #endif
787827cba2SAaron LI }
797827cba2SAaron LI 
807f8103cdSRoy Marples void
control_free(struct fd_list * fd)817f8103cdSRoy Marples control_free(struct fd_list *fd)
827f8103cdSRoy Marples {
837f8103cdSRoy Marples 
847f8103cdSRoy Marples #ifdef PRIVSEP
857f8103cdSRoy Marples 	if (fd->ctx->ps_control_client == fd)
867f8103cdSRoy Marples 		fd->ctx->ps_control_client = NULL;
877f8103cdSRoy Marples #endif
887f8103cdSRoy Marples 
8980aa9461SRoy Marples 	eloop_event_delete(fd->ctx->eloop, fd->fd);
9080aa9461SRoy Marples 	close(fd->fd);
917f8103cdSRoy Marples 	TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
927f8103cdSRoy Marples 	control_queue_free(fd);
937f8103cdSRoy Marples 	free(fd);
947f8103cdSRoy Marples }
957f8103cdSRoy Marples 
967827cba2SAaron LI static void
control_hangup(struct fd_list * fd)97f3744ac9SRoy Marples control_hangup(struct fd_list *fd)
987827cba2SAaron LI {
997827cba2SAaron LI 
10080aa9461SRoy Marples #ifdef PRIVSEP
10180aa9461SRoy Marples 	if (IN_PRIVSEP(fd->ctx)) {
10280aa9461SRoy Marples 		if (ps_ctl_sendeof(fd) == -1)
10380aa9461SRoy Marples 			logerr(__func__);
10480aa9461SRoy Marples 	}
10580aa9461SRoy Marples #endif
10680aa9461SRoy Marples 	control_free(fd);
107f3744ac9SRoy Marples }
108f3744ac9SRoy Marples 
109*0b4c9755SRoy Marples static int
control_handle_read(struct fd_list * fd)110f3744ac9SRoy Marples control_handle_read(struct fd_list *fd)
111f3744ac9SRoy Marples {
112f3744ac9SRoy Marples 	char buffer[1024];
113f3744ac9SRoy Marples 	ssize_t bytes;
114f3744ac9SRoy Marples 
115f3744ac9SRoy Marples 	bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
116*0b4c9755SRoy Marples 	if (bytes == -1)
117f3744ac9SRoy Marples 		logerr(__func__);
118*0b4c9755SRoy Marples 	if (bytes == -1 || bytes == 0) {
119f3744ac9SRoy Marples 		control_hangup(fd);
120*0b4c9755SRoy Marples 		return -1;
1217827cba2SAaron LI 	}
1227f8103cdSRoy Marples 
1237f8103cdSRoy Marples #ifdef PRIVSEP
1247f8103cdSRoy Marples 	if (IN_PRIVSEP(fd->ctx)) {
1257f8103cdSRoy Marples 		ssize_t err;
1267f8103cdSRoy Marples 
1277f8103cdSRoy Marples 		fd->flags |= FD_SENDLEN;
1287f8103cdSRoy Marples 		err = ps_ctl_handleargs(fd, buffer, (size_t)bytes);
1297f8103cdSRoy Marples 		fd->flags &= ~FD_SENDLEN;
1307f8103cdSRoy Marples 		if (err == -1) {
1317f8103cdSRoy Marples 			logerr(__func__);
132*0b4c9755SRoy Marples 			return 0;
1337f8103cdSRoy Marples 		}
1347f8103cdSRoy Marples 		if (err == 1 &&
1357f8103cdSRoy Marples 		    ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
1367f8103cdSRoy Marples 			logerr(__func__);
13780aa9461SRoy Marples 			control_free(fd);
138*0b4c9755SRoy Marples 			return -1;
1397f8103cdSRoy Marples 		}
140*0b4c9755SRoy Marples 		return 0;
1417f8103cdSRoy Marples 	}
1427f8103cdSRoy Marples #endif
1437f8103cdSRoy Marples 
1447f8103cdSRoy Marples 	control_recvdata(fd, buffer, (size_t)bytes);
145*0b4c9755SRoy Marples 	return 0;
1467f8103cdSRoy Marples }
1477f8103cdSRoy Marples 
148*0b4c9755SRoy Marples static int
control_handle_write(struct fd_list * fd)14980aa9461SRoy Marples control_handle_write(struct fd_list *fd)
15080aa9461SRoy Marples {
15180aa9461SRoy Marples 	struct iovec iov[2];
15280aa9461SRoy Marples 	int iov_len;
15380aa9461SRoy Marples 	struct fd_data *data;
15480aa9461SRoy Marples 
15580aa9461SRoy Marples 	data = TAILQ_FIRST(&fd->queue);
15680aa9461SRoy Marples 
15780aa9461SRoy Marples 	if (data->data_flags & FD_SENDLEN) {
15880aa9461SRoy Marples 		iov[0].iov_base = &data->data_len;
15980aa9461SRoy Marples 		iov[0].iov_len = sizeof(size_t);
16080aa9461SRoy Marples 		iov[1].iov_base = data->data;
16180aa9461SRoy Marples 		iov[1].iov_len = data->data_len;
16280aa9461SRoy Marples 		iov_len = 2;
16380aa9461SRoy Marples 	} else {
16480aa9461SRoy Marples 		iov[0].iov_base = data->data;
16580aa9461SRoy Marples 		iov[0].iov_len = data->data_len;
16680aa9461SRoy Marples 		iov_len = 1;
16780aa9461SRoy Marples 	}
16880aa9461SRoy Marples 
16980aa9461SRoy Marples 	if (writev(fd->fd, iov, iov_len) == -1) {
170f3744ac9SRoy Marples 		if (errno != EPIPE && errno != ENOTCONN) {
171f3744ac9SRoy Marples 			// We don't get ELE_HANGUP for some reason
17280aa9461SRoy Marples 			logerr("%s: write", __func__);
173f3744ac9SRoy Marples 		}
174f3744ac9SRoy Marples 		control_hangup(fd);
175*0b4c9755SRoy Marples 		return -1;
17680aa9461SRoy Marples 	}
17780aa9461SRoy Marples 
17880aa9461SRoy Marples 	TAILQ_REMOVE(&fd->queue, data, next);
17980aa9461SRoy Marples #ifdef CTL_FREE_LIST
18080aa9461SRoy Marples 	TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
18180aa9461SRoy Marples #else
18280aa9461SRoy Marples 	if (data->data_size != 0)
18380aa9461SRoy Marples 		free(data->data);
18480aa9461SRoy Marples 	free(data);
18580aa9461SRoy Marples #endif
18680aa9461SRoy Marples 
18780aa9461SRoy Marples 	if (TAILQ_FIRST(&fd->queue) != NULL)
188*0b4c9755SRoy Marples 		return 0;
18980aa9461SRoy Marples 
19080aa9461SRoy Marples #ifdef PRIVSEP
19180aa9461SRoy Marples 	if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
19280aa9461SRoy Marples 		if (ps_ctl_sendeof(fd) == -1)
19380aa9461SRoy Marples 			logerr(__func__);
19480aa9461SRoy Marples 	}
19580aa9461SRoy Marples #endif
19680aa9461SRoy Marples 
19780aa9461SRoy Marples 	/* Done sending data, stop watching write to fd */
19880aa9461SRoy Marples 	if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ,
19980aa9461SRoy Marples 	    control_handle_data, fd) == -1)
20080aa9461SRoy Marples 		logerr("%s: eloop_event_add", __func__);
201*0b4c9755SRoy Marples 	return 0;
20280aa9461SRoy Marples }
20380aa9461SRoy Marples 
20480aa9461SRoy Marples static void
control_handle_data(void * arg,unsigned short events)20580aa9461SRoy Marples control_handle_data(void *arg, unsigned short events)
20680aa9461SRoy Marples {
20780aa9461SRoy Marples 	struct fd_list *fd = arg;
20880aa9461SRoy Marples 
209f3744ac9SRoy Marples 	if (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP)))
21080aa9461SRoy Marples 		logerrx("%s: unexpected event 0x%04x", __func__, events);
21180aa9461SRoy Marples 
212*0b4c9755SRoy Marples 	if (events & ELE_WRITE && !(events & ELE_HANGUP)) {
213*0b4c9755SRoy Marples 		if (control_handle_write(fd) == -1)
214*0b4c9755SRoy Marples 			return;
215*0b4c9755SRoy Marples 	}
216*0b4c9755SRoy Marples 	if (events & ELE_READ) {
217*0b4c9755SRoy Marples 		if (control_handle_read(fd) == -1)
218*0b4c9755SRoy Marples 			return;
219*0b4c9755SRoy Marples 	}
220f3744ac9SRoy Marples 	if (events & ELE_HANGUP)
221f3744ac9SRoy Marples 		control_hangup(fd);
22280aa9461SRoy Marples }
22380aa9461SRoy Marples 
2247f8103cdSRoy Marples void
control_recvdata(struct fd_list * fd,char * data,size_t len)2257f8103cdSRoy Marples control_recvdata(struct fd_list *fd, char *data, size_t len)
2267f8103cdSRoy Marples {
2277f8103cdSRoy Marples 	char *p = data, *e;
2287f8103cdSRoy Marples 	char *argvp[255], **ap;
2297f8103cdSRoy Marples 	int argc;
2307827cba2SAaron LI 
2317827cba2SAaron LI 	/* Each command is \n terminated
2327827cba2SAaron LI 	 * Each argument is NULL separated */
2337f8103cdSRoy Marples 	while (len != 0) {
2347827cba2SAaron LI 		argc = 0;
2357827cba2SAaron LI 		ap = argvp;
2367f8103cdSRoy Marples 		while (len != 0) {
2377f8103cdSRoy Marples 			if (*p == '\0') {
2387f8103cdSRoy Marples 				p++;
2397f8103cdSRoy Marples 				len--;
2407f8103cdSRoy Marples 				continue;
2417f8103cdSRoy Marples 			}
2427f8103cdSRoy Marples 			e = memchr(p, '\0', len);
2437f8103cdSRoy Marples 			if (e == NULL) {
2447f8103cdSRoy Marples 				errno = EINVAL;
2457f8103cdSRoy Marples 				logerrx("%s: no terminator", __func__);
2467827cba2SAaron LI 				return;
2477827cba2SAaron LI 			}
2487f8103cdSRoy Marples 			if ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {
2497f8103cdSRoy Marples 				errno = ENOBUFS;
2507f8103cdSRoy Marples 				logerrx("%s: no arg buffer", __func__);
2517f8103cdSRoy Marples 				return;
2527f8103cdSRoy Marples 			}
2537f8103cdSRoy Marples 			*ap++ = p;
2547f8103cdSRoy Marples 			argc++;
2557f8103cdSRoy Marples 			e++;
2567f8103cdSRoy Marples 			len -= (size_t)(e - p);
2577f8103cdSRoy Marples 			p = e;
2587f8103cdSRoy Marples 			e--;
2597f8103cdSRoy Marples 			if (*(--e) == '\n') {
2607f8103cdSRoy Marples 				*e = '\0';
2617827cba2SAaron LI 				break;
2627827cba2SAaron LI 			}
2637827cba2SAaron LI 		}
2647f8103cdSRoy Marples 		if (argc == 0) {
2657f8103cdSRoy Marples 			logerrx("%s: no args", __func__);
2667f8103cdSRoy Marples 			continue;
2677f8103cdSRoy Marples 		}
2687827cba2SAaron LI 		*ap = NULL;
2697827cba2SAaron LI 		if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
2707827cba2SAaron LI 			logerr(__func__);
2717827cba2SAaron LI 			if (errno != EINTR && errno != EAGAIN) {
27280aa9461SRoy Marples 				control_free(fd);
2737827cba2SAaron LI 				return;
2747827cba2SAaron LI 			}
2757827cba2SAaron LI 		}
2767827cba2SAaron LI 	}
2777827cba2SAaron LI }
2787827cba2SAaron LI 
2797f8103cdSRoy Marples struct fd_list *
control_new(struct dhcpcd_ctx * ctx,int fd,unsigned int flags)2807f8103cdSRoy Marples control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
2817f8103cdSRoy Marples {
2827f8103cdSRoy Marples 	struct fd_list *l;
2837f8103cdSRoy Marples 
2847f8103cdSRoy Marples 	l = malloc(sizeof(*l));
2857f8103cdSRoy Marples 	if (l == NULL)
2867f8103cdSRoy Marples 		return NULL;
2877f8103cdSRoy Marples 
2887f8103cdSRoy Marples 	l->ctx = ctx;
2897f8103cdSRoy Marples 	l->fd = fd;
2907f8103cdSRoy Marples 	l->flags = flags;
2917f8103cdSRoy Marples 	TAILQ_INIT(&l->queue);
2927f8103cdSRoy Marples #ifdef CTL_FREE_LIST
2937f8103cdSRoy Marples 	TAILQ_INIT(&l->free_queue);
2947f8103cdSRoy Marples #endif
2957f8103cdSRoy Marples 	TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
2967f8103cdSRoy Marples 	return l;
2977f8103cdSRoy Marples }
2987f8103cdSRoy Marples 
2997827cba2SAaron LI static void
control_handle1(struct dhcpcd_ctx * ctx,int lfd,unsigned int fd_flags,unsigned short events)30080aa9461SRoy Marples control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags,
30180aa9461SRoy Marples     unsigned short events)
3027827cba2SAaron LI {
3037827cba2SAaron LI 	struct sockaddr_un run;
3047827cba2SAaron LI 	socklen_t len;
3057827cba2SAaron LI 	struct fd_list *l;
3067827cba2SAaron LI 	int fd, flags;
3077827cba2SAaron LI 
30880aa9461SRoy Marples 	if (events != ELE_READ)
30980aa9461SRoy Marples 		logerrx("%s: unexpected event 0x%04x", __func__, events);
31080aa9461SRoy Marples 
3117827cba2SAaron LI 	len = sizeof(run);
3127827cba2SAaron LI 	if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)
3138d36e1dfSRoy Marples 		goto error;
3147827cba2SAaron LI 	if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
3157827cba2SAaron LI 	    fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
3168d36e1dfSRoy Marples 		goto error;
3177827cba2SAaron LI 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
3187827cba2SAaron LI 	    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
3198d36e1dfSRoy Marples 		goto error;
3208d36e1dfSRoy Marples 
3217f8103cdSRoy Marples #ifdef PRIVSEP
3227f8103cdSRoy Marples 	if (IN_PRIVSEP(ctx) && !IN_PRIVSEP_SE(ctx))
3237f8103cdSRoy Marples 		;
3247f8103cdSRoy Marples 	else
3257f8103cdSRoy Marples #endif
3267f8103cdSRoy Marples 	fd_flags |= FD_SENDLEN;
3277f8103cdSRoy Marples 
3287f8103cdSRoy Marples 	l = control_new(ctx, fd, fd_flags);
3298d36e1dfSRoy Marples 	if (l == NULL)
3308d36e1dfSRoy Marples 		goto error;
3318d36e1dfSRoy Marples 
33280aa9461SRoy Marples 	if (eloop_event_add(ctx->eloop, l->fd, ELE_READ,
33380aa9461SRoy Marples 	    control_handle_data, l) == -1)
33480aa9461SRoy Marples 		logerr("%s: eloop_event_add", __func__);
3358d36e1dfSRoy Marples 	return;
3368d36e1dfSRoy Marples 
3378d36e1dfSRoy Marples error:
3388d36e1dfSRoy Marples 	logerr(__func__);
3398d36e1dfSRoy Marples 	if (fd != -1)
3407827cba2SAaron LI 		close(fd);
3417827cba2SAaron LI }
3427827cba2SAaron LI 
3437827cba2SAaron LI static void
control_handle(void * arg,unsigned short events)34480aa9461SRoy Marples control_handle(void *arg, unsigned short events)
3457827cba2SAaron LI {
3467827cba2SAaron LI 	struct dhcpcd_ctx *ctx = arg;
3477827cba2SAaron LI 
34880aa9461SRoy Marples 	control_handle1(ctx, ctx->control_fd, 0, events);
3497827cba2SAaron LI }
3507827cba2SAaron LI 
3517827cba2SAaron LI static void
control_handle_unpriv(void * arg,unsigned short events)35280aa9461SRoy Marples control_handle_unpriv(void *arg, unsigned short events)
3537827cba2SAaron LI {
3547827cba2SAaron LI 	struct dhcpcd_ctx *ctx = arg;
3557827cba2SAaron LI 
35680aa9461SRoy Marples 	control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV, events);
3577827cba2SAaron LI }
3587827cba2SAaron LI 
3597827cba2SAaron LI static int
make_path(char * path,size_t len,const char * ifname,sa_family_t family,bool unpriv)360b2927f2bSRoy Marples make_path(char *path, size_t len, const char *ifname, sa_family_t family,
361b2927f2bSRoy Marples     bool unpriv)
3627827cba2SAaron LI {
363d4fb1e02SRoy Marples 	const char *per;
364b2927f2bSRoy Marples 	const char *sunpriv;
365d4fb1e02SRoy Marples 
366d4fb1e02SRoy Marples 	switch(family) {
367d4fb1e02SRoy Marples 	case AF_INET:
368d4fb1e02SRoy Marples 		per = "-4";
369d4fb1e02SRoy Marples 		break;
370d4fb1e02SRoy Marples 	case AF_INET6:
371d4fb1e02SRoy Marples 		per = "-6";
372d4fb1e02SRoy Marples 		break;
373d4fb1e02SRoy Marples 	default:
374d4fb1e02SRoy Marples 		per = "";
375d4fb1e02SRoy Marples 		break;
376d4fb1e02SRoy Marples 	}
377b2927f2bSRoy Marples 	if (unpriv)
378b2927f2bSRoy Marples 		sunpriv = ifname ? ".unpriv" : "unpriv.";
379b2927f2bSRoy Marples 	else
380b2927f2bSRoy Marples 		sunpriv = "";
3817f8103cdSRoy Marples 	return snprintf(path, len, CONTROLSOCKET,
382b2927f2bSRoy Marples 	    ifname ? ifname : "", ifname ? per : "",
383b2927f2bSRoy Marples 	    sunpriv, ifname ? "." : "");
3847827cba2SAaron LI }
3857f8103cdSRoy Marples 
3867f8103cdSRoy Marples static int
make_sock(struct sockaddr_un * sa,const char * ifname,sa_family_t family,bool unpriv)3877f8103cdSRoy Marples make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
3887f8103cdSRoy Marples     bool unpriv)
3897f8103cdSRoy Marples {
3907f8103cdSRoy Marples 	int fd;
3917f8103cdSRoy Marples 
3927f8103cdSRoy Marples 	if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0)) == -1)
3937f8103cdSRoy Marples 		return -1;
3947f8103cdSRoy Marples 	memset(sa, 0, sizeof(*sa));
3957f8103cdSRoy Marples 	sa->sun_family = AF_UNIX;
396b2927f2bSRoy Marples 	make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family, unpriv);
3977827cba2SAaron LI 	return fd;
3987827cba2SAaron LI }
3997827cba2SAaron LI 
4007827cba2SAaron LI #define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
4017827cba2SAaron LI #define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
4027827cba2SAaron LI 
4037827cba2SAaron LI static int
control_start1(struct dhcpcd_ctx * ctx,const char * ifname,sa_family_t family,mode_t fmode)404d4fb1e02SRoy Marples control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family,
405d4fb1e02SRoy Marples     mode_t fmode)
4067827cba2SAaron LI {
4077827cba2SAaron LI 	struct sockaddr_un sa;
4087827cba2SAaron LI 	int fd;
4097827cba2SAaron LI 	socklen_t len;
4107827cba2SAaron LI 
411d4fb1e02SRoy Marples 	fd = make_sock(&sa, ifname, family, (fmode & S_UNPRIV) == S_UNPRIV);
412d4fb1e02SRoy Marples 	if (fd == -1)
4137827cba2SAaron LI 		return -1;
414d4fb1e02SRoy Marples 
4157827cba2SAaron LI 	len = (socklen_t)SUN_LEN(&sa);
4167827cba2SAaron LI 	unlink(sa.sun_path);
4177827cba2SAaron LI 	if (bind(fd, (struct sockaddr *)&sa, len) == -1 ||
4187827cba2SAaron LI 	    chmod(sa.sun_path, fmode) == -1 ||
4197827cba2SAaron LI 	    (ctx->control_group &&
4207827cba2SAaron LI 	    chown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||
4217827cba2SAaron LI 	    listen(fd, sizeof(ctx->control_fds)) == -1)
4227827cba2SAaron LI 	{
4237827cba2SAaron LI 		close(fd);
4247827cba2SAaron LI 		unlink(sa.sun_path);
4257827cba2SAaron LI 		return -1;
4267827cba2SAaron LI 	}
4277827cba2SAaron LI 
4287f8103cdSRoy Marples #ifdef PRIVSEP_RIGHTS
4297f8103cdSRoy Marples 	if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) {
4307f8103cdSRoy Marples 		close(fd);
4317f8103cdSRoy Marples 		unlink(sa.sun_path);
4327f8103cdSRoy Marples 		return -1;
4337f8103cdSRoy Marples 	}
4347f8103cdSRoy Marples #endif
4357f8103cdSRoy Marples 
4360a68f8d2SRoy Marples 	if ((fmode & S_UNPRIV) == S_UNPRIV)
437b2927f2bSRoy Marples 		strlcpy(ctx->control_sock_unpriv, sa.sun_path,
438b2927f2bSRoy Marples 		    sizeof(ctx->control_sock_unpriv));
4390a68f8d2SRoy Marples 	else
4400a68f8d2SRoy Marples 		strlcpy(ctx->control_sock, sa.sun_path,
4410a68f8d2SRoy Marples 		    sizeof(ctx->control_sock));
4427827cba2SAaron LI 	return fd;
4437827cba2SAaron LI }
4447827cba2SAaron LI 
4457827cba2SAaron LI int
control_start(struct dhcpcd_ctx * ctx,const char * ifname,sa_family_t family)446d4fb1e02SRoy Marples control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family)
4477827cba2SAaron LI {
4487827cba2SAaron LI 	int fd;
4497827cba2SAaron LI 
4507f8103cdSRoy Marples #ifdef PRIVSEP
4517f8103cdSRoy Marples 	if (IN_PRIVSEP_SE(ctx)) {
4527f8103cdSRoy Marples 		make_path(ctx->control_sock, sizeof(ctx->control_sock),
453b2927f2bSRoy Marples 		    ifname, family, false);
4540a68f8d2SRoy Marples 		make_path(ctx->control_sock_unpriv,
4550a68f8d2SRoy Marples 		    sizeof(ctx->control_sock_unpriv),
456b2927f2bSRoy Marples 		    ifname, family, true);
4577f8103cdSRoy Marples 		return 0;
4587f8103cdSRoy Marples 	}
4597f8103cdSRoy Marples #endif
4607f8103cdSRoy Marples 
461d4fb1e02SRoy Marples 	if ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1)
4627827cba2SAaron LI 		return -1;
4637827cba2SAaron LI 
4647827cba2SAaron LI 	ctx->control_fd = fd;
46580aa9461SRoy Marples 	if (eloop_event_add(ctx->eloop, fd, ELE_READ,
46680aa9461SRoy Marples 	    control_handle, ctx) == -1)
46780aa9461SRoy Marples 		logerr("%s: eloop_event_add", __func__);
4687827cba2SAaron LI 
469b2927f2bSRoy Marples 	if ((fd = control_start1(ctx, ifname, family, S_UNPRIV)) != -1) {
4707827cba2SAaron LI 		ctx->control_unpriv_fd = fd;
47180aa9461SRoy Marples 		if (eloop_event_add(ctx->eloop, fd, ELE_READ,
47280aa9461SRoy Marples 		    control_handle_unpriv, ctx) == -1)
47380aa9461SRoy Marples 			logerr("%s: eloop_event_add", __func__);
4747827cba2SAaron LI 	}
4757827cba2SAaron LI 	return ctx->control_fd;
4767827cba2SAaron LI }
4777827cba2SAaron LI 
4786e63cc1fSRoy Marples static int
control_unlink(struct dhcpcd_ctx * ctx,const char * file)4796e63cc1fSRoy Marples control_unlink(struct dhcpcd_ctx *ctx, const char *file)
4806e63cc1fSRoy Marples {
4816e63cc1fSRoy Marples 	int retval = 0;
4826e63cc1fSRoy Marples 
4836e63cc1fSRoy Marples 	errno = 0;
4846e63cc1fSRoy Marples #ifdef PRIVSEP
4857f8103cdSRoy Marples 	if (IN_PRIVSEP(ctx))
4866e63cc1fSRoy Marples 		retval = (int)ps_root_unlink(ctx, file);
4876e63cc1fSRoy Marples 	else
4886e63cc1fSRoy Marples #else
4896e63cc1fSRoy Marples 		UNUSED(ctx);
4906e63cc1fSRoy Marples #endif
4916e63cc1fSRoy Marples 		retval = unlink(file);
4926e63cc1fSRoy Marples 
4936e63cc1fSRoy Marples 	return retval == -1 && errno != ENOENT ? -1 : 0;
4946e63cc1fSRoy Marples }
4956e63cc1fSRoy Marples 
4967827cba2SAaron LI int
control_stop(struct dhcpcd_ctx * ctx)4977827cba2SAaron LI control_stop(struct dhcpcd_ctx *ctx)
4987827cba2SAaron LI {
4997827cba2SAaron LI 	int retval = 0;
5007827cba2SAaron LI 	struct fd_list *l;
5017827cba2SAaron LI 
5027f8103cdSRoy Marples 	while ((l = TAILQ_FIRST(&ctx->control_fds)) != NULL) {
5037f8103cdSRoy Marples 		control_free(l);
5047f8103cdSRoy Marples 	}
5057f8103cdSRoy Marples 
5067f8103cdSRoy Marples #ifdef PRIVSEP
5077f8103cdSRoy Marples 	if (IN_PRIVSEP_SE(ctx)) {
508f3744ac9SRoy Marples 		if (ctx->control_sock[0] != '\0' &&
509f3744ac9SRoy Marples 		    ps_root_unlink(ctx, ctx->control_sock) == -1)
5107f8103cdSRoy Marples 			retval = -1;
511f3744ac9SRoy Marples 		if (ctx->control_sock_unpriv[0] != '\0' &&
512f3744ac9SRoy Marples 		    ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1)
5137f8103cdSRoy Marples 			retval = -1;
5147f8103cdSRoy Marples 		return retval;
5157f8103cdSRoy Marples 	} else if (ctx->options & DHCPCD_FORKED)
5167f8103cdSRoy Marples 		return retval;
5177f8103cdSRoy Marples #endif
518b9ccd228SRoy Marples 
5196e63cc1fSRoy Marples 	if (ctx->control_fd != -1) {
5206e63cc1fSRoy Marples 		eloop_event_delete(ctx->eloop, ctx->control_fd);
5216e63cc1fSRoy Marples 		close(ctx->control_fd);
5226e63cc1fSRoy Marples 		ctx->control_fd = -1;
5236e63cc1fSRoy Marples 		if (control_unlink(ctx, ctx->control_sock) == -1)
5247827cba2SAaron LI 			retval = -1;
5256e63cc1fSRoy Marples 	}
5267827cba2SAaron LI 
5277827cba2SAaron LI 	if (ctx->control_unpriv_fd != -1) {
5287827cba2SAaron LI 		eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd);
5297827cba2SAaron LI 		close(ctx->control_unpriv_fd);
5307827cba2SAaron LI 		ctx->control_unpriv_fd = -1;
531b2927f2bSRoy Marples 		if (control_unlink(ctx, ctx->control_sock_unpriv) == -1)
5327827cba2SAaron LI 			retval = -1;
5337827cba2SAaron LI 	}
5347827cba2SAaron LI 
5357827cba2SAaron LI 	return retval;
5367827cba2SAaron LI }
5377827cba2SAaron LI 
5387827cba2SAaron LI int
control_open(const char * ifname,sa_family_t family,bool unpriv)539d4fb1e02SRoy Marples control_open(const char *ifname, sa_family_t family, bool unpriv)
5407827cba2SAaron LI {
5417827cba2SAaron LI 	struct sockaddr_un sa;
5427827cba2SAaron LI 	int fd;
5437827cba2SAaron LI 
544d4fb1e02SRoy Marples 	if ((fd = make_sock(&sa, ifname, family, unpriv)) != -1) {
5457827cba2SAaron LI 		socklen_t len;
5467827cba2SAaron LI 
5477827cba2SAaron LI 		len = (socklen_t)SUN_LEN(&sa);
5487827cba2SAaron LI 		if (connect(fd, (struct sockaddr *)&sa, len) == -1) {
5497827cba2SAaron LI 			close(fd);
5507827cba2SAaron LI 			fd = -1;
5517827cba2SAaron LI 		}
5527827cba2SAaron LI 	}
5537827cba2SAaron LI 	return fd;
5547827cba2SAaron LI }
5557827cba2SAaron LI 
5567827cba2SAaron LI ssize_t
control_send(struct dhcpcd_ctx * ctx,int argc,char * const * argv)5577827cba2SAaron LI control_send(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
5587827cba2SAaron LI {
5597827cba2SAaron LI 	char buffer[1024];
5607827cba2SAaron LI 	int i;
5617827cba2SAaron LI 	size_t len, l;
5627827cba2SAaron LI 
5637827cba2SAaron LI 	if (argc > 255) {
5647827cba2SAaron LI 		errno = ENOBUFS;
5657827cba2SAaron LI 		return -1;
5667827cba2SAaron LI 	}
5677827cba2SAaron LI 	len = 0;
5687827cba2SAaron LI 	for (i = 0; i < argc; i++) {
5697827cba2SAaron LI 		l = strlen(argv[i]) + 1;
5707827cba2SAaron LI 		if (len + l > sizeof(buffer)) {
5717827cba2SAaron LI 			errno = ENOBUFS;
5727827cba2SAaron LI 			return -1;
5737827cba2SAaron LI 		}
5747827cba2SAaron LI 		memcpy(buffer + len, argv[i], l);
5757827cba2SAaron LI 		len += l;
5767827cba2SAaron LI 	}
5777827cba2SAaron LI 	return write(ctx->control_fd, buffer, len);
5787827cba2SAaron LI }
5797827cba2SAaron LI 
5807827cba2SAaron LI int
control_queue(struct fd_list * fd,void * data,size_t data_len)5817f8103cdSRoy Marples control_queue(struct fd_list *fd, void *data, size_t data_len)
5827827cba2SAaron LI {
5837827cba2SAaron LI 	struct fd_data *d;
58480aa9461SRoy Marples 	unsigned short events;
5857827cba2SAaron LI 
5867f8103cdSRoy Marples 	if (data_len == 0) {
5877f8103cdSRoy Marples 		errno = EINVAL;
5887f8103cdSRoy Marples 		return -1;
5897f8103cdSRoy Marples 	}
5908d36e1dfSRoy Marples 
5918d36e1dfSRoy Marples #ifdef CTL_FREE_LIST
5928d36e1dfSRoy Marples 	struct fd_data *df;
5938d36e1dfSRoy Marples 
5948d36e1dfSRoy Marples 	d = NULL;
5958d36e1dfSRoy Marples 	TAILQ_FOREACH(df, &fd->free_queue, next) {
5968d36e1dfSRoy Marples 		if (d == NULL || d->data_size < df->data_size) {
5978d36e1dfSRoy Marples 			d = df;
5988d36e1dfSRoy Marples 			if (d->data_size <= data_len)
5998d36e1dfSRoy Marples 				break;
6008d36e1dfSRoy Marples 		}
6018d36e1dfSRoy Marples 	}
6028d36e1dfSRoy Marples 	if (d != NULL)
6037827cba2SAaron LI 		TAILQ_REMOVE(&fd->free_queue, d, next);
6048d36e1dfSRoy Marples 	else
6058d36e1dfSRoy Marples #endif
6068d36e1dfSRoy Marples 	{
6078d36e1dfSRoy Marples 		d = calloc(1, sizeof(*d));
6087827cba2SAaron LI 		if (d == NULL)
6097827cba2SAaron LI 			return -1;
6107827cba2SAaron LI 	}
6118d36e1dfSRoy Marples 
6128d36e1dfSRoy Marples 	if (d->data_size == 0)
6138d36e1dfSRoy Marples 		d->data = NULL;
6148d36e1dfSRoy Marples 	if (d->data_size < data_len) {
6158d36e1dfSRoy Marples 		void *nbuf = realloc(d->data, data_len);
6168d36e1dfSRoy Marples 		if (nbuf == NULL) {
6178d36e1dfSRoy Marples 			free(d->data);
6188d36e1dfSRoy Marples 			free(d);
6198d36e1dfSRoy Marples 			return -1;
6208d36e1dfSRoy Marples 		}
6218d36e1dfSRoy Marples 		d->data = nbuf;
6228d36e1dfSRoy Marples 		d->data_size = data_len;
6238d36e1dfSRoy Marples 	}
6248d36e1dfSRoy Marples 	memcpy(d->data, data, data_len);
6258d36e1dfSRoy Marples 	d->data_len = data_len;
6267f8103cdSRoy Marples 	d->data_flags = fd->flags & FD_SENDLEN;
6278d36e1dfSRoy Marples 
6287827cba2SAaron LI 	TAILQ_INSERT_TAIL(&fd->queue, d, next);
62980aa9461SRoy Marples 	events = ELE_WRITE;
63080aa9461SRoy Marples 	if (fd->flags & FD_LISTEN)
63180aa9461SRoy Marples 		events |= ELE_READ;
63280aa9461SRoy Marples 	return eloop_event_add(fd->ctx->eloop, fd->fd, events,
63380aa9461SRoy Marples 	    control_handle_data, fd);
6347827cba2SAaron LI }
635