add TCP transport

This commit is contained in:
Alexandre Ratchov 2011-04-18 12:14:27 +02:00
parent 6c7f04bf10
commit 3f1215502b
8 changed files with 314 additions and 66 deletions

View File

@ -33,6 +33,7 @@
.Op Fl h Ar fmt
.Op Fl i Ar file
.Op Fl j Ar flag
.Op Fl L Ar addr
.Op Fl m Ar mode
.Op Fl o Ar file
.Op Fl q Ar device
@ -140,6 +141,13 @@ For instance, this feature could be used to request mono streams to
be sent on multiple outputs or to record a stereo input into a mono stream.
The default is
.Ar on .
.It Fl L Ar addr
Listen on the given address for incoming TCP connections.
Without this option,
.Nm
listens only on the Unix-domain socket, thus it's not reachable
from the network.
The port number is 6523 plus the unit number.
.It Fl l
Detach and become a daemon.
.It Fl m Ar mode

View File

@ -260,6 +260,16 @@ struct cfdev {
SLIST_HEAD(cfdevlist, cfdev);
/*
* TCP addresses to listen on
*/
struct cfnet {
SLIST_ENTRY(cfnet) entry;
char *addr;
};
SLIST_HEAD(cfnetlist, cfnet);
void
cfdev_add(struct cfdevlist *list, struct cfdev *templ, char *path)
{
@ -319,6 +329,20 @@ cfmid_add(struct cfmidlist *list, char *path)
SLIST_INSERT_HEAD(list, cm, entry);
}
void
cfnet_add(struct cfnetlist *list, char *addr)
{
struct cfnet *cn;
cn = malloc(sizeof(struct cfnet));
if (cn == NULL) {
perror("malloc");
abort();
}
cn->addr = addr;
SLIST_INSERT_HEAD(list, cn, entry);
}
void
setsig(void)
{
@ -416,11 +440,11 @@ aucat_usage(void)
{
(void)fputs("usage: " PROG_AUCAT " [-dlnu] [-a flag] [-b nframes] "
"[-C min:max] [-c min:max] [-e enc]\n\t"
"[-f device] [-h fmt] [-i file] [-j flag] [-m mode]"
"[-o file] [-q device]\n\t"
"[-r rate] [-s name] [-t mode] [-U unit] "
"[-v volume] [-x policy]\n\t"
"[-z nframes]\n",
"[-f device] [-h fmt] [-i file] [-j flag] [-L addr] [-m mode] "
"[-o file]\n\t"
"[-q device] [-r rate] [-s name] [-t mode] [-U unit] "
"[-v volume]\n\t"
"[-x policy] [-z nframes]\n",
stderr);
}
@ -428,10 +452,11 @@ int
aucat_main(int argc, char **argv)
{
struct cfdevlist cfdevs;
struct cfnetlist cfnets;
struct cfmid *cm;
struct cfstr *cs;
struct cfdev *cd;
struct listen *listen = NULL;
struct cfnet *cn;
int c, u_flag, d_flag, l_flag, n_flag, unit;
char base[PATH_MAX], path[PATH_MAX];
unsigned mode, rate;
@ -450,6 +475,7 @@ aucat_main(int argc, char **argv)
l_flag = 0;
n_flag = 0;
SLIST_INIT(&cfdevs);
SLIST_INIT(&cfnets);
nfile = nsock = 0;
/*
@ -488,7 +514,7 @@ aucat_main(int argc, char **argv)
cd->round = 0;
cd->hold = 1;
while ((c = getopt(argc, argv, "a:dnb:c:C:e:r:h:x:v:i:o:f:m:luq:s:U:t:j:z:")) != -1) {
while ((c = getopt(argc, argv, "a:dnb:c:C:e:r:h:x:v:i:o:f:m:luq:s:U:L:t:j:z:")) != -1) {
switch (c) {
case 'd':
#ifdef DEBUG
@ -508,6 +534,9 @@ aucat_main(int argc, char **argv)
if (str)
errx(1, "%s: unit number is %s", optarg, str);
break;
case 'L':
cfnet_add(&cfnets, optarg);
break;
case 'm':
cs->mode = opt_mode();
cd->mode = cs->mode;
@ -768,9 +797,12 @@ aucat_main(int argc, char **argv)
if (nsock > 0) {
snprintf(path, sizeof(path), "%s/%s%u", base,
AUCAT_PATH, unit);
listen = listen_new(&listen_ops, path);
if (listen == NULL)
exit(1);
listen_new_un(path);
while (!SLIST_EMPTY(&cfnets)) {
cn = SLIST_FIRST(&cfnets);
SLIST_REMOVE_HEAD(&cfnets, entry);
listen_new_tcp(cn->addr, AUCAT_PORT + unit);
}
}
if (geteuid() == 0)
privdrop();
@ -805,8 +837,8 @@ aucat_main(int argc, char **argv)
break;
}
fatal:
if (nsock > 0)
file_close(&listen->file);
listen_closeall();
/*
* give a chance to drain
*/
@ -830,7 +862,7 @@ void
midicat_usage(void)
{
(void)fputs("usage: " PROG_MIDICAT " [-dl] "
"[-i file] [-o file] [-q port] [-s name] [-U unit]\n",
"[-i file] [-L addr] [-o file] [-q port] [-s name] [-U unit]\n",
stderr);
}
@ -838,10 +870,11 @@ int
midicat_main(int argc, char **argv)
{
struct cfdevlist cfdevs;
struct cfnetlist cfnets;
struct cfmid *cm;
struct cfstr *cs;
struct cfdev *cd;
struct listen *listen = NULL;
struct cfnet *cn;
int c, d_flag, l_flag, unit, fd;
char base[PATH_MAX], path[PATH_MAX];
struct file *stdx;
@ -858,6 +891,7 @@ midicat_main(int argc, char **argv)
d_flag = 0;
l_flag = 0;
SLIST_INIT(&cfdevs);
SLIST_INIT(&cfnets);
nsock = 0;
/*
@ -916,6 +950,9 @@ midicat_main(int argc, char **argv)
if (str)
errx(1, "%s: unit number is %s", optarg, str);
break;
case 'L':
cfnet_add(&cfnets, optarg);
break;
default:
midicat_usage();
exit(1);
@ -1038,9 +1075,12 @@ midicat_main(int argc, char **argv)
if (nsock > 0) {
snprintf(path, sizeof(path), "%s/%s%u", base,
MIDICAT_PATH, unit);
listen = listen_new(&listen_ops, path);
if (listen == NULL)
exit(1);
listen_new_un(path);
while (!SLIST_EMPTY(&cfnets)) {
cn = SLIST_FIRST(&cfnets);
SLIST_REMOVE_HEAD(&cfnets, entry);
listen_new_tcp(cn->addr, MIDICAT_PORT + unit);
}
}
if (geteuid() == 0)
privdrop();
@ -1068,8 +1108,8 @@ midicat_main(int argc, char **argv)
break;
}
fatal:
if (nsock > 0)
file_close(&listen->file);
listen_closeall();
/*
* give a chance to drain
*/

View File

@ -35,6 +35,8 @@ extern int debug_level;
*/
#define MIDICAT_PATH "midicat"
#define AUCAT_PATH "aucat"
#define MIDICAT_PORT 6533
#define AUCAT_PORT 6523
#define DEFAULT_OPT "default"
/*

View File

@ -19,6 +19,9 @@
#include <sys/stat.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@ -29,6 +32,7 @@
#include <unistd.h>
#include "conf.h"
#include "file.h"
#include "listen.h"
#include "sock.h"
#ifdef COMPAT_STRLCPY
@ -48,8 +52,8 @@ struct fileops listen_ops = {
listen_revents
};
struct listen *
listen_new(struct fileops *ops, char *path)
void
listen_new_un(char *path)
{
int sock, oldumask;
struct sockaddr_un sockname;
@ -58,7 +62,7 @@ listen_new(struct fileops *ops, char *path)
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
return NULL;
exit(1);
}
if (unlink(path) < 0 && errno != ENOENT) {
perror("unlink");
@ -77,7 +81,7 @@ listen_new(struct fileops *ops, char *path)
perror("listen");
goto bad_close;
}
f = (struct listen *)file_new(ops, path, 1);
f = (struct listen *)file_new(&listen_ops, path, 1);
if (f == NULL)
goto bad_close;
f->path = strdup(path);
@ -86,10 +90,70 @@ listen_new(struct fileops *ops, char *path)
exit(1);
}
f->fd = sock;
return f;
return;
bad_close:
close(sock);
return NULL;
exit(1);
}
void
listen_new_tcp(char *addr, unsigned port)
{
char serv[sizeof(unsigned) * 3 + 1];
struct addrinfo *ailist, *ai, aihints;
struct listen *f;
int s, error, opt = 1, n = 0;
/*
* obtain a list of possible addresses for the host/port
*/
memset(&aihints, 0, sizeof(struct addrinfo));
snprintf(serv, sizeof(serv), "%u", port);
aihints.ai_flags |= AI_PASSIVE;
aihints.ai_socktype = SOCK_STREAM;
aihints.ai_protocol = IPPROTO_TCP;
error = getaddrinfo(addr, serv, &aihints, &ailist);
if (error) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
exit(1);
}
/*
* for each address, try create a listening socket bound on
* that address
*/
for (ai = ailist; ai != NULL; ai = ai->ai_next) {
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s < 0) {
perror("socket");
continue;
}
opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0) {
perror("setsockopt");
goto bad_close;
}
if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) {
perror("bind");
goto bad_close;
}
if (listen(s, 1) < 0) {
perror("listen");
goto bad_close;
}
f = (struct listen *)file_new(&listen_ops, addr, 1);
if (f == NULL) {
bad_close:
close(s);
continue;
}
f->path = NULL;
f->fd = s;
n++;
}
freeaddrinfo(ailist);
if (n == 0)
exit(1);
}
int
@ -119,6 +183,7 @@ listen_revents(struct file *file, struct pollfd *pfd)
caddrlen = sizeof(caddrlen);
sock = accept(f->fd, &caddr, &caddrlen);
if (sock < 0) {
/* XXX: should kill the socket */
perror("accept");
return 0;
}
@ -140,7 +205,21 @@ listen_close(struct file *file)
{
struct listen *f = (struct listen *)file;
unlink(f->path);
free(f->path);
if (f->path != NULL) {
unlink(f->path);
free(f->path);
}
close(f->fd);
}
void
listen_closeall(void)
{
struct file *f, *fnext;
for (f = LIST_FIRST(&file_list); f != NULL; f = fnext) {
fnext = LIST_NEXT(f, entry);
if (f->ops == &listen_ops)
file_close(f);
}
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: listen.h,v 1.5 2009/07/25 10:52:19 ratchov Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@ -28,11 +28,12 @@ struct listen {
int fd;
};
struct listen *listen_new(struct fileops *, char *);
void listen_new_un(char *);
void listen_new_tcp(char *, unsigned);
int listen_nfds(struct file *);
int listen_pollfd(struct file *, struct pollfd *, int);
int listen_revents(struct file *, struct pollfd *);
void listen_close(struct file *);
extern struct fileops listen_ops;
void listen_closeall(void);
#endif /* !defined(LISTEN_H) */

View File

@ -24,6 +24,7 @@
.Nm midicat
.Op Fl dl
.Op Fl i Ar file
.Op Fl L Ar addr
.Op Fl o Ar file
.Op Fl q Ar port
.Op Fl s Ar name
@ -57,6 +58,13 @@ Read data to send from this file.
If the option argument is
.Sq -
then standard input will be used.
.It Fl L Ar addr
Listen on the given address for incoming TCP connections.
Without this option,
.Nm
listens only on the Unix-domain socket, thus it's not reachable
from the network.
The port number is 6533 plus the unit number.
.It Fl l
Detach and become a daemon.
.It Fl o Ar file

4
configure vendored
View File

@ -27,7 +27,7 @@ prefix=/usr/local # where to install sndio
so="libsndio.so.\${MAJ}.\${MIN}" # shared libs to build
alsa=no # do we want alsa support ?
sun=no # do we want sun support ?
precision=16 # aucat arithmetic precision
precision=16 # aucat arithmetic precision
unset vars # variables passed as arguments
unset bindir # path where to install binaries
unset datadir # path where to install doc
@ -47,7 +47,7 @@ case `uname` in
so="$so libsndio.so.\${MAJ} libsndio.so"
defs='-DCOMPAT_GETPEEREID -DCOMPAT_ISSETUGID \\\
-DCOMPAT_STRLCPY -DCOMPAT_STRTONUM \\\
-DCOMPAT_LETOH -DCOMPAT_PACKED'
-DCOMPAT_LETOH -DCOMPAT_PACKED -DDEV_RANDOM=\\"/dev/urandom\\"'
;;
OpenBSD)
sun=yes

View File

@ -19,7 +19,8 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <netinet/in.h> /* IPPROTO_XXX */
#include <netdb.h> /* gethostbyname */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@ -28,6 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sndio.h"
#include "aucat.h"
#include "debug.h"
@ -35,6 +37,10 @@
#include "bsd-compat.h"
#endif
#ifndef DEV_RANDOM
#define DEV_RANDOM "/dev/arandom"
#endif
/*
* read a message, return 0 if not completed
*/
@ -199,7 +205,26 @@ aucat_wdata(struct aucat *hdl, const void *buf, size_t len, unsigned wbpf, int *
int
aucat_gencookie(unsigned char *cookie)
{
arc4random_buf(cookie, AMSG_COOKIELEN);
int fd;
ssize_t n, len;
fd = open(DEV_RANDOM, O_RDONLY);
if (fd < 0) {
DPERROR(DEV_RANDOM);
return 0;
}
len = AMSG_COOKIELEN;
while (len > 0) {
n = read(fd, cookie, AMSG_COOKIELEN);
if (n < 0) {
DPERROR(DEV_RANDOM);
close(fd);
return 0;
}
cookie += n;
len -= n;
}
close(fd);
return 1;
}
@ -260,16 +285,121 @@ bad_gen:
return res;
}
int
aucat_connect_tcp(struct aucat *hdl, char *host, char *unit, unsigned mode)
{
int s, error;
struct addrinfo *ailist, *ai, aihints;
unsigned port;
char serv[NI_MAXSERV];
if (sscanf(unit, "%u", &port) != 1) {
DPRINTF("%s: bad unit number\n", unit);
return 0;
}
if (mode & (MIO_IN | MIO_OUT)) {
port += MIDICAT_PORT;
} else if (mode & (SIO_PLAY | SIO_REC)) {
port += AUCAT_PORT;
} else {
DPRINTF("aucat_connect_tcp: unknown mode\n");
abort();
}
snprintf(serv, sizeof(serv), "%u", port);
memset(&aihints, 0, sizeof(struct addrinfo));
aihints.ai_socktype = SOCK_STREAM;
aihints.ai_protocol = IPPROTO_TCP;
error = getaddrinfo(host, serv, &aihints, &ailist);
if (error) {
DPRINTF("%s: %s\n", host, gai_strerror(error));
return 0;
}
s = -1;
for (ai = ailist; ai != NULL; ai = ai->ai_next) {
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s < 0) {
DPERROR("socket");
continue;
}
if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
DPERROR("connect");
close(s);
continue;
}
break;
}
freeaddrinfo(ailist);
if (s < 0)
return 0;
hdl->fd = s;
return 1;
}
int
aucat_connect_un(struct aucat *hdl, char *unit, unsigned mode)
{
struct sockaddr_un ca;
socklen_t len = sizeof(struct sockaddr_un);
char *sock;
uid_t uid;
int s;
if (mode & (MIO_IN | MIO_OUT)) {
sock = MIDICAT_PATH;
} else if (mode & (SIO_PLAY | SIO_REC)) {
sock = AUCAT_PATH;
} else {
DPRINTF("aucat_connect_un: unknown mode\n");
abort();
}
uid = geteuid();
snprintf(ca.sun_path, sizeof(ca.sun_path),
"/tmp/aucat-%u/%s%s", uid, sock, unit);
ca.sun_family = AF_UNIX;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0)
return 0;
while (connect(s, (struct sockaddr *)&ca, len) < 0) {
if (errno == EINTR)
continue;
DPERROR("aucat_init: connect");
/* try shared server */
snprintf(ca.sun_path, sizeof(ca.sun_path),
"/tmp/aucat/%s%s", sock, unit);
while (connect(s, (struct sockaddr *)&ca, len) < 0) {
if (errno == EINTR)
continue;
DPERROR("aucat_init: connect");
close(s);
return 0;
}
break;
}
hdl->fd = s;
return 1;
}
int
aucat_open(struct aucat *hdl, const char *str, char *sock, unsigned mode, int nbio)
{
extern char *__progname;
int s, eof;
int eof, hashost;
char unit[4], *sep, *opt;
struct sockaddr_un ca;
socklen_t len = sizeof(struct sockaddr_un);
uid_t uid;
char host[NI_MAXHOST];
sep = strchr(str, '/');
if (sep == NULL) {
hashost = 0;
} else {
if (sep - str >= sizeof(host)) {
DPRINTF("aucat_open: %s: host too long\n", str);
return 0;
}
memcpy(host, str, sep - str);
host[sep - str] = '\0';
hashost = 1;
str = sep + 1;
}
sep = strchr(str, '.');
if (sep == NULL) {
opt = "default";
@ -283,36 +413,17 @@ aucat_open(struct aucat *hdl, const char *str, char *sock, unsigned mode, int nb
strlcpy(unit, str, opt - str);
}
DPRINTF("aucat_init: trying %s -> %s.%s\n", str, unit, opt);
uid = geteuid();
if (strchr(str, '/') != NULL)
return 0;
snprintf(ca.sun_path, sizeof(ca.sun_path),
"/tmp/aucat-%u/%s%s", uid, sock, unit);
ca.sun_family = AF_UNIX;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0)
goto bad_free;
while (connect(s, (struct sockaddr *)&ca, len) < 0) {
if (errno == EINTR)
continue;
DPERROR("aucat_init: connect");
/* try shared server */
snprintf(ca.sun_path, sizeof(ca.sun_path),
"/tmp/aucat/%s%s", sock, unit);
while (connect(s, (struct sockaddr *)&ca, len) < 0) {
if (errno == EINTR)
continue;
DPERROR("aucat_init: connect");
goto bad_connect;
}
break;
if (hashost) {
if (!aucat_connect_tcp(hdl, host, unit, mode))
return 0;
} else {
if (!aucat_connect_un(hdl, unit, mode))
return 0;
}
if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) {
if (fcntl(hdl->fd, F_SETFD, FD_CLOEXEC) < 0) {
DPERROR("FD_CLOEXEC");
goto bad_connect;
}
hdl->fd = s;
hdl->rstate = RSTATE_MSG;
hdl->rtodo = sizeof(struct amsg);
hdl->wstate = WSTATE_IDLE;
@ -350,9 +461,8 @@ aucat_open(struct aucat *hdl, const char *str, char *sock, unsigned mode, int nb
}
return 1;
bad_connect:
while (close(s) < 0 && errno == EINTR)
while (close(hdl->fd) < 0 && errno == EINTR)
; /* retry */
bad_free:
return 0;
}