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>
50 #include <pulse/xmalloc.h>
51 #include <pulse/util.h>
53 #include <pulsecore/socket.h>
54 #include <pulsecore/socket-util.h>
55 #include <pulsecore/core-util.h>
56 #include <pulsecore/log.h>
57 #include <pulsecore/macro.h>
58 #include <pulsecore/core-error.h>
59 #include <pulsecore/refcnt.h>
60 #include <pulsecore/arpa-inet.h>
62 #include "socket-server.h"
64 struct pa_socket_server
{
68 char *tcpwrap_service
;
70 pa_socket_server_on_connection_cb_t on_connection
;
73 pa_io_event
*io_event
;
74 pa_mainloop_api
*mainloop
;
76 SOCKET_SERVER_GENERIC
,
83 static void callback(pa_mainloop_api
*mainloop
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
84 pa_socket_server
*s
= userdata
;
89 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
90 pa_assert(s
->mainloop
== mainloop
);
91 pa_assert(s
->io_event
== e
);
94 pa_assert(fd
== s
->fd
);
96 pa_socket_server_ref(s
);
98 if ((nfd
= pa_accept_cloexec(fd
, NULL
, NULL
)) < 0) {
99 pa_log("accept(): %s", pa_cstrerror(errno
));
103 if (!s
->on_connection
) {
110 if (s
->tcpwrap_service
) {
111 struct request_info req
;
113 request_init(&req
, RQ_DAEMON
, s
->tcpwrap_service
, RQ_FILE
, nfd
, NULL
);
115 if (!hosts_access(&req
)) {
116 pa_log_warn("TCP connection refused by tcpwrap.");
121 pa_log_info("TCP connection accepted by tcpwrap.");
125 /* There should be a check for socket type here */
126 if (s
->type
== SOCKET_SERVER_IPV4
)
127 pa_make_tcp_socket_low_delay(fd
);
129 pa_make_socket_low_delay(fd
);
131 pa_assert_se(io
= pa_iochannel_new(s
->mainloop
, nfd
, nfd
));
132 s
->on_connection(s
, io
, s
->userdata
);
135 pa_socket_server_unref(s
);
138 pa_socket_server
* pa_socket_server_new(pa_mainloop_api
*m
, int fd
) {
144 s
= pa_xnew0(pa_socket_server
, 1);
149 pa_assert_se(s
->io_event
= m
->io_new(m
, fd
, PA_IO_EVENT_INPUT
, callback
, s
));
151 s
->type
= SOCKET_SERVER_GENERIC
;
156 pa_socket_server
* pa_socket_server_ref(pa_socket_server
*s
) {
158 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
166 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
168 struct sockaddr_un sa
;
174 if ((fd
= pa_socket_cloexec(PF_UNIX
, SOCK_STREAM
, 0)) < 0) {
175 pa_log("socket(): %s", pa_cstrerror(errno
));
179 memset(&sa
, 0, sizeof(sa
));
180 sa
.sun_family
= AF_UNIX
;
181 pa_strlcpy(sa
.sun_path
, filename
, sizeof(sa
.sun_path
));
183 pa_make_socket_low_delay(fd
);
185 if (bind(fd
, (struct sockaddr
*) &sa
, (socklen_t
) SUN_LEN(&sa
)) < 0) {
186 pa_log("bind(): %s", pa_cstrerror(errno
));
190 /* Allow access from all clients. Sockets like this one should
191 * always be put inside a directory with proper access rights,
192 * because not all OS check the access rights on the socket
194 chmod(filename
, 0777);
196 if (listen(fd
, 5) < 0) {
197 pa_log("listen(): %s", pa_cstrerror(errno
));
201 pa_assert_se(s
= pa_socket_server_new(m
, fd
));
203 s
->filename
= pa_xstrdup(filename
);
204 s
->type
= SOCKET_SERVER_UNIX
;
215 #else /* HAVE_SYS_UN_H */
217 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
221 #endif /* HAVE_SYS_UN_H */
223 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
) {
224 pa_socket_server
*ss
;
226 struct sockaddr_in sa
;
232 if ((fd
= pa_socket_cloexec(PF_INET
, SOCK_STREAM
, 0)) < 0) {
233 pa_log("socket(PF_INET): %s", pa_cstrerror(errno
));
238 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
239 pa_log("setsockopt(): %s", pa_cstrerror(errno
));
242 pa_make_tcp_socket_low_delay(fd
);
244 memset(&sa
, 0, sizeof(sa
));
245 sa
.sin_family
= AF_INET
;
246 sa
.sin_port
= htons(port
);
247 sa
.sin_addr
.s_addr
= htonl(address
);
249 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
251 if (errno
== EADDRINUSE
&& fallback
) {
254 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) >= 0)
258 pa_log("bind(): %s", pa_cstrerror(errno
));
265 if (listen(fd
, 5) < 0) {
266 pa_log("listen(): %s", pa_cstrerror(errno
));
270 if ((ss
= pa_socket_server_new(m
, fd
))) {
271 ss
->type
= SOCKET_SERVER_IPV4
;
272 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
285 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
) {
286 pa_socket_server
*ss
;
288 struct sockaddr_in6 sa
;
294 if ((fd
= pa_socket_cloexec(PF_INET6
, SOCK_STREAM
, 0)) < 0) {
295 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno
));
301 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
, sizeof(on
)) < 0)
302 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno
));
307 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
308 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno
));
311 pa_make_tcp_socket_low_delay(fd
);
313 memset(&sa
, 0, sizeof(sa
));
314 sa
.sin6_family
= AF_INET6
;
315 sa
.sin6_port
= htons(port
);
316 memcpy(sa
.sin6_addr
.s6_addr
, address
, 16);
318 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
320 if (errno
== EADDRINUSE
&& fallback
) {
323 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) >= 0)
327 pa_log("bind(): %s", pa_cstrerror(errno
));
334 if (listen(fd
, 5) < 0) {
335 pa_log("listen(): %s", pa_cstrerror(errno
));
339 if ((ss
= pa_socket_server_new(m
, fd
))) {
340 ss
->type
= SOCKET_SERVER_IPV6
;
341 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
354 pa_socket_server
* pa_socket_server_new_ipv4_loopback(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
358 return pa_socket_server_new_ipv4(m
, INADDR_LOOPBACK
, port
, fallback
, tcpwrap_service
);
362 pa_socket_server
* pa_socket_server_new_ipv6_loopback(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
366 return pa_socket_server_new_ipv6(m
, in6addr_loopback
.s6_addr
, port
, fallback
, tcpwrap_service
);
370 pa_socket_server
* pa_socket_server_new_ipv4_any(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
374 return pa_socket_server_new_ipv4(m
, INADDR_ANY
, port
, fallback
, tcpwrap_service
);
378 pa_socket_server
* pa_socket_server_new_ipv6_any(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
382 return pa_socket_server_new_ipv6(m
, in6addr_any
.s6_addr
, port
, fallback
, tcpwrap_service
);
386 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
) {
393 if (inet_pton(AF_INET
, name
, &ipv4
) > 0)
394 return pa_socket_server_new_ipv4(m
, ntohl(ipv4
.s_addr
), port
, fallback
, tcpwrap_service
);
400 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
) {
401 struct in6_addr ipv6
;
407 if (inet_pton(AF_INET6
, name
, &ipv6
) > 0)
408 return pa_socket_server_new_ipv6(m
, ipv6
.s6_addr
, port
, fallback
, tcpwrap_service
);
414 static void socket_server_free(pa_socket_server
*s
) {
419 pa_xfree(s
->filename
);
424 pa_xfree(s
->tcpwrap_service
);
426 s
->mainloop
->io_free(s
->io_event
);
430 void pa_socket_server_unref(pa_socket_server
*s
) {
432 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
434 if (PA_REFCNT_DEC(s
) <= 0)
435 socket_server_free(s
);
438 void pa_socket_server_set_callback(pa_socket_server
*s
, pa_socket_server_on_connection_cb_t on_connection
, void *userdata
) {
440 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
442 s
->on_connection
= on_connection
;
443 s
->userdata
= userdata
;
446 char *pa_socket_server_get_address(pa_socket_server
*s
, char *c
, size_t l
) {
448 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
454 case SOCKET_SERVER_IPV6
: {
455 struct sockaddr_in6 sa
;
456 socklen_t sa_len
= sizeof(sa
);
458 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
459 pa_log("getsockname(): %s", pa_cstrerror(errno
));
463 if (memcmp(&in6addr_any
, &sa
.sin6_addr
, sizeof(in6addr_any
)) == 0) {
465 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
468 pa_snprintf(c
, l
, "tcp6:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin6_port
));
470 } else if (memcmp(&in6addr_loopback
, &sa
.sin6_addr
, sizeof(in6addr_loopback
)) == 0) {
473 if (!(id
= pa_machine_id()))
476 pa_snprintf(c
, l
, "{%s}tcp6:localhost:%u", id
, (unsigned) ntohs(sa
.sin6_port
));
479 char ip
[INET6_ADDRSTRLEN
];
481 if (!inet_ntop(AF_INET6
, &sa
.sin6_addr
, ip
, sizeof(ip
))) {
482 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
486 pa_snprintf(c
, l
, "tcp6:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin6_port
));
493 case SOCKET_SERVER_IPV4
: {
494 struct sockaddr_in sa
;
495 socklen_t sa_len
= sizeof(sa
);
497 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
498 pa_log("getsockname(): %s", pa_cstrerror(errno
));
502 if (sa
.sin_addr
.s_addr
== INADDR_ANY
) {
504 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
507 pa_snprintf(c
, l
, "tcp:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin_port
));
508 } else if (sa
.sin_addr
.s_addr
== INADDR_LOOPBACK
) {
511 if (!(id
= pa_machine_id()))
514 pa_snprintf(c
, l
, "{%s}tcp:localhost:%u", id
, (unsigned) ntohs(sa
.sin_port
));
517 char ip
[INET_ADDRSTRLEN
];
519 if (!inet_ntop(AF_INET
, &sa
.sin_addr
, ip
, sizeof(ip
))) {
520 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
524 pa_snprintf(c
, l
, "tcp:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin_port
));
530 case SOCKET_SERVER_UNIX
: {
536 if (!(id
= pa_machine_id()))
539 pa_snprintf(c
, l
, "{%s}unix:%s", id
, s
->filename
);