2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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/>.
28 #include <sys/types.h>
36 #define SUN_LEN(ptr) \
37 ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
47 /* Solaris requires that the allow_severity and deny_severity variables be
48 * defined in the client program. */
51 int allow_severity = LOG_INFO;
52 int deny_severity = LOG_WARNING;
55 #endif /* HAVE_LIBWRAP */
57 #ifdef HAVE_SYSTEMD_DAEMON
58 #include <systemd/sd-daemon.h>
67 #include <pulse/xmalloc.h>
68 #include <pulse/util.h>
70 #include <pulsecore/socket.h>
71 #include <pulsecore/socket-util.h>
72 #include <pulsecore/core-util.h>
73 #include <pulsecore/log.h>
74 #include <pulsecore/macro.h>
75 #include <pulsecore/core-error.h>
76 #include <pulsecore/refcnt.h>
77 #include <pulsecore/arpa-inet.h>
79 #include "socket-server.h"
81 struct pa_socket_server {
86 char *tcpwrap_service;
88 pa_socket_server_on_connection_cb_t on_connection;
91 pa_io_event *io_event;
92 pa_mainloop_api *mainloop;
100 static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
101 pa_socket_server *s = userdata;
106 pa_assert(PA_REFCNT_VALUE(s) >= 1);
107 pa_assert(s->mainloop == mainloop);
108 pa_assert(s->io_event == e);
111 pa_assert(fd == s->fd);
113 pa_socket_server_ref(s);
115 if ((nfd = pa_accept_cloexec(fd, NULL, NULL)) < 0) {
116 pa_log("accept(): %s", pa_cstrerror(errno));
120 if (!s->on_connection) {
127 if (s->tcpwrap_service) {
128 struct request_info req;
130 request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
132 if (!hosts_access(&req)) {
133 pa_log_warn("TCP connection refused by tcpwrap.");
138 pa_log_info("TCP connection accepted by tcpwrap.");
142 /* There should be a check for socket type here */
143 if (s->type == SOCKET_SERVER_IPV4)
144 pa_make_tcp_socket_low_delay(nfd);
146 pa_make_socket_low_delay(nfd);
148 pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd));
149 s->on_connection(s, io, s->userdata);
152 pa_socket_server_unref(s);
155 static pa_socket_server* socket_server_new(pa_mainloop_api *m, int fd) {
161 s = pa_xnew0(pa_socket_server, 1);
166 pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));
171 pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
173 pa_assert(PA_REFCNT_VALUE(s) >= 1);
181 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
183 bool activated = false;
184 struct sockaddr_un sa;
190 #ifdef HAVE_SYSTEMD_DAEMON
192 int n = sd_listen_fds(0);
194 for (int i = 0; i < n; ++i) {
195 if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, filename, 0) > 0) {
196 fd = SD_LISTEN_FDS_START + i;
198 pa_log_info("Found socket activation socket for '%s' \\o/", filename);
207 if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
208 pa_log("socket(PF_UNIX): %s", pa_cstrerror(errno));
212 memset(&sa, 0, sizeof(sa));
213 sa.sun_family = AF_UNIX;
214 pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
216 pa_make_socket_low_delay(fd);
218 if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {
219 pa_log("bind(): %s", pa_cstrerror(errno));
223 /* Allow access from all clients. Sockets like this one should
224 * always be put inside a directory with proper access rights,
225 * because not all OS check the access rights on the socket
227 chmod(filename, 0777);
230 /* https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings */
231 /* https://docs.microsoft.com/en-us/windows/win32/secauthz/modifying-the-acls-of-an-object-in-c-- */
232 /* https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora */
233 PSECURITY_DESCRIPTOR sd;
234 if (ConvertStringSecurityDescriptorToSecurityDescriptorA(
236 "(A;;FRFW;;;WD)", /* allow all users to read/write */
237 SDDL_REVISION_1, &sd, NULL
240 BOOL acl_present, acl_default;
241 if (GetSecurityDescriptorDacl(sd, &acl_present, &acl, &acl_default)) {
242 if (SetNamedSecurityInfo(filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, acl, NULL) != ERROR_SUCCESS) {
243 pa_log_warn("Failed to set DACL for socket: failed to apply DACL: error %lu.", GetLastError());
247 pa_log_warn("Failed to set DACL for socket: failed to get security descriptor DACL: error %lu.", GetLastError());
250 pa_log_warn("Failed to set DACL for socket: failed to parse security descriptor: error %lu.", GetLastError());
254 if (listen(fd, 5) < 0) {
255 pa_log("listen(): %s", pa_cstrerror(errno));
260 pa_assert_se(s = socket_server_new(m, fd));
262 s->filename = pa_xstrdup(filename);
263 s->type = SOCKET_SERVER_UNIX;
264 s->activated = activated;
275 #else /* HAVE_SYS_UN_H */
277 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
281 #endif /* HAVE_SYS_UN_H */
283 pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, bool fallback, const char *tcpwrap_service) {
284 pa_socket_server *ss;
286 bool activated = false;
287 struct sockaddr_in sa;
293 #ifdef HAVE_SYSTEMD_DAEMON
295 int n = sd_listen_fds(0);
297 for (int i = 0; i < n; ++i) {
298 if (sd_is_socket_inet(SD_LISTEN_FDS_START + i, AF_INET, SOCK_STREAM, 1, port) > 0) {
299 fd = SD_LISTEN_FDS_START + i;
301 pa_log_info("Found socket activation socket for ipv4 in port '%d' \\o/", port);
310 if ((fd = pa_socket_cloexec(PF_INET, SOCK_STREAM, 0)) < 0) {
311 pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
316 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
317 pa_log("setsockopt(): %s", pa_cstrerror(errno));
320 pa_make_tcp_socket_low_delay(fd);
322 memset(&sa, 0, sizeof(sa));
323 sa.sin_family = AF_INET;
324 sa.sin_port = htons(port);
325 sa.sin_addr.s_addr = htonl(address);
327 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
329 if (errno == EADDRINUSE && fallback) {
332 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
333 pa_log("bind(): %s", pa_cstrerror(errno));
337 pa_log("bind(): %s", pa_cstrerror(errno));
342 if (listen(fd, 5) < 0) {
343 pa_log("listen(): %s", pa_cstrerror(errno));
348 pa_assert_se(ss = socket_server_new(m, fd));
350 ss->type = SOCKET_SERVER_IPV4;
351 ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
352 ss->activated = activated;
364 pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, bool fallback, const char *tcpwrap_service) {
365 pa_socket_server *ss;
367 bool activated = false;
368 struct sockaddr_in6 sa;
374 #ifdef HAVE_SYSTEMD_DAEMON
376 int n = sd_listen_fds(0);
378 for (int i = 0; i < n; ++i) {
379 if (sd_is_socket_inet(SD_LISTEN_FDS_START + i, AF_INET6, SOCK_STREAM, 1, port) > 0) {
380 fd = SD_LISTEN_FDS_START + i;
382 pa_log_info("Found socket activation socket for ipv6 in port '%d' \\o/", port);
391 if ((fd = pa_socket_cloexec(PF_INET6, SOCK_STREAM, 0)) < 0) {
392 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
398 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &on, sizeof(on)) < 0)
399 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
404 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
405 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
408 pa_make_tcp_socket_low_delay(fd);
410 memset(&sa, 0, sizeof(sa));
411 sa.sin6_family = AF_INET6;
412 sa.sin6_port = htons(port);
413 memcpy(sa.sin6_addr.s6_addr, address, 16);
415 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
417 if (errno == EADDRINUSE && fallback) {
420 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
421 pa_log("bind(): %s", pa_cstrerror(errno));
425 pa_log("bind(): %s", pa_cstrerror(errno));
430 if (listen(fd, 5) < 0) {
431 pa_log("listen(): %s", pa_cstrerror(errno));
436 pa_assert_se(ss = socket_server_new(m, fd));
438 ss->type = SOCKET_SERVER_IPV6;
439 ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
440 ss->activated = activated;
452 pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
456 return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, fallback, tcpwrap_service);
460 pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
464 return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, fallback, tcpwrap_service);
468 pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
472 return pa_socket_server_new_ipv4(m, INADDR_ANY, port, fallback, tcpwrap_service);
476 pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
480 return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, fallback, tcpwrap_service);
484 pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service) {
491 if (inet_pton(AF_INET, name, &ipv4) > 0)
492 return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, fallback, tcpwrap_service);
498 pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service) {
499 struct in6_addr ipv6;
505 if (inet_pton(AF_INET6, name, &ipv6) > 0)
506 return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, fallback, tcpwrap_service);
512 static void socket_server_free(pa_socket_server*s) {
515 if (!s->activated && s->filename)
517 pa_xfree(s->filename);
521 pa_xfree(s->tcpwrap_service);
523 s->mainloop->io_free(s->io_event);
527 void pa_socket_server_unref(pa_socket_server *s) {
529 pa_assert(PA_REFCNT_VALUE(s) >= 1);
531 if (PA_REFCNT_DEC(s) <= 0)
532 socket_server_free(s);
535 void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
537 pa_assert(PA_REFCNT_VALUE(s) >= 1);
539 s->on_connection = on_connection;
540 s->userdata = userdata;
543 char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
545 pa_assert(PA_REFCNT_VALUE(s) >= 1);
551 case SOCKET_SERVER_IPV6: {
552 struct sockaddr_in6 sa;
553 socklen_t sa_len = sizeof(sa);
555 if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
556 pa_log("getsockname(): %s", pa_cstrerror(errno));
560 if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
562 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
565 pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
567 } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
570 if (!(id = pa_machine_id()))
573 pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port));
576 char ip[INET6_ADDRSTRLEN];
578 if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
579 pa_log("inet_ntop(): %s", pa_cstrerror(errno));
583 pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
590 case SOCKET_SERVER_IPV4: {
591 struct sockaddr_in sa;
592 socklen_t sa_len = sizeof(sa);
594 if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
595 pa_log("getsockname(): %s", pa_cstrerror(errno));
599 if (sa.sin_addr.s_addr == INADDR_ANY) {
601 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
604 pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
605 } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
608 if (!(id = pa_machine_id()))
611 pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port));
614 char ip[INET_ADDRSTRLEN];
616 if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
617 pa_log("inet_ntop(): %s", pa_cstrerror(errno));
621 pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
627 case SOCKET_SERVER_UNIX: {
633 if (!(id = pa_machine_id()))
636 pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);