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
;
89 enum { SOCKET_SERVER_GENERIC
, SOCKET_SERVER_IPV4
, SOCKET_SERVER_UNIX
, SOCKET_SERVER_IPV6
} type
;
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_xnew(pa_socket_server
, 1);
157 s
->on_connection
= NULL
;
159 s
->tcpwrap_service
= NULL
;
162 pa_assert_se(s
->io_event
= m
->io_new(m
, fd
, PA_IO_EVENT_INPUT
, callback
, s
));
164 s
->type
= SOCKET_SERVER_GENERIC
;
169 pa_socket_server
* pa_socket_server_ref(pa_socket_server
*s
) {
171 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
179 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
181 struct sockaddr_un sa
;
187 if ((fd
= pa_socket_cloexec(PF_UNIX
, SOCK_STREAM
, 0)) < 0) {
188 pa_log("socket(): %s", pa_cstrerror(errno
));
192 memset(&sa
, 0, sizeof(sa
));
193 sa
.sun_family
= AF_UNIX
;
194 pa_strlcpy(sa
.sun_path
, filename
, sizeof(sa
.sun_path
));
196 pa_make_socket_low_delay(fd
);
198 if (bind(fd
, (struct sockaddr
*) &sa
, (socklen_t
) SUN_LEN(&sa
)) < 0) {
199 pa_log("bind(): %s", pa_cstrerror(errno
));
203 /* Allow access from all clients. Sockets like this one should
204 * always be put inside a directory with proper access rights,
205 * because not all OS check the access rights on the socket
207 chmod(filename
, 0777);
209 if (listen(fd
, 5) < 0) {
210 pa_log("listen(): %s", pa_cstrerror(errno
));
214 pa_assert_se(s
= pa_socket_server_new(m
, fd
));
216 s
->filename
= pa_xstrdup(filename
);
217 s
->type
= SOCKET_SERVER_UNIX
;
228 #else /* HAVE_SYS_UN_H */
230 pa_socket_server
* pa_socket_server_new_unix(pa_mainloop_api
*m
, const char *filename
) {
234 #endif /* HAVE_SYS_UN_H */
236 pa_socket_server
* pa_socket_server_new_ipv4(pa_mainloop_api
*m
, uint32_t address
, uint16_t port
, const char *tcpwrap_service
) {
237 pa_socket_server
*ss
;
239 struct sockaddr_in sa
;
245 if ((fd
= pa_socket_cloexec(PF_INET
, SOCK_STREAM
, 0)) < 0) {
246 pa_log("socket(PF_INET): %s", pa_cstrerror(errno
));
251 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
252 pa_log("setsockopt(): %s", pa_cstrerror(errno
));
255 pa_make_tcp_socket_low_delay(fd
);
257 memset(&sa
, 0, sizeof(sa
));
258 sa
.sin_family
= AF_INET
;
259 sa
.sin_port
= htons(port
);
260 sa
.sin_addr
.s_addr
= htonl(address
);
262 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
263 pa_log("bind(): %s", pa_cstrerror(errno
));
267 if (listen(fd
, 5) < 0) {
268 pa_log("listen(): %s", pa_cstrerror(errno
));
272 if ((ss
= pa_socket_server_new(m
, fd
))) {
273 ss
->type
= SOCKET_SERVER_IPV4
;
274 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
287 pa_socket_server
* pa_socket_server_new_ipv6(pa_mainloop_api
*m
, const uint8_t address
[16], uint16_t port
, const char *tcpwrap_service
) {
288 pa_socket_server
*ss
;
290 struct sockaddr_in6 sa
;
296 if ((fd
= pa_socket_cloexec(PF_INET6
, SOCK_STREAM
, 0)) < 0) {
297 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno
));
303 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
, sizeof(on
)) < 0)
304 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno
));
309 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
310 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno
));
313 pa_make_tcp_socket_low_delay(fd
);
315 memset(&sa
, 0, sizeof(sa
));
316 sa
.sin6_family
= AF_INET6
;
317 sa
.sin6_port
= htons(port
);
318 memcpy(sa
.sin6_addr
.s6_addr
, address
, 16);
320 if (bind(fd
, (struct sockaddr
*) &sa
, sizeof(sa
)) < 0) {
321 pa_log("bind(): %s", pa_cstrerror(errno
));
325 if (listen(fd
, 5) < 0) {
326 pa_log("listen(): %s", pa_cstrerror(errno
));
330 if ((ss
= pa_socket_server_new(m
, fd
))) {
331 ss
->type
= SOCKET_SERVER_IPV6
;
332 ss
->tcpwrap_service
= pa_xstrdup(tcpwrap_service
);
345 pa_socket_server
* pa_socket_server_new_ipv4_loopback(pa_mainloop_api
*m
, uint16_t port
, const char *tcpwrap_service
) {
349 return pa_socket_server_new_ipv4(m
, INADDR_LOOPBACK
, port
, tcpwrap_service
);
353 pa_socket_server
* pa_socket_server_new_ipv6_loopback(pa_mainloop_api
*m
, uint16_t port
, const char *tcpwrap_service
) {
357 return pa_socket_server_new_ipv6(m
, in6addr_loopback
.s6_addr
, port
, tcpwrap_service
);
361 pa_socket_server
* pa_socket_server_new_ipv4_any(pa_mainloop_api
*m
, uint16_t port
, const char *tcpwrap_service
) {
365 return pa_socket_server_new_ipv4(m
, INADDR_ANY
, port
, tcpwrap_service
);
369 pa_socket_server
* pa_socket_server_new_ipv6_any(pa_mainloop_api
*m
, uint16_t port
, const char *tcpwrap_service
) {
373 return pa_socket_server_new_ipv6(m
, in6addr_any
.s6_addr
, port
, tcpwrap_service
);
377 pa_socket_server
* pa_socket_server_new_ipv4_string(pa_mainloop_api
*m
, const char *name
, uint16_t port
, const char *tcpwrap_service
) {
384 if (inet_pton(AF_INET
, name
, &ipv4
) > 0)
385 return pa_socket_server_new_ipv4(m
, ntohl(ipv4
.s_addr
), port
, tcpwrap_service
);
391 pa_socket_server
* pa_socket_server_new_ipv6_string(pa_mainloop_api
*m
, const char *name
, uint16_t port
, const char *tcpwrap_service
) {
392 struct in6_addr ipv6
;
398 if (inet_pton(AF_INET6
, name
, &ipv6
) > 0)
399 return pa_socket_server_new_ipv6(m
, ipv6
.s6_addr
, port
, tcpwrap_service
);
405 static void socket_server_free(pa_socket_server
*s
) {
410 pa_xfree(s
->filename
);
415 pa_xfree(s
->tcpwrap_service
);
417 s
->mainloop
->io_free(s
->io_event
);
421 void pa_socket_server_unref(pa_socket_server
*s
) {
423 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
425 if (PA_REFCNT_DEC(s
) <= 0)
426 socket_server_free(s
);
429 void pa_socket_server_set_callback(pa_socket_server
*s
, pa_socket_server_on_connection_cb_t on_connection
, void *userdata
) {
431 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
433 s
->on_connection
= on_connection
;
434 s
->userdata
= userdata
;
437 char *pa_socket_server_get_address(pa_socket_server
*s
, char *c
, size_t l
) {
439 pa_assert(PA_REFCNT_VALUE(s
) >= 1);
445 case SOCKET_SERVER_IPV6
: {
446 struct sockaddr_in6 sa
;
447 socklen_t sa_len
= sizeof(sa
);
449 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
450 pa_log("getsockname(): %s", pa_cstrerror(errno
));
454 if (memcmp(&in6addr_any
, &sa
.sin6_addr
, sizeof(in6addr_any
)) == 0) {
456 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
459 pa_snprintf(c
, l
, "tcp6:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin6_port
));
461 } else if (memcmp(&in6addr_loopback
, &sa
.sin6_addr
, sizeof(in6addr_loopback
)) == 0) {
464 if (!(id
= pa_machine_id()))
467 pa_snprintf(c
, l
, "{%s}tcp6:localhost:%u", id
, (unsigned) ntohs(sa
.sin6_port
));
470 char ip
[INET6_ADDRSTRLEN
];
472 if (!inet_ntop(AF_INET6
, &sa
.sin6_addr
, ip
, sizeof(ip
))) {
473 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
477 pa_snprintf(c
, l
, "tcp6:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin6_port
));
484 case SOCKET_SERVER_IPV4
: {
485 struct sockaddr_in sa
;
486 socklen_t sa_len
= sizeof(sa
);
488 if (getsockname(s
->fd
, (struct sockaddr
*) &sa
, &sa_len
) < 0) {
489 pa_log("getsockname(): %s", pa_cstrerror(errno
));
493 if (sa
.sin_addr
.s_addr
== INADDR_ANY
) {
495 if (!pa_get_fqdn(fqdn
, sizeof(fqdn
)))
498 pa_snprintf(c
, l
, "tcp:%s:%u", fqdn
, (unsigned) ntohs(sa
.sin_port
));
499 } else if (sa
.sin_addr
.s_addr
== INADDR_LOOPBACK
) {
502 if (!(id
= pa_machine_id()))
505 pa_snprintf(c
, l
, "{%s}tcp:localhost:%u", id
, (unsigned) ntohs(sa
.sin_port
));
508 char ip
[INET_ADDRSTRLEN
];
510 if (!inet_ntop(AF_INET
, &sa
.sin_addr
, ip
, sizeof(ip
))) {
511 pa_log("inet_ntop(): %s", pa_cstrerror(errno
));
515 pa_snprintf(c
, l
, "tcp:[%s]:%u", ip
, (unsigned) ntohs(sa
.sin_port
));
521 case SOCKET_SERVER_UNIX
: {
527 if (!(id
= pa_machine_id()))
530 pa_snprintf(c
, l
, "{%s}unix:%s", id
, s
->filename
);