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