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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
38 #define SUN_LEN(ptr) \
39 ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
49 /* Solaris requires that the allow_severity and deny_severity variables be
50 * defined in the client program. */
53 int allow_severity
= LOG_INFO
;
54 int deny_severity
= LOG_WARNING
;
57 #endif /* HAVE_LIBWRAP */
59 #include <pulse/xmalloc.h>
60 #include <pulse/util.h>
62 #include <pulsecore/socket.h>
63 #include <pulsecore/socket-util.h>
64 #include <pulsecore/core-util.h>
65 #include <pulsecore/log.h>
66 #include <pulsecore/macro.h>
67 #include <pulsecore/core-error.h>
68 #include <pulsecore/refcnt.h>
69 #include <pulsecore/arpa-inet.h>
71 #include "socket-server.h"
73 struct pa_socket_server
{
77 char *tcpwrap_service
;
79 pa_socket_server_on_connection_cb_t on_connection
;
82 pa_io_event
*io_event
;
83 pa_mainloop_api
*mainloop
;
85 SOCKET_SERVER_GENERIC
,
92 static void callback(pa_mainloop_api
*mainloop
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
93 pa_socket_server
*s
= userdata
;
98 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
99 pa_assert(s
->mainloop
== mainloop
);
100 pa_assert(s
->io_event
== e
);
103 pa_assert(fd
== s
->fd
);
105 pa_socket_server_ref(s
);
107 if ((nfd
= pa_accept_cloexec(fd
, NULL
, NULL
)) < 0) {
108 pa_log("accept(): %s", pa_cstrerror(errno
));
112 if (!s
->on_connection
) {
119 if (s
->tcpwrap_service
) {
120 struct request_info req
;
122 request_init(&req
, RQ_DAEMON
, s
->tcpwrap_service
, RQ_FILE
, nfd
, NULL
);
124 if (!hosts_access(&req
)) {
125 pa_log_warn("TCP connection refused by tcpwrap.");
130 pa_log_info("TCP connection accepted by tcpwrap.");
134 /* There should be a check for socket type here */
135 if (s
->type
== SOCKET_SERVER_IPV4
)
136 pa_make_tcp_socket_low_delay(fd
);
138 pa_make_socket_low_delay(fd
);
140 pa_assert_se(io
= pa_iochannel_new(s
->mainloop
, nfd
, nfd
));
141 s
->on_connection(s
, io
, s
->userdata
);
144 pa_socket_server_unref(s
);
147 pa_socket_server
* pa_socket_server_new(pa_mainloop_api
*m
, int fd
) {
153 s
= pa_xnew0(pa_socket_server
, 1);
158 pa_assert_se(s
->io_event
= m
->io_new(m
, fd
, PA_IO_EVENT_INPUT
, callback
, s
));
160 s
->type
= SOCKET_SERVER_GENERIC
;
165 pa_socket_server
* pa_socket_server_ref(pa_socket_server
*s
) {
167 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
175 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
177 struct sockaddr_un sa
;
183 if ((fd
= pa_socket_cloexec(PF_UNIX
, SOCK_STREAM
, 0)) < 0) {
184 pa_log("socket(): %s", pa_cstrerror(errno
));
188 memset(&sa
, 0, sizeof(sa
));
189 sa
.sun_family
= AF_UNIX
;
190 pa_strlcpy(sa
.sun_path
, filename
, sizeof(sa
.sun_path
));
192 pa_make_socket_low_delay(fd
);
194 if (bind(fd
, (struct sockaddr
*) &sa
, (socklen_t
) SUN_LEN(&sa
)) < 0) {
195 pa_log("bind(): %s", pa_cstrerror(errno
));
199 /* Allow access from all clients. Sockets like this one should
200 * always be put inside a directory with proper access rights,
201 * because not all OS check the access rights on the socket
203 chmod(filename
, 0777);
205 if (listen(fd
, 5) < 0) {
206 pa_log("listen(): %s", pa_cstrerror(errno
));
210 pa_assert_se(s
= pa_socket_server_new(m
, fd
));
212 s
->filename
= pa_xstrdup(filename
);
213 s
->type
= SOCKET_SERVER_UNIX
;
224 #else /* HAVE_SYS_UN_H */
226 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
230 #endif /* HAVE_SYS_UN_H */
232 pa_socket_server
* pa_socket_server_new_ipv4(pa_mainloop_api
*m
, uint32_t address
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
233 pa_socket_server
*ss
;
235 struct sockaddr_in sa
;
241 if ((fd
= pa_socket_cloexec(PF_INET
, SOCK_STREAM
, 0)) < 0) {
242 pa_log("socket(PF_INET): %s", pa_cstrerror(errno
));
247 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, (const void *) &on
, sizeof(on
)) < 0)
248 pa_log("setsockopt(): %s", pa_cstrerror(errno
));
251 pa_make_tcp_socket_low_delay(fd
);
253 memset(&sa
, 0, sizeof(sa
));
254 sa
.sin_family
= AF_INET
;
255 sa
.sin_port
= htons(port
);
256 sa
.sin_addr
.s_addr
= htonl(address
);
258 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
260 if (errno
== EADDRINUSE
&& fallback
) {
263 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) >= 0)
267 pa_log("bind(): %s", pa_cstrerror(errno
));
274 if (listen(fd
, 5) < 0) {
275 pa_log("listen(): %s", pa_cstrerror(errno
));
279 if ((ss
= pa_socket_server_new(m
, fd
))) {
280 ss
->type
= SOCKET_SERVER_IPV4
;
281 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
294 pa_socket_server
* pa_socket_server_new_ipv6(pa_mainloop_api
*m
, const uint8_t address
[16], uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
295 pa_socket_server
*ss
;
297 struct sockaddr_in6 sa
;
303 if ((fd
= pa_socket_cloexec(PF_INET6
, SOCK_STREAM
, 0)) < 0) {
304 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno
));
310 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, (const void *) &on
, sizeof(on
)) < 0)
311 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno
));
316 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, (const void *) &on
, sizeof(on
)) < 0)
317 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno
));
320 pa_make_tcp_socket_low_delay(fd
);
322 memset(&sa
, 0, sizeof(sa
));
323 sa
.sin6_family
= AF_INET6
;
324 sa
.sin6_port
= htons(port
);
325 memcpy(sa
.sin6_addr
.s6_addr
, address
, 16);
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)
336 pa_log("bind(): %s", pa_cstrerror(errno
));
343 if (listen(fd
, 5) < 0) {
344 pa_log("listen(): %s", pa_cstrerror(errno
));
348 if ((ss
= pa_socket_server_new(m
, fd
))) {
349 ss
->type
= SOCKET_SERVER_IPV6
;
350 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
363 pa_socket_server
* pa_socket_server_new_ipv4_loopback(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
367 return pa_socket_server_new_ipv4(m
, INADDR_LOOPBACK
, port
, fallback
, tcpwrap_service
);
371 pa_socket_server
* pa_socket_server_new_ipv6_loopback(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
375 return pa_socket_server_new_ipv6(m
, in6addr_loopback
.s6_addr
, port
, fallback
, tcpwrap_service
);
379 pa_socket_server
* pa_socket_server_new_ipv4_any(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
383 return pa_socket_server_new_ipv4(m
, INADDR_ANY
, port
, fallback
, tcpwrap_service
);
387 pa_socket_server
* pa_socket_server_new_ipv6_any(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
391 return pa_socket_server_new_ipv6(m
, in6addr_any
.s6_addr
, port
, fallback
, tcpwrap_service
);
395 pa_socket_server
* pa_socket_server_new_ipv4_string(pa_mainloop_api
*m
, const char *name
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
402 if (inet_pton(AF_INET
, name
, &ipv4
) > 0)
403 return pa_socket_server_new_ipv4(m
, ntohl(ipv4
.s_addr
), port
, fallback
, tcpwrap_service
);
409 pa_socket_server
* pa_socket_server_new_ipv6_string(pa_mainloop_api
*m
, const char *name
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
410 struct in6_addr ipv6
;
416 if (inet_pton(AF_INET6
, name
, &ipv6
) > 0)
417 return pa_socket_server_new_ipv6(m
, ipv6
.s6_addr
, port
, fallback
, tcpwrap_service
);
423 static void socket_server_free(pa_socket_server
*s
) {
428 pa_xfree(s
->filename
);
433 pa_xfree(s
->tcpwrap_service
);
435 s
->mainloop
->io_free(s
->io_event
);
439 void pa_socket_server_unref(pa_socket_server
*s
) {
441 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
443 if (PA_REFCNT_DEC(s
) <= 0)
444 socket_server_free(s
);
447 void pa_socket_server_set_callback(pa_socket_server
*s
, pa_socket_server_on_connection_cb_t on_connection
, void *userdata
) {
449 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
451 s
->on_connection
= on_connection
;
452 s
->userdata
= userdata
;
455 char *pa_socket_server_get_address(pa_socket_server
*s
, char *c
, size_t l
) {
457 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
463 case SOCKET_SERVER_IPV6
: {
464 struct sockaddr_in6 sa
;
465 socklen_t sa_len
= sizeof(sa
);
467 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
468 pa_log("getsockname(): %s", pa_cstrerror(errno
));
472 if (memcmp(&in6addr_any
, &sa
.sin6_addr
, sizeof(in6addr_any
)) == 0) {
474 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
477 pa_snprintf(c
, l
, "tcp6:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin6_port
));
479 } else if (memcmp(&in6addr_loopback
, &sa
.sin6_addr
, sizeof(in6addr_loopback
)) == 0) {
482 if (!(id
= pa_machine_id()))
485 pa_snprintf(c
, l
, "{%s}tcp6:localhost:%u", id
, (unsigned) ntohs(sa
.sin6_port
));
488 char ip
[INET6_ADDRSTRLEN
];
490 if (!inet_ntop(AF_INET6
, &sa
.sin6_addr
, ip
, sizeof(ip
))) {
491 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
495 pa_snprintf(c
, l
, "tcp6:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin6_port
));
502 case SOCKET_SERVER_IPV4
: {
503 struct sockaddr_in sa
;
504 socklen_t sa_len
= sizeof(sa
);
506 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
507 pa_log("getsockname(): %s", pa_cstrerror(errno
));
511 if (sa
.sin_addr
.s_addr
== INADDR_ANY
) {
513 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
516 pa_snprintf(c
, l
, "tcp:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin_port
));
517 } else if (sa
.sin_addr
.s_addr
== INADDR_LOOPBACK
) {
520 if (!(id
= pa_machine_id()))
523 pa_snprintf(c
, l
, "{%s}tcp:localhost:%u", id
, (unsigned) ntohs(sa
.sin_port
));
526 char ip
[INET_ADDRSTRLEN
];
528 if (!inet_ntop(AF_INET
, &sa
.sin_addr
, ip
, sizeof(ip
))) {
529 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
533 pa_snprintf(c
, l
, "tcp:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin_port
));
539 case SOCKET_SERVER_UNIX
: {
545 if (!(id
= pa_machine_id()))
548 pa_snprintf(c
, l
, "{%s}unix:%s", id
, s
->filename
);