proto_uds.c revision 204076
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 204076 2010-02-18 23:16:19Z pjd $");
32
33/* UDS - UNIX Domain Socket */
34
35#include <sys/un.h>
36
37#include <assert.h>
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 "hast.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)
127{
128	struct uds_ctx *uctx = ctx;
129
130	assert(uctx != NULL);
131	assert(uctx->uc_magic == UDS_CTX_MAGIC);
132	assert(uctx->uc_side == UDS_SIDE_CLIENT);
133	assert(uctx->uc_fd >= 0);
134
135	if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
136	    sizeof(uctx->uc_sun)) < 0) {
137		return (errno);
138	}
139
140	return (0);
141}
142
143static int
144uds_server(const char *addr, void **ctxp)
145{
146	struct uds_ctx *uctx;
147	int ret;
148
149	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
150	if (ret != 0)
151		return (ret);
152
153	uctx = *ctxp;
154
155	unlink(uctx->uc_sun.sun_path);
156	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
157	    sizeof(uctx->uc_sun)) < 0) {
158		ret = errno;
159		uds_close(uctx);
160		return (ret);
161	}
162	if (listen(uctx->uc_fd, 8) < 0) {
163		ret = errno;
164		uds_close(uctx);
165		return (ret);
166	}
167
168	return (0);
169}
170
171static int
172uds_accept(void *ctx, void **newctxp)
173{
174	struct uds_ctx *uctx = ctx;
175	struct uds_ctx *newuctx;
176	socklen_t fromlen;
177	int ret;
178
179	assert(uctx != NULL);
180	assert(uctx->uc_magic == UDS_CTX_MAGIC);
181	assert(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
182	assert(uctx->uc_fd >= 0);
183
184	newuctx = malloc(sizeof(*newuctx));
185	if (newuctx == NULL)
186		return (errno);
187
188	fromlen = sizeof(uctx->uc_sun);
189	newuctx->uc_fd = accept(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
190	    &fromlen);
191	if (newuctx->uc_fd < 0) {
192		ret = errno;
193		free(newuctx);
194		return (ret);
195	}
196
197	newuctx->uc_side = UDS_SIDE_SERVER_WORK;
198	newuctx->uc_magic = UDS_CTX_MAGIC;
199	*newctxp = newuctx;
200
201	return (0);
202}
203
204static int
205uds_send(void *ctx, const unsigned char *data, size_t size)
206{
207	struct uds_ctx *uctx = ctx;
208
209	assert(uctx != NULL);
210	assert(uctx->uc_magic == UDS_CTX_MAGIC);
211	assert(uctx->uc_fd >= 0);
212
213	return (proto_common_send(uctx->uc_fd, data, size));
214}
215
216static int
217uds_recv(void *ctx, unsigned char *data, size_t size)
218{
219	struct uds_ctx *uctx = ctx;
220
221	assert(uctx != NULL);
222	assert(uctx->uc_magic == UDS_CTX_MAGIC);
223	assert(uctx->uc_fd >= 0);
224
225	return (proto_common_recv(uctx->uc_fd, data, size));
226}
227
228static int
229uds_descriptor(const void *ctx)
230{
231	const struct uds_ctx *uctx = ctx;
232
233	assert(uctx != NULL);
234	assert(uctx->uc_magic == UDS_CTX_MAGIC);
235
236	return (uctx->uc_fd);
237}
238
239static bool
240uds_address_match(const void *ctx __unused, const char *addr __unused)
241{
242
243	assert(!"proto_address_match() not supported on UNIX domain sockets");
244	abort();
245}
246
247static void
248uds_local_address(const void *ctx, char *addr, size_t size)
249{
250	const struct uds_ctx *uctx = ctx;
251	struct sockaddr_un sun;
252	socklen_t sunlen;
253
254	assert(uctx != NULL);
255	assert(uctx->uc_magic == UDS_CTX_MAGIC);
256	assert(addr != NULL);
257
258	sunlen = sizeof(sun);
259	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
260		strlcpy(addr, "N/A", size);
261		return;
262	}
263	assert(sun.sun_family == AF_UNIX);
264	if (sun.sun_path[0] == '\0') {
265		strlcpy(addr, "N/A", size);
266		return;
267	}
268	snprintf(addr, size, "uds://%s", sun.sun_path);
269}
270
271static void
272uds_remote_address(const void *ctx, char *addr, size_t size)
273{
274	const struct uds_ctx *uctx = ctx;
275	struct sockaddr_un sun;
276	socklen_t sunlen;
277
278	assert(uctx != NULL);
279	assert(uctx->uc_magic == UDS_CTX_MAGIC);
280	assert(addr != NULL);
281
282	sunlen = sizeof(sun);
283	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
284		strlcpy(addr, "N/A", size);
285		return;
286	}
287	assert(sun.sun_family == AF_UNIX);
288	if (sun.sun_path[0] == '\0') {
289		strlcpy(addr, "N/A", size);
290		return;
291	}
292	snprintf(addr, size, "uds://%s", sun.sun_path);
293}
294
295static void
296uds_close(void *ctx)
297{
298	struct uds_ctx *uctx = ctx;
299
300	assert(uctx != NULL);
301	assert(uctx->uc_magic == UDS_CTX_MAGIC);
302
303	if (uctx->uc_fd >= 0)
304		close(uctx->uc_fd);
305	unlink(uctx->uc_sun.sun_path);
306	uctx->uc_magic = 0;
307	free(uctx);
308}
309
310static struct hast_proto uds_proto = {
311	.hp_name = "uds",
312	.hp_client = uds_client,
313	.hp_connect = uds_connect,
314	.hp_server = uds_server,
315	.hp_accept = uds_accept,
316	.hp_send = uds_send,
317	.hp_recv = uds_recv,
318	.hp_descriptor = uds_descriptor,
319	.hp_address_match = uds_address_match,
320	.hp_local_address = uds_local_address,
321	.hp_remote_address = uds_remote_address,
322	.hp_close = uds_close
323};
324
325static __constructor void
326uds_ctor(void)
327{
328
329	proto_register(&uds_proto);
330}
331