proto_uds.c revision 210876
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 210876 2010-08-05 18:27:41Z 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 "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};
59
60static void uds_close(void *ctx);
61
62static int
63uds_addr(const char *addr, struct sockaddr_un *sunp)
64{
65
66	if (addr == NULL)
67		return (-1);
68
69	if (strncasecmp(addr, "uds://", 6) == 0)
70		addr += 6;
71	else if (strncasecmp(addr, "unix://", 7) == 0)
72		addr += 7;
73	else if (addr[0] == '/' &&	/* If it starts from /... */
74	    strstr(addr, "://") == NULL)/* ...and there is no prefix... */
75		;			/* ...we assume its us. */
76	else
77		return (-1);
78
79	sunp->sun_family = AF_UNIX;
80	if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
81	    sizeof(sunp->sun_path)) {
82		return (ENAMETOOLONG);
83	}
84	sunp->sun_len = SUN_LEN(sunp);
85
86	return (0);
87}
88
89static int
90uds_common_setup(const char *addr, void **ctxp, int side)
91{
92	struct uds_ctx *uctx;
93	int ret;
94
95	uctx = malloc(sizeof(*uctx));
96	if (uctx == NULL)
97		return (errno);
98
99	/* Parse given address. */
100	if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) {
101		free(uctx);
102		return (ret);
103	}
104
105	uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
106	if (uctx->uc_fd == -1) {
107		ret = errno;
108		free(uctx);
109		return (ret);
110	}
111
112	uctx->uc_side = side;
113	uctx->uc_magic = UDS_CTX_MAGIC;
114	*ctxp = uctx;
115
116	return (0);
117}
118
119static int
120uds_client(const char *addr, void **ctxp)
121{
122
123	return (uds_common_setup(addr, ctxp, UDS_SIDE_CLIENT));
124}
125
126static int
127uds_connect(void *ctx)
128{
129	struct uds_ctx *uctx = ctx;
130
131	assert(uctx != NULL);
132	assert(uctx->uc_magic == UDS_CTX_MAGIC);
133	assert(uctx->uc_side == UDS_SIDE_CLIENT);
134	assert(uctx->uc_fd >= 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	assert(uctx != NULL);
181	assert(uctx->uc_magic == UDS_CTX_MAGIC);
182	assert(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
183	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	assert(uctx != NULL);
211	assert(uctx->uc_magic == UDS_CTX_MAGIC);
212	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	assert(uctx != NULL);
223	assert(uctx->uc_magic == UDS_CTX_MAGIC);
224	assert(uctx->uc_fd >= 0);
225
226	return (proto_common_recv(uctx->uc_fd, data, size));
227}
228
229static int
230uds_descriptor(const void *ctx)
231{
232	const struct uds_ctx *uctx = ctx;
233
234	assert(uctx != NULL);
235	assert(uctx->uc_magic == UDS_CTX_MAGIC);
236
237	return (uctx->uc_fd);
238}
239
240static bool
241uds_address_match(const void *ctx __unused, const char *addr __unused)
242{
243
244	assert(!"proto_address_match() not supported on UNIX domain sockets");
245	abort();
246}
247
248static void
249uds_local_address(const void *ctx, char *addr, size_t size)
250{
251	const struct uds_ctx *uctx = ctx;
252	struct sockaddr_un sun;
253	socklen_t sunlen;
254
255	assert(uctx != NULL);
256	assert(uctx->uc_magic == UDS_CTX_MAGIC);
257	assert(addr != NULL);
258
259	sunlen = sizeof(sun);
260	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
261		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
262		return;
263	}
264	assert(sun.sun_family == AF_UNIX);
265	if (sun.sun_path[0] == '\0') {
266		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
267		return;
268	}
269	PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
270}
271
272static void
273uds_remote_address(const void *ctx, char *addr, size_t size)
274{
275	const struct uds_ctx *uctx = ctx;
276	struct sockaddr_un sun;
277	socklen_t sunlen;
278
279	assert(uctx != NULL);
280	assert(uctx->uc_magic == UDS_CTX_MAGIC);
281	assert(addr != NULL);
282
283	sunlen = sizeof(sun);
284	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
285		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
286		return;
287	}
288	assert(sun.sun_family == AF_UNIX);
289	if (sun.sun_path[0] == '\0') {
290		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
291		return;
292	}
293	snprintf(addr, size, "uds://%s", sun.sun_path);
294}
295
296static void
297uds_close(void *ctx)
298{
299	struct uds_ctx *uctx = ctx;
300
301	assert(uctx != NULL);
302	assert(uctx->uc_magic == UDS_CTX_MAGIC);
303
304	if (uctx->uc_fd >= 0)
305		close(uctx->uc_fd);
306	unlink(uctx->uc_sun.sun_path);
307	uctx->uc_magic = 0;
308	free(uctx);
309}
310
311static struct hast_proto uds_proto = {
312	.hp_name = "uds",
313	.hp_client = uds_client,
314	.hp_connect = uds_connect,
315	.hp_server = uds_server,
316	.hp_accept = uds_accept,
317	.hp_send = uds_send,
318	.hp_recv = uds_recv,
319	.hp_descriptor = uds_descriptor,
320	.hp_address_match = uds_address_match,
321	.hp_local_address = uds_local_address,
322	.hp_remote_address = uds_remote_address,
323	.hp_close = uds_close
324};
325
326static __constructor void
327uds_ctor(void)
328{
329
330	proto_register(&uds_proto, false);
331}
332