/* Public domain. */

/* #define _POSIX_C_SOURCE 2 */

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PATH_OFFSET offsetof(struct sockaddr_un, sun_path)

static void die(const char *message)
{
	perror(message);
	exit(1);
}

static void usage(const char *name)
{
	printf("\
Usage: %s [options] <connectaddr> <bindaddr>\n\
\n\
Connect to Unix domain socket <connectaddr> after binding to address\n\
determined by <bindaddr>.\n\
\n\
By default, bind to <bindaddr> with a sun_path just long enough to hold\n\
<bindaddr> with no additional null termination.  Any '@' characters in\n\
the path will be replaced with null characters.\n\
\n\
Options:\n\
\n\
   -l N   Use a sun_path length of N.  <bindaddr> will be copied to sun_path\n\
          without null termination and the rest of sun_path, if longer, will\n\
          be filled with the '.' character.\n\
\n\
   -L     Like -l, but use sizeof(addr->sun_path).\n\
\n", name);
	exit(1);
}

int main(int argc, char *argv[])
{
	int fd;
	struct sockaddr_un *bindaddrp;
	struct sockaddr_un connaddr;
	char *bindpath, *connpath, *fspath;
	socklen_t bindsize;
	char buf[1];
	int c;
	long i, len = -1;

	while ((c = getopt(argc, argv, "hl:L")) != -1) {
		switch (c) {
		case 'l':
			len = atol(optarg);
			break;
		case 'L':
			len = sizeof(bindaddrp->sun_path);
			break;
		case 'h':
		case '?':
			usage(argv[0]);
			break;
		}
	}
	if (argc - optind != 2)
		usage(argv[0]);
	connpath = argv[optind++];
	bindpath = argv[optind++];
	if (len < 0)
		len = strlen(bindpath);

	bindsize = PATH_OFFSET + len;
	if (!(bindaddrp = malloc(bindsize)))
		die("malloc");
	memset(bindaddrp, 0, PATH_OFFSET);
	memset(bindaddrp->sun_path, '.', len);
	bindaddrp->sun_family = AF_UNIX;
	for (i = 0; i < len; i++) {
		if (bindpath[i] == 0)
			break;
		else if (bindpath[i] == '@')
			bindaddrp->sun_path[i] = 0;
		else
			bindaddrp->sun_path[i] = bindpath[i];
	}

	if (bindaddrp->sun_path[0] != 0) {
		if (!(fspath = malloc(len + 1)))
			die("malloc");
		strncpy(fspath, bindaddrp->sun_path, len);
		fspath[len] = 0;
		unlink(fspath);
		free(fspath);
	}
	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
		die("socket");
	if (bind(fd, (struct sockaddr *)bindaddrp, bindsize))
		die("bind");

	memset(&connaddr, 0, sizeof(connaddr));
	connaddr.sun_family = AF_UNIX;
	strncpy(connaddr.sun_path, connpath, sizeof(connaddr.sun_path));
	if (connect(fd, (struct sockaddr *)&connaddr, sizeof(connaddr)))
		die("connect");
	read(fd, &buf, sizeof(buf)); /* Wait for other end to be closed */
	return 0;
}

