1204076Spjd/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
5204076Spjd * All rights reserved.
6204076Spjd *
7204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
8204076Spjd * the FreeBSD Foundation.
9204076Spjd *
10204076Spjd * Redistribution and use in source and binary forms, with or without
11204076Spjd * modification, are permitted provided that the following conditions
12204076Spjd * are met:
13204076Spjd * 1. Redistributions of source code must retain the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer.
15204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
16204076Spjd *    notice, this list of conditions and the following disclaimer in the
17204076Spjd *    documentation and/or other materials provided with the distribution.
18204076Spjd *
19204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29204076Spjd * SUCH DAMAGE.
30204076Spjd */
31204076Spjd
32204076Spjd#include <sys/cdefs.h>
33204076Spjd__FBSDID("$FreeBSD: stable/11/sbin/hastd/proto_uds.c 330449 2018-03-05 07:26:05Z eadler $");
34204076Spjd
35204076Spjd/* UDS - UNIX Domain Socket */
36204076Spjd
37218465Spjd#include <sys/types.h>
38219873Spjd#include <sys/socket.h>
39204076Spjd#include <sys/un.h>
40204076Spjd
41204076Spjd#include <errno.h>
42204076Spjd#include <stdbool.h>
43204076Spjd#include <stdint.h>
44204076Spjd#include <stdio.h>
45204076Spjd#include <string.h>
46204076Spjd#include <unistd.h>
47204076Spjd
48210876Spjd#include "pjdlog.h"
49204076Spjd#include "proto_impl.h"
50204076Spjd
51204076Spjd#define	UDS_CTX_MAGIC	0xd541c
52204076Spjdstruct uds_ctx {
53204076Spjd	int			uc_magic;
54204076Spjd	struct sockaddr_un	uc_sun;
55204076Spjd	int			uc_fd;
56204076Spjd	int			uc_side;
57204076Spjd#define	UDS_SIDE_CLIENT		0
58204076Spjd#define	UDS_SIDE_SERVER_LISTEN	1
59204076Spjd#define	UDS_SIDE_SERVER_WORK	2
60218464Spjd	pid_t			uc_owner;
61204076Spjd};
62204076Spjd
63204076Spjdstatic void uds_close(void *ctx);
64204076Spjd
65204076Spjdstatic int
66204076Spjduds_addr(const char *addr, struct sockaddr_un *sunp)
67204076Spjd{
68204076Spjd
69204076Spjd	if (addr == NULL)
70204076Spjd		return (-1);
71204076Spjd
72204076Spjd	if (strncasecmp(addr, "uds://", 6) == 0)
73204076Spjd		addr += 6;
74204076Spjd	else if (strncasecmp(addr, "unix://", 7) == 0)
75204076Spjd		addr += 7;
76204076Spjd	else if (addr[0] == '/' &&	/* If it starts from /... */
77204076Spjd	    strstr(addr, "://") == NULL)/* ...and there is no prefix... */
78204076Spjd		;			/* ...we assume its us. */
79204076Spjd	else
80204076Spjd		return (-1);
81204076Spjd
82204076Spjd	sunp->sun_family = AF_UNIX;
83204076Spjd	if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
84204076Spjd	    sizeof(sunp->sun_path)) {
85204076Spjd		return (ENAMETOOLONG);
86204076Spjd	}
87204076Spjd	sunp->sun_len = SUN_LEN(sunp);
88204076Spjd
89204076Spjd	return (0);
90204076Spjd}
91204076Spjd
92204076Spjdstatic int
93204076Spjduds_common_setup(const char *addr, void **ctxp, int side)
94204076Spjd{
95204076Spjd	struct uds_ctx *uctx;
96204076Spjd	int ret;
97204076Spjd
98204076Spjd	uctx = malloc(sizeof(*uctx));
99204076Spjd	if (uctx == NULL)
100204076Spjd		return (errno);
101204076Spjd
102204076Spjd	/* Parse given address. */
103204076Spjd	if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) {
104204076Spjd		free(uctx);
105204076Spjd		return (ret);
106204076Spjd	}
107204076Spjd
108204076Spjd	uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
109204076Spjd	if (uctx->uc_fd == -1) {
110204076Spjd		ret = errno;
111204076Spjd		free(uctx);
112204076Spjd		return (ret);
113204076Spjd	}
114204076Spjd
115204076Spjd	uctx->uc_side = side;
116218464Spjd	uctx->uc_owner = 0;
117204076Spjd	uctx->uc_magic = UDS_CTX_MAGIC;
118204076Spjd	*ctxp = uctx;
119204076Spjd
120204076Spjd	return (0);
121204076Spjd}
122204076Spjd
123204076Spjdstatic int
124219818Spjduds_client(const char *srcaddr, const char *dstaddr, void **ctxp)
125204076Spjd{
126219818Spjd	int ret;
127204076Spjd
128219818Spjd	ret = uds_common_setup(dstaddr, ctxp, UDS_SIDE_CLIENT);
129219818Spjd	if (ret != 0)
130219818Spjd		return (ret);
131219818Spjd
132219818Spjd	PJDLOG_ASSERT(srcaddr == NULL);
133219818Spjd
134219818Spjd	return (0);
135204076Spjd}
136204076Spjd
137204076Spjdstatic int
138218192Spjduds_connect(void *ctx, int timeout)
139204076Spjd{
140204076Spjd	struct uds_ctx *uctx = ctx;
141204076Spjd
142218138Spjd	PJDLOG_ASSERT(uctx != NULL);
143218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
144218138Spjd	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
145218138Spjd	PJDLOG_ASSERT(uctx->uc_fd >= 0);
146218193Spjd	PJDLOG_ASSERT(timeout >= -1);
147204076Spjd
148204076Spjd	if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
149229945Spjd	    sizeof(uctx->uc_sun)) == -1) {
150204076Spjd		return (errno);
151204076Spjd	}
152204076Spjd
153204076Spjd	return (0);
154204076Spjd}
155204076Spjd
156204076Spjdstatic int
157218193Spjduds_connect_wait(void *ctx, int timeout)
158218193Spjd{
159218193Spjd	struct uds_ctx *uctx = ctx;
160218193Spjd
161218193Spjd	PJDLOG_ASSERT(uctx != NULL);
162218193Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
163218193Spjd	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
164218193Spjd	PJDLOG_ASSERT(uctx->uc_fd >= 0);
165218193Spjd	PJDLOG_ASSERT(timeout >= 0);
166218193Spjd
167218193Spjd	return (0);
168218193Spjd}
169218193Spjd
170218193Spjdstatic int
171204076Spjduds_server(const char *addr, void **ctxp)
172204076Spjd{
173204076Spjd	struct uds_ctx *uctx;
174204076Spjd	int ret;
175204076Spjd
176204076Spjd	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
177204076Spjd	if (ret != 0)
178204076Spjd		return (ret);
179204076Spjd
180204076Spjd	uctx = *ctxp;
181204076Spjd
182218464Spjd	(void)unlink(uctx->uc_sun.sun_path);
183204076Spjd	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
184229945Spjd	    sizeof(uctx->uc_sun)) == -1) {
185204076Spjd		ret = errno;
186204076Spjd		uds_close(uctx);
187204076Spjd		return (ret);
188204076Spjd	}
189218464Spjd	uctx->uc_owner = getpid();
190229945Spjd	if (listen(uctx->uc_fd, 8) == -1) {
191204076Spjd		ret = errno;
192204076Spjd		uds_close(uctx);
193204076Spjd		return (ret);
194204076Spjd	}
195204076Spjd
196204076Spjd	return (0);
197204076Spjd}
198204076Spjd
199204076Spjdstatic int
200204076Spjduds_accept(void *ctx, void **newctxp)
201204076Spjd{
202204076Spjd	struct uds_ctx *uctx = ctx;
203204076Spjd	struct uds_ctx *newuctx;
204204076Spjd	socklen_t fromlen;
205204076Spjd	int ret;
206204076Spjd
207218138Spjd	PJDLOG_ASSERT(uctx != NULL);
208218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
209218138Spjd	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
210218138Spjd	PJDLOG_ASSERT(uctx->uc_fd >= 0);
211204076Spjd
212204076Spjd	newuctx = malloc(sizeof(*newuctx));
213204076Spjd	if (newuctx == NULL)
214204076Spjd		return (errno);
215204076Spjd
216218464Spjd	fromlen = sizeof(newuctx->uc_sun);
217218464Spjd	newuctx->uc_fd = accept(uctx->uc_fd,
218218464Spjd	    (struct sockaddr *)&newuctx->uc_sun, &fromlen);
219229945Spjd	if (newuctx->uc_fd == -1) {
220204076Spjd		ret = errno;
221204076Spjd		free(newuctx);
222204076Spjd		return (ret);
223204076Spjd	}
224204076Spjd
225204076Spjd	newuctx->uc_side = UDS_SIDE_SERVER_WORK;
226204076Spjd	newuctx->uc_magic = UDS_CTX_MAGIC;
227204076Spjd	*newctxp = newuctx;
228204076Spjd
229204076Spjd	return (0);
230204076Spjd}
231204076Spjd
232204076Spjdstatic int
233218194Spjduds_send(void *ctx, const unsigned char *data, size_t size, int fd)
234204076Spjd{
235204076Spjd	struct uds_ctx *uctx = ctx;
236204076Spjd
237218138Spjd	PJDLOG_ASSERT(uctx != NULL);
238218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
239218138Spjd	PJDLOG_ASSERT(uctx->uc_fd >= 0);
240204076Spjd
241218194Spjd	return (proto_common_send(uctx->uc_fd, data, size, fd));
242204076Spjd}
243204076Spjd
244204076Spjdstatic int
245218194Spjduds_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
246204076Spjd{
247204076Spjd	struct uds_ctx *uctx = ctx;
248204076Spjd
249218138Spjd	PJDLOG_ASSERT(uctx != NULL);
250218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
251218138Spjd	PJDLOG_ASSERT(uctx->uc_fd >= 0);
252204076Spjd
253218194Spjd	return (proto_common_recv(uctx->uc_fd, data, size, fdp));
254204076Spjd}
255204076Spjd
256204076Spjdstatic int
257204076Spjduds_descriptor(const void *ctx)
258204076Spjd{
259204076Spjd	const struct uds_ctx *uctx = ctx;
260204076Spjd
261218138Spjd	PJDLOG_ASSERT(uctx != NULL);
262218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
263204076Spjd
264204076Spjd	return (uctx->uc_fd);
265204076Spjd}
266204076Spjd
267204076Spjdstatic void
268204076Spjduds_local_address(const void *ctx, char *addr, size_t size)
269204076Spjd{
270204076Spjd	const struct uds_ctx *uctx = ctx;
271204076Spjd	struct sockaddr_un sun;
272204076Spjd	socklen_t sunlen;
273204076Spjd
274218138Spjd	PJDLOG_ASSERT(uctx != NULL);
275218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
276218138Spjd	PJDLOG_ASSERT(addr != NULL);
277204076Spjd
278204076Spjd	sunlen = sizeof(sun);
279229945Spjd	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) {
280210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
281204076Spjd		return;
282204076Spjd	}
283218138Spjd	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
284204076Spjd	if (sun.sun_path[0] == '\0') {
285210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
286204076Spjd		return;
287204076Spjd	}
288210876Spjd	PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
289204076Spjd}
290204076Spjd
291204076Spjdstatic void
292204076Spjduds_remote_address(const void *ctx, char *addr, size_t size)
293204076Spjd{
294204076Spjd	const struct uds_ctx *uctx = ctx;
295204076Spjd	struct sockaddr_un sun;
296204076Spjd	socklen_t sunlen;
297204076Spjd
298218138Spjd	PJDLOG_ASSERT(uctx != NULL);
299218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
300218138Spjd	PJDLOG_ASSERT(addr != NULL);
301204076Spjd
302204076Spjd	sunlen = sizeof(sun);
303229945Spjd	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) {
304210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
305204076Spjd		return;
306204076Spjd	}
307218138Spjd	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
308204076Spjd	if (sun.sun_path[0] == '\0') {
309210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
310204076Spjd		return;
311204076Spjd	}
312204076Spjd	snprintf(addr, size, "uds://%s", sun.sun_path);
313204076Spjd}
314204076Spjd
315204076Spjdstatic void
316204076Spjduds_close(void *ctx)
317204076Spjd{
318204076Spjd	struct uds_ctx *uctx = ctx;
319204076Spjd
320218138Spjd	PJDLOG_ASSERT(uctx != NULL);
321218138Spjd	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
322204076Spjd
323204076Spjd	if (uctx->uc_fd >= 0)
324204076Spjd		close(uctx->uc_fd);
325218464Spjd	/*
326218464Spjd	 * Unlink the socket only if we are the owner and this is descriptor
327218464Spjd	 * we listen on.
328218464Spjd	 */
329218464Spjd	if (uctx->uc_side == UDS_SIDE_SERVER_LISTEN &&
330218464Spjd	    uctx->uc_owner == getpid()) {
331218474Spjd		PJDLOG_ASSERT(uctx->uc_sun.sun_path[0] != '\0');
332218474Spjd		if (unlink(uctx->uc_sun.sun_path) == -1) {
333218474Spjd			pjdlog_errno(LOG_WARNING,
334218474Spjd			    "Unable to unlink socket file %s",
335218474Spjd			    uctx->uc_sun.sun_path);
336218474Spjd		}
337218464Spjd	}
338218464Spjd	uctx->uc_owner = 0;
339204076Spjd	uctx->uc_magic = 0;
340204076Spjd	free(uctx);
341204076Spjd}
342204076Spjd
343219873Spjdstatic struct proto uds_proto = {
344219873Spjd	.prt_name = "uds",
345219873Spjd	.prt_client = uds_client,
346219873Spjd	.prt_connect = uds_connect,
347219873Spjd	.prt_connect_wait = uds_connect_wait,
348219873Spjd	.prt_server = uds_server,
349219873Spjd	.prt_accept = uds_accept,
350219873Spjd	.prt_send = uds_send,
351219873Spjd	.prt_recv = uds_recv,
352219873Spjd	.prt_descriptor = uds_descriptor,
353219873Spjd	.prt_local_address = uds_local_address,
354219873Spjd	.prt_remote_address = uds_remote_address,
355219873Spjd	.prt_close = uds_close
356204076Spjd};
357204076Spjd
358204076Spjdstatic __constructor void
359204076Spjduds_ctor(void)
360204076Spjd{
361204076Spjd
362210869Spjd	proto_register(&uds_proto, false);
363204076Spjd}
364