1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <errno.h>
26#include <stdio.h>
27#include <unistd.h>
28
29#ifdef HAVE_NETINET_IN_H
30#include <netinet/in.h>
31#endif
32
33#include <pulse/xmalloc.h>
34
35#include <pulsecore/core-error.h>
36#include <pulsecore/module.h>
37#include <pulsecore/socket.h>
38#include <pulsecore/socket-server.h>
39#include <pulsecore/socket-util.h>
40#include <pulsecore/core-util.h>
41#include <pulsecore/modargs.h>
42#include <pulsecore/log.h>
43#include <pulsecore/native-common.h>
44#include <pulsecore/creds.h>
45#include <pulsecore/arpa-inet.h>
46
47#ifdef USE_TCP_SOCKETS
48#define SOCKET_DESCRIPTION "(TCP sockets)"
49#define SOCKET_USAGE "port=<TCP port number> listen=<address to listen on>"
50#else
51#define SOCKET_DESCRIPTION "(UNIX sockets)"
52#define SOCKET_USAGE "socket=<path to UNIX socket>"
53#endif
54
55#if defined(USE_PROTOCOL_SIMPLE)
56#  include <pulsecore/protocol-simple.h>
57#  define TCPWRAP_SERVICE "pulseaudio-simple"
58#  define IPV4_PORT 4711
59#  define UNIX_SOCKET "simple"
60#  define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
61
62  PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION);
63  PA_MODULE_USAGE("rate=<sample rate> "
64                  "format=<sample format> "
65                  "channels=<number of channels> "
66                  "sink=<sink to connect to> "
67                  "source=<source to connect to> "
68                  "playback=<enable playback?> "
69                  "record=<enable record?> "
70                  SOCKET_USAGE);
71#elif defined(USE_PROTOCOL_CLI)
72#  include <pulsecore/protocol-cli.h>
73#  define TCPWRAP_SERVICE "pulseaudio-cli"
74#  define IPV4_PORT 4712
75#  define UNIX_SOCKET "cli"
76#  define MODULE_ARGUMENTS
77
78  PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION);
79  PA_MODULE_USAGE(SOCKET_USAGE);
80#elif defined(USE_PROTOCOL_HTTP)
81#  include <pulsecore/protocol-http.h>
82#  define TCPWRAP_SERVICE "pulseaudio-http"
83#  define IPV4_PORT 4714
84#  define UNIX_SOCKET "http"
85#  define MODULE_ARGUMENTS
86
87  PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION);
88  PA_MODULE_USAGE(SOCKET_USAGE);
89#elif defined(USE_PROTOCOL_NATIVE)
90#  include <pulsecore/protocol-native.h>
91#  define TCPWRAP_SERVICE "pulseaudio-native"
92#  define IPV4_PORT PA_NATIVE_DEFAULT_PORT
93#  define UNIX_SOCKET PA_NATIVE_DEFAULT_UNIX_SOCKET
94#  define MODULE_ARGUMENTS_COMMON "cookie", "auth-cookie", "auth-cookie-enabled", "auth-anonymous",
95
96#  if defined(HAVE_CREDS) && !defined(USE_TCP_SOCKETS)
97#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-group", "auth-group-enable", "srbchannel",
98#    define AUTH_USAGE "auth-group=<system group to allow access> auth-group-enable=<enable auth by UNIX group?> "
99#    define SRB_USAGE "srbchannel=<enable shared ringbuffer communication channel?> "
100#  elif defined(USE_TCP_SOCKETS)
101#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
102#    define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
103#    define SRB_USAGE
104#  else
105#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
106#    define AUTH_USAGE
107#    define SRB_USAGE
108#    endif
109
110  PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION);
111  PA_MODULE_USAGE("auth-anonymous=<don't check for cookies?> "
112                  "auth-cookie=<path to cookie file> "
113                  "auth-cookie-enabled=<enable cookie authentication?> "
114                  AUTH_USAGE
115                  SRB_USAGE
116                  SOCKET_USAGE);
117#elif defined(USE_PROTOCOL_ESOUND)
118#  include <pulsecore/protocol-esound.h>
119#  include <pulsecore/esound.h>
120#  define TCPWRAP_SERVICE "esound"
121#  define IPV4_PORT ESD_DEFAULT_PORT
122#  define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie", "auth-cookie", "auth-cookie-enabled",
123
124#  if defined(USE_TCP_SOCKETS)
125#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
126#    define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
127#  else
128#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
129#    define AUTH_USAGE
130#  endif
131
132  PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION);
133  PA_MODULE_USAGE("sink=<sink to connect to> "
134                  "source=<source to connect to> "
135                  "auth-anonymous=<don't verify cookies?> "
136                  "auth-cookie=<path to cookie file> "
137                  "auth-cookie-enabled=<enable cookie authentication?> "
138                  AUTH_USAGE
139                  SOCKET_USAGE);
140#else
141#  error "Broken build system"
142#endif
143
144PA_MODULE_LOAD_ONCE(false);
145PA_MODULE_AUTHOR("Lennart Poettering");
146PA_MODULE_VERSION(PACKAGE_VERSION);
147
148static const char* const valid_modargs[] = {
149    MODULE_ARGUMENTS
150#if defined(USE_TCP_SOCKETS)
151    "port",
152    "listen",
153#else
154    "socket",
155#endif
156    NULL
157};
158
159struct userdata {
160    pa_module *module;
161
162#if defined(USE_PROTOCOL_SIMPLE)
163    pa_simple_protocol *simple_protocol;
164    pa_simple_options *simple_options;
165#elif defined(USE_PROTOCOL_CLI)
166    pa_cli_protocol *cli_protocol;
167#elif defined(USE_PROTOCOL_HTTP)
168    pa_http_protocol *http_protocol;
169#elif defined(USE_PROTOCOL_NATIVE)
170    pa_native_protocol *native_protocol;
171    pa_native_options *native_options;
172#else
173    pa_esound_protocol *esound_protocol;
174    pa_esound_options *esound_options;
175#endif
176
177#if defined(USE_TCP_SOCKETS)
178    pa_socket_server *socket_server_ipv4;
179#  ifdef HAVE_IPV6
180    pa_socket_server *socket_server_ipv6;
181#  endif
182#else
183    pa_socket_server *socket_server_unix;
184    char *socket_path;
185#endif
186};
187
188static void socket_server_on_connection_cb(pa_socket_server*s, pa_iochannel *io, void *userdata) {
189    struct userdata *u = userdata;
190
191    pa_assert(s);
192    pa_assert(io);
193    pa_assert(u);
194
195#if defined(USE_PROTOCOL_SIMPLE)
196    pa_simple_protocol_connect(u->simple_protocol, io, u->simple_options);
197#elif defined(USE_PROTOCOL_CLI)
198    pa_cli_protocol_connect(u->cli_protocol, io, u->module);
199#elif defined(USE_PROTOCOL_HTTP)
200    pa_http_protocol_connect(u->http_protocol, io, u->module);
201#elif defined(USE_PROTOCOL_NATIVE)
202    pa_native_protocol_connect(u->native_protocol, io, u->native_options);
203#else
204    pa_esound_protocol_connect(u->esound_protocol, io, u->esound_options);
205#endif
206}
207
208int pa__init(pa_module*m) {
209    pa_modargs *ma = NULL;
210    struct userdata *u = NULL;
211
212#if defined(USE_TCP_SOCKETS)
213    uint32_t port = IPV4_PORT;
214    bool port_fallback = true;
215    const char *listen_on;
216#else
217    int r;
218#endif
219
220#if defined(USE_PROTOCOL_NATIVE) || defined(USE_PROTOCOL_HTTP)
221    char t[256];
222#endif
223
224    pa_assert(m);
225
226    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
227        pa_log("Failed to parse module arguments");
228        goto fail;
229    }
230
231    m->userdata = u = pa_xnew0(struct userdata, 1);
232    u->module = m;
233
234#if defined(USE_PROTOCOL_SIMPLE)
235    u->simple_protocol = pa_simple_protocol_get(m->core);
236
237    u->simple_options = pa_simple_options_new();
238    if (pa_simple_options_parse(u->simple_options, m->core, ma) < 0)
239        goto fail;
240    u->simple_options->module = m;
241#elif defined(USE_PROTOCOL_CLI)
242    u->cli_protocol = pa_cli_protocol_get(m->core);
243#elif defined(USE_PROTOCOL_HTTP)
244    u->http_protocol = pa_http_protocol_get(m->core);
245#elif defined(USE_PROTOCOL_NATIVE)
246    u->native_protocol = pa_native_protocol_get(m->core);
247
248    u->native_options = pa_native_options_new();
249    if (pa_native_options_parse(u->native_options, m->core, ma) < 0)
250        goto fail;
251    u->native_options->module = m;
252#else
253    u->esound_protocol = pa_esound_protocol_get(m->core);
254
255    u->esound_options = pa_esound_options_new();
256    if (pa_esound_options_parse(u->esound_options, m->core, ma) < 0)
257        goto fail;
258    u->esound_options->module = m;
259#endif
260
261#if defined(USE_TCP_SOCKETS)
262
263    if (pa_in_system_mode() || pa_modargs_get_value(ma, "port", NULL))
264        port_fallback = false;
265
266    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
267        pa_log("port= expects a numerical argument between 1 and 65535.");
268        goto fail;
269    }
270
271    listen_on = pa_modargs_get_value(ma, "listen", NULL);
272
273    if (listen_on) {
274#  ifdef HAVE_IPV6
275        u->socket_server_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
276#  endif
277        u->socket_server_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
278    } else {
279#  ifdef HAVE_IPV6
280        u->socket_server_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
281#  endif
282        u->socket_server_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
283    }
284
285#  ifdef HAVE_IPV6
286    if (!u->socket_server_ipv4 && !u->socket_server_ipv6)
287#  else
288    if (!u->socket_server_ipv4)
289#  endif
290        goto fail;
291
292    if (u->socket_server_ipv4)
293        pa_socket_server_set_callback(u->socket_server_ipv4, socket_server_on_connection_cb, u);
294#  ifdef HAVE_IPV6
295    if (u->socket_server_ipv6)
296        pa_socket_server_set_callback(u->socket_server_ipv6, socket_server_on_connection_cb, u);
297#  endif
298
299#else
300
301#  if defined(USE_PROTOCOL_ESOUND)
302
303    /* Windows doesn't support getuid(), so we ignore the per-user Esound socket compile flag.
304     * Moreover, Esound Unix sockets haven't been supported on Windows historically. */
305#    if defined(USE_PER_USER_ESOUND_SOCKET) && !defined(OS_IS_WIN32)
306    u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
307#    else
308    u->socket_path = pa_xstrdup("/tmp/.esd/socket");
309#    endif
310
311    /* This socket doesn't reside in our own runtime dir but in
312     * /tmp/.esd/, hence we have to create the dir first */
313
314    if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755U : 0700U, (uid_t)-1, (gid_t)-1, false) < 0) {
315        pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));
316        goto fail;
317    }
318
319#  else
320    if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
321        pa_log("Failed to generate socket path.");
322        goto fail;
323    }
324#  endif
325
326    if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
327        pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
328        goto fail;
329    } else if (r > 0)
330        pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
331
332    if (!(u->socket_server_unix = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
333        goto fail;
334
335    pa_socket_server_set_callback(u->socket_server_unix, socket_server_on_connection_cb, u);
336
337#endif
338
339#if defined(USE_PROTOCOL_NATIVE)
340#  if defined(USE_TCP_SOCKETS)
341    if (u->socket_server_ipv4)
342        if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
343            pa_native_protocol_add_server_string(u->native_protocol, t);
344
345#    ifdef HAVE_IPV6
346    if (u->socket_server_ipv6)
347        if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
348            pa_native_protocol_add_server_string(u->native_protocol, t);
349#    endif
350#  else
351    if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
352        pa_native_protocol_add_server_string(u->native_protocol, t);
353
354#  endif
355#endif
356
357#if defined(USE_PROTOCOL_HTTP)
358#if defined(USE_TCP_SOCKETS)
359    if (u->socket_server_ipv4)
360        if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
361            pa_http_protocol_add_server_string(u->http_protocol, t);
362
363#ifdef HAVE_IPV6
364    if (u->socket_server_ipv6)
365        if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
366            pa_http_protocol_add_server_string(u->http_protocol, t);
367#endif /* HAVE_IPV6 */
368#else /* USE_TCP_SOCKETS */
369    if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
370        pa_http_protocol_add_server_string(u->http_protocol, t);
371
372#endif /* USE_TCP_SOCKETS */
373#endif /* USE_PROTOCOL_HTTP */
374
375    if (ma)
376        pa_modargs_free(ma);
377
378    return 0;
379
380fail:
381
382    if (ma)
383        pa_modargs_free(ma);
384
385    pa__done(m);
386
387    return -1;
388}
389
390void pa__done(pa_module*m) {
391    struct userdata *u;
392
393    pa_assert(m);
394
395    if (!(u = m->userdata))
396        return;
397
398#if defined(USE_PROTOCOL_SIMPLE)
399    if (u->simple_protocol) {
400        pa_simple_protocol_disconnect(u->simple_protocol, u->module);
401        pa_simple_protocol_unref(u->simple_protocol);
402    }
403    if (u->simple_options)
404        pa_simple_options_unref(u->simple_options);
405#elif defined(USE_PROTOCOL_CLI)
406    if (u->cli_protocol) {
407        pa_cli_protocol_disconnect(u->cli_protocol, u->module);
408        pa_cli_protocol_unref(u->cli_protocol);
409    }
410#elif defined(USE_PROTOCOL_HTTP)
411    if (u->http_protocol) {
412        char t[256];
413
414#if defined(USE_TCP_SOCKETS)
415        if (u->socket_server_ipv4)
416            if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
417                pa_http_protocol_remove_server_string(u->http_protocol, t);
418
419#ifdef HAVE_IPV6
420        if (u->socket_server_ipv6)
421            if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
422                pa_http_protocol_remove_server_string(u->http_protocol, t);
423#endif /* HAVE_IPV6 */
424#else /* USE_TCP_SOCKETS */
425        if (u->socket_server_unix)
426            if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
427                pa_http_protocol_remove_server_string(u->http_protocol, t);
428#endif /* USE_PROTOCOL_HTTP */
429
430        pa_http_protocol_disconnect(u->http_protocol, u->module);
431        pa_http_protocol_unref(u->http_protocol);
432    }
433#elif defined(USE_PROTOCOL_NATIVE)
434    if (u->native_protocol) {
435
436        char t[256];
437
438#  if defined(USE_TCP_SOCKETS)
439        if (u->socket_server_ipv4)
440            if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
441                pa_native_protocol_remove_server_string(u->native_protocol, t);
442
443#    ifdef HAVE_IPV6
444        if (u->socket_server_ipv6)
445            if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
446                pa_native_protocol_remove_server_string(u->native_protocol, t);
447#    endif
448#  else
449        if (u->socket_server_unix)
450            if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
451                pa_native_protocol_remove_server_string(u->native_protocol, t);
452#  endif
453
454        pa_native_protocol_disconnect(u->native_protocol, u->module);
455        pa_native_protocol_unref(u->native_protocol);
456    }
457    if (u->native_options)
458        pa_native_options_unref(u->native_options);
459#else
460    if (u->esound_protocol) {
461        pa_esound_protocol_disconnect(u->esound_protocol, u->module);
462        pa_esound_protocol_unref(u->esound_protocol);
463    }
464    if (u->esound_options)
465        pa_esound_options_unref(u->esound_options);
466#endif
467
468#if defined(USE_TCP_SOCKETS)
469    if (u->socket_server_ipv4)
470        pa_socket_server_unref(u->socket_server_ipv4);
471#  ifdef HAVE_IPV6
472    if (u->socket_server_ipv6)
473        pa_socket_server_unref(u->socket_server_ipv6);
474#  endif
475#else
476    if (u->socket_server_unix)
477        pa_socket_server_unref(u->socket_server_unix);
478
479# if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
480    if (u->socket_path) {
481        char *p = pa_parent_dir(u->socket_path);
482        rmdir(p);
483        pa_xfree(p);
484    }
485# endif
486
487    pa_xfree(u->socket_path);
488#endif
489
490    pa_xfree(u);
491}
492