From f8cbde54dab2783e2c6ba699dfaee9ef51b1e098 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Jul 2004 00:08:44 +0000 Subject: auth support in esound and native AUTH and SET_NAME operatins in native simple library git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@51 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 33 ++++++++++--- src/authkey.c | 115 +++++++++++++++++++++++++++++++++++++++++++++ src/authkey.h | 9 ++++ src/main.c | 2 +- src/module-oss.c | 3 +- src/module-protocol-stub.c | 23 +++++++-- src/pacat-simple.c | 52 ++++++++++++++++++++ src/pacat.c | 13 +++-- src/polyp-error.c | 25 ++++++++++ src/polyp-error.h | 10 ++++ src/polyp.c | 108 +++++++++++++++++++++++++++++++----------- src/polyp.h | 6 +-- src/protocol-esound.c | 35 ++++++++++---- src/protocol-native-spec.h | 10 +++- src/protocol-native.c | 57 ++++++++++++++++++++++ src/simple.c | 70 ++++++++++++++------------- src/simple.h | 7 +-- src/tagstruct.c | 31 +++++++++++- src/tagstruct.h | 2 + src/todo | 12 ++++- src/util.c | 84 +++++++++++++++++++++++++++++++++ src/util.h | 6 +++ 22 files changed, 615 insertions(+), 98 deletions(-) create mode 100644 src/authkey.c create mode 100644 src/authkey.h create mode 100644 src/pacat-simple.c create mode 100644 src/polyp-error.c create mode 100644 src/polyp-error.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 62b1a3a6..70e7e09b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ AM_CFLAGS=-ansi -D_GNU_SOURCE -bin_PROGRAMS = polypaudio pacat +bin_PROGRAMS = polypaudio pacat pacat-simple pkglib_LTLIBRARIES=libiochannel.la \ libsocket-server.la \ @@ -34,6 +34,7 @@ pkglib_LTLIBRARIES=libiochannel.la \ libtagstruct.la \ libpstream-util.la \ libpdispatch.la \ + libauthkey.la \ libprotocol-simple.la \ libprotocol-esound.la \ libprotocol-native.la \ @@ -49,7 +50,9 @@ pkglib_LTLIBRARIES=libiochannel.la \ module-esound-protocol-unix.la \ module-native-protocol-tcp.la \ module-native-protocol-unix.la \ - libpolyp.la + libpolyp.la \ + libpolyp-simple.la \ + libpolyp-error.la polypaudio_SOURCES = idxset.c idxset.h \ queue.c queue.h \ @@ -139,14 +142,17 @@ libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h libprotocol_native_la_LDFLAGS = -avoid-version -libprotocol_native_la_LIBADD = libsocket-server.la libiochannel.la libpacket.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la +libprotocol_native_la_LIBADD = libsocket-server.la libiochannel.la libpacket.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libtagstruct_la_SOURCES = tagstruct.c tagstruct.h libtagstruct_la_LDFLAGS = -avoid-version libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h protocol-esound-spec.h libprotocol_esound_la_LDFLAGS = -avoid-version -libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la +libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la libauthkey.la + +libauthkey_la_SOURCES = authkey.c authkey.h +libauthkey_la_LDFLAGS = -avoid-version module_simple_protocol_tcp_la_SOURCES = module-protocol-stub.c module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) @@ -221,7 +227,22 @@ libpolyp_la_SOURCES = polyp.c polyp.h \ packet.c packet.h \ queue.c queue.h \ dynarray.c dynarray.h \ - memchunk.c memchunk.h + memchunk.c memchunk.h \ + authkey.c authkey.h +libpolyp_la_CFLAGS = $(AM_CFLAGS) +#libpolyp_la_LIBADD = libpolyp-error.la + +libpolyp_error_la_SOURCES = polyp-error.c polyp-error.h +libpolyp_error_la_CFLAGS = $(AM_CFLAGS) -pacat_SOURCES = pacat.c +libpolyp_simple_la_SOURCES = simple.c simple.h +libpolyp_simple_la_CFLAGS = $(AM_CFLAGS) +libpolyp_simple_la_LIBADD = libpolyp.la #libpolyp-error.la + +pacat_SOURCES = pacat.c #$(libpolyp_la_SOURCES) pacat_LDADD = libpolyp.la +pacat_CFLAGS = $(AM_CFLAGS) + +pacat_simple_SOURCES = pacat-simple.c +pacat_simple_LDADD = libpolyp-simple.a +pacat_simple_CFLAGS = $(AM_CFLAGS) diff --git a/src/authkey.c b/src/authkey.c new file mode 100644 index 00000000..cd284fb5 --- /dev/null +++ b/src/authkey.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "authkey.h" +#include "util.h" + +#define RANDOM_DEVICE "/dev/urandom" + +static int load(const char *fn, void *data, size_t length) { + int fd = -1, ret = -1; + ssize_t r; + + assert(fn && data && length); + + if ((fd = open(fn, O_RDONLY)) < 0) + goto finish; + + if ((r = pa_loop_read(fd, data, length)) < 0 || (size_t) r != length) { + ret = -2; + goto finish; + } + + ret = 0; + +finish: + if (fd >= 0) + close(fd); + + return ret; +} + +static int generate(const char *fn, void *data, size_t length) { + int fd = -1, random_fd = -1, ret = -1; + ssize_t r; + assert(fn && data && length); + + if ((fd = open(fn, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR | S_IWUSR)) < 0) + goto finish; + + if ((random_fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) { + + if ((r = pa_loop_read(random_fd, data, length)) < 0 || (size_t) r != length) { + ret = -2; + goto finish; + } + + } else { + uint8_t *p; + size_t l; + fprintf(stderr, "WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s, falling back to unsecure pseudo RNG.\n", strerror(errno)); + + srandom(time(NULL)); + + for (p = data, l = length; l > 0; p++, l--) + *p = (uint8_t) random(); + } + + if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) { + ret = -2; + goto finish; + } + + ret = 0; + +finish: + if (fd >= 0) { + if (ret != 0) + unlink(fn); + close(fd); + } + if (random_fd >= 0) + close(random_fd); + + return ret; +} + +int pa_authkey_load(const char *path, void *data, size_t length) { + int ret, i; + + assert(path && data && length); + + for (i = 0; i < 10; i++) { + if ((ret = load(path, data, length)) < 0) + if (ret == -1 && errno == ENOENT) + if ((ret = generate(path, data, length)) < 0) + if (ret == -1 && errno == EEXIST) + continue; + break; + } + + if (ret < 0) + fprintf(stderr, "Failed to load authorization key '%s': %s\n", path, (ret == -1) ? strerror(errno) : "file corrupt"); + + return ret; +} + +int pa_authkey_load_from_home(const char *fn, void *data, size_t length) { + char *home; + char path[PATH_MAX]; + + if (!(home = getenv("HOME"))) + return -2; + + snprintf(path, sizeof(path), "%s/%s", home, fn); + + return pa_authkey_load(path, data, length); +} diff --git a/src/authkey.h b/src/authkey.h new file mode 100644 index 00000000..cc0fcb28 --- /dev/null +++ b/src/authkey.h @@ -0,0 +1,9 @@ +#ifndef fooauthkeyhfoo +#define fooauthkeyhfoo + +#include + +int pa_authkey_load(const char *path, void *data, size_t len); +int pa_authkey_load_from_home(const char *fn, void *data, size_t length); + +#endif diff --git a/src/main.c b/src/main.c index f44a8d47..88552fed 100644 --- a/src/main.c +++ b/src/main.c @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) { pa_module_load(c, "module-cli-protocol-unix", NULL); pa_module_load(c, "module-native-protocol-tcp", NULL);*/ pa_module_load(c, "module-native-protocol-unix", NULL); -/* pa_module_load(c, "module-esound-protocol-tcp", NULL);*/ + pa_module_load(c, "module-esound-protocol-tcp", NULL); pa_module_load(c, "module-cli", NULL); fprintf(stderr, "main: mainloop entry.\n"); diff --git a/src/module-oss.c b/src/module-oss.c index 3a1afd47..310c89c0 100644 --- a/src/module-oss.c +++ b/src/module-oss.c @@ -77,7 +77,8 @@ static void do_read(struct userdata *u) { assert(memchunk.memblock); if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { pa_memblock_unref(memchunk.memblock); - fprintf(stderr, "read() failed: %s\n", strerror(errno)); + if (errno != EAGAIN) + fprintf(stderr, "read() failed: %s\n", strerror(errno)); return; } diff --git a/src/module-protocol-stub.c b/src/module-protocol-stub.c index 1a655454..713e0ab8 100644 --- a/src/module-protocol-stub.c +++ b/src/module-protocol-stub.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -50,13 +52,24 @@ int module_init(struct pa_core *c, struct pa_module*m) { assert(c && m); #ifdef USE_TCP_SOCKETS - if (!(s = pa_socket_server_new_ipv4(c->mainloop, INADDR_LOOPBACK, IPV4_PORT))) + if (!(s = pa_socket_server_new_ipv4(c->mainloop, INADDR_ANY, IPV4_PORT))) return -1; #else if (pa_make_secure_dir(UNIX_SOCKET_DIR) < 0) { fprintf(stderr, "Failed to create secure socket directory.\n"); return -1; } + + { + int r; + if ((r = pa_unix_socket_remove_stale(UNIX_SOCKET)) < 0) { + fprintf(stderr, "Failed to remove stale UNIX socket '%s': %s\n", UNIX_SOCKET, strerror(errno)); + return -1; + } + + if (r) + fprintf(stderr, "Removed stale UNIX socket '%s'.", UNIX_SOCKET); + } if (!(s = pa_socket_server_new_unix(c->mainloop, UNIX_SOCKET))) { rmdir(UNIX_SOCKET_DIR); @@ -69,8 +82,12 @@ int module_init(struct pa_core *c, struct pa_module*m) { #else m->userdata = protocol_new(c, s); #endif - - assert(m->userdata); + + if (!m->userdata) { + pa_socket_server_free(s); + return -1; + } + return 0; } diff --git a/src/pacat-simple.c b/src/pacat-simple.c new file mode 100644 index 00000000..5408221c --- /dev/null +++ b/src/pacat-simple.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "simple.h" +#include "polyp-error.h" + +#define BUFSIZE 1024 + +int main(int argc, char*argv[]) { + static const struct pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 + }; + struct pa_simple *s = NULL; + int ret = 1; + int error; + + if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) { + fprintf(stderr, "Failed to connect to server: %s\n", pa_strerror(error)); + goto finish; + } + + for (;;) { + uint8_t buf[BUFSIZE]; + ssize_t r; + + if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) { + if (r == 0) /* eof */ + break; + + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + goto finish; + } + + if (pa_simple_write(s, buf, r, &error) < 0) { + fprintf(stderr, "Failed to write data: %s\n", pa_strerror(error)); + goto finish; + } + } + + ret = 0; + +finish: + + if (s) + pa_simple_free(s); + + return ret; +} diff --git a/src/pacat.c b/src/pacat.c index 32220aeb..c69148e6 100644 --- a/src/pacat.c +++ b/src/pacat.c @@ -59,18 +59,17 @@ static void stream_write_callback(struct pa_stream *s, size_t length, void *user do_write(length); } -static void stream_complete_callback(struct pa_context*c, struct pa_stream *s, void *userdata) { - assert(c); +static void stream_complete_callback(struct pa_stream*s, int success, void *userdata) { + assert(s); - if (!s) { + if (!success) { fprintf(stderr, "Stream creation failed.\n"); mainloop_api->quit(mainloop_api, 1); return; } - stream = s; - pa_stream_set_die_callback(stream, stream_die_callback, NULL); - pa_stream_set_write_callback(stream, stream_write_callback, NULL); + pa_stream_set_die_callback(s, stream_die_callback, NULL); + pa_stream_set_write_callback(s, stream_write_callback, NULL); } static void context_complete_callback(struct pa_context *c, int success, void *userdata) { @@ -87,7 +86,7 @@ static void context_complete_callback(struct pa_context *c, int success, void *u goto fail; } - if (pa_stream_new(c, PA_STREAM_PLAYBACK, NULL, "pacat", &ss, NULL, stream_complete_callback, NULL) < 0) { + if (!(stream = pa_stream_new(c, PA_STREAM_PLAYBACK, NULL, "pacat", &ss, NULL, stream_complete_callback, NULL))) { fprintf(stderr, "pa_stream_new() failed.\n"); goto fail; } diff --git a/src/polyp-error.c b/src/polyp-error.c new file mode 100644 index 00000000..861711a2 --- /dev/null +++ b/src/polyp-error.c @@ -0,0 +1,25 @@ +#include + +#include "polyp-error.h" +#include "protocol-native-spec.h" + +static const char* const errortab[PA_ERROR_MAX] = { + [PA_ERROR_OK] = "OK", + [PA_ERROR_ACCESS] = "Access denied", + [PA_ERROR_COMMAND] = "Unknown command", + [PA_ERROR_INVALID] = "Invalid argument", + [PA_ERROR_EXIST] = "Entity exists", + [PA_ERROR_NOENTITY] = "No such entity", + [PA_ERROR_CONNECTIONREFUSED] = "Connection refused", + [PA_ERROR_PROTOCOL] = "Protocol corrupt", + [PA_ERROR_TIMEOUT] = "Timeout", + [PA_ERROR_AUTHKEY] = "Not authorization key", + [PA_ERROR_INTERNAL] = "Internal error" +}; + +const char*pa_strerror(uint32_t error) { + if (error >= PA_ERROR_MAX) + return NULL; + + return errortab[error]; +} diff --git a/src/polyp-error.h b/src/polyp-error.h new file mode 100644 index 00000000..7407f34c --- /dev/null +++ b/src/polyp-error.h @@ -0,0 +1,10 @@ +#ifndef foopolyperrhfoo +#define foopolyperrhfoo + +#include + +#include "protocol-native-spec.h" + +const char* pa_strerror(uint32_t error); + +#endif diff --git a/src/polyp.c b/src/polyp.c index eb9a3c20..c15d5d9f 100644 --- a/src/polyp.c +++ b/src/polyp.c @@ -10,6 +10,7 @@ #include "dynarray.h" #include "socket-client.h" #include "pstream-util.h" +#include "authkey.h" #define DEFAULT_QUEUE_LENGTH 10240 #define DEFAULT_MAX_LENGTH 20480 @@ -26,14 +27,16 @@ struct pa_context { struct pa_dynarray *streams; struct pa_stream *first_stream; uint32_t ctag; - uint32_t errno; - enum { CONTEXT_UNCONNECTED, CONTEXT_CONNECTING, CONTEXT_READY, CONTEXT_DEAD} state; + uint32_t error; + enum { CONTEXT_UNCONNECTED, CONTEXT_CONNECTING, CONTEXT_AUTHORIZING, CONTEXT_SETTING_NAME, CONTEXT_READY, CONTEXT_DEAD} state; void (*connect_complete_callback)(struct pa_context*c, int success, void *userdata); void *connect_complete_userdata; void (*die_callback)(struct pa_context*c, void *userdata); void *die_userdata; + + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; }; struct pa_stream { @@ -52,7 +55,7 @@ struct pa_stream { void (*write_callback)(struct pa_stream *p, size_t length, void *userdata); void *write_userdata; - void (*create_complete_callback)(struct pa_context*c, struct pa_stream *s, void *userdata); + void (*create_complete_callback)(struct pa_stream *s, int success, void *userdata); void *create_complete_userdata; void (*die_callback)(struct pa_stream*c, void *userdata); @@ -85,7 +88,7 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * c->streams = pa_dynarray_new(); assert(c->streams); c->first_stream = NULL; - c->errno = PA_ERROR_OK; + c->error = PA_ERROR_OK; c->state = CONTEXT_UNCONNECTED; c->ctag = 0; @@ -158,9 +161,11 @@ static int pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packe if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { fprintf(stderr, "polyp.c: invalid packet.\n"); + context_dead(c); return -1; } + return 0; } @@ -170,7 +175,7 @@ static int pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int assert(p && chunk && c && chunk->memblock && chunk->memblock->data); if (!(s = pa_dynarray_get(c->streams, channel))) - return -1; + return 0; if (s->read_callback) s->read_callback(s, chunk->memblock->data + chunk->index, chunk->length, s->read_userdata); @@ -178,15 +183,57 @@ static int pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int return 0; } +static int auth_complete_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_context *c = userdata; + assert(pd && c && (c->state == CONTEXT_AUTHORIZING || c->state == CONTEXT_SETTING_NAME)); + + if (command != PA_COMMAND_REPLY) { + if (command == PA_COMMAND_ERROR && pa_tagstruct_getu32(t, &c->error) < 0) + c->error = PA_ERROR_PROTOCOL; + else if (command == PA_COMMAND_TIMEOUT) + c->error = PA_ERROR_TIMEOUT; + + c->state = CONTEXT_DEAD; + + if (c->connect_complete_callback) + c->connect_complete_callback(c, 0, c->connect_complete_userdata); + + return -1; + } + + if (c->state == CONTEXT_AUTHORIZING) { + struct pa_tagstruct *t; + c->state = CONTEXT_SETTING_NAME; + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_SET_NAME); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, c->name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, auth_complete_callback, c); + } else { + assert(c->state == CONTEXT_SETTING_NAME); + + c->state = CONTEXT_READY; + + if (c->connect_complete_callback) + c->connect_complete_callback(c, 1, c->connect_complete_userdata); + } + + return 0; +} + static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) { struct pa_context *c = userdata; + struct pa_tagstruct *t; + uint32_t tag; assert(client && io && c && c->state == CONTEXT_CONNECTING); pa_socket_client_free(client); c->client = NULL; if (!io) { - c->errno = PA_ERROR_CONNECTIONREFUSED; + c->error = PA_ERROR_CONNECTIONREFUSED; c->state = CONTEXT_UNCONNECTED; if (c->connect_complete_callback) @@ -204,18 +251,27 @@ static void on_connection(struct pa_socket_client *client, struct pa_iochannel*i c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); assert(c->pdispatch); - c->state = CONTEXT_READY; - - if (c->connect_complete_callback) - c->connect_complete_callback(c, 1, c->connect_complete_userdata); + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_AUTH); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_put_arbitrary(t, c->auth_cookie, sizeof(c->auth_cookie)); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, auth_complete_callback, c); + c->state = CONTEXT_AUTHORIZING; } int pa_context_connect(struct pa_context *c, const char *server, void (*complete) (struct pa_context*c, int success, void *userdata), void *userdata) { assert(c && c->state == CONTEXT_UNCONNECTED); + if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) { + c->error = PA_ERROR_AUTHKEY; + return -1; + } + assert(!c->client); if (!(c->client = pa_socket_client_new_unix(c->mainloop, server ? server : DEFAULT_SERVER))) { - c->errno = PA_ERROR_CONNECTIONREFUSED; + c->error = PA_ERROR_CONNECTIONREFUSED; return -1; } @@ -240,7 +296,7 @@ int pa_context_is_ready(struct pa_context *c) { int pa_context_errno(struct pa_context *c) { assert(c); - return c->errno; + return c->error; } void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata) { @@ -258,12 +314,12 @@ static int command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t t if (pa_tagstruct_getu32(t, &channel) < 0 || pa_tagstruct_getu32(t, &bytes) < 0 || !pa_tagstruct_eof(t)) { - c->errno = PA_ERROR_PROTOCOL; + c->error = PA_ERROR_PROTOCOL; return -1; } if (!(s = pa_dynarray_get(c->streams, channel))) { - c->errno = PA_ERROR_PROTOCOL; + c->error = PA_ERROR_PROTOCOL; return -1; } @@ -286,21 +342,19 @@ static int create_playback_callback(struct pa_pdispatch *pd, uint32_t command, u struct pa_context *c = s->context; assert(c); - if (command == PA_COMMAND_ERROR && pa_tagstruct_getu32(t, &s->context->errno) < 0) { - s->context->errno = PA_ERROR_PROTOCOL; - ret = -1; - } else if (command == PA_COMMAND_TIMEOUT) { - s->context->errno = PA_ERROR_TIMEOUT; - ret = -1; - } - + if (command == PA_COMMAND_ERROR && pa_tagstruct_getu32(t, &s->context->error) < 0) + s->context->error = PA_ERROR_PROTOCOL; + else if (command == PA_COMMAND_TIMEOUT) + s->context->error = PA_ERROR_TIMEOUT; + + ret = -1; goto fail; } if (pa_tagstruct_getu32(t, &s->channel) < 0 || pa_tagstruct_getu32(t, &s->device_index) < 0 || !pa_tagstruct_eof(t)) { - s->context->errno = PA_ERROR_PROTOCOL; + s->context->error = PA_ERROR_PROTOCOL; ret = -1; goto fail; } @@ -310,24 +364,24 @@ static int create_playback_callback(struct pa_pdispatch *pd, uint32_t command, u s->state = STREAM_READY; assert(s->create_complete_callback); - s->create_complete_callback(s->context, s, s->create_complete_userdata); + s->create_complete_callback(s, 1, s->create_complete_userdata); return 0; fail: assert(s->create_complete_callback); - s->create_complete_callback(s->context, NULL, s->create_complete_userdata); + s->create_complete_callback(s, 0, s->create_complete_userdata); pa_stream_free(s); return ret; } -int pa_stream_new( +struct pa_stream* pa_stream_new( struct pa_context *c, enum pa_stream_direction dir, const char *dev, const char *name, const struct pa_sample_spec *ss, const struct pa_buffer_attr *attr, - void (*complete) (struct pa_context*c, struct pa_stream *s, void *userdata), + void (*complete) (struct pa_stream*s, int success, void *userdata), void *userdata) { struct pa_stream *s; diff --git a/src/polyp.h b/src/polyp.h index 171e3bdf..77a6966f 100644 --- a/src/polyp.h +++ b/src/polyp.h @@ -23,18 +23,18 @@ void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_cont int pa_context_is_dead(struct pa_context *c); int pa_context_is_ready(struct pa_context *c); -int pa_contect_errno(struct pa_context *c); +int pa_context_errno(struct pa_context *c); struct pa_stream; -int pa_stream_new( +struct pa_stream* pa_stream_new( struct pa_context *c, enum pa_stream_direction dir, const char *dev, const char *name, const struct pa_sample_spec *ss, const struct pa_buffer_attr *attr, - void (*complete) (struct pa_context*c, struct pa_stream *s, void *userdata), + void (*complete) (struct pa_stream*s, int success, void *userdata), void *userdata); void pa_stream_free(struct pa_stream *p); diff --git a/src/protocol-esound.c b/src/protocol-esound.c index 8198e72f..cd6448fc 100644 --- a/src/protocol-esound.c +++ b/src/protocol-esound.c @@ -13,6 +13,14 @@ #include "sink.h" #include "sample.h" +#include "authkey.h" + +#define COOKIE_FILE ".esd_auth" + +#define MEMBLOCKQ_LENGTH (10*1204) +#define MEMBLOCKQ_PREBUF (2*1024) +#define BUFSIZE (1024) + /* This is heavily based on esound's code */ struct connection { @@ -38,6 +46,7 @@ struct pa_protocol_esound { struct pa_idxset *connections; uint32_t sink_index; unsigned n_player; + uint8_t esd_key[ESD_KEY_LEN]; }; typedef struct proto_handler { @@ -46,11 +55,6 @@ typedef struct proto_handler { const char *description; } esd_proto_handler_info_t; -#define MEMBLOCKQ_LENGTH (10*1204) -#define MEMBLOCKQ_PREBUF (2*1024) - -#define BUFSIZE (1024) - static void sink_input_drop_cb(struct pa_sink_input *i, size_t length); static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); static void sink_input_kill_cb(struct pa_sink_input *i); @@ -162,7 +166,14 @@ static int esd_proto_connect(struct connection *c, const void *data, size_t leng int *ok; assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); - c->authorized = 1; + if (!c->authorized) { + if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { + fprintf(stderr, "protocol-esound.c: Kicked client with invalid authorization key.\n"); + return -1; + } + + c->authorized = 1; + } ekey = *(uint32_t*)(data+ESD_KEY_LEN); if (ekey == ESD_ENDIAN_KEY) @@ -583,17 +594,21 @@ struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa p = malloc(sizeof(struct pa_protocol_esound)); assert(p); - p->public = 1; + + if (pa_authkey_load_from_home(COOKIE_FILE, p->esd_key, sizeof(p->esd_key)) < 0) { + free(p); + return NULL; + } + + p->public = 0; p->server = server; + pa_socket_server_set_callback(p->server, on_connection, p); p->core = core; p->connections = pa_idxset_new(NULL, NULL); assert(p->connections); p->sink_index = PA_IDXSET_INVALID; - p->n_player = 0; - pa_socket_server_set_callback(p->server, on_connection, p); - return p; } diff --git a/src/protocol-native-spec.h b/src/protocol-native-spec.h index df11ae3c..07fc735b 100644 --- a/src/protocol-native-spec.h +++ b/src/protocol-native-spec.h @@ -11,6 +11,8 @@ enum { PA_COMMAND_DELETE_RECORD_STREAM, PA_COMMAND_EXIT, PA_COMMAND_REQUEST, + PA_COMMAND_AUTH, + PA_COMMAND_SET_NAME, PA_COMMAND_MAX }; @@ -23,7 +25,13 @@ enum { PA_ERROR_NOENTITY, PA_ERROR_CONNECTIONREFUSED, PA_ERROR_PROTOCOL, - PA_ERROR_TIMEOUT + PA_ERROR_TIMEOUT, + PA_ERROR_AUTHKEY, + PA_ERROR_INTERNAL, + PA_ERROR_MAX }; +#define PA_NATIVE_COOKIE_LENGTH 256 +#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie" + #endif diff --git a/src/protocol-native.c b/src/protocol-native.c index 8e4dcae1..9463a469 100644 --- a/src/protocol-native.c +++ b/src/protocol-native.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,6 +13,7 @@ #include "tagstruct.h" #include "pdispatch.h" #include "pstream-util.h" +#include "authkey.h" struct connection; struct pa_protocol_native; @@ -46,6 +48,7 @@ struct pa_protocol_native { struct pa_core *core; struct pa_socket_server *server; struct pa_idxset *connections; + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; }; static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); @@ -58,15 +61,21 @@ static void request_bytes(struct playback_stream*s); static int command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static int command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static int command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static int command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static int command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_ERROR] = { NULL }, + [PA_COMMAND_TIMEOUT] = { NULL }, [PA_COMMAND_REPLY] = { NULL }, [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { command_create_playback_stream }, [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { command_delete_playback_stream }, [PA_COMMAND_CREATE_RECORD_STREAM] = { NULL }, [PA_COMMAND_DELETE_RECORD_STREAM] = { NULL }, + [PA_COMMAND_AUTH] = { command_auth }, + [PA_COMMAND_REQUEST] = { NULL }, [PA_COMMAND_EXIT] = { command_exit }, + [PA_COMMAND_SET_NAME] = { command_set_name }, }; /* structure management */ @@ -294,6 +303,40 @@ static int command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, return 0; } +static int command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const void*cookie; + assert(c && t); + + if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 || + !pa_tagstruct_eof(t)) + return -1; + + if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) { + fprintf(stderr, "protocol-native.c: Denied access to client with invalid authorization key.\n"); + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return 0; + } + + c->authorized = 1; + pa_pstream_send_simple_ack(c->pstream, tag); + return 0; +} + +static int command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || + !pa_tagstruct_eof(t)) + return -1; + + pa_client_rename(c->client, name); + pa_pstream_send_simple_ack(c->pstream, tag); + return 0; +} + /*** pstream callbacks ***/ static int packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) { @@ -340,6 +383,13 @@ static void die_callback(struct pa_pstream *p, void *userdata) { fprintf(stderr, "protocol-native: connection died.\n"); } +/*** client callbacks ***/ + +static void client_kill_cb(struct pa_client *c) { + assert(c && c->userdata); + connection_free(c->userdata); +} + /*** socket server callbacks ***/ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { @@ -354,6 +404,8 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo assert(p->core); c->client = pa_client_new(p->core, "NATIVE", "Client"); assert(c->client); + c->client->kill = client_kill_cb; + c->client->userdata = c; c->pstream = pa_pstream_new(p->core->mainloop, io); assert(c->pstream); @@ -380,6 +432,11 @@ struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct p p = malloc(sizeof(struct pa_protocol_native)); assert(p); + if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, p->auth_cookie, sizeof(p->auth_cookie)) < 0) { + free(p); + return NULL; + } + p->public = 1; p->server = server; p->core = core; diff --git a/src/simple.c b/src/simple.c index a90d22bd..c1d1e96c 100644 --- a/src/simple.c +++ b/src/simple.c @@ -1,23 +1,19 @@ +#include +#include + #include "simple.h" #include "polyp.h" #include "mainloop.h" +#include "polyp-error.h" struct pa_simple { - struct mainloop *mainloop; + struct pa_mainloop *mainloop; struct pa_context *context; struct pa_stream *stream; - size_t requested; int dead; }; -static void playback_callback(struct pa_stream *p, size_t length, void *userdata) { - struct pa_stream *sp = userdata; - assert(p && length && sp); - - sp->requested = length; -} - struct pa_simple* pa_simple_new( const char *server, const char *name, @@ -25,9 +21,11 @@ struct pa_simple* pa_simple_new( const char *dev, const char *stream_name, const struct pa_sample_spec *ss, - const struct pa_buffer_attr *attr) { + const struct pa_buffer_attr *attr, + int *perror) { struct pa_simple *p; + int error = PA_ERROR_INTERNAL; assert(ss); p = malloc(sizeof(struct pa_simple)); @@ -36,39 +34,43 @@ struct pa_simple* pa_simple_new( p->stream = NULL; p->mainloop = pa_mainloop_new(); assert(p->mainloop); - p->requested = 0; p->dead = 0; if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) goto fail; - if (pa_context_connect(c, server, NULL, NULL) < 0) + if (pa_context_connect(p->context, server, NULL, NULL) < 0) { + error = pa_context_errno(p->context); goto fail; + } - while (!pa_context_is_ready(c)) { - if (pa_context_is_dead(c)) + while (!pa_context_is_ready(p->context)) { + if (pa_context_is_dead(p->context)) { + error = pa_context_errno(p->context); goto fail; + } - if (mainloop_iterate(p->mainloop) < 0) + if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) goto fail; } - if (!(p->stream = pa_stream_new(p->context, dir, sink, stream_name, ss, attr, NULL, NULL))) + if (!(p->stream = pa_stream_new(p->context, dir, dev, stream_name, ss, attr, NULL, NULL))) goto fail; - while (!pa_stream_is_ready(c)) { - if (pa_stream_is_dead(c)) + while (!pa_stream_is_ready(p->stream)) { + if (pa_stream_is_dead(p->stream)) { + error = pa_context_errno(p->context); goto fail; + } - if (mainloop_iterate(p->mainloop) < 0) + if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) goto fail; } - pa_stream_set_write_callback(p->stream, playback_callback, p); - return p; fail: + *perror = error; pa_simple_free(p); return NULL; } @@ -83,38 +85,40 @@ void pa_simple_free(struct pa_simple *s) { pa_context_free(s->context); if (s->mainloop) - mainloop_free(s->mainloop); + pa_mainloop_free(s->mainloop); free(s); } -int pa_simple_write(struct pa_simple *s, const void*data, size_t length) { - assert(s && data); +int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) { + assert(p && data); while (length > 0) { size_t l; - while (!s->requested) { - if (pa_context_is_dead(c)) + while (!(l = pa_stream_writable_size(p->stream))) { + if (pa_context_is_dead(p->context)) { + *perror = pa_context_errno(p->context); return -1; + } - if (mainloop_iterate(s->mainloop) < 0) + if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { + *perror = PA_ERROR_INTERNAL; return -1; + } } - l = length; - if (l > s->requested) - l = s->requested; + if (l > length) + l = length; - pa_stream_write(s->stream, data, l); + pa_stream_write(p->stream, data, l); data += l; length -= l; - s->requested = -l; } return 0; } -int pa_simple_read(struct pa_simple *s, const void*data, size_t length) { +int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *perror) { assert(0); } diff --git a/src/simple.h b/src/simple.h index 80693056..ed181201 100644 --- a/src/simple.h +++ b/src/simple.h @@ -15,11 +15,12 @@ struct pa_simple* pa_simple_new( const char *dev, const char *stream_name, const struct pa_sample_spec *ss, - const struct pa_buffer_attr *attr); + const struct pa_buffer_attr *attr, + int *error); void pa_simple_free(struct pa_simple *s); -int pa_simple_write(struct pa_simple *s, const void*data, size_t length); -int pa_simple_read(struct pa_simple *s, const void*data, size_t length); +int pa_simple_write(struct pa_simple *s, const void*data, size_t length, int *error); +int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *error); #endif diff --git a/src/tagstruct.c b/src/tagstruct.c index 67f52461..e57e755c 100644 --- a/src/tagstruct.c +++ b/src/tagstruct.c @@ -13,7 +13,8 @@ enum tags { TAG_S16 = 's', TAG_U8 = 'B', TAG_S8 = 'b', - TAG_SAMPLE_SPEC = 'a' + TAG_SAMPLE_SPEC = 'a', + TAG_ARBITRARY = 'x' }; struct pa_tagstruct { @@ -100,6 +101,18 @@ void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample t->length += 7; } + +void pa_tagstruct_put_arbitrary(struct pa_tagstruct *t, const void *p, size_t length) { + assert(t && p); + + extend(t, 5+length); + t->data[t->length] = TAG_ARBITRARY; + *((uint32_t*) (t->data+t->length+1)) = htonl(length); + if (length) + memcpy(t->data+t->length+5, p, length); + t->length += 5+length; +} + int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s) { int error = 0; size_t n; @@ -173,6 +186,22 @@ int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec * return 0; } +int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length) { + assert(t && p); + + if (t->rindex+5+length > t->length) + return -1; + + if (t->data[t->rindex] != TAG_ARBITRARY) + return -1; + + if (ntohl(*((uint32_t*) (t->data+t->rindex+1))) != length) + return -1; + + *p = t->data+t->rindex+5; + t->rindex += 5+length; + return 0; +} int pa_tagstruct_eof(struct pa_tagstruct*t) { assert(t); diff --git a/src/tagstruct.h b/src/tagstruct.h index be4e01fa..2c1ae587 100644 --- a/src/tagstruct.h +++ b/src/tagstruct.h @@ -16,11 +16,13 @@ void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s); void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i); void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c); void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss); +void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t length); int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s); int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i); int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c); int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss); +int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length); int pa_tagstruct_eof(struct pa_tagstruct*t); const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l); diff --git a/src/todo b/src/todo index 505eeac2..5b0d893f 100644 --- a/src/todo +++ b/src/todo @@ -3,17 +3,25 @@ sync() function more functions - simple library -- kill() routines in all modules + - config parser/cmdline + - move more stuff from module-oss[-dma] to liboss-util -- svn-id and license in every file - in module-oss: create source first, than sink +- rename files +- svn-id and license in every file +- documentation + + + -- post 0.1 - future cancellation - client-ui - clip cache - autoloading/autounloading +- ldap/rendezvous +- doxygen drivers: - libao diff --git a/src/util.c b/src/util.c index 5fff55e3..4ade681a 100644 --- a/src/util.c +++ b/src/util.c @@ -124,3 +124,87 @@ int pa_make_tcp_socket_low_delay(int fd) { return ret; } + +ssize_t pa_loop_read(int fd, void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = read(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data += r; + size -= r; + } + + return ret; +} + +ssize_t pa_loop_write(int fd, const void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = write(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data += r; + size -= r; + } + + return ret; +} + +int pa_unix_socket_is_stale(const char *fn) { + struct sockaddr_un sa; + int fd = -1, ret = -1; + + if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto finish; + } + + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { + if (errno == ECONNREFUSED) + ret = 1; + } else + ret = 0; + +finish: + if (fd >= 0) + close(fd); + + return ret; +} + +int pa_unix_socket_remove_stale(const char *fn) { + int r; + + if ((r = pa_unix_socket_is_stale(fn)) < 0) + return errno != ENOENT ? -1 : 0; + + if (!r) + return 0; + + /* Yes, here is a race condition. But who cares? */ + if (unlink(fn) < 0) + return -1; + + return 0; +} diff --git a/src/util.h b/src/util.h index b8759ad8..40095e01 100644 --- a/src/util.h +++ b/src/util.h @@ -12,4 +12,10 @@ int pa_make_secure_dir(const char* dir); int pa_make_socket_low_delay(int fd); int pa_make_tcp_socket_low_delay(int fd); +ssize_t pa_loop_read(int fd, void*data, size_t size); +ssize_t pa_loop_write(int fd, const void*data, size_t size); + +int pa_unix_socket_is_stale(const char *fn); +int pa_unix_socket_remove_stale(const char *fn); + #endif -- cgit