view irccd/transport.c @ 1183:1845a0509a93

misc: update copyright years
author David Demelier <markand@malikania.fr>
date Wed, 01 Feb 2023 12:43:11 +0100
parents f06e9761cc90
children 67fa43998a91
line wrap: on
line source

/*
 * transport.c -- remote command support
 *
 * Copyright (c) 2013-2023 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <unistd.h>

#include <irccd/config.h>
#include <irccd/log.h>
#include <irccd/util.h>

#include "peer.h"
#include "transport.h"

static struct sockaddr_un addr;
static int fd = -1;

int
wrap_bind(const char *path, uid_t *uid, gid_t *gid)
{
	assert(path);

	int oldumask;

	addr.sun_family = AF_UNIX;

	if (irc_util_strlcpy(addr.sun_path, path, sizeof (addr.sun_path)) >= sizeof (addr.sun_path)) {
		errno = ENAMETOOLONG;
		goto err;
	}

	/* Silently remove the file first. */
	unlink(path);

	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
		goto err;

	/* -ux, -gx, -orwx */
	oldumask = umask(S_IXUSR | S_IXGRP | S_IRWXO);

	if (bind(fd, (const struct sockaddr *)&addr, sizeof (addr)) < 0)
		goto err;
	if (uid && gid && chown(path, *uid, *gid) < 0)
		goto err;

	umask(oldumask);

	if (listen(fd, 16) < 0)
		goto err;

	irc_log_info("transport: listening on %s", path);
	irc_log_debug("transport: file descriptor %d", fd);

	if (uid && gid)
		irc_log_info("transport: uid=%d, gid=%d", (int)*uid, (int)*gid);

	return 0;

err:
	irc_log_warn("transport: %s: %s", path, strerror(errno));

	if (fd != -1) {
		close(fd);
		fd = -1;
	}

	return -1;
}

int
transport_bind(const char *path)
{
	return wrap_bind(path, NULL, NULL);
}

int
transport_bindp(const char *path, uid_t uid, gid_t gid)
{
	return wrap_bind(path, &uid, &gid);
}

void
transport_prepare(struct pollfd *pfd)
{
	assert(pfd);

	if (fd < 0)
		return;

	pfd->fd = fd;
	pfd->events = POLLIN;
}

struct peer *
transport_flush(const struct pollfd *pfd)
{
	assert(pfd);

	struct peer *peer;
	int newfd;

	if (fd < 0 || pfd->fd != fd || !(pfd->revents & POLLIN))
		return NULL;

	if ((newfd = accept(fd, NULL, NULL)) < 0) {
		irc_log_warn("transport: %s", strerror(errno));
		return NULL;
	}

	peer = peer_new(newfd);

	irc_log_info("transport: new client connected");
	peer_send(peer, "IRCCD %d.%d.%d", IRCCD_VERSION_MAJOR,
	    IRCCD_VERSION_MINOR, IRCCD_VERSION_PATCH);

	return peer;
}

void
transport_finish(void)
{
	/* Connection socket. */
	if (fd != -1)
		close(fd);

	unlink(addr.sun_path);
	memset(&addr, 0, sizeof (addr));
}