xref: /freebsd/sbin/hastd/proto_uds.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  *
6  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 /* UDS - UNIX Domain Socket */
33 
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 
38 #include <errno.h>
39 #include <stdbool.h>
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "pjdlog.h"
46 #include "proto_impl.h"
47 
48 #define	UDS_CTX_MAGIC	0xd541c
49 struct uds_ctx {
50 	int			uc_magic;
51 	struct sockaddr_un	uc_sun;
52 	int			uc_fd;
53 	int			uc_side;
54 #define	UDS_SIDE_CLIENT		0
55 #define	UDS_SIDE_SERVER_LISTEN	1
56 #define	UDS_SIDE_SERVER_WORK	2
57 	pid_t			uc_owner;
58 };
59 
60 static void uds_close(void *ctx);
61 
62 static int
63 uds_addr(const char *addr, struct sockaddr_un *sunp)
64 {
65 
66 	if (addr == NULL)
67 		return (-1);
68 
69 	if (strncasecmp(addr, "uds://", 6) == 0)
70 		addr += 6;
71 	else if (strncasecmp(addr, "unix://", 7) == 0)
72 		addr += 7;
73 	else if (addr[0] == '/' &&	/* If it starts from /... */
74 	    strstr(addr, "://") == NULL)/* ...and there is no prefix... */
75 		;			/* ...we assume its us. */
76 	else
77 		return (-1);
78 
79 	sunp->sun_family = AF_UNIX;
80 	if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
81 	    sizeof(sunp->sun_path)) {
82 		return (ENAMETOOLONG);
83 	}
84 	sunp->sun_len = SUN_LEN(sunp);
85 
86 	return (0);
87 }
88 
89 static int
90 uds_common_setup(const char *addr, void **ctxp, int side)
91 {
92 	struct uds_ctx *uctx;
93 	int ret;
94 
95 	uctx = malloc(sizeof(*uctx));
96 	if (uctx == NULL)
97 		return (errno);
98 
99 	/* Parse given address. */
100 	if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) {
101 		free(uctx);
102 		return (ret);
103 	}
104 
105 	uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
106 	if (uctx->uc_fd == -1) {
107 		ret = errno;
108 		free(uctx);
109 		return (ret);
110 	}
111 
112 	uctx->uc_side = side;
113 	uctx->uc_owner = 0;
114 	uctx->uc_magic = UDS_CTX_MAGIC;
115 	*ctxp = uctx;
116 
117 	return (0);
118 }
119 
120 static int
121 uds_client(const char *srcaddr, const char *dstaddr, void **ctxp)
122 {
123 	int ret;
124 
125 	ret = uds_common_setup(dstaddr, ctxp, UDS_SIDE_CLIENT);
126 	if (ret != 0)
127 		return (ret);
128 
129 	PJDLOG_ASSERT(srcaddr == NULL);
130 
131 	return (0);
132 }
133 
134 static int
135 uds_connect(void *ctx, int timeout)
136 {
137 	struct uds_ctx *uctx = ctx;
138 
139 	PJDLOG_ASSERT(uctx != NULL);
140 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
141 	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
142 	PJDLOG_ASSERT(uctx->uc_fd >= 0);
143 	PJDLOG_ASSERT(timeout >= -1);
144 
145 	if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
146 	    sizeof(uctx->uc_sun)) == -1) {
147 		return (errno);
148 	}
149 
150 	return (0);
151 }
152 
153 static int
154 uds_connect_wait(void *ctx, int timeout)
155 {
156 	struct uds_ctx *uctx = ctx;
157 
158 	PJDLOG_ASSERT(uctx != NULL);
159 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
160 	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
161 	PJDLOG_ASSERT(uctx->uc_fd >= 0);
162 	PJDLOG_ASSERT(timeout >= 0);
163 
164 	return (0);
165 }
166 
167 static int
168 uds_server(const char *addr, void **ctxp)
169 {
170 	struct uds_ctx *uctx;
171 	int ret;
172 
173 	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
174 	if (ret != 0)
175 		return (ret);
176 
177 	uctx = *ctxp;
178 
179 	(void)unlink(uctx->uc_sun.sun_path);
180 	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
181 	    sizeof(uctx->uc_sun)) == -1) {
182 		ret = errno;
183 		uds_close(uctx);
184 		return (ret);
185 	}
186 	uctx->uc_owner = getpid();
187 	if (listen(uctx->uc_fd, 8) == -1) {
188 		ret = errno;
189 		uds_close(uctx);
190 		return (ret);
191 	}
192 
193 	return (0);
194 }
195 
196 static int
197 uds_accept(void *ctx, void **newctxp)
198 {
199 	struct uds_ctx *uctx = ctx;
200 	struct uds_ctx *newuctx;
201 	socklen_t fromlen;
202 	int ret;
203 
204 	PJDLOG_ASSERT(uctx != NULL);
205 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
206 	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
207 	PJDLOG_ASSERT(uctx->uc_fd >= 0);
208 
209 	newuctx = malloc(sizeof(*newuctx));
210 	if (newuctx == NULL)
211 		return (errno);
212 
213 	fromlen = sizeof(newuctx->uc_sun);
214 	newuctx->uc_fd = accept(uctx->uc_fd,
215 	    (struct sockaddr *)&newuctx->uc_sun, &fromlen);
216 	if (newuctx->uc_fd == -1) {
217 		ret = errno;
218 		free(newuctx);
219 		return (ret);
220 	}
221 
222 	newuctx->uc_side = UDS_SIDE_SERVER_WORK;
223 	newuctx->uc_magic = UDS_CTX_MAGIC;
224 	*newctxp = newuctx;
225 
226 	return (0);
227 }
228 
229 static int
230 uds_send(void *ctx, const unsigned char *data, size_t size, int fd)
231 {
232 	struct uds_ctx *uctx = ctx;
233 
234 	PJDLOG_ASSERT(uctx != NULL);
235 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
236 	PJDLOG_ASSERT(uctx->uc_fd >= 0);
237 
238 	return (proto_common_send(uctx->uc_fd, data, size, fd));
239 }
240 
241 static int
242 uds_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
243 {
244 	struct uds_ctx *uctx = ctx;
245 
246 	PJDLOG_ASSERT(uctx != NULL);
247 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
248 	PJDLOG_ASSERT(uctx->uc_fd >= 0);
249 
250 	return (proto_common_recv(uctx->uc_fd, data, size, fdp));
251 }
252 
253 static int
254 uds_descriptor(const void *ctx)
255 {
256 	const struct uds_ctx *uctx = ctx;
257 
258 	PJDLOG_ASSERT(uctx != NULL);
259 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
260 
261 	return (uctx->uc_fd);
262 }
263 
264 static void
265 uds_local_address(const void *ctx, char *addr, size_t size)
266 {
267 	const struct uds_ctx *uctx = ctx;
268 	struct sockaddr_un sun;
269 	socklen_t sunlen;
270 
271 	PJDLOG_ASSERT(uctx != NULL);
272 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
273 	PJDLOG_ASSERT(addr != NULL);
274 
275 	sunlen = sizeof(sun);
276 	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) {
277 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
278 		return;
279 	}
280 	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
281 	if (sun.sun_path[0] == '\0') {
282 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
283 		return;
284 	}
285 	PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
286 }
287 
288 static void
289 uds_remote_address(const void *ctx, char *addr, size_t size)
290 {
291 	const struct uds_ctx *uctx = ctx;
292 	struct sockaddr_un sun;
293 	socklen_t sunlen;
294 
295 	PJDLOG_ASSERT(uctx != NULL);
296 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
297 	PJDLOG_ASSERT(addr != NULL);
298 
299 	sunlen = sizeof(sun);
300 	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) {
301 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
302 		return;
303 	}
304 	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
305 	if (sun.sun_path[0] == '\0') {
306 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
307 		return;
308 	}
309 	snprintf(addr, size, "uds://%s", sun.sun_path);
310 }
311 
312 static void
313 uds_close(void *ctx)
314 {
315 	struct uds_ctx *uctx = ctx;
316 
317 	PJDLOG_ASSERT(uctx != NULL);
318 	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
319 
320 	if (uctx->uc_fd >= 0)
321 		close(uctx->uc_fd);
322 	/*
323 	 * Unlink the socket only if we are the owner and this is descriptor
324 	 * we listen on.
325 	 */
326 	if (uctx->uc_side == UDS_SIDE_SERVER_LISTEN &&
327 	    uctx->uc_owner == getpid()) {
328 		PJDLOG_ASSERT(uctx->uc_sun.sun_path[0] != '\0');
329 		if (unlink(uctx->uc_sun.sun_path) == -1) {
330 			pjdlog_errno(LOG_WARNING,
331 			    "Unable to unlink socket file %s",
332 			    uctx->uc_sun.sun_path);
333 		}
334 	}
335 	uctx->uc_owner = 0;
336 	uctx->uc_magic = 0;
337 	free(uctx);
338 }
339 
340 static struct proto uds_proto = {
341 	.prt_name = "uds",
342 	.prt_client = uds_client,
343 	.prt_connect = uds_connect,
344 	.prt_connect_wait = uds_connect_wait,
345 	.prt_server = uds_server,
346 	.prt_accept = uds_accept,
347 	.prt_send = uds_send,
348 	.prt_recv = uds_recv,
349 	.prt_descriptor = uds_descriptor,
350 	.prt_local_address = uds_local_address,
351 	.prt_remote_address = uds_remote_address,
352 	.prt_close = uds_close
353 };
354 
355 static __constructor void
356 uds_ctor(void)
357 {
358 
359 	proto_register(&uds_proto, false);
360 }
361