From 99e0779b51ccf4482dd35e78fe2f80744633004e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 15 Dec 2004 01:02:50 +0000 Subject: * Publish server info in mDNS in addition to sinks/sources * Split off address parser * Add port= argument to module-zeroconf-publish git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@324 fefdeb5f-60dc-0310-8127-8f9354f1896f --- polyp/Makefile.am | 10 +++- polyp/module-zeroconf-publish.c | 61 ++++++++++++++++++++-- polyp/parseaddr.c | 112 ++++++++++++++++++++++++++++++++++++++++ polyp/parseaddr.h | 42 +++++++++++++++ polyp/polyplib-browser.c | 1 - polyp/socket-client.c | 108 ++++++++------------------------------ polyp/util.c | 2 +- 7 files changed, 242 insertions(+), 94 deletions(-) create mode 100644 polyp/parseaddr.c create mode 100644 polyp/parseaddr.h (limited to 'polyp') diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 79accfe6..0c491ce5 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -87,6 +87,7 @@ modlib_LTLIBRARIES= \ libiochannel.la \ libsocket-server.la \ libsocket-client.la \ + libparseaddr.la \ libpacket.la \ libpstream.la \ liboss-util.la \ @@ -228,7 +229,8 @@ polypaudio_SOURCES = idxset.c idxset.h \ polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) polypaudio_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) -polypaudio_LDFLAGS= $(AM_LDFLAGS) -export-dynamic -dlopen force #-static $(foreach f,$(modlib_LTLIBRARIES),-dlpreopen $(f)) +polypaudio_LDFLAGS= $(AM_LDFLAGS) -export-dynamic -dlopen force +#q-static $(foreach f,$(modlib_LTLIBRARIES),-dlpreopen $(f)) libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h libprotocol_simple_la_LDFLAGS = -avoid-version @@ -240,7 +242,10 @@ libsocket_server_la_LIBADD = $(AM_LIBADD) libiochannel.la libsocket-util.la $(LI libsocket_client_la_SOURCES = socket-client.c socket-client.h libsocket_client_la_LDFLAGS = -avoid-version -libsocket_client_la_LIBADD = $(AM_LIBADD) libiochannel.la libsocket-util.la +libsocket_client_la_LIBADD = $(AM_LIBADD) libiochannel.la libsocket-util.la libparseaddr.la + +libparseaddr_la_SOURCES = parseaddr.c parseaddr.h +libparseaddr_la_LDFLAGS = -avoid-version libpstream_la_SOURCES = pstream.c pstream.h libpstream_la_LDFLAGS = -avoid-version @@ -434,6 +439,7 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \ util.c util.h \ memblock.c memblock.h \ socket-client.c socket-client.h \ + parseaddr.c parseaddr.h \ packet.c packet.h \ queue.c queue.h \ dynarray.c dynarray.h \ diff --git a/polyp/module-zeroconf-publish.c b/polyp/module-zeroconf-publish.c index c88ac766..511ceb47 100644 --- a/polyp/module-zeroconf-publish.c +++ b/polyp/module-zeroconf-publish.c @@ -41,15 +41,22 @@ #include "subscribe.h" #include "dynarray.h" #include "endianmacros.h" +#include "modargs.h" PA_MODULE_AUTHOR("Lennart Poettering") PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher") PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("port=") #define SERVICE_NAME_SINK "_polypaudio-sink._tcp" #define SERVICE_NAME_SOURCE "_polypaudio-source._tcp" #define SERVICE_NAME_SERVER "_polypaudio-server._tcp" +static const char* const valid_modargs[] = { + "port", + NULL +}; + struct service { sw_discovery_oid oid; char *name; @@ -74,6 +81,9 @@ struct userdata { struct pa_hashmap *services; struct pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray; struct pa_subscription *subscription; + + uint16_t port; + sw_discovery_oid server_oid; }; static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) { @@ -127,7 +137,7 @@ static int publish_service(struct userdata *u, struct service *s) { s->published = 0; } - snprintf(t, sizeof(t), "%s@%s", s->name, pa_get_host_name(hn, sizeof(hn))); + snprintf(t, sizeof(t), "Networked Audio device %s on %s", s->name, pa_get_host_name(hn, sizeof(hn))); if (sw_text_record_init(&txt) != SW_OKAY) { pa_log(__FILE__": sw_text_record_init() failed\n"); @@ -160,7 +170,7 @@ static int publish_service(struct userdata *u, struct service *s) { if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, s->loaded.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, - NULL, NULL, PA_NATIVE_DEFAULT_PORT, sw_text_record_bytes(txt), sw_text_record_len(txt), + NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), publish_reply, s, &s->oid) != SW_OKAY) { pa_log(__FILE__": failed to register sink on zeroconf.\n"); goto finish; @@ -171,7 +181,7 @@ static int publish_service(struct userdata *u, struct service *s) { if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, s->autoload.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, - NULL, NULL, PA_NATIVE_DEFAULT_PORT, sw_text_record_bytes(txt), sw_text_record_len(txt), + NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), publish_reply, s, &s->oid) != SW_OKAY) { pa_log(__FILE__": failed to register sink on zeroconf.\n"); goto finish; @@ -375,13 +385,28 @@ fail: int pa__init(struct pa_core *c, struct pa_module*m) { struct userdata *u; - uint32_t index; + uint32_t index, port = PA_NATIVE_DEFAULT_PORT; struct pa_sink *sink; struct pa_source *source; struct pa_autoload_entry *autoload; + struct pa_modargs *ma = NULL; + char t[256], hn[256]; + int free_txt = 0; + sw_text_record txt; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) { + pa_log(__FILE__": invalid port specified.\n"); + goto fail; + } m->userdata = u = pa_xmalloc(sizeof(struct userdata)); u->core = c; + u->port = (uint16_t) port; if (!(u->howl_wrapper = pa_howl_wrapper_get(c))) goto fail; @@ -409,10 +434,38 @@ int pa__init(struct pa_core *c, struct pa_module*m) { if (publish_autoload(u, autoload) < 0) goto fail; + snprintf(t, sizeof(t), "Networked Audio on %s", pa_get_host_name(hn, sizeof(hn))); + + if (sw_text_record_init(&txt) != SW_OKAY) { + pa_log(__FILE__": sw_text_record_init() failed\n"); + goto fail; + } + free_txt = 1; + + txt_record_server_data(u->core, txt); + + if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, + SERVICE_NAME_SERVER, + NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), + publish_reply, u, &u->server_oid) != SW_OKAY) { + pa_log(__FILE__": failed to register server on zeroconf.\n"); + goto fail; + } + + sw_text_record_fina(txt); + pa_modargs_free(ma); + return 0; fail: pa__done(c, m); + + if (ma) + pa_modargs_free(ma); + + if (free_txt) + sw_text_record_fina(txt); + return -1; } diff --git a/polyp/parseaddr.c b/polyp/parseaddr.c new file mode 100644 index 00000000..05ed508b --- /dev/null +++ b/polyp/parseaddr.c @@ -0,0 +1,112 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "xmalloc.h" +#include "util.h" +#include "parseaddr.h" + +/* Parse addresses in one of the following forms: + * HOSTNAME + * HOSTNAME:PORT + * [HOSTNAME] + * [HOSTNAME]:PORT + * + * Return a newly allocated string of the hostname and fill in *ret_port if specified */ + +static char *parse_host(const char *s, uint16_t *ret_port) { + assert(s && ret_port); + if (*s == '[') { + char *e; + if (!(e = strchr(s+1, ']'))) + return NULL; + + if (e[1] == ':') + *ret_port = atoi(e+2); + else if (e[1] != 0) + return NULL; + + return pa_xstrndup(s+1, e-s-1); + } else { + char *e; + + if (!(e = strrchr(s, ':'))) + return pa_xstrdup(s); + + *ret_port = atoi(e+1); + return pa_xstrndup(s, e-s); + } +} + +int pa_parse_address(const char *name, struct pa_parsed_address *ret_p) { + const char *p; + assert(name && ret_p); + memset(ret_p, 0, sizeof(struct pa_parsed_address)); + ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO; + + if (*name == '{') { + char hn[256], *pfx; + /* The URL starts with a host specification for detecting local connections */ + + if (!pa_get_host_name(hn, sizeof(hn))) + return -1; + + pfx = pa_sprintf_malloc("{%s}", hn); + if (!pa_startswith(name, pfx)) { + pa_xfree(pfx); + /* Not local */ + return -1; + } + + p = name + strlen(pfx); + pa_xfree(pfx); + } else + p = name; + + if (*p == '/') + ret_p->type = PA_PARSED_ADDRESS_UNIX; + else if (pa_startswith(p, "unix:")) { + ret_p->type = PA_PARSED_ADDRESS_UNIX; + p += sizeof("unix:")-1; + } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) { + ret_p->type = PA_PARSED_ADDRESS_TCP4; + p += sizeof("tcp:")-1; + } else if (pa_startswith(p, "tcp6:")) { + ret_p->type = PA_PARSED_ADDRESS_TCP6; + p += sizeof("tcp6:")-1; + } + + if (ret_p->type == PA_PARSED_ADDRESS_UNIX) + ret_p->path_or_host = pa_xstrdup(p); + else + if (!(ret_p->path_or_host = parse_host(p, &ret_p->port))) + return -1; + + + return 0; +} diff --git a/polyp/parseaddr.h b/polyp/parseaddr.h new file mode 100644 index 00000000..5ddc0351 --- /dev/null +++ b/polyp/parseaddr.h @@ -0,0 +1,42 @@ +#ifndef fooparseaddrhfoo +#define fooparseaddrhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +enum pa_parsed_address_type { + PA_PARSED_ADDRESS_UNIX, + PA_PARSED_ADDRESS_TCP4, + PA_PARSED_ADDRESS_TCP6, + PA_PARSED_ADDRESS_TCP_AUTO +}; + +struct pa_parsed_address { + enum pa_parsed_address_type type; + char *path_or_host; + uint16_t port; +}; + +int pa_parse_address(const char *a, struct pa_parsed_address *ret_p); + +#endif diff --git a/polyp/polyplib-browser.c b/polyp/polyplib-browser.c index a1bd3fb7..7e56e2ce 100644 --- a/polyp/polyplib-browser.c +++ b/polyp/polyplib-browser.c @@ -221,7 +221,6 @@ static sw_result browse_reply( switch (status) { case SW_DISCOVERY_BROWSE_ADD_SERVICE: { sw_discovery_oid oid; - fprintf(stderr, "debug: new service: %s\n", name); if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY) pa_log("sw_discovery_resolve() failed\n"); diff --git a/polyp/socket-client.c b/polyp/socket-client.c index c58c7bd4..0581e553 100644 --- a/polyp/socket-client.c +++ b/polyp/socket-client.c @@ -39,6 +39,7 @@ #include "util.h" #include "xmalloc.h" #include "log.h" +#include "parseaddr.h" struct pa_socket_client { int ref; @@ -254,121 +255,56 @@ struct pa_socket_client* pa_socket_client_new_ipv6(struct pa_mainloop_api *m, ui return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); } -/* Parse addresses in one of the following forms: - * HOSTNAME - * HOSTNAME:PORT - * [HOSTNAME] - * [HOSTNAME]:PORT - * - * Return a newly allocated string of the hostname and fill in *port if specified */ - -static char *parse_address(const char *s, uint16_t *port) { - assert(s && port); - if (*s == '[') { - char *e; - if (!(e = strchr(s+1, ']'))) - return NULL; - - if (e[1] == ':') - *port = atoi(e+2); - else if (e[1] != 0) - return NULL; - - return pa_xstrndup(s+1, e-s-1); - } else { - char *e; - - if (!(e = strrchr(s, ':'))) - return pa_xstrdup(s); - - *port = atoi(e+1); - return pa_xstrndup(s, e-s); - } -} - struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, const char*name, uint16_t default_port) { - const char *p; struct pa_socket_client *c = NULL; - enum { KIND_UNIX, KIND_TCP_AUTO, KIND_TCP4, KIND_TCP6 } kind = KIND_TCP_AUTO; + struct pa_parsed_address a; assert(m && name); - if (*name == '{') { - char hn[256], *pfx; - /* The URL starts with a host specification for detecting local connections */ - - if (!pa_get_host_name(hn, sizeof(hn))) - return NULL; - - pfx = pa_sprintf_malloc("{%s}", hn); - if (!pa_startswith(name, pfx)) - /* Not local */ - return NULL; - - p = name + strlen(pfx); - } else - p = name; - - if (*p == '/') - kind = KIND_UNIX; - else if (pa_startswith(p, "unix:")) { - kind = KIND_UNIX; - p += sizeof("unix:")-1; - } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) { - kind = KIND_TCP4; - p += sizeof("tcp:")-1; - } else if (pa_startswith(p, "tcp6:")) { - kind = KIND_TCP6; - p += sizeof("tcp6:")-1; - } + if (pa_parse_address(name, &a) < 0) + return NULL; - switch (kind) { - case KIND_UNIX: - return pa_socket_client_new_unix(m, p); + switch (a.type) { + case PA_PARSED_ADDRESS_UNIX: + c = pa_socket_client_new_unix(m, a.path_or_host); + break; - case KIND_TCP_AUTO: /* Fallthrough */ - case KIND_TCP4: - case KIND_TCP6: { - uint16_t port = default_port; - char *h; + case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */ + case PA_PARSED_ADDRESS_TCP6: /* Fallthrough */ + case PA_PARSED_ADDRESS_TCP_AUTO:{ int ret; struct addrinfo hints, *res; - if (!(h = parse_address(p, &port))) - return NULL; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = kind == KIND_TCP4 ? AF_INET : (kind == KIND_TCP6 ? AF_INET6 : AF_UNSPEC); + hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? AF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? AF_INET6 : AF_UNSPEC); - ret = getaddrinfo(h, NULL, &hints, &res); - pa_xfree(h); + ret = getaddrinfo(a.path_or_host, NULL, &hints, &res); if (ret < 0 || !res || !res->ai_addr) - return NULL; + goto finish; if (res->ai_family == AF_INET) { if (res->ai_addrlen != sizeof(struct sockaddr_in)) - return NULL; + goto finish; assert(res->ai_addr->sa_family == res->ai_family); - ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(port); + ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(a.port); } else if (res->ai_family == AF_INET6) { if (res->ai_addrlen != sizeof(struct sockaddr_in6)) - return NULL; + goto finish; assert(res->ai_addr->sa_family == res->ai_family); - ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(port); + ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(a.port); } else - return NULL; + goto finish; c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); - return c; } } - /* Should never be reached */ - assert(0); - return NULL; +finish: + pa_xfree(a.path_or_host); + return c; } diff --git a/polyp/util.c b/polyp/util.c index ff1aebf3..ee3fa87d 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -880,7 +880,7 @@ int pa_atoi(const char *s, int32_t *ret_i) { l = strtol(s, &x, 0); - if (x || *x) + if (!x || *x) return -1; *ret_i = (int32_t) l; -- cgit