pulseaudio/src/pulsecore/socket-client.c
Ondrej Holecek 5effc83479 update FSF addresses to FSF web page
FSF addresses used in PA sources are no longer valid and rpmlint
generates numerous warnings during packaging because of this.
This patch changes all FSF addresses to FSF web page according to
the GPL how-to: https://www.gnu.org/licenses/gpl-howto.en.html

Done automatically by sed-ing through sources.
2015-01-14 22:20:40 +02:00

546 lines
13 KiB
C

/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/* #undef HAVE_LIBASYNCNS */
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_LIBASYNCNS
#include <asyncns.h>
#endif
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/socket.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
#include <pulsecore/parseaddr.h>
#include <pulsecore/macro.h>
#include <pulsecore/refcnt.h>
#include <pulsecore/arpa-inet.h>
#include "socket-client.h"
#define CONNECT_TIMEOUT 5
struct pa_socket_client {
PA_REFCNT_DECLARE;
int fd;
pa_mainloop_api *mainloop;
pa_io_event *io_event;
pa_time_event *timeout_event;
pa_defer_event *defer_event;
pa_socket_client_cb_t callback;
void *userdata;
bool local;
#ifdef HAVE_LIBASYNCNS
asyncns_t *asyncns;
asyncns_query_t * asyncns_query;
pa_io_event *asyncns_io_event;
#endif
};
static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
pa_socket_client *c;
pa_assert(m);
c = pa_xnew0(pa_socket_client, 1);
PA_REFCNT_INIT(c);
c->mainloop = m;
c->fd = -1;
return c;
}
static void free_events(pa_socket_client *c) {
pa_assert(c);
if (c->io_event) {
c->mainloop->io_free(c->io_event);
c->io_event = NULL;
}
if (c->timeout_event) {
c->mainloop->time_free(c->timeout_event);
c->timeout_event = NULL;
}
if (c->defer_event) {
c->mainloop->defer_free(c->defer_event);
c->defer_event = NULL;
}
}
static void do_call(pa_socket_client *c) {
pa_iochannel *io = NULL;
int error;
socklen_t lerror;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(c->callback);
pa_socket_client_ref(c);
if (c->fd < 0)
goto finish;
lerror = sizeof(error);
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) {
pa_log("getsockopt(): %s", pa_cstrerror(errno));
goto finish;
}
if (lerror != sizeof(error)) {
pa_log("getsockopt() returned invalid size.");
goto finish;
}
if (error != 0) {
pa_log_debug("connect(): %s", pa_cstrerror(error));
errno = error;
goto finish;
}
io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
finish:
if (!io && c->fd >= 0)
pa_close(c->fd);
c->fd = -1;
free_events(c);
c->callback(c, io, c->userdata);
pa_socket_client_unref(c);
}
static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_socket_client *c = userdata;
pa_assert(m);
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(c->defer_event == e);
do_call(c);
}
static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_socket_client *c = userdata;
pa_assert(m);
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(c->io_event == e);
pa_assert(fd >= 0);
do_call(c);
}
static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(sa);
pa_assert(len > 0);
pa_make_fd_nonblock(c->fd);
if (connect(c->fd, sa, len) < 0) {
#ifdef OS_IS_WIN32
if (WSAGetLastError() != EWOULDBLOCK) {
pa_log_debug("connect(): %d", WSAGetLastError());
#else
if (errno != EINPROGRESS) {
pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno), errno);
#endif
return -1;
}
c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
} else
c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c);
return 0;
}
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
struct sockaddr_in sa;
pa_assert(m);
pa_assert(port > 0);
pa_zero(sa);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(address);
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
}
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
#ifdef HAVE_SYS_UN_H
struct sockaddr_un sa;
pa_assert(m);
pa_assert(filename);
pa_zero(sa);
sa.sun_family = AF_UNIX;
pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
#else /* HAVE_SYS_UN_H */
return NULL;
#endif /* HAVE_SYS_UN_H */
}
static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
pa_assert(c);
pa_assert(sa);
pa_assert(salen);
c->local = pa_socket_address_is_local(sa);
if ((c->fd = pa_socket_cloexec(sa->sa_family, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
return -1;
}
#ifdef HAVE_IPV6
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
#else
if (sa->sa_family == AF_INET)
#endif
pa_make_tcp_socket_low_delay(c->fd);
else
pa_make_socket_low_delay(c->fd);
if (do_connect(c, sa, (socklen_t) salen) < 0)
return -1;
return 0;
}
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
pa_socket_client *c;
pa_assert(m);
pa_assert(sa);
pa_assert(salen > 0);
c = socket_client_new(m);
if (sockaddr_prepare(c, sa, salen) < 0)
goto fail;
return c;
fail:
pa_socket_client_unref(c);
return NULL;
}
static void socket_client_free(pa_socket_client *c) {
pa_assert(c);
pa_assert(c->mainloop);
free_events(c);
if (c->fd >= 0)
pa_close(c->fd);
#ifdef HAVE_LIBASYNCNS
if (c->asyncns_query)
asyncns_cancel(c->asyncns, c->asyncns_query);
if (c->asyncns)
asyncns_free(c->asyncns);
if (c->asyncns_io_event)
c->mainloop->io_free(c->asyncns_io_event);
#endif
pa_xfree(c);
}
void pa_socket_client_unref(pa_socket_client *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (PA_REFCNT_DEC(c) <= 0)
socket_client_free(c);
}
pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_REFCNT_INC(c);
return c;
}
void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
c->callback = on_connection;
c->userdata = userdata;
}
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
#ifdef HAVE_IPV6
struct sockaddr_in6 sa;
pa_assert(m);
pa_assert(address);
pa_assert(port > 0);
pa_zero(sa);
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
#else
return NULL;
#endif
}
#ifdef HAVE_LIBASYNCNS
static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_socket_client *c = userdata;
struct addrinfo *res = NULL;
int ret;
pa_assert(m);
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(c->asyncns_io_event == e);
pa_assert(fd >= 0);
if (asyncns_wait(c->asyncns, 0) < 0)
goto fail;
if (!asyncns_isdone(c->asyncns, c->asyncns_query))
return;
ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res);
c->asyncns_query = NULL;
if (ret != 0 || !res)
goto fail;
if (res->ai_addr)
if (sockaddr_prepare(c, res->ai_addr, res->ai_addrlen) < 0)
goto fail;
asyncns_freeaddrinfo(res);
m->io_free(c->asyncns_io_event);
c->asyncns_io_event = NULL;
return;
fail:
m->io_free(c->asyncns_io_event);
c->asyncns_io_event = NULL;
errno = EHOSTUNREACH;
do_call(c);
return;
}
#endif
static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
pa_socket_client *c = userdata;
pa_assert(m);
pa_assert(e);
pa_assert(c);
if (c->fd >= 0) {
pa_close(c->fd);
c->fd = -1;
}
errno = ETIMEDOUT;
do_call(c);
}
static void start_timeout(pa_socket_client *c, bool use_rtclock) {
struct timeval tv;
pa_assert(c);
pa_assert(!c->timeout_event);
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);
}
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, bool use_rtclock, const char*name, uint16_t default_port) {
pa_socket_client *c = NULL;
pa_parsed_address a;
pa_assert(m);
pa_assert(name);
if (pa_parse_address(name, &a) < 0)
return NULL;
if (!a.port)
a.port = default_port;
switch (a.type) {
case PA_PARSED_ADDRESS_UNIX:
if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
start_timeout(c, use_rtclock);
break;
case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */
case PA_PARSED_ADDRESS_TCP6: /* Fallthrough */
case PA_PARSED_ADDRESS_TCP_AUTO: {
struct addrinfo hints;
char port[12];
pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
pa_zero(hints);
if (a.type == PA_PARSED_ADDRESS_TCP4)
hints.ai_family = PF_INET;
#ifdef HAVE_IPV6
else if (a.type == PA_PARSED_ADDRESS_TCP6)
hints.ai_family = PF_INET6;
#endif
else
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#if defined(HAVE_LIBASYNCNS)
{
asyncns_t *asyncns;
if (!(asyncns = asyncns_new(1)))
goto finish;
c = socket_client_new(m);
c->asyncns = asyncns;
c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
pa_assert_se(c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints));
start_timeout(c, use_rtclock);
}
#elif defined(HAVE_GETADDRINFO)
{
int ret;
struct addrinfo *res = NULL;
ret = getaddrinfo(a.path_or_host, port, &hints, &res);
if (ret < 0 || !res)
goto finish;
if (res->ai_addr) {
if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
start_timeout(c, use_rtclock);
}
freeaddrinfo(res);
}
#else
{
struct hostent *host = NULL;
struct sockaddr_in s;
#ifdef HAVE_IPV6
/* FIXME: PF_INET6 support */
if (hints.ai_family == PF_INET6) {
pa_log_error("IPv6 is not supported on Windows");
goto finish;
}
#endif
host = gethostbyname(a.path_or_host);
if (!host) {
unsigned int addr = inet_addr(a.path_or_host);
if (addr != INADDR_NONE)
host = gethostbyaddr((char*)&addr, 4, AF_INET);
}
if (!host)
goto finish;
pa_zero(s);
s.sin_family = AF_INET;
memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr));
s.sin_port = htons(a.port);
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
start_timeout(c, use_rtclock);
}
#endif /* HAVE_LIBASYNCNS */
}
}
finish:
pa_xfree(a.path_or_host);
return c;
}
/* Return non-zero when the target sockaddr is considered
local. "local" means UNIX socket or TCP socket on localhost. Other
local IP addresses are not considered local. */
bool pa_socket_client_is_local(pa_socket_client *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
return c->local;
}