proto_uds.c revision 219818
1167465Smp/*-
259243Sobrien * Copyright (c) 2009-2010 The FreeBSD Foundation
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * This software was developed by Pawel Jakub Dawidek under sponsorship from
659243Sobrien * the FreeBSD Foundation.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
11100616Smp * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
1659243Sobrien *
1759243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1859243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1959243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2059243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2159243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2259243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2359243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2459243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2559243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2659243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2759243Sobrien * SUCH DAMAGE.
2859243Sobrien */
2959243Sobrien
3059243Sobrien#include <sys/cdefs.h>
3159243Sobrien__FBSDID("$FreeBSD: head/sbin/hastd/proto_uds.c 219818 2011-03-21 08:54:59Z pjd $");
3259243Sobrien
3359243Sobrien/* UDS - UNIX Domain Socket */
3459243Sobrien
3559243Sobrien#include <sys/types.h>
3659243Sobrien#include <sys/un.h>
3759243Sobrien
3859243Sobrien#include <errno.h>
3959243Sobrien#include <stdbool.h>
4059243Sobrien#include <stdint.h>
4159243Sobrien#include <stdio.h>
4259243Sobrien#include <string.h>
4359243Sobrien#include <unistd.h>
4459243Sobrien
4559243Sobrien#include "hast.h"
4659243Sobrien#include "pjdlog.h"
4759243Sobrien#include "proto_impl.h"
4859243Sobrien
4959243Sobrien#define	UDS_CTX_MAGIC	0xd541c
5059243Sobrienstruct uds_ctx {
5159243Sobrien	int			uc_magic;
5259243Sobrien	struct sockaddr_un	uc_sun;
5359243Sobrien	int			uc_fd;
5459243Sobrien	int			uc_side;
5559243Sobrien#define	UDS_SIDE_CLIENT		0
5659243Sobrien#define	UDS_SIDE_SERVER_LISTEN	1
5759243Sobrien#define	UDS_SIDE_SERVER_WORK	2
5859243Sobrien	pid_t			uc_owner;
5959243Sobrien};
6059243Sobrien
6159243Sobrienstatic void uds_close(void *ctx);
6259243Sobrien
6359243Sobrienstatic int
6459243Sobrienuds_addr(const char *addr, struct sockaddr_un *sunp)
6559243Sobrien{
6659243Sobrien
6759243Sobrien	if (addr == NULL)
6859243Sobrien		return (-1);
6959243Sobrien
7059243Sobrien	if (strncasecmp(addr, "uds://", 6) == 0)
7159243Sobrien		addr += 6;
7259243Sobrien	else if (strncasecmp(addr, "unix://", 7) == 0)
7359243Sobrien		addr += 7;
7459243Sobrien	else if (addr[0] == '/' &&	/* If it starts from /... */
7559243Sobrien	    strstr(addr, "://") == NULL)/* ...and there is no prefix... */
7659243Sobrien		;			/* ...we assume its us. */
7759243Sobrien	else
7859243Sobrien		return (-1);
7959243Sobrien
8059243Sobrien	sunp->sun_family = AF_UNIX;
8159243Sobrien	if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
8259243Sobrien	    sizeof(sunp->sun_path)) {
8359243Sobrien		return (ENAMETOOLONG);
8459243Sobrien	}
8559243Sobrien	sunp->sun_len = SUN_LEN(sunp);
8659243Sobrien
8759243Sobrien	return (0);
8859243Sobrien}
8959243Sobrien
9059243Sobrienstatic int
9159243Sobrienuds_common_setup(const char *addr, void **ctxp, int side)
9259243Sobrien{
9359243Sobrien	struct uds_ctx *uctx;
9459243Sobrien	int ret;
9559243Sobrien
9659243Sobrien	uctx = malloc(sizeof(*uctx));
9759243Sobrien	if (uctx == NULL)
9859243Sobrien		return (errno);
9959243Sobrien
10059243Sobrien	/* Parse given address. */
10159243Sobrien	if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) {
10259243Sobrien		free(uctx);
10359243Sobrien		return (ret);
10459243Sobrien	}
10559243Sobrien
10659243Sobrien	uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
10759243Sobrien	if (uctx->uc_fd == -1) {
10859243Sobrien		ret = errno;
10959243Sobrien		free(uctx);
11059243Sobrien		return (ret);
11159243Sobrien	}
11259243Sobrien
11369408Sache	uctx->uc_side = side;
11469408Sache	uctx->uc_owner = 0;
11569408Sache	uctx->uc_magic = UDS_CTX_MAGIC;
11659243Sobrien	*ctxp = uctx;
11759243Sobrien
11859243Sobrien	return (0);
11959243Sobrien}
12059243Sobrien
12159243Sobrienstatic int
12259243Sobrienuds_client(const char *srcaddr, const char *dstaddr, void **ctxp)
12359243Sobrien{
12459243Sobrien	int ret;
12559243Sobrien
12659243Sobrien	ret = uds_common_setup(dstaddr, ctxp, UDS_SIDE_CLIENT);
12759243Sobrien	if (ret != 0)
12859243Sobrien		return (ret);
12959243Sobrien
13059243Sobrien	PJDLOG_ASSERT(srcaddr == NULL);
13159243Sobrien
13259243Sobrien	return (0);
13359243Sobrien}
13459243Sobrien
13559243Sobrienstatic int
13659243Sobrienuds_connect(void *ctx, int timeout)
13759243Sobrien{
13859243Sobrien	struct uds_ctx *uctx = ctx;
13959243Sobrien
14059243Sobrien	PJDLOG_ASSERT(uctx != NULL);
14159243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
14259243Sobrien	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
14359243Sobrien	PJDLOG_ASSERT(uctx->uc_fd >= 0);
14459243Sobrien	PJDLOG_ASSERT(timeout >= -1);
14559243Sobrien
14659243Sobrien	if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
14759243Sobrien	    sizeof(uctx->uc_sun)) < 0) {
14859243Sobrien		return (errno);
14959243Sobrien	}
15059243Sobrien
15159243Sobrien	return (0);
15259243Sobrien}
15359243Sobrien
15459243Sobrienstatic int
15559243Sobrienuds_connect_wait(void *ctx, int timeout)
15659243Sobrien{
15759243Sobrien	struct uds_ctx *uctx = ctx;
15859243Sobrien
15959243Sobrien	PJDLOG_ASSERT(uctx != NULL);
16059243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
16159243Sobrien	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
16259243Sobrien	PJDLOG_ASSERT(uctx->uc_fd >= 0);
16359243Sobrien	PJDLOG_ASSERT(timeout >= 0);
16459243Sobrien
16559243Sobrien	return (0);
16659243Sobrien}
16759243Sobrien
16859243Sobrienstatic int
16959243Sobrienuds_server(const char *addr, void **ctxp)
17059243Sobrien{
17159243Sobrien	struct uds_ctx *uctx;
17259243Sobrien	int ret;
17359243Sobrien
17459243Sobrien	ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN);
17559243Sobrien	if (ret != 0)
17659243Sobrien		return (ret);
17759243Sobrien
17859243Sobrien	uctx = *ctxp;
17959243Sobrien
18059243Sobrien	(void)unlink(uctx->uc_sun.sun_path);
18159243Sobrien	if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
18259243Sobrien	    sizeof(uctx->uc_sun)) < 0) {
18359243Sobrien		ret = errno;
18459243Sobrien		uds_close(uctx);
18559243Sobrien		return (ret);
18659243Sobrien	}
18759243Sobrien	uctx->uc_owner = getpid();
18859243Sobrien	if (listen(uctx->uc_fd, 8) < 0) {
18959243Sobrien		ret = errno;
19059243Sobrien		uds_close(uctx);
19159243Sobrien		return (ret);
19259243Sobrien	}
19359243Sobrien
19459243Sobrien	return (0);
19559243Sobrien}
19659243Sobrien
19759243Sobrienstatic int
19859243Sobrienuds_accept(void *ctx, void **newctxp)
19959243Sobrien{
20059243Sobrien	struct uds_ctx *uctx = ctx;
20159243Sobrien	struct uds_ctx *newuctx;
20259243Sobrien	socklen_t fromlen;
20359243Sobrien	int ret;
20459243Sobrien
20559243Sobrien	PJDLOG_ASSERT(uctx != NULL);
20659243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
20759243Sobrien	PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
20859243Sobrien	PJDLOG_ASSERT(uctx->uc_fd >= 0);
20959243Sobrien
21059243Sobrien	newuctx = malloc(sizeof(*newuctx));
21159243Sobrien	if (newuctx == NULL)
21259243Sobrien		return (errno);
21359243Sobrien
21459243Sobrien	fromlen = sizeof(newuctx->uc_sun);
21559243Sobrien	newuctx->uc_fd = accept(uctx->uc_fd,
21659243Sobrien	    (struct sockaddr *)&newuctx->uc_sun, &fromlen);
21759243Sobrien	if (newuctx->uc_fd < 0) {
21859243Sobrien		ret = errno;
21959243Sobrien		free(newuctx);
22059243Sobrien		return (ret);
22159243Sobrien	}
22259243Sobrien
22359243Sobrien	newuctx->uc_side = UDS_SIDE_SERVER_WORK;
22459243Sobrien	newuctx->uc_magic = UDS_CTX_MAGIC;
22559243Sobrien	*newctxp = newuctx;
22659243Sobrien
22759243Sobrien	return (0);
22859243Sobrien}
22959243Sobrien
23059243Sobrienstatic int
23159243Sobrienuds_send(void *ctx, const unsigned char *data, size_t size, int fd)
23259243Sobrien{
23359243Sobrien	struct uds_ctx *uctx = ctx;
23459243Sobrien
23559243Sobrien	PJDLOG_ASSERT(uctx != NULL);
23659243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
23759243Sobrien	PJDLOG_ASSERT(uctx->uc_fd >= 0);
23859243Sobrien
23959243Sobrien	return (proto_common_send(uctx->uc_fd, data, size, fd));
24059243Sobrien}
24159243Sobrien
24259243Sobrienstatic int
24359243Sobrienuds_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
24459243Sobrien{
24559243Sobrien	struct uds_ctx *uctx = ctx;
24659243Sobrien
24759243Sobrien	PJDLOG_ASSERT(uctx != NULL);
24859243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
24959243Sobrien	PJDLOG_ASSERT(uctx->uc_fd >= 0);
25059243Sobrien
25159243Sobrien	return (proto_common_recv(uctx->uc_fd, data, size, fdp));
25259243Sobrien}
25359243Sobrien
25459243Sobrienstatic int
25559243Sobrienuds_descriptor(const void *ctx)
25659243Sobrien{
25759243Sobrien	const struct uds_ctx *uctx = ctx;
25859243Sobrien
25959243Sobrien	PJDLOG_ASSERT(uctx != NULL);
26059243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
26159243Sobrien
26259243Sobrien	return (uctx->uc_fd);
26359243Sobrien}
26459243Sobrien
26559243Sobrienstatic void
26659243Sobrienuds_local_address(const void *ctx, char *addr, size_t size)
26759243Sobrien{
26859243Sobrien	const struct uds_ctx *uctx = ctx;
26959243Sobrien	struct sockaddr_un sun;
27059243Sobrien	socklen_t sunlen;
27159243Sobrien
27259243Sobrien	PJDLOG_ASSERT(uctx != NULL);
27359243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
27459243Sobrien	PJDLOG_ASSERT(addr != NULL);
27559243Sobrien
27659243Sobrien	sunlen = sizeof(sun);
27759243Sobrien	if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
27859243Sobrien		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
27959243Sobrien		return;
28059243Sobrien	}
28159243Sobrien	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
28259243Sobrien	if (sun.sun_path[0] == '\0') {
28359243Sobrien		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
28459243Sobrien		return;
28559243Sobrien	}
28659243Sobrien	PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
28759243Sobrien}
28859243Sobrien
28959243Sobrienstatic void
29059243Sobrienuds_remote_address(const void *ctx, char *addr, size_t size)
29159243Sobrien{
29259243Sobrien	const struct uds_ctx *uctx = ctx;
29359243Sobrien	struct sockaddr_un sun;
294100616Smp	socklen_t sunlen;
295100616Smp
296100616Smp	PJDLOG_ASSERT(uctx != NULL);
297100616Smp	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
298100616Smp	PJDLOG_ASSERT(addr != NULL);
29959243Sobrien
30059243Sobrien	sunlen = sizeof(sun);
30159243Sobrien	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
30259243Sobrien		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
30359243Sobrien		return;
30459243Sobrien	}
30559243Sobrien	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
30659243Sobrien	if (sun.sun_path[0] == '\0') {
30759243Sobrien		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
30859243Sobrien		return;
30959243Sobrien	}
31059243Sobrien	snprintf(addr, size, "uds://%s", sun.sun_path);
311145479Smp}
31259243Sobrien
31359243Sobrienstatic void
31459243Sobrienuds_close(void *ctx)
315145479Smp{
31659243Sobrien	struct uds_ctx *uctx = ctx;
31759243Sobrien
31859243Sobrien	PJDLOG_ASSERT(uctx != NULL);
31959243Sobrien	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
32059243Sobrien
32159243Sobrien	if (uctx->uc_fd >= 0)
32259243Sobrien		close(uctx->uc_fd);
32359243Sobrien	/*
32459243Sobrien	 * Unlink the socket only if we are the owner and this is descriptor
32559243Sobrien	 * we listen on.
32659243Sobrien	 */
32759243Sobrien	if (uctx->uc_side == UDS_SIDE_SERVER_LISTEN &&
32859243Sobrien	    uctx->uc_owner == getpid()) {
32959243Sobrien		PJDLOG_ASSERT(uctx->uc_sun.sun_path[0] != '\0');
33059243Sobrien		if (unlink(uctx->uc_sun.sun_path) == -1) {
33159243Sobrien			pjdlog_errno(LOG_WARNING,
33259243Sobrien			    "Unable to unlink socket file %s",
33359243Sobrien			    uctx->uc_sun.sun_path);
33459243Sobrien		}
33559243Sobrien	}
33659243Sobrien	uctx->uc_owner = 0;
33759243Sobrien	uctx->uc_magic = 0;
33859243Sobrien	free(uctx);
33959243Sobrien}
34059243Sobrien
34159243Sobrienstatic struct hast_proto uds_proto = {
34259243Sobrien	.hp_name = "uds",
34359243Sobrien	.hp_client = uds_client,
34459243Sobrien	.hp_connect = uds_connect,
34559243Sobrien	.hp_connect_wait = uds_connect_wait,
34659243Sobrien	.hp_server = uds_server,
34759243Sobrien	.hp_accept = uds_accept,
34859243Sobrien	.hp_send = uds_send,
34959243Sobrien	.hp_recv = uds_recv,
35059243Sobrien	.hp_descriptor = uds_descriptor,
35159243Sobrien	.hp_local_address = uds_local_address,
35259243Sobrien	.hp_remote_address = uds_remote_address,
35359243Sobrien	.hp_close = uds_close
35459243Sobrien};
35559243Sobrien
35659243Sobrienstatic __constructor void
35759243Sobrienuds_ctor(void)
35859243Sobrien{
35959243Sobrien
36059243Sobrien	proto_register(&uds_proto, false);
36159243Sobrien}
36259243Sobrien