proto_uds.c revision 218193
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 218193 2011-02-02 15:46:28Z 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 >= -1);
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_connect_wait(void *ctx, int timeout)
146{
147	struct uds_ctx *uctx = ctx;
148
149	PJDLOG_ASSERT(uctx != NULL);
150	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
151	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
152	PJDLOG_ASSERT(uctx->uc_fd >= 0);
153	PJDLOG_ASSERT(timeout >= 0);
154
155	return (0);
156}
157
158static int
159uds_server(const char *addr, void **ctxp)
160{
161	struct uds_ctx *uctx;
162	int ret;
163
164	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
165	if (ret != 0)
166		return (ret);
167
168	uctx = *ctxp;
169
170	unlink(uctx->uc_sun.sun_path);
171	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
172	    sizeof(uctx->uc_sun)) < 0) {
173		ret = errno;
174		uds_close(uctx);
175		return (ret);
176	}
177	if (listen(uctx->uc_fd, 8) < 0) {
178		ret = errno;
179		uds_close(uctx);
180		return (ret);
181	}
182
183	return (0);
184}
185
186static int
187uds_accept(void *ctx, void **newctxp)
188{
189	struct uds_ctx *uctx = ctx;
190	struct uds_ctx *newuctx;
191	socklen_t fromlen;
192	int ret;
193
194	PJDLOG_ASSERT(uctx != NULL);
195	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
196	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
197	PJDLOG_ASSERT(uctx->uc_fd >= 0);
198
199	newuctx = malloc(sizeof(*newuctx));
200	if (newuctx == NULL)
201		return (errno);
202
203	fromlen = sizeof(uctx->uc_sun);
204	newuctx->uc_fd = accept(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
205	    &fromlen);
206	if (newuctx->uc_fd < 0) {
207		ret = errno;
208		free(newuctx);
209		return (ret);
210	}
211
212	newuctx->uc_side = UDS_SIDE_SERVER_WORK;
213	newuctx->uc_magic = UDS_CTX_MAGIC;
214	*newctxp = newuctx;
215
216	return (0);
217}
218
219static int
220uds_send(void *ctx, const unsigned char *data, size_t size)
221{
222	struct uds_ctx *uctx = ctx;
223
224	PJDLOG_ASSERT(uctx != NULL);
225	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
226	PJDLOG_ASSERT(uctx->uc_fd >= 0);
227
228	return (proto_common_send(uctx->uc_fd, data, size));
229}
230
231static int
232uds_recv(void *ctx, unsigned char *data, size_t size)
233{
234	struct uds_ctx *uctx = ctx;
235
236	PJDLOG_ASSERT(uctx != NULL);
237	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
238	PJDLOG_ASSERT(uctx->uc_fd >= 0);
239
240	return (proto_common_recv(uctx->uc_fd, data, size));
241}
242
243static int
244uds_descriptor_send(void *ctx, int fd)
245{
246	struct uds_ctx *uctx = ctx;
247
248	PJDLOG_ASSERT(uctx != NULL);
249	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
250	PJDLOG_ASSERT(uctx->uc_fd >= 0);
251	PJDLOG_ASSERT(fd >= 0);
252
253	return (proto_common_descriptor_send(uctx->uc_fd, fd));
254}
255
256static int
257uds_descriptor_recv(void *ctx, int *fdp)
258{
259	struct uds_ctx *uctx = ctx;
260
261	PJDLOG_ASSERT(uctx != NULL);
262	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
263	PJDLOG_ASSERT(uctx->uc_fd >= 0);
264	PJDLOG_ASSERT(fdp != NULL);
265
266	return (proto_common_descriptor_recv(uctx->uc_fd, fdp));
267}
268
269static int
270uds_descriptor(const void *ctx)
271{
272	const struct uds_ctx *uctx = ctx;
273
274	PJDLOG_ASSERT(uctx != NULL);
275	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
276
277	return (uctx->uc_fd);
278}
279
280static void
281uds_local_address(const void *ctx, char *addr, size_t size)
282{
283	const struct uds_ctx *uctx = ctx;
284	struct sockaddr_un sun;
285	socklen_t sunlen;
286
287	PJDLOG_ASSERT(uctx != NULL);
288	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
289	PJDLOG_ASSERT(addr != NULL);
290
291	sunlen = sizeof(sun);
292	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
293		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
294		return;
295	}
296	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
297	if (sun.sun_path[0] == '\0') {
298		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
299		return;
300	}
301	PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
302}
303
304static void
305uds_remote_address(const void *ctx, char *addr, size_t size)
306{
307	const struct uds_ctx *uctx = ctx;
308	struct sockaddr_un sun;
309	socklen_t sunlen;
310
311	PJDLOG_ASSERT(uctx != NULL);
312	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
313	PJDLOG_ASSERT(addr != NULL);
314
315	sunlen = sizeof(sun);
316	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
317		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
318		return;
319	}
320	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
321	if (sun.sun_path[0] == '\0') {
322		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
323		return;
324	}
325	snprintf(addr, size, "uds://%s", sun.sun_path);
326}
327
328static void
329uds_close(void *ctx)
330{
331	struct uds_ctx *uctx = ctx;
332
333	PJDLOG_ASSERT(uctx != NULL);
334	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
335
336	if (uctx->uc_fd >= 0)
337		close(uctx->uc_fd);
338	unlink(uctx->uc_sun.sun_path);
339	uctx->uc_magic = 0;
340	free(uctx);
341}
342
343static struct hast_proto uds_proto = {
344	.hp_name = "uds",
345	.hp_client = uds_client,
346	.hp_connect = uds_connect,
347	.hp_connect_wait = uds_connect_wait,
348	.hp_server = uds_server,
349	.hp_accept = uds_accept,
350	.hp_send = uds_send,
351	.hp_recv = uds_recv,
352	.hp_descriptor_send = uds_descriptor_send,
353	.hp_descriptor_recv = uds_descriptor_recv,
354	.hp_descriptor = uds_descriptor,
355	.hp_local_address = uds_local_address,
356	.hp_remote_address = uds_remote_address,
357	.hp_close = uds_close
358};
359
360static __constructor void
361uds_ctor(void)
362{
363
364	proto_register(&uds_proto, false);
365}
366