From 3f1215502b1b78008b70704d5794bbf4f7ab5abc Mon Sep 17 00:00:00 2001 From: Alexandre Ratchov Date: Mon, 18 Apr 2011 12:14:27 +0200 Subject: [PATCH] add TCP transport --- aucat/aucat.1 | 8 +++ aucat/aucat.c | 78 ++++++++++++++++----- aucat/conf.h | 2 + aucat/listen.c | 95 ++++++++++++++++++++++--- aucat/listen.h | 7 +- aucat/midicat.1 | 8 +++ configure | 4 +- libsndio/aucat.c | 178 ++++++++++++++++++++++++++++++++++++++--------- 8 files changed, 314 insertions(+), 66 deletions(-) diff --git a/aucat/aucat.1 b/aucat/aucat.1 index a491ab2..08036b8 100644 --- a/aucat/aucat.1 +++ b/aucat/aucat.1 @@ -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 diff --git a/aucat/aucat.c b/aucat/aucat.c index e918a0f..6f574b6 100644 --- a/aucat/aucat.c +++ b/aucat/aucat.c @@ -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 */ diff --git a/aucat/conf.h b/aucat/conf.h index c239f2e..ff7a898 100644 --- a/aucat/conf.h +++ b/aucat/conf.h @@ -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" /* diff --git a/aucat/listen.c b/aucat/listen.c index d143275..91204cc 100644 --- a/aucat/listen.c +++ b/aucat/listen.c @@ -19,6 +19,9 @@ #include #include +#include +#include + #include #include #include @@ -29,6 +32,7 @@ #include #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); + } +} diff --git a/aucat/listen.h b/aucat/listen.h index 8b156d6..4c5f4b6 100644 --- a/aucat/listen.h +++ b/aucat/listen.h @@ -1,4 +1,4 @@ -/* $OpenBSD: listen.h,v 1.5 2009/07/25 10:52:19 ratchov Exp $ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Alexandre Ratchov * @@ -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) */ diff --git a/aucat/midicat.1 b/aucat/midicat.1 index d9f31d6..00b735a 100644 --- a/aucat/midicat.1 +++ b/aucat/midicat.1 @@ -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 diff --git a/configure b/configure index 4448ae5..f8bcbc8 100755 --- a/configure +++ b/configure @@ -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 diff --git a/libsndio/aucat.c b/libsndio/aucat.c index a4330d8..42222a1 100644 --- a/libsndio/aucat.c +++ b/libsndio/aucat.c @@ -19,7 +19,8 @@ #include #include #include - +#include /* IPPROTO_XXX */ +#include /* gethostbyname */ #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#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; }