xref: /netbsd-src/external/bsd/blocklist/lib/bl.c (revision 00bc1baa9ca2b42c6cb83ea76ff150ea93508e64)
1*00bc1baaSchristos /*	$NetBSD: bl.c,v 1.3 2024/08/02 17:11:55 christos Exp $	*/
2df83713dSchristos 
3df83713dSchristos /*-
4df83713dSchristos  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5df83713dSchristos  * All rights reserved.
6df83713dSchristos  *
7df83713dSchristos  * This code is derived from software contributed to The NetBSD Foundation
8df83713dSchristos  * by Christos Zoulas.
9df83713dSchristos  *
10df83713dSchristos  * Redistribution and use in source and binary forms, with or without
11df83713dSchristos  * modification, are permitted provided that the following conditions
12df83713dSchristos  * are met:
13df83713dSchristos  * 1. Redistributions of source code must retain the above copyright
14df83713dSchristos  *    notice, this list of conditions and the following disclaimer.
15df83713dSchristos  * 2. Redistributions in binary form must reproduce the above copyright
16df83713dSchristos  *    notice, this list of conditions and the following disclaimer in the
17df83713dSchristos  *    documentation and/or other materials provided with the distribution.
18df83713dSchristos  *
19df83713dSchristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20df83713dSchristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21df83713dSchristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22df83713dSchristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23df83713dSchristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24df83713dSchristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25df83713dSchristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26df83713dSchristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27df83713dSchristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28df83713dSchristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29df83713dSchristos  * POSSIBILITY OF SUCH DAMAGE.
30df83713dSchristos  */
31df83713dSchristos #ifdef HAVE_CONFIG_H
32df83713dSchristos #include "config.h"
33df83713dSchristos #endif
34df83713dSchristos 
35df83713dSchristos #include <sys/cdefs.h>
36*00bc1baaSchristos __RCSID("$NetBSD: bl.c,v 1.3 2024/08/02 17:11:55 christos Exp $");
37df83713dSchristos 
38df83713dSchristos #include <sys/param.h>
39df83713dSchristos #include <sys/types.h>
40df83713dSchristos #include <sys/socket.h>
41df83713dSchristos #include <sys/stat.h>
42df83713dSchristos #include <sys/un.h>
43df83713dSchristos 
44df83713dSchristos #include <stdio.h>
45df83713dSchristos #include <string.h>
46df83713dSchristos #include <syslog.h>
47df83713dSchristos #include <signal.h>
48df83713dSchristos #include <fcntl.h>
49df83713dSchristos #include <stdlib.h>
50df83713dSchristos #include <unistd.h>
51df83713dSchristos #include <stdint.h>
52df83713dSchristos #include <stdbool.h>
53df83713dSchristos #include <errno.h>
54df83713dSchristos #include <stdarg.h>
55df83713dSchristos #include <netinet/in.h>
56df83713dSchristos #ifdef _REENTRANT
57df83713dSchristos #include <pthread.h>
58df83713dSchristos #endif
59df83713dSchristos 
60df83713dSchristos #include "bl.h"
61df83713dSchristos 
62*00bc1baaSchristos #ifndef SYSLOG_DATA_INIT
63*00bc1baaSchristos struct syslog_data {
64*00bc1baaSchristos 	int dummy;
65*00bc1baaSchristos };
66*00bc1baaSchristos #define SYSLOG_DATA_INIT  { 0 }
67*00bc1baaSchristos 
68*00bc1baaSchristos static void
69*00bc1baaSchristos vsyslog_r(int priority, struct syslog_data *sd, const char *fmt, va_list ap)
70*00bc1baaSchristos {
71*00bc1baaSchristos 	vsyslog(priority, fmt, ap);
72*00bc1baaSchristos }
73*00bc1baaSchristos #endif
74*00bc1baaSchristos 
75df83713dSchristos typedef struct {
76df83713dSchristos 	uint32_t bl_len;
77df83713dSchristos 	uint32_t bl_version;
78df83713dSchristos 	uint32_t bl_type;
79df83713dSchristos 	uint32_t bl_salen;
80df83713dSchristos 	struct sockaddr_storage bl_ss;
81df83713dSchristos 	char bl_data[];
82df83713dSchristos } bl_message_t;
83df83713dSchristos 
84df83713dSchristos struct blocklist {
85df83713dSchristos #ifdef _REENTRANT
86df83713dSchristos 	pthread_mutex_t b_mutex;
87df83713dSchristos # define BL_INIT(b)	pthread_mutex_init(&b->b_mutex, NULL)
88df83713dSchristos # define BL_LOCK(b)	pthread_mutex_lock(&b->b_mutex)
89df83713dSchristos # define BL_UNLOCK(b)	pthread_mutex_unlock(&b->b_mutex)
90df83713dSchristos #else
91df83713dSchristos # define BL_INIT(b)	do {} while(/*CONSTCOND*/0)
92df83713dSchristos # define BL_LOCK(b)	BL_INIT(b)
93df83713dSchristos # define BL_UNLOCK(b)	BL_INIT(b)
94df83713dSchristos #endif
95df83713dSchristos 	int b_fd;
96df83713dSchristos 	int b_connected;
97df83713dSchristos 	struct sockaddr_un b_sun;
98*00bc1baaSchristos 	struct syslog_data b_syslog_data;
99*00bc1baaSchristos 	void (*b_fun)(int, struct syslog_data *, const char *, va_list);
100df83713dSchristos 	bl_info_t b_info;
101df83713dSchristos };
102df83713dSchristos 
103df83713dSchristos #define BL_VERSION	1
104df83713dSchristos 
105df83713dSchristos bool
106df83713dSchristos bl_isconnected(bl_t b)
107df83713dSchristos {
108df83713dSchristos 	return b->b_connected == 0;
109df83713dSchristos }
110df83713dSchristos 
111df83713dSchristos int
112df83713dSchristos bl_getfd(bl_t b)
113df83713dSchristos {
114df83713dSchristos 	return b->b_fd;
115df83713dSchristos }
116df83713dSchristos 
117df83713dSchristos static void
118df83713dSchristos bl_reset(bl_t b, bool locked)
119df83713dSchristos {
120df83713dSchristos 	int serrno = errno;
121df83713dSchristos 	if (!locked)
122df83713dSchristos 		BL_LOCK(b);
123df83713dSchristos 	close(b->b_fd);
124df83713dSchristos 	errno = serrno;
125df83713dSchristos 	b->b_fd = -1;
126df83713dSchristos 	b->b_connected = -1;
127df83713dSchristos 	if (!locked)
128df83713dSchristos 		BL_UNLOCK(b);
129df83713dSchristos }
130df83713dSchristos 
131df83713dSchristos static void
132*00bc1baaSchristos bl_log(bl_t b, int level, const char *fmt, ...)
133df83713dSchristos {
134df83713dSchristos 	va_list ap;
135df83713dSchristos 	int serrno = errno;
136df83713dSchristos 
137*00bc1baaSchristos 	if (b->b_fun == NULL)
138*00bc1baaSchristos 		return;
139*00bc1baaSchristos 
140df83713dSchristos 	va_start(ap, fmt);
141*00bc1baaSchristos 	(*b->b_fun)(level, &b->b_syslog_data, fmt, ap);
142df83713dSchristos 	va_end(ap);
143df83713dSchristos 	errno = serrno;
144df83713dSchristos }
145df83713dSchristos 
146df83713dSchristos static int
147df83713dSchristos bl_init(bl_t b, bool srv)
148df83713dSchristos {
149df83713dSchristos 	static int one = 1;
150df83713dSchristos 	/* AF_UNIX address of local logger */
151df83713dSchristos 	mode_t om;
152df83713dSchristos 	int rv, serrno;
153df83713dSchristos 	struct sockaddr_un *sun = &b->b_sun;
154df83713dSchristos 
155df83713dSchristos #ifndef SOCK_NONBLOCK
156df83713dSchristos #define SOCK_NONBLOCK 0
157df83713dSchristos #endif
158df83713dSchristos #ifndef SOCK_CLOEXEC
159df83713dSchristos #define SOCK_CLOEXEC 0
160df83713dSchristos #endif
161df83713dSchristos #ifndef SOCK_NOSIGPIPE
162df83713dSchristos #define SOCK_NOSIGPIPE 0
163df83713dSchristos #endif
164df83713dSchristos 
165df83713dSchristos 	BL_LOCK(b);
166df83713dSchristos 
167df83713dSchristos 	if (b->b_fd == -1) {
168df83713dSchristos 		b->b_fd = socket(PF_LOCAL,
169df83713dSchristos 		    SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0);
170df83713dSchristos 		if (b->b_fd == -1) {
171*00bc1baaSchristos 			bl_log(b, LOG_ERR, "%s: socket failed (%s)",
172df83713dSchristos 			    __func__, strerror(errno));
173df83713dSchristos 			BL_UNLOCK(b);
174df83713dSchristos 			return -1;
175df83713dSchristos 		}
176df83713dSchristos #if SOCK_CLOEXEC == 0
177df83713dSchristos 		fcntl(b->b_fd, F_SETFD, FD_CLOEXEC);
178df83713dSchristos #endif
179df83713dSchristos #if SOCK_NONBLOCK == 0
180df83713dSchristos 		fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK);
181df83713dSchristos #endif
182df83713dSchristos #if SOCK_NOSIGPIPE == 0
183df83713dSchristos #ifdef SO_NOSIGPIPE
184df83713dSchristos 		int o = 1;
185df83713dSchristos 		setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o));
186df83713dSchristos #else
187df83713dSchristos 		signal(SIGPIPE, SIG_IGN);
188df83713dSchristos #endif
189df83713dSchristos #endif
190df83713dSchristos 	}
191df83713dSchristos 
192df83713dSchristos 	if (bl_isconnected(b)) {
193df83713dSchristos 		BL_UNLOCK(b);
194df83713dSchristos 		return 0;
195df83713dSchristos 	}
196df83713dSchristos 
197df83713dSchristos 	/*
198df83713dSchristos 	 * We try to connect anyway even when we are a server to verify
199df83713dSchristos 	 * that no other server is listening to the socket. If we succeed
200df83713dSchristos 	 * to connect and we are a server, someone else owns it.
201df83713dSchristos 	 */
202df83713dSchristos 	rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
203df83713dSchristos 	if (rv == 0) {
204df83713dSchristos 		if (srv) {
205*00bc1baaSchristos 			bl_log(b, LOG_ERR,
206df83713dSchristos 			    "%s: another daemon is handling `%s'",
207df83713dSchristos 			    __func__, sun->sun_path);
208df83713dSchristos 			goto out;
209df83713dSchristos 		}
210df83713dSchristos 	} else {
211df83713dSchristos 		if (!srv) {
212df83713dSchristos 			/*
213df83713dSchristos 			 * If the daemon is not running, we just try a
214df83713dSchristos 			 * connect, so leave the socket alone until it does
215df83713dSchristos 			 * and only log once.
216df83713dSchristos 			 */
217df83713dSchristos 			if (b->b_connected != 1) {
218*00bc1baaSchristos 				bl_log(b, LOG_DEBUG,
219df83713dSchristos 				    "%s: connect failed for `%s' (%s)",
220df83713dSchristos 				    __func__, sun->sun_path, strerror(errno));
221df83713dSchristos 				b->b_connected = 1;
222df83713dSchristos 			}
223df83713dSchristos 			BL_UNLOCK(b);
224df83713dSchristos 			return -1;
225df83713dSchristos 		}
226*00bc1baaSchristos 		bl_log(b, LOG_DEBUG, "Connected to blocklist server", __func__);
227df83713dSchristos 	}
228df83713dSchristos 
229df83713dSchristos 	if (srv) {
230df83713dSchristos 		(void)unlink(sun->sun_path);
231df83713dSchristos 		om = umask(0);
232df83713dSchristos 		rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
233df83713dSchristos 		serrno = errno;
234df83713dSchristos 		(void)umask(om);
235df83713dSchristos 		errno = serrno;
236df83713dSchristos 		if (rv == -1) {
237*00bc1baaSchristos 			bl_log(b, LOG_ERR, "%s: bind failed for `%s' (%s)",
238df83713dSchristos 			    __func__, sun->sun_path, strerror(errno));
239df83713dSchristos 			goto out;
240df83713dSchristos 		}
241df83713dSchristos 	}
242df83713dSchristos 
243df83713dSchristos 	b->b_connected = 0;
244df83713dSchristos #define GOT_FD		1
245df83713dSchristos #if defined(LOCAL_CREDS)
246df83713dSchristos #define CRED_LEVEL	0
247df83713dSchristos #define	CRED_NAME	LOCAL_CREDS
248df83713dSchristos #define CRED_SC_UID	sc_euid
249df83713dSchristos #define CRED_SC_GID	sc_egid
250df83713dSchristos #define CRED_MESSAGE	SCM_CREDS
251df83713dSchristos #define CRED_SIZE	SOCKCREDSIZE(NGROUPS_MAX)
252df83713dSchristos #define CRED_TYPE	struct sockcred
253df83713dSchristos #define GOT_CRED	2
254df83713dSchristos #elif defined(SO_PASSCRED)
255df83713dSchristos #define CRED_LEVEL	SOL_SOCKET
256df83713dSchristos #define	CRED_NAME	SO_PASSCRED
257df83713dSchristos #define CRED_SC_UID	uid
258df83713dSchristos #define CRED_SC_GID	gid
259df83713dSchristos #define CRED_MESSAGE	SCM_CREDENTIALS
260df83713dSchristos #define CRED_SIZE	sizeof(struct ucred)
261df83713dSchristos #define CRED_TYPE	struct ucred
262df83713dSchristos #define GOT_CRED	2
263df83713dSchristos #else
264df83713dSchristos #define GOT_CRED	0
265df83713dSchristos /*
266df83713dSchristos  * getpeereid() and LOCAL_PEERCRED don't help here
267df83713dSchristos  * because we are not a stream socket!
268df83713dSchristos  */
269df83713dSchristos #define	CRED_SIZE	0
270df83713dSchristos #define CRED_TYPE	void * __unused
271df83713dSchristos #endif
272df83713dSchristos 
273df83713dSchristos #ifdef CRED_LEVEL
274df83713dSchristos 	if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME,
275df83713dSchristos 	    &one, (socklen_t)sizeof(one)) == -1) {
276*00bc1baaSchristos 		bl_log(b, LOG_ERR, "%s: setsockopt %s "
277df83713dSchristos 		    "failed (%s)", __func__, __STRING(CRED_NAME),
278df83713dSchristos 		    strerror(errno));
279df83713dSchristos 		goto out;
280df83713dSchristos 	}
281df83713dSchristos #endif
282df83713dSchristos 
283df83713dSchristos 	BL_UNLOCK(b);
284df83713dSchristos 	return 0;
285df83713dSchristos out:
286df83713dSchristos 	bl_reset(b, true);
287df83713dSchristos 	BL_UNLOCK(b);
288df83713dSchristos 	return -1;
289df83713dSchristos }
290df83713dSchristos 
291df83713dSchristos bl_t
292*00bc1baaSchristos bl_create(bool srv, const char *path,
293*00bc1baaSchristos     void (*fun)(int, struct syslog_data *, const char *, va_list))
294df83713dSchristos {
295*00bc1baaSchristos 	static struct syslog_data sd = SYSLOG_DATA_INIT;
296df83713dSchristos 	bl_t b = calloc(1, sizeof(*b));
297df83713dSchristos 	if (b == NULL)
298*00bc1baaSchristos 		return NULL;
299*00bc1baaSchristos 	b->b_fun = fun;
300*00bc1baaSchristos 	b->b_syslog_data = sd;
301df83713dSchristos 	b->b_fd = -1;
302df83713dSchristos 	b->b_connected = -1;
303df83713dSchristos 	BL_INIT(b);
304df83713dSchristos 
305df83713dSchristos 	memset(&b->b_sun, 0, sizeof(b->b_sun));
306df83713dSchristos 	b->b_sun.sun_family = AF_LOCAL;
307df83713dSchristos #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
308df83713dSchristos 	b->b_sun.sun_len = sizeof(b->b_sun);
309df83713dSchristos #endif
310df83713dSchristos 	strlcpy(b->b_sun.sun_path,
311df83713dSchristos 	    path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path));
312df83713dSchristos 
313df83713dSchristos 	bl_init(b, srv);
314df83713dSchristos 	return b;
315df83713dSchristos }
316df83713dSchristos 
317df83713dSchristos void
318df83713dSchristos bl_destroy(bl_t b)
319df83713dSchristos {
320df83713dSchristos 	bl_reset(b, false);
321df83713dSchristos 	free(b);
322df83713dSchristos }
323df83713dSchristos 
324df83713dSchristos static int
325df83713dSchristos bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa,
326df83713dSchristos     socklen_t slen, const char *ctx)
327df83713dSchristos {
328df83713dSchristos 	uint8_t family;
329df83713dSchristos 
330df83713dSchristos 	memset(ss, 0, sizeof(*ss));
331df83713dSchristos 
332df83713dSchristos 	switch (slen) {
333df83713dSchristos 	case 0:
334df83713dSchristos 		return 0;
335df83713dSchristos 	case sizeof(struct sockaddr_in):
336df83713dSchristos 		family = AF_INET;
337df83713dSchristos 		break;
338df83713dSchristos 	case sizeof(struct sockaddr_in6):
339df83713dSchristos 		family = AF_INET6;
340df83713dSchristos 		break;
341df83713dSchristos 	default:
342*00bc1baaSchristos 		bl_log(b, LOG_ERR, "%s: invalid socket len %u (%s)",
343df83713dSchristos 		    __func__, (unsigned)slen, ctx);
344df83713dSchristos 		errno = EINVAL;
345df83713dSchristos 		return -1;
346df83713dSchristos 	}
347df83713dSchristos 
348df83713dSchristos 	memcpy(ss, sa, slen);
349df83713dSchristos 
350df83713dSchristos 	if (ss->ss_family != family) {
351*00bc1baaSchristos 		bl_log(b, LOG_INFO,
352df83713dSchristos 		    "%s: correcting socket family %d to %d (%s)",
353df83713dSchristos 		    __func__, ss->ss_family, family, ctx);
354df83713dSchristos 		ss->ss_family = family;
355df83713dSchristos 	}
356df83713dSchristos 
357df83713dSchristos #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
358df83713dSchristos 	if (ss->ss_len != slen) {
359*00bc1baaSchristos 		bl_log(b, LOG_INFO,
360df83713dSchristos 		    "%s: correcting socket len %u to %u (%s)",
361df83713dSchristos 		    __func__, ss->ss_len, (unsigned)slen, ctx);
362df83713dSchristos 		ss->ss_len = (uint8_t)slen;
363df83713dSchristos 	}
364df83713dSchristos #endif
365df83713dSchristos 	return 0;
366df83713dSchristos }
367df83713dSchristos 
368df83713dSchristos int
369df83713dSchristos bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa,
370df83713dSchristos     socklen_t slen, const char *ctx)
371df83713dSchristos {
372df83713dSchristos 	struct msghdr   msg;
373df83713dSchristos 	struct iovec    iov;
374df83713dSchristos 	union {
375df83713dSchristos 		char ctrl[CMSG_SPACE(sizeof(int))];
376df83713dSchristos 		uint32_t fd;
377df83713dSchristos 	} ua;
378df83713dSchristos 	struct cmsghdr *cmsg;
379df83713dSchristos 	union {
380df83713dSchristos 		bl_message_t bl;
381df83713dSchristos 		char buf[512];
382df83713dSchristos 	} ub;
383df83713dSchristos 	size_t ctxlen, tried;
384df83713dSchristos #define NTRIES	5
385df83713dSchristos 
386df83713dSchristos 	ctxlen = strlen(ctx);
387df83713dSchristos 	if (ctxlen > 128)
388df83713dSchristos 		ctxlen = 128;
389df83713dSchristos 
390df83713dSchristos 	iov.iov_base = ub.buf;
391df83713dSchristos 	iov.iov_len = sizeof(bl_message_t) + ctxlen;
392df83713dSchristos 	ub.bl.bl_len = (uint32_t)iov.iov_len;
393df83713dSchristos 	ub.bl.bl_version = BL_VERSION;
394df83713dSchristos 	ub.bl.bl_type = (uint32_t)e;
395df83713dSchristos 
396df83713dSchristos 	if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1)
397df83713dSchristos 		return -1;
398df83713dSchristos 
399df83713dSchristos 
400df83713dSchristos 	ub.bl.bl_salen = slen;
401df83713dSchristos 	memcpy(ub.bl.bl_data, ctx, ctxlen);
402df83713dSchristos 
403df83713dSchristos 	msg.msg_name = NULL;
404df83713dSchristos 	msg.msg_namelen = 0;
405df83713dSchristos 	msg.msg_iov = &iov;
406df83713dSchristos 	msg.msg_iovlen = 1;
407df83713dSchristos 	msg.msg_flags = 0;
408df83713dSchristos 
409df83713dSchristos 	msg.msg_control = ua.ctrl;
410df83713dSchristos 	msg.msg_controllen = sizeof(ua.ctrl);
411df83713dSchristos 
412df83713dSchristos 	cmsg = CMSG_FIRSTHDR(&msg);
413df83713dSchristos 	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
414df83713dSchristos 	cmsg->cmsg_level = SOL_SOCKET;
415df83713dSchristos 	cmsg->cmsg_type = SCM_RIGHTS;
416df83713dSchristos 
417df83713dSchristos 	memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd));
418df83713dSchristos 
419df83713dSchristos 	tried = 0;
420df83713dSchristos again:
421df83713dSchristos 	if (bl_init(b, false) == -1)
422df83713dSchristos 		return -1;
423df83713dSchristos 
424df83713dSchristos 	if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) {
425df83713dSchristos 		bl_reset(b, false);
426df83713dSchristos 		goto again;
427df83713dSchristos 	}
428df83713dSchristos 	return tried >= NTRIES ? -1 : 0;
429df83713dSchristos }
430df83713dSchristos 
431df83713dSchristos bl_info_t *
432df83713dSchristos bl_recv(bl_t b)
433df83713dSchristos {
434df83713dSchristos         struct msghdr   msg;
435df83713dSchristos         struct iovec    iov;
436df83713dSchristos 	union {
437df83713dSchristos 		char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)];
438df83713dSchristos 		uint32_t fd;
439df83713dSchristos 		CRED_TYPE sc;
440df83713dSchristos 	} ua;
441df83713dSchristos 	struct cmsghdr *cmsg;
442df83713dSchristos 	CRED_TYPE *sc;
443df83713dSchristos 	union {
444df83713dSchristos 		bl_message_t bl;
445df83713dSchristos 		char buf[512];
446df83713dSchristos 	} ub;
447df83713dSchristos 	int got;
448df83713dSchristos 	ssize_t rlen;
449123a18feSchristos 	size_t rem;
450df83713dSchristos 	bl_info_t *bi = &b->b_info;
451df83713dSchristos 
452df83713dSchristos 	got = 0;
453df83713dSchristos 	memset(bi, 0, sizeof(*bi));
454df83713dSchristos 
455df83713dSchristos 	iov.iov_base = ub.buf;
456df83713dSchristos 	iov.iov_len = sizeof(ub);
457df83713dSchristos 
458df83713dSchristos 	msg.msg_name = NULL;
459df83713dSchristos 	msg.msg_namelen = 0;
460df83713dSchristos 	msg.msg_iov = &iov;
461df83713dSchristos 	msg.msg_iovlen = 1;
462df83713dSchristos 	msg.msg_flags = 0;
463df83713dSchristos 
464df83713dSchristos 	msg.msg_control = ua.ctrl;
465df83713dSchristos 	msg.msg_controllen = sizeof(ua.ctrl) + 100;
466df83713dSchristos 
467df83713dSchristos         rlen = recvmsg(b->b_fd, &msg, 0);
468df83713dSchristos         if (rlen == -1) {
469*00bc1baaSchristos 		bl_log(b, LOG_ERR, "%s: recvmsg failed (%s)", __func__,
470df83713dSchristos 		    strerror(errno));
471df83713dSchristos 		return NULL;
472df83713dSchristos         }
473df83713dSchristos 
474df83713dSchristos 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
475df83713dSchristos 		if (cmsg->cmsg_level != SOL_SOCKET) {
476*00bc1baaSchristos 			bl_log(b, LOG_ERR,
477df83713dSchristos 			    "%s: unexpected cmsg_level %d",
478df83713dSchristos 			    __func__, cmsg->cmsg_level);
479df83713dSchristos 			continue;
480df83713dSchristos 		}
481df83713dSchristos 		switch (cmsg->cmsg_type) {
482df83713dSchristos 		case SCM_RIGHTS:
483df83713dSchristos 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
484*00bc1baaSchristos 				bl_log(b, LOG_ERR,
485df83713dSchristos 				    "%s: unexpected cmsg_len %d != %zu",
486df83713dSchristos 				    __func__, cmsg->cmsg_len,
487df83713dSchristos 				    CMSG_LEN(2 * sizeof(int)));
488df83713dSchristos 				continue;
489df83713dSchristos 			}
490df83713dSchristos 			memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd));
491df83713dSchristos 			got |= GOT_FD;
492df83713dSchristos 			break;
493df83713dSchristos #ifdef CRED_MESSAGE
494df83713dSchristos 		case CRED_MESSAGE:
495df83713dSchristos 			sc = (void *)CMSG_DATA(cmsg);
496df83713dSchristos 			bi->bi_uid = sc->CRED_SC_UID;
497df83713dSchristos 			bi->bi_gid = sc->CRED_SC_GID;
498df83713dSchristos 			got |= GOT_CRED;
499df83713dSchristos 			break;
500df83713dSchristos #endif
501df83713dSchristos 		default:
502*00bc1baaSchristos 			bl_log(b, LOG_ERR,
503df83713dSchristos 			    "%s: unexpected cmsg_type %d",
504df83713dSchristos 			    __func__, cmsg->cmsg_type);
505df83713dSchristos 			continue;
506df83713dSchristos 		}
507df83713dSchristos 
508df83713dSchristos 	}
509df83713dSchristos 
510df83713dSchristos 	if (got != (GOT_CRED|GOT_FD)) {
511*00bc1baaSchristos 		bl_log(b, LOG_ERR, "message missing %s %s",
512df83713dSchristos #if GOT_CRED != 0
513df83713dSchristos 		    (got & GOT_CRED) == 0 ? "cred" :
514df83713dSchristos #endif
515df83713dSchristos 		    "", (got & GOT_FD) == 0 ? "fd" : "");
516df83713dSchristos 		return NULL;
517df83713dSchristos 	}
518df83713dSchristos 
519123a18feSchristos 	rem = (size_t)rlen;
520123a18feSchristos 	if (rem < sizeof(ub.bl)) {
521*00bc1baaSchristos 		bl_log(b, LOG_ERR, "message too short %zd", rlen);
522df83713dSchristos 		return NULL;
523df83713dSchristos 	}
524123a18feSchristos 	rem -= sizeof(ub.bl);
525df83713dSchristos 
526df83713dSchristos 	if (ub.bl.bl_version != BL_VERSION) {
527*00bc1baaSchristos 		bl_log(b, LOG_ERR, "bad version %d", ub.bl.bl_version);
528df83713dSchristos 		return NULL;
529df83713dSchristos 	}
530df83713dSchristos 
531df83713dSchristos 	bi->bi_type = ub.bl.bl_type;
532df83713dSchristos 	bi->bi_slen = ub.bl.bl_salen;
533df83713dSchristos 	bi->bi_ss = ub.bl.bl_ss;
534df83713dSchristos #ifndef CRED_MESSAGE
535df83713dSchristos 	bi->bi_uid = -1;
536df83713dSchristos 	bi->bi_gid = -1;
537df83713dSchristos #endif
538123a18feSchristos 	rem = MIN(sizeof(bi->bi_msg), rem);
539123a18feSchristos 	if (rem == 0)
540123a18feSchristos 		bi->bi_msg[0] = '\0';
541123a18feSchristos 	else
542123a18feSchristos 		strlcpy(bi->bi_msg, ub.bl.bl_data, rem);
543df83713dSchristos 	return bi;
544df83713dSchristos }
545