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
9 published by the Free Software Foundation; either version 2.1 of the
10 License, 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 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 /* #undef HAVE_LIBASYNCNS */
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
45 #ifdef HAVE_LIBASYNCNS
49 #include <pulse/rtclock.h>
50 #include <pulse/timeval.h>
51 #include <pulse/xmalloc.h>
53 #include <pulsecore/socket.h>
54 #include <pulsecore/socket-util.h>
55 #include <pulsecore/core-error.h>
56 #include <pulsecore/core-rtclock.h>
57 #include <pulsecore/core-util.h>
58 #include <pulsecore/socket-util.h>
59 #include <pulsecore/log.h>
60 #include <pulsecore/parseaddr.h>
61 #include <pulsecore/macro.h>
62 #include <pulsecore/refcnt.h>
63 #include <pulsecore/arpa-inet.h>
65 #include "socket-client.h"
67 #define CONNECT_TIMEOUT 5
69 struct pa_socket_client
{
73 pa_mainloop_api
*mainloop
;
74 pa_io_event
*io_event
;
75 pa_time_event
*timeout_event
;
76 pa_defer_event
*defer_event
;
78 pa_socket_client_cb_t callback
;
83 #ifdef HAVE_LIBASYNCNS
85 asyncns_query_t
* asyncns_query
;
86 pa_io_event
*asyncns_io_event
;
90 static pa_socket_client
* socket_client_new(pa_mainloop_api
*m
) {
94 c
= pa_xnew0(pa_socket_client
, 1);
102 static void free_events(pa_socket_client
*c
) {
106 c
->mainloop
->io_free(c
->io_event
);
110 if (c
->timeout_event
) {
111 c
->mainloop
->time_free(c
->timeout_event
);
112 c
->timeout_event
= NULL
;
115 if (c
->defer_event
) {
116 c
->mainloop
->defer_free(c
->defer_event
);
117 c
->defer_event
= NULL
;
121 static void do_call(pa_socket_client
*c
) {
122 pa_iochannel
*io
= NULL
;
127 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
128 pa_assert(c
->callback
);
130 pa_socket_client_ref(c
);
135 lerror
= sizeof(error
);
136 if (getsockopt(c
->fd
, SOL_SOCKET
, SO_ERROR
, (void*)&error
, &lerror
) < 0) {
137 pa_log("getsockopt(): %s", pa_cstrerror(errno
));
141 if (lerror
!= sizeof(error
)) {
142 pa_log("getsockopt() returned invalid size.");
147 pa_log_debug("connect(): %s", pa_cstrerror(error
));
152 io
= pa_iochannel_new(c
->mainloop
, c
->fd
, c
->fd
);
155 if (!io
&& c
->fd
>= 0)
161 c
->callback(c
, io
, c
->userdata
);
163 pa_socket_client_unref(c
);
166 static void connect_defer_cb(pa_mainloop_api
*m
, pa_defer_event
*e
, void *userdata
) {
167 pa_socket_client
*c
= userdata
;
171 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
172 pa_assert(c
->defer_event
== e
);
177 static void connect_io_cb(pa_mainloop_api
*m
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
178 pa_socket_client
*c
= userdata
;
182 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
183 pa_assert(c
->io_event
== e
);
189 static int do_connect(pa_socket_client
*c
, const struct sockaddr
*sa
, socklen_t len
) {
191 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
195 pa_make_fd_nonblock(c
->fd
);
197 if (connect(c
->fd
, sa
, len
) < 0) {
199 if (WSAGetLastError() != EWOULDBLOCK
) {
200 pa_log_debug("connect(): %d", WSAGetLastError());
202 if (errno
!= EINPROGRESS
) {
203 pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno
), errno
);
208 c
->io_event
= c
->mainloop
->io_new(c
->mainloop
, c
->fd
, PA_IO_EVENT_OUTPUT
, connect_io_cb
, c
);
210 c
->defer_event
= c
->mainloop
->defer_new(c
->mainloop
, connect_defer_cb
, c
);
215 pa_socket_client
* pa_socket_client_new_ipv4(pa_mainloop_api
*m
, uint32_t address
, uint16_t port
) {
216 struct sockaddr_in sa
;
222 sa
.sin_family
= AF_INET
;
223 sa
.sin_port
= htons(port
);
224 sa
.sin_addr
.s_addr
= htonl(address
);
226 return pa_socket_client_new_sockaddr(m
, (struct sockaddr
*) &sa
, sizeof(sa
));
230 pa_socket_client
* pa_socket_client_new_unix(pa_mainloop_api
*m
, const char *filename
) {
232 struct sockaddr_un sa
;
238 sa
.sun_family
= AF_UNIX
;
239 pa_strlcpy(sa
.sun_path
, filename
, sizeof(sa
.sun_path
));
241 return pa_socket_client_new_sockaddr(m
, (struct sockaddr
*) &sa
, sizeof(sa
));
242 #else /* HAVE_SYS_UN_H */
245 #endif /* HAVE_SYS_UN_H */
248 static int sockaddr_prepare(pa_socket_client
*c
, const struct sockaddr
*sa
, size_t salen
) {
253 c
->local
= pa_socket_address_is_local(sa
);
255 if ((c
->fd
= pa_socket_cloexec(sa
->sa_family
, SOCK_STREAM
, 0)) < 0) {
256 pa_log("socket(): %s", pa_cstrerror(errno
));
261 if (sa
->sa_family
== AF_INET
|| sa
->sa_family
== AF_INET6
)
263 if (sa
->sa_family
== AF_INET
)
265 pa_make_tcp_socket_low_delay(c
->fd
);
267 pa_make_socket_low_delay(c
->fd
);
269 if (do_connect(c
, sa
, (socklen_t
) salen
) < 0)
275 pa_socket_client
* pa_socket_client_new_sockaddr(pa_mainloop_api
*m
, const struct sockaddr
*sa
, size_t salen
) {
280 pa_assert(salen
> 0);
282 c
= socket_client_new(m
);
284 if (sockaddr_prepare(c
, sa
, salen
) < 0)
290 pa_socket_client_unref(c
);
294 static void socket_client_free(pa_socket_client
*c
) {
296 pa_assert(c
->mainloop
);
303 #ifdef HAVE_LIBASYNCNS
304 if (c
->asyncns_query
)
305 asyncns_cancel(c
->asyncns
, c
->asyncns_query
);
307 asyncns_free(c
->asyncns
);
308 if (c
->asyncns_io_event
)
309 c
->mainloop
->io_free(c
->asyncns_io_event
);
315 void pa_socket_client_unref(pa_socket_client
*c
) {
317 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
319 if (PA_REFCNT_DEC(c
) <= 0)
320 socket_client_free(c
);
323 pa_socket_client
* pa_socket_client_ref(pa_socket_client
*c
) {
325 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
331 void pa_socket_client_set_callback(pa_socket_client
*c
, pa_socket_client_cb_t on_connection
, void *userdata
) {
333 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
335 c
->callback
= on_connection
;
336 c
->userdata
= userdata
;
339 pa_socket_client
* pa_socket_client_new_ipv6(pa_mainloop_api
*m
, uint8_t address
[16], uint16_t port
) {
341 struct sockaddr_in6 sa
;
348 sa
.sin6_family
= AF_INET6
;
349 sa
.sin6_port
= htons(port
);
350 memcpy(&sa
.sin6_addr
, address
, sizeof(sa
.sin6_addr
));
352 return pa_socket_client_new_sockaddr(m
, (struct sockaddr
*) &sa
, sizeof(sa
));
359 #ifdef HAVE_LIBASYNCNS
361 static void asyncns_cb(pa_mainloop_api
*m
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
362 pa_socket_client
*c
= userdata
;
363 struct addrinfo
*res
= NULL
;
368 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
369 pa_assert(c
->asyncns_io_event
== e
);
372 if (asyncns_wait(c
->asyncns
, 0) < 0)
375 if (!asyncns_isdone(c
->asyncns
, c
->asyncns_query
))
378 ret
= asyncns_getaddrinfo_done(c
->asyncns
, c
->asyncns_query
, &res
);
379 c
->asyncns_query
= NULL
;
381 if (ret
!= 0 || !res
)
385 if (sockaddr_prepare(c
, res
->ai_addr
, res
->ai_addrlen
) < 0)
388 asyncns_freeaddrinfo(res
);
390 m
->io_free(c
->asyncns_io_event
);
391 c
->asyncns_io_event
= NULL
;
395 m
->io_free(c
->asyncns_io_event
);
396 c
->asyncns_io_event
= NULL
;
398 errno
= EHOSTUNREACH
;
406 static void timeout_cb(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
407 pa_socket_client
*c
= userdata
;
422 static void start_timeout(pa_socket_client
*c
, pa_bool_t use_rtclock
) {
426 pa_assert(!c
->timeout_event
);
428 c
->timeout_event
= c
->mainloop
->time_new(c
->mainloop
, pa_timeval_rtstore(&tv
, pa_rtclock_now() + CONNECT_TIMEOUT
* PA_USEC_PER_SEC
, use_rtclock
), timeout_cb
, c
);
431 pa_socket_client
* pa_socket_client_new_string(pa_mainloop_api
*m
, pa_bool_t use_rtclock
, const char*name
, uint16_t default_port
) {
432 pa_socket_client
*c
= NULL
;
438 if (pa_parse_address(name
, &a
) < 0)
442 a
.port
= default_port
;
445 case PA_PARSED_ADDRESS_UNIX
:
446 if ((c
= pa_socket_client_new_unix(m
, a
.path_or_host
)))
447 start_timeout(c
, use_rtclock
);
450 case PA_PARSED_ADDRESS_TCP4
: /* Fallthrough */
451 case PA_PARSED_ADDRESS_TCP6
: /* Fallthrough */
452 case PA_PARSED_ADDRESS_TCP_AUTO
: {
453 struct addrinfo hints
;
456 pa_snprintf(port
, sizeof(port
), "%u", (unsigned) a
.port
);
459 if (a
.type
== PA_PARSED_ADDRESS_TCP4
)
460 hints
.ai_family
= PF_INET
;
462 else if (a
.type
== PA_PARSED_ADDRESS_TCP6
)
463 hints
.ai_family
= PF_INET6
;
466 hints
.ai_family
= PF_UNSPEC
;
468 hints
.ai_socktype
= SOCK_STREAM
;
470 #if defined(HAVE_LIBASYNCNS)
474 if (!(asyncns
= asyncns_new(1)))
477 c
= socket_client_new(m
);
478 c
->asyncns
= asyncns
;
479 c
->asyncns_io_event
= m
->io_new(m
, asyncns_fd(c
->asyncns
), PA_IO_EVENT_INPUT
, asyncns_cb
, c
);
480 pa_assert_se(c
->asyncns_query
= asyncns_getaddrinfo(c
->asyncns
, a
.path_or_host
, port
, &hints
));
481 start_timeout(c
, use_rtclock
);
483 #elif defined(HAVE_GETADDRINFO)
486 struct addrinfo
*res
= NULL
;
488 ret
= getaddrinfo(a
.path_or_host
, port
, &hints
, &res
);
494 if ((c
= pa_socket_client_new_sockaddr(m
, res
->ai_addr
, res
->ai_addrlen
)))
495 start_timeout(c
, use_rtclock
);
502 struct hostent
*host
= NULL
;
503 struct sockaddr_in s
;
506 /* FIXME: PF_INET6 support */
507 if (hints
.ai_family
== PF_INET6
) {
508 pa_log_error("IPv6 is not supported on Windows");
513 host
= gethostbyname(a
.path_or_host
);
515 unsigned int addr
= inet_addr(a
.path_or_host
);
516 if (addr
!= INADDR_NONE
)
517 host
= gethostbyaddr((char*)&addr
, 4, AF_INET
);
524 s
.sin_family
= AF_INET
;
525 memcpy(&s
.sin_addr
, host
->h_addr
, sizeof(struct in_addr
));
526 s
.sin_port
= htons(a
.port
);
528 if ((c
= pa_socket_client_new_sockaddr(m
, (struct sockaddr
*)&s
, sizeof(s
))))
529 start_timeout(c
, use_rtclock
);
531 #endif /* HAVE_LIBASYNCNS */
536 pa_xfree(a
.path_or_host
);
541 /* Return non-zero when the target sockaddr is considered
542 local. "local" means UNIX socket or TCP socket on localhost. Other
543 local IP addresses are not considered local. */
544 pa_bool_t
pa_socket_client_is_local(pa_socket_client
*c
) {
546 pa_assert(PA_REFCNT_VALUE(c
) >= 1);