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#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33/* UDS - UNIX Domain Socket */
34
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <sys/un.h>
38
39#include <errno.h>
40#include <stdbool.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <string.h>
44#include <unistd.h>
45
46#include "pjdlog.h"
47#include "proto_impl.h"
48
49#define	UDS_CTX_MAGIC	0xd541c
50struct uds_ctx {
51	int			uc_magic;
52	struct sockaddr_un	uc_sun;
53	int			uc_fd;
54	int			uc_side;
55#define	UDS_SIDE_CLIENT		0
56#define	UDS_SIDE_SERVER_LISTEN	1
57#define	UDS_SIDE_SERVER_WORK	2
58	pid_t			uc_owner;
59};
60
61static void uds_close(void *ctx);
62
63static int
64uds_addr(const char *addr, struct sockaddr_un *sunp)
65{
66
67	if (addr == NULL)
68		return (-1);
69
70	if (strncasecmp(addr, "uds://", 6) == 0)
71		addr += 6;
72	else if (strncasecmp(addr, "unix://", 7) == 0)
73		addr += 7;
74	else if (addr[0] == '/' &&	/* If it starts from /... */
75	    strstr(addr, "://") == NULL)/* ...and there is no prefix... */
76		;			/* ...we assume its us. */
77	else
78		return (-1);
79
80	sunp->sun_family = AF_UNIX;
81	if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
82	    sizeof(sunp->sun_path)) {
83		return (ENAMETOOLONG);
84	}
85	sunp->sun_len = SUN_LEN(sunp);
86
87	return (0);
88}
89
90static int
91uds_common_setup(const char *addr, void **ctxp, int side)
92{
93	struct uds_ctx *uctx;
94	int ret;
95
96	uctx = malloc(sizeof(*uctx));
97	if (uctx == NULL)
98		return (errno);
99
100	/* Parse given address. */
101	if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) {
102		free(uctx);
103		return (ret);
104	}
105
106	uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
107	if (uctx->uc_fd == -1) {
108		ret = errno;
109		free(uctx);
110		return (ret);
111	}
112
113	uctx->uc_side = side;
114	uctx->uc_owner = 0;
115	uctx->uc_magic = UDS_CTX_MAGIC;
116	*ctxp = uctx;
117
118	return (0);
119}
120
121static int
122uds_client(const char *srcaddr, const char *dstaddr, void **ctxp)
123{
124	int ret;
125
126	ret = uds_common_setup(dstaddr, ctxp, UDS_SIDE_CLIENT);
127	if (ret != 0)
128		return (ret);
129
130	PJDLOG_ASSERT(srcaddr == NULL);
131
132	return (0);
133}
134
135static int
136uds_connect(void *ctx, int timeout)
137{
138	struct uds_ctx *uctx = ctx;
139
140	PJDLOG_ASSERT(uctx != NULL);
141	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
142	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
143	PJDLOG_ASSERT(uctx->uc_fd >= 0);
144	PJDLOG_ASSERT(timeout >= -1);
145
146	if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
147	    sizeof(uctx->uc_sun)) == -1) {
148		return (errno);
149	}
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 ret;
173
174	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
175	if (ret != 0)
176		return (ret);
177
178	uctx = *ctxp;
179
180	(void)unlink(uctx->uc_sun.sun_path);
181	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
182	    sizeof(uctx->uc_sun)) == -1) {
183		ret = errno;
184		uds_close(uctx);
185		return (ret);
186	}
187	uctx->uc_owner = getpid();
188	if (listen(uctx->uc_fd, 8) == -1) {
189		ret = errno;
190		uds_close(uctx);
191		return (ret);
192	}
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 ret;
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 == -1) {
218		ret = errno;
219		free(newuctx);
220		return (ret);
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) == -1) {
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) == -1) {
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_client = uds_client,
344	.prt_connect = uds_connect,
345	.prt_connect_wait = uds_connect_wait,
346	.prt_server = uds_server,
347	.prt_accept = uds_accept,
348	.prt_send = uds_send,
349	.prt_recv = uds_recv,
350	.prt_descriptor = uds_descriptor,
351	.prt_local_address = uds_local_address,
352	.prt_remote_address = uds_remote_address,
353	.prt_close = uds_close
354};
355
356static __constructor void
357uds_ctor(void)
358{
359
360	proto_register(&uds_proto, false);
361}
362