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