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>
35 #ifdef HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
41 #define SUN_LEN(ptr) \
42 ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
45 #ifdef HAVE_ARPA_INET_H
46 #include <arpa/inet.h>
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
56 #ifndef HAVE_INET_NTOP
57 #include "inet_ntop.h"
60 #ifndef HAVE_INET_PTON
61 #include "inet_pton.h"
66 #include <pulse/xmalloc.h>
67 #include <pulse/util.h>
69 #include <pulsecore/socket-util.h>
70 #include <pulsecore/core-util.h>
71 #include <pulsecore/log.h>
72 #include <pulsecore/macro.h>
73 #include <pulsecore/core-error.h>
74 #include <pulsecore/refcnt.h>
76 #include "socket-server.h"
78 struct pa_socket_server
{
82 char *tcpwrap_service
;
84 pa_socket_server_on_connection_cb_t on_connection
;
87 pa_io_event
*io_event
;
88 pa_mainloop_api
*mainloop
;
90 SOCKET_SERVER_GENERIC
,
97 static void callback(pa_mainloop_api
*mainloop
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
98 pa_socket_server
*s
= userdata
;
103 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
104 pa_assert(s
->mainloop
== mainloop
);
105 pa_assert(s
->io_event
== e
);
108 pa_assert(fd
== s
->fd
);
110 pa_socket_server_ref(s
);
112 if ((nfd
= pa_accept_cloexec(fd
, NULL
, NULL
)) < 0) {
113 pa_log("accept(): %s", pa_cstrerror(errno
));
117 if (!s
->on_connection
) {
124 if (s
->tcpwrap_service
) {
125 struct request_info req
;
127 request_init(&req
, RQ_DAEMON
, s
->tcpwrap_service
, RQ_FILE
, nfd
, NULL
);
129 if (!hosts_access(&req
)) {
130 pa_log_warn("TCP connection refused by tcpwrap.");
135 pa_log_info("TCP connection accepted by tcpwrap.");
139 /* There should be a check for socket type here */
140 if (s
->type
== SOCKET_SERVER_IPV4
)
141 pa_make_tcp_socket_low_delay(fd
);
143 pa_make_socket_low_delay(fd
);
145 pa_assert_se(io
= pa_iochannel_new(s
->mainloop
, nfd
, nfd
));
146 s
->on_connection(s
, io
, s
->userdata
);
149 pa_socket_server_unref(s
);
152 pa_socket_server
* pa_socket_server_new(pa_mainloop_api
*m
, int fd
) {
158 s
= pa_xnew0(pa_socket_server
, 1);
163 pa_assert_se(s
->io_event
= m
->io_new(m
, fd
, PA_IO_EVENT_INPUT
, callback
, s
));
165 s
->type
= SOCKET_SERVER_GENERIC
;
170 pa_socket_server
* pa_socket_server_ref(pa_socket_server
*s
) {
172 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
180 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
182 struct sockaddr_un sa
;
188 if ((fd
= pa_socket_cloexec(PF_UNIX
, SOCK_STREAM
, 0)) < 0) {
189 pa_log("socket(): %s", pa_cstrerror(errno
));
193 memset(&sa
, 0, sizeof(sa
));
194 sa
.sun_family
= AF_UNIX
;
195 pa_strlcpy(sa
.sun_path
, filename
, sizeof(sa
.sun_path
));
197 pa_make_socket_low_delay(fd
);
199 if (bind(fd
, (struct sockaddr
*) &sa
, (socklen_t
) SUN_LEN(&sa
)) < 0) {
200 pa_log("bind(): %s", pa_cstrerror(errno
));
204 /* Allow access from all clients. Sockets like this one should
205 * always be put inside a directory with proper access rights,
206 * because not all OS check the access rights on the socket
208 chmod(filename
, 0777);
210 if (listen(fd
, 5) < 0) {
211 pa_log("listen(): %s", pa_cstrerror(errno
));
215 pa_assert_se(s
= pa_socket_server_new(m
, fd
));
217 s
->filename
= pa_xstrdup(filename
);
218 s
->type
= SOCKET_SERVER_UNIX
;
229 #else /* HAVE_SYS_UN_H */
231 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
235 #endif /* HAVE_SYS_UN_H */
237 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
) {
238 pa_socket_server
*ss
;
240 struct sockaddr_in sa
;
246 if ((fd
= pa_socket_cloexec(PF_INET
, SOCK_STREAM
, 0)) < 0) {
247 pa_log("socket(PF_INET): %s", pa_cstrerror(errno
));
252 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
253 pa_log("setsockopt(): %s", pa_cstrerror(errno
));
256 pa_make_tcp_socket_low_delay(fd
);
258 memset(&sa
, 0, sizeof(sa
));
259 sa
.sin_family
= AF_INET
;
260 sa
.sin_port
= htons(port
);
261 sa
.sin_addr
.s_addr
= htonl(address
);
263 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
265 if (errno
== EADDRINUSE
&& fallback
) {
268 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) >= 0)
272 pa_log("bind(): %s", pa_cstrerror(errno
));
279 if (listen(fd
, 5) < 0) {
280 pa_log("listen(): %s", pa_cstrerror(errno
));
284 if ((ss
= pa_socket_server_new(m
, fd
))) {
285 ss
->type
= SOCKET_SERVER_IPV4
;
286 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
299 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
) {
300 pa_socket_server
*ss
;
302 struct sockaddr_in6 sa
;
308 if ((fd
= pa_socket_cloexec(PF_INET6
, SOCK_STREAM
, 0)) < 0) {
309 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno
));
315 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
, sizeof(on
)) < 0)
316 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno
));
321 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
322 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno
));
325 pa_make_tcp_socket_low_delay(fd
);
327 memset(&sa
, 0, sizeof(sa
));
328 sa
.sin6_family
= AF_INET6
;
329 sa
.sin6_port
= htons(port
);
330 memcpy(sa
.sin6_addr
.s6_addr
, address
, 16);
332 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
334 if (errno
== EADDRINUSE
&& fallback
) {
337 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) >= 0)
341 pa_log("bind(): %s", pa_cstrerror(errno
));
348 if (listen(fd
, 5) < 0) {
349 pa_log("listen(): %s", pa_cstrerror(errno
));
353 if ((ss
= pa_socket_server_new(m
, fd
))) {
354 ss
->type
= SOCKET_SERVER_IPV6
;
355 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
368 pa_socket_server
* pa_socket_server_new_ipv4_loopback(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
372 return pa_socket_server_new_ipv4(m
, INADDR_LOOPBACK
, port
, fallback
, tcpwrap_service
);
376 pa_socket_server
* pa_socket_server_new_ipv6_loopback(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
380 return pa_socket_server_new_ipv6(m
, in6addr_loopback
.s6_addr
, port
, fallback
, tcpwrap_service
);
384 pa_socket_server
* pa_socket_server_new_ipv4_any(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
388 return pa_socket_server_new_ipv4(m
, INADDR_ANY
, port
, fallback
, tcpwrap_service
);
392 pa_socket_server
* pa_socket_server_new_ipv6_any(pa_mainloop_api
*m
, uint16_t port
, pa_bool_t fallback
, const char *tcpwrap_service
) {
396 return pa_socket_server_new_ipv6(m
, in6addr_any
.s6_addr
, port
, fallback
, tcpwrap_service
);
400 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
) {
407 if (inet_pton(AF_INET
, name
, &ipv4
) > 0)
408 return pa_socket_server_new_ipv4(m
, ntohl(ipv4
.s_addr
), port
, fallback
, tcpwrap_service
);
414 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
) {
415 struct in6_addr ipv6
;
421 if (inet_pton(AF_INET6
, name
, &ipv6
) > 0)
422 return pa_socket_server_new_ipv6(m
, ipv6
.s6_addr
, port
, fallback
, tcpwrap_service
);
428 static void socket_server_free(pa_socket_server
*s
) {
433 pa_xfree(s
->filename
);
438 pa_xfree(s
->tcpwrap_service
);
440 s
->mainloop
->io_free(s
->io_event
);
444 void pa_socket_server_unref(pa_socket_server
*s
) {
446 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
448 if (PA_REFCNT_DEC(s
) <= 0)
449 socket_server_free(s
);
452 void pa_socket_server_set_callback(pa_socket_server
*s
, pa_socket_server_on_connection_cb_t on_connection
, void *userdata
) {
454 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
456 s
->on_connection
= on_connection
;
457 s
->userdata
= userdata
;
460 char *pa_socket_server_get_address(pa_socket_server
*s
, char *c
, size_t l
) {
462 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
468 case SOCKET_SERVER_IPV6
: {
469 struct sockaddr_in6 sa
;
470 socklen_t sa_len
= sizeof(sa
);
472 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
473 pa_log("getsockname(): %s", pa_cstrerror(errno
));
477 if (memcmp(&in6addr_any
, &sa
.sin6_addr
, sizeof(in6addr_any
)) == 0) {
479 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
482 pa_snprintf(c
, l
, "tcp6:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin6_port
));
484 } else if (memcmp(&in6addr_loopback
, &sa
.sin6_addr
, sizeof(in6addr_loopback
)) == 0) {
487 if (!(id
= pa_machine_id()))
490 pa_snprintf(c
, l
, "{%s}tcp6:localhost:%u", id
, (unsigned) ntohs(sa
.sin6_port
));
493 char ip
[INET6_ADDRSTRLEN
];
495 if (!inet_ntop(AF_INET6
, &sa
.sin6_addr
, ip
, sizeof(ip
))) {
496 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
500 pa_snprintf(c
, l
, "tcp6:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin6_port
));
507 case SOCKET_SERVER_IPV4
: {
508 struct sockaddr_in sa
;
509 socklen_t sa_len
= sizeof(sa
);
511 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
512 pa_log("getsockname(): %s", pa_cstrerror(errno
));
516 if (sa
.sin_addr
.s_addr
== INADDR_ANY
) {
518 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
521 pa_snprintf(c
, l
, "tcp:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin_port
));
522 } else if (sa
.sin_addr
.s_addr
== INADDR_LOOPBACK
) {
525 if (!(id
= pa_machine_id()))
528 pa_snprintf(c
, l
, "{%s}tcp:localhost:%u", id
, (unsigned) ntohs(sa
.sin_port
));
531 char ip
[INET_ADDRSTRLEN
];
533 if (!inet_ntop(AF_INET
, &sa
.sin_addr
, ip
, sizeof(ip
))) {
534 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
538 pa_snprintf(c
, l
, "tcp:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin_port
));
544 case SOCKET_SERVER_UNIX
: {
550 if (!(id
= pa_machine_id()))
553 pa_snprintf(c
, l
, "{%s}unix:%s", id
, s
->filename
);