proto_uds.c revision 218192
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: head/sbin/hastd/proto_uds.c 218192 2011-02-02 15:42:00Z pjd $");
32
33/* UDS - UNIX Domain Socket */
34
35#include <sys/un.h>
36
37#include <errno.h>
38#include <stdbool.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "hast.h"
45#include "pjdlog.h"
46#include "proto_impl.h"
47
48#define	UDS_CTX_MAGIC	0xd541c
49struct 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};
58
59static void uds_close(void *ctx);
60
61static int
62uds_addr(const char *addr, struct sockaddr_un *sunp)
63{
64
65	if (addr == NULL)
66		return (-1);
67
68	if (strncasecmp(addr, "uds://", 6) == 0)
69		addr += 6;
70	else if (strncasecmp(addr, "unix://", 7) == 0)
71		addr += 7;
72	else if (addr[0] == '/' &&	/* If it starts from /... */
73	    strstr(addr, "://") == NULL)/* ...and there is no prefix... */
74		;			/* ...we assume its us. */
75	else
76		return (-1);
77
78	sunp->sun_family = AF_UNIX;
79	if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
80	    sizeof(sunp->sun_path)) {
81		return (ENAMETOOLONG);
82	}
83	sunp->sun_len = SUN_LEN(sunp);
84
85	return (0);
86}
87
88static int
89uds_common_setup(const char *addr, void **ctxp, int side)
90{
91	struct uds_ctx *uctx;
92	int ret;
93
94	uctx = malloc(sizeof(*uctx));
95	if (uctx == NULL)
96		return (errno);
97
98	/* Parse given address. */
99	if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) {
100		free(uctx);
101		return (ret);
102	}
103
104	uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
105	if (uctx->uc_fd == -1) {
106		ret = errno;
107		free(uctx);
108		return (ret);
109	}
110
111	uctx->uc_side = side;
112	uctx->uc_magic = UDS_CTX_MAGIC;
113	*ctxp = uctx;
114
115	return (0);
116}
117
118static int
119uds_client(const char *addr, void **ctxp)
120{
121
122	return (uds_common_setup(addr, ctxp, UDS_SIDE_CLIENT));
123}
124
125static int
126uds_connect(void *ctx, int timeout)
127{
128	struct uds_ctx *uctx = ctx;
129
130	PJDLOG_ASSERT(uctx != NULL);
131	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
132	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
133	PJDLOG_ASSERT(uctx->uc_fd >= 0);
134	PJDLOG_ASSERT(timeout >= 0);
135
136	if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
137	    sizeof(uctx->uc_sun)) < 0) {
138		return (errno);
139	}
140
141	return (0);
142}
143
144static int
145uds_server(const char *addr, void **ctxp)
146{
147	struct uds_ctx *uctx;
148	int ret;
149
150	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
151	if (ret != 0)
152		return (ret);
153
154	uctx = *ctxp;
155
156	unlink(uctx->uc_sun.sun_path);
157	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
158	    sizeof(uctx->uc_sun)) < 0) {
159		ret = errno;
160		uds_close(uctx);
161		return (ret);
162	}
163	if (listen(uctx->uc_fd, 8) < 0) {
164		ret = errno;
165		uds_close(uctx);
166		return (ret);
167	}
168
169	return (0);
170}
171
172static int
173uds_accept(void *ctx, void **newctxp)
174{
175	struct uds_ctx *uctx = ctx;
176	struct uds_ctx *newuctx;
177	socklen_t fromlen;
178	int ret;
179
180	PJDLOG_ASSERT(uctx != NULL);
181	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
182	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
183	PJDLOG_ASSERT(uctx->uc_fd >= 0);
184
185	newuctx = malloc(sizeof(*newuctx));
186	if (newuctx == NULL)
187		return (errno);
188
189	fromlen = sizeof(uctx->uc_sun);
190	newuctx->uc_fd = accept(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
191	    &fromlen);
192	if (newuctx->uc_fd < 0) {
193		ret = errno;
194		free(newuctx);
195		return (ret);
196	}
197
198	newuctx->uc_side = UDS_SIDE_SERVER_WORK;
199	newuctx->uc_magic = UDS_CTX_MAGIC;
200	*newctxp = newuctx;
201
202	return (0);
203}
204
205static int
206uds_send(void *ctx, const unsigned char *data, size_t size)
207{
208	struct uds_ctx *uctx = ctx;
209
210	PJDLOG_ASSERT(uctx != NULL);
211	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
212	PJDLOG_ASSERT(uctx->uc_fd >= 0);
213
214	return (proto_common_send(uctx->uc_fd, data, size));
215}
216
217static int
218uds_recv(void *ctx, unsigned char *data, size_t size)
219{
220	struct uds_ctx *uctx = ctx;
221
222	PJDLOG_ASSERT(uctx != NULL);
223	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
224	PJDLOG_ASSERT(uctx->uc_fd >= 0);
225
226	return (proto_common_recv(uctx->uc_fd, data, size));
227}
228
229static int
230uds_descriptor_send(void *ctx, 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	PJDLOG_ASSERT(fd >= 0);
238
239	return (proto_common_descriptor_send(uctx->uc_fd, fd));
240}
241
242static int
243uds_descriptor_recv(void *ctx, 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	PJDLOG_ASSERT(fdp != NULL);
251
252	return (proto_common_descriptor_recv(uctx->uc_fd, fdp));
253}
254
255static int
256uds_descriptor(const void *ctx)
257{
258	const struct uds_ctx *uctx = ctx;
259
260	PJDLOG_ASSERT(uctx != NULL);
261	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
262
263	return (uctx->uc_fd);
264}
265
266static void
267uds_local_address(const void *ctx, char *addr, size_t size)
268{
269	const struct uds_ctx *uctx = ctx;
270	struct sockaddr_un sun;
271	socklen_t sunlen;
272
273	PJDLOG_ASSERT(uctx != NULL);
274	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
275	PJDLOG_ASSERT(addr != NULL);
276
277	sunlen = sizeof(sun);
278	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
279		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
280		return;
281	}
282	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
283	if (sun.sun_path[0] == '\0') {
284		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
285		return;
286	}
287	PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
288}
289
290static void
291uds_remote_address(const void *ctx, char *addr, size_t size)
292{
293	const struct uds_ctx *uctx = ctx;
294	struct sockaddr_un sun;
295	socklen_t sunlen;
296
297	PJDLOG_ASSERT(uctx != NULL);
298	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
299	PJDLOG_ASSERT(addr != NULL);
300
301	sunlen = sizeof(sun);
302	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
303		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
304		return;
305	}
306	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
307	if (sun.sun_path[0] == '\0') {
308		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
309		return;
310	}
311	snprintf(addr, size, "uds://%s", sun.sun_path);
312}
313
314static void
315uds_close(void *ctx)
316{
317	struct uds_ctx *uctx = ctx;
318
319	PJDLOG_ASSERT(uctx != NULL);
320	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
321
322	if (uctx->uc_fd >= 0)
323		close(uctx->uc_fd);
324	unlink(uctx->uc_sun.sun_path);
325	uctx->uc_magic = 0;
326	free(uctx);
327}
328
329static struct hast_proto uds_proto = {
330	.hp_name = "uds",
331	.hp_client = uds_client,
332	.hp_connect = uds_connect,
333	.hp_server = uds_server,
334	.hp_accept = uds_accept,
335	.hp_send = uds_send,
336	.hp_recv = uds_recv,
337	.hp_descriptor_send = uds_descriptor_send,
338	.hp_descriptor_recv = uds_descriptor_recv,
339	.hp_descriptor = uds_descriptor,
340	.hp_local_address = uds_local_address,
341	.hp_remote_address = uds_remote_address,
342	.hp_close = uds_close
343};
344
345static __constructor void
346uds_ctor(void)
347{
348
349	proto_register(&uds_proto, false);
350}
351