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_ARPA_INET_H
39 #include <arpa/inet.h>
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
48 #ifdef HAVE_LIBASYNCNS
52 #include <pulse/rtclock.h>
53 #include <pulse/timeval.h>
54 #include <pulse/xmalloc.h>
56 #include <pulsecore/socket.h>
57 #include <pulsecore/socket-util.h>
58 #include <pulsecore/core-error.h>
59 #include <pulsecore/core-rtclock.h>
60 #include <pulsecore/core-util.h>
61 #include <pulsecore/socket-util.h>
62 #include <pulsecore/log.h>
63 #include <pulsecore/parseaddr.h>
64 #include <pulsecore/macro.h>
65 #include <pulsecore/refcnt.h>
67 #include "socket-client.h"
69 #define CONNECT_TIMEOUT 5
71 struct pa_socket_client
{
75 pa_mainloop_api
*mainloop
;
76 pa_io_event
*io_event
;
77 pa_time_event
*timeout_event
;
78 pa_defer_event
*defer_event
;
80 pa_socket_client_cb_t callback
;
85 #ifdef HAVE_LIBASYNCNS
87 asyncns_query_t
* asyncns_query
;
88 pa_io_event
*asyncns_io_event
;
92 static pa_socket_client
* socket_client_new(pa_mainloop_api
*m
) {
96 c
= pa_xnew0(pa_socket_client
, 1);
104 static void free_events(pa_socket_client
*c
) {
108 c
->mainloop
->io_free(c
->io_event
);
112 if (c
->timeout_event
) {
113 c
->mainloop
->time_free(c
->timeout_event
);
114 c
->timeout_event
= NULL
;
117 if (c
->defer_event
) {
118 c
->mainloop
->defer_free(c
->defer_event
);
119 c
->defer_event
= NULL
;
123 static void do_call(pa_socket_client
*c
) {
124 pa_iochannel
*io
= NULL
;
129 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
130 pa_assert(c
->callback
);
132 pa_socket_client_ref(c
);
137 lerror
= sizeof(error
);
138 if (getsockopt(c
->fd
, SOL_SOCKET
, SO_ERROR
, (void*)&error
, &lerror
) < 0) {
139 pa_log("getsockopt(): %s", pa_cstrerror(errno
));
143 if (lerror
!= sizeof(error
)) {
144 pa_log("getsockopt() returned invalid size.");
149 pa_log_debug("connect(): %s", pa_cstrerror(error
));
154 io
= pa_iochannel_new(c
->mainloop
, c
->fd
, c
->fd
);
157 if (!io
&& c
->fd
>= 0)
163 c
->callback(c
, io
, c
->userdata
);
165 pa_socket_client_unref(c
);
168 static void connect_defer_cb(pa_mainloop_api
*m
, pa_defer_event
*e
, void *userdata
) {
169 pa_socket_client
*c
= userdata
;
173 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
174 pa_assert(c
->defer_event
== e
);
179 static void connect_io_cb(pa_mainloop_api
*m
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
180 pa_socket_client
*c
= userdata
;
184 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
185 pa_assert(c
->io_event
== e
);
191 static int do_connect(pa_socket_client
*c
, const struct sockaddr
*sa
, socklen_t len
) {
193 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
197 pa_make_fd_nonblock(c
->fd
);
199 if (connect(c
->fd
, sa
, len
) < 0) {
201 if (WSAGetLastError() != EWOULDBLOCK
) {
202 pa_log_debug("connect(): %d", WSAGetLastError());
204 if (errno
!= EINPROGRESS
) {
205 pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno
), errno
);
210 c
->io_event
= c
->mainloop
->io_new(c
->mainloop
, c
->fd
, PA_IO_EVENT_OUTPUT
, connect_io_cb
, c
);
212 c
->defer_event
= c
->mainloop
->defer_new(c
->mainloop
, connect_defer_cb
, c
);
217 pa_socket_client
* pa_socket_client_new_ipv4(pa_mainloop_api
*m
, uint32_t address
, uint16_t port
) {
218 struct sockaddr_in sa
;
224 sa
.sin_family
= AF_INET
;
225 sa
.sin_port
= htons(port
);
226 sa
.sin_addr
.s_addr
= htonl(address
);
228 return pa_socket_client_new_sockaddr(m
, (struct sockaddr
*) &sa
, sizeof(sa
));
232 pa_socket_client
* pa_socket_client_new_unix(pa_mainloop_api
*m
, const char *filename
) {
234 struct sockaddr_un sa
;
240 sa
.sun_family
= AF_UNIX
;
241 pa_strlcpy(sa
.sun_path
, filename
, sizeof(sa
.sun_path
));
243 return pa_socket_client_new_sockaddr(m
, (struct sockaddr
*) &sa
, sizeof(sa
));
244 #else /* HAVE_SYS_UN_H */
247 #endif /* HAVE_SYS_UN_H */
250 static int sockaddr_prepare(pa_socket_client
*c
, const struct sockaddr
*sa
, size_t salen
) {
255 c
->local
= pa_socket_address_is_local(sa
);
257 if ((c
->fd
= pa_socket_cloexec(sa
->sa_family
, SOCK_STREAM
, 0)) < 0) {
258 pa_log("socket(): %s", pa_cstrerror(errno
));
263 if (sa
->sa_family
== AF_INET
|| sa
->sa_family
== AF_INET6
)
265 if (sa
->sa_family
== AF_INET
)
267 pa_make_tcp_socket_low_delay(c
->fd
);
269 pa_make_socket_low_delay(c
->fd
);
271 if (do_connect(c
, sa
, (socklen_t
) salen
) < 0)
277 pa_socket_client
* pa_socket_client_new_sockaddr(pa_mainloop_api
*m
, const struct sockaddr
*sa
, size_t salen
) {
282 pa_assert(salen
> 0);
284 c
= socket_client_new(m
);
286 if (sockaddr_prepare(c
, sa
, salen
) < 0)
292 pa_socket_client_unref(c
);
296 static void socket_client_free(pa_socket_client
*c
) {
298 pa_assert(c
->mainloop
);
305 #ifdef HAVE_LIBASYNCNS
306 if (c
->asyncns_query
)
307 asyncns_cancel(c
->asyncns
, c
->asyncns_query
);
309 asyncns_free(c
->asyncns
);
310 if (c
->asyncns_io_event
)
311 c
->mainloop
->io_free(c
->asyncns_io_event
);
317 void pa_socket_client_unref(pa_socket_client
*c
) {
319 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
321 if (PA_REFCNT_DEC(c
) <= 0)
322 socket_client_free(c
);
325 pa_socket_client
* pa_socket_client_ref(pa_socket_client
*c
) {
327 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
333 void pa_socket_client_set_callback(pa_socket_client
*c
, pa_socket_client_cb_t on_connection
, void *userdata
) {
335 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
337 c
->callback
= on_connection
;
338 c
->userdata
= userdata
;
341 pa_socket_client
* pa_socket_client_new_ipv6(pa_mainloop_api
*m
, uint8_t address
[16], uint16_t port
) {
343 struct sockaddr_in6 sa
;
350 sa
.sin6_family
= AF_INET6
;
351 sa
.sin6_port
= htons(port
);
352 memcpy(&sa
.sin6_addr
, address
, sizeof(sa
.sin6_addr
));
354 return pa_socket_client_new_sockaddr(m
, (struct sockaddr
*) &sa
, sizeof(sa
));
361 #ifdef HAVE_LIBASYNCNS
363 static void asyncns_cb(pa_mainloop_api
*m
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
364 pa_socket_client
*c
= userdata
;
365 struct addrinfo
*res
= NULL
;
370 pa_assert(PA_REFCNT_VALUE(c
) >= 1);
371 pa_assert(c
->asyncns_io_event
== e
);
374 if (asyncns_wait(c
->asyncns
, 0) < 0)
377 if (!asyncns_isdone(c
->asyncns
, c
->asyncns_query
))
380 ret
= asyncns_getaddrinfo_done(c
->asyncns
, c
->asyncns_query
, &res
);
381 c
->asyncns_query
= NULL
;
383 if (ret
!= 0 || !res
)
387 if (sockaddr_prepare(c
, res
->ai_addr
, res
->ai_addrlen
) < 0)
390 asyncns_freeaddrinfo(res
);
392 m
->io_free(c
->asyncns_io_event
);
393 c
->asyncns_io_event
= NULL
;
397 m
->io_free(c
->asyncns_io_event
);
398 c
->asyncns_io_event
= NULL
;
400 errno
= EHOSTUNREACH
;
408 static void timeout_cb(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
409 pa_socket_client
*c
= userdata
;
424 static void start_timeout(pa_socket_client
*c
, pa_bool_t use_rtclock
) {
428 pa_assert(!c
->timeout_event
);
430 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
);
433 pa_socket_client
* pa_socket_client_new_string(pa_mainloop_api
*m
, pa_bool_t use_rtclock
, const char*name
, uint16_t default_port
) {
434 pa_socket_client
*c
= NULL
;
440 if (pa_parse_address(name
, &a
) < 0)
444 a
.port
= default_port
;
447 case PA_PARSED_ADDRESS_UNIX
:
448 if ((c
= pa_socket_client_new_unix(m
, a
.path_or_host
)))
449 start_timeout(c
, use_rtclock
);
452 case PA_PARSED_ADDRESS_TCP4
: /* Fallthrough */
453 case PA_PARSED_ADDRESS_TCP6
: /* Fallthrough */
454 case PA_PARSED_ADDRESS_TCP_AUTO
: {
455 struct addrinfo hints
;
458 pa_snprintf(port
, sizeof(port
), "%u", (unsigned) a
.port
);
461 if (a
.type
== PA_PARSED_ADDRESS_TCP4
)
462 hints
.ai_family
= PF_INET
;
464 else if (a
.type
== PA_PARSED_ADDRESS_TCP6
)
465 hints
.ai_family
= PF_INET6
;
468 hints
.ai_family
= PF_UNSPEC
;
470 hints
.ai_socktype
= SOCK_STREAM
;
472 #if defined(HAVE_LIBASYNCNS)
476 if (!(asyncns
= asyncns_new(1)))
479 c
= socket_client_new(m
);
480 c
->asyncns
= asyncns
;
481 c
->asyncns_io_event
= m
->io_new(m
, asyncns_fd(c
->asyncns
), PA_IO_EVENT_INPUT
, asyncns_cb
, c
);
482 pa_assert_se(c
->asyncns_query
= asyncns_getaddrinfo(c
->asyncns
, a
.path_or_host
, port
, &hints
));
483 start_timeout(c
, use_rtclock
);
485 #elif defined(HAVE_GETADDRINFO)
488 struct addrinfo
*res
= NULL
;
490 ret
= getaddrinfo(a
.path_or_host
, port
, &hints
, &res
);
496 if ((c
= pa_socket_client_new_sockaddr(m
, res
->ai_addr
, res
->ai_addrlen
)))
497 start_timeout(c
, use_rtclock
);
504 struct hostent
*host
= NULL
;
505 struct sockaddr_in s
;
508 /* FIXME: PF_INET6 support */
509 if (hints
.ai_family
== PF_INET6
) {
510 pa_log_error("IPv6 is not supported on Windows");
515 host
= gethostbyname(a
.path_or_host
);
517 unsigned int addr
= inet_addr(a
.path_or_host
);
518 if (addr
!= INADDR_NONE
)
519 host
= gethostbyaddr((char*)&addr
, 4, AF_INET
);
526 s
.sin_family
= AF_INET
;
527 memcpy(&s
.sin_addr
, host
->h_addr
, sizeof(struct in_addr
));
528 s
.sin_port
= htons(a
.port
);
530 if ((c
= pa_socket_client_new_sockaddr(m
, (struct sockaddr
*)&s
, sizeof(s
))))
531 start_timeout(c
, use_rtclock
);
533 #endif /* HAVE_LIBASYNCNS */
538 pa_xfree(a
.path_or_host
);
543 /* Return non-zero when the target sockaddr is considered
544 local. "local" means UNIX socket or TCP socket on localhost. Other
545 local IP addresses are not considered local. */
546 pa_bool_t
pa_socket_client_is_local(pa_socket_client
*c
) {
548 pa_assert(PA_REFCNT_VALUE(c
) >= 1);