153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci    This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci    Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci    Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci    PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci    it under the terms of the GNU Lesser General Public License as
953a5a1b3Sopenharmony_ci    published by the Free Software Foundation; either version 2.1 of the
1053a5a1b3Sopenharmony_ci    License, or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci    PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci    WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci    Lesser General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci    You should have received a copy of the GNU Lesser General Public
1853a5a1b3Sopenharmony_ci    License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci/* #undef HAVE_LIBASYNCNS */
2653a5a1b3Sopenharmony_ci
2753a5a1b3Sopenharmony_ci#include <unistd.h>
2853a5a1b3Sopenharmony_ci#include <stdio.h>
2953a5a1b3Sopenharmony_ci#include <errno.h>
3053a5a1b3Sopenharmony_ci#include <string.h>
3153a5a1b3Sopenharmony_ci#include <stdlib.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_UN_H
3453a5a1b3Sopenharmony_ci#include <sys/un.h>
3553a5a1b3Sopenharmony_ci#endif
3653a5a1b3Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3753a5a1b3Sopenharmony_ci#include <netinet/in.h>
3853a5a1b3Sopenharmony_ci#endif
3953a5a1b3Sopenharmony_ci#ifdef HAVE_NETDB_H
4053a5a1b3Sopenharmony_ci#include <netdb.h>
4153a5a1b3Sopenharmony_ci#endif
4253a5a1b3Sopenharmony_ci
4353a5a1b3Sopenharmony_ci#ifdef HAVE_LIBASYNCNS
4453a5a1b3Sopenharmony_ci#include <asyncns.h>
4553a5a1b3Sopenharmony_ci#endif
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
4853a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
4953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci#include <pulsecore/socket.h>
5253a5a1b3Sopenharmony_ci#include <pulsecore/socket-util.h>
5353a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
5453a5a1b3Sopenharmony_ci#include <pulsecore/core-rtclock.h>
5553a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
5653a5a1b3Sopenharmony_ci#include <pulsecore/socket-util.h>
5753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
5853a5a1b3Sopenharmony_ci#include <pulsecore/parseaddr.h>
5953a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
6053a5a1b3Sopenharmony_ci#include <pulsecore/refcnt.h>
6153a5a1b3Sopenharmony_ci#include <pulsecore/arpa-inet.h>
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci#include "socket-client.h"
6453a5a1b3Sopenharmony_ci
6553a5a1b3Sopenharmony_ci#define CONNECT_TIMEOUT 5
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_cistruct pa_socket_client {
6853a5a1b3Sopenharmony_ci    PA_REFCNT_DECLARE;
6953a5a1b3Sopenharmony_ci    int fd;
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_ci    pa_mainloop_api *mainloop;
7253a5a1b3Sopenharmony_ci    pa_io_event *io_event;
7353a5a1b3Sopenharmony_ci    pa_time_event *timeout_event;
7453a5a1b3Sopenharmony_ci    pa_defer_event *defer_event;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    pa_socket_client_cb_t callback;
7753a5a1b3Sopenharmony_ci    void *userdata;
7853a5a1b3Sopenharmony_ci
7953a5a1b3Sopenharmony_ci    bool local;
8053a5a1b3Sopenharmony_ci
8153a5a1b3Sopenharmony_ci#ifdef HAVE_LIBASYNCNS
8253a5a1b3Sopenharmony_ci    asyncns_t *asyncns;
8353a5a1b3Sopenharmony_ci    asyncns_query_t * asyncns_query;
8453a5a1b3Sopenharmony_ci    pa_io_event *asyncns_io_event;
8553a5a1b3Sopenharmony_ci#endif
8653a5a1b3Sopenharmony_ci};
8753a5a1b3Sopenharmony_ci
8853a5a1b3Sopenharmony_cistatic pa_socket_client* socket_client_new(pa_mainloop_api *m) {
8953a5a1b3Sopenharmony_ci    pa_socket_client *c;
9053a5a1b3Sopenharmony_ci    pa_assert(m);
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_ci    c = pa_xnew0(pa_socket_client, 1);
9353a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(c);
9453a5a1b3Sopenharmony_ci    c->mainloop = m;
9553a5a1b3Sopenharmony_ci    c->fd = -1;
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_ci    return c;
9853a5a1b3Sopenharmony_ci}
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_cistatic void free_events(pa_socket_client *c) {
10153a5a1b3Sopenharmony_ci    pa_assert(c);
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_ci    if (c->io_event) {
10453a5a1b3Sopenharmony_ci        c->mainloop->io_free(c->io_event);
10553a5a1b3Sopenharmony_ci        c->io_event = NULL;
10653a5a1b3Sopenharmony_ci    }
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci    if (c->timeout_event) {
10953a5a1b3Sopenharmony_ci        c->mainloop->time_free(c->timeout_event);
11053a5a1b3Sopenharmony_ci        c->timeout_event = NULL;
11153a5a1b3Sopenharmony_ci    }
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci    if (c->defer_event) {
11453a5a1b3Sopenharmony_ci        c->mainloop->defer_free(c->defer_event);
11553a5a1b3Sopenharmony_ci        c->defer_event = NULL;
11653a5a1b3Sopenharmony_ci    }
11753a5a1b3Sopenharmony_ci}
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_cistatic void do_call(pa_socket_client *c) {
12053a5a1b3Sopenharmony_ci    pa_iochannel *io = NULL;
12153a5a1b3Sopenharmony_ci    int error;
12253a5a1b3Sopenharmony_ci    socklen_t lerror;
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    pa_assert(c);
12553a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
12653a5a1b3Sopenharmony_ci    pa_assert(c->callback);
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_ci    pa_socket_client_ref(c);
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    if (c->fd < 0)
13153a5a1b3Sopenharmony_ci        goto finish;
13253a5a1b3Sopenharmony_ci
13353a5a1b3Sopenharmony_ci    lerror = sizeof(error);
13453a5a1b3Sopenharmony_ci    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) {
13553a5a1b3Sopenharmony_ci        pa_log("getsockopt(): %s", pa_cstrerror(errno));
13653a5a1b3Sopenharmony_ci        goto finish;
13753a5a1b3Sopenharmony_ci    }
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    if (lerror != sizeof(error)) {
14053a5a1b3Sopenharmony_ci        pa_log("getsockopt() returned invalid size.");
14153a5a1b3Sopenharmony_ci        goto finish;
14253a5a1b3Sopenharmony_ci    }
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_ci    if (error != 0) {
14553a5a1b3Sopenharmony_ci        pa_log_debug("connect(): %s", pa_cstrerror(error));
14653a5a1b3Sopenharmony_ci        errno = error;
14753a5a1b3Sopenharmony_ci        goto finish;
14853a5a1b3Sopenharmony_ci    }
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci    io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_cifinish:
15353a5a1b3Sopenharmony_ci    if (!io && c->fd >= 0)
15453a5a1b3Sopenharmony_ci        pa_close(c->fd);
15553a5a1b3Sopenharmony_ci    c->fd = -1;
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_ci    free_events(c);
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci    c->callback(c, io, c->userdata);
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci    pa_socket_client_unref(c);
16253a5a1b3Sopenharmony_ci}
16353a5a1b3Sopenharmony_ci
16453a5a1b3Sopenharmony_cistatic void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
16553a5a1b3Sopenharmony_ci    pa_socket_client *c = userdata;
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ci    pa_assert(m);
16853a5a1b3Sopenharmony_ci    pa_assert(c);
16953a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
17053a5a1b3Sopenharmony_ci    pa_assert(c->defer_event == e);
17153a5a1b3Sopenharmony_ci
17253a5a1b3Sopenharmony_ci    do_call(c);
17353a5a1b3Sopenharmony_ci}
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_cistatic void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
17653a5a1b3Sopenharmony_ci    pa_socket_client *c = userdata;
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci    pa_assert(m);
17953a5a1b3Sopenharmony_ci    pa_assert(c);
18053a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
18153a5a1b3Sopenharmony_ci    pa_assert(c->io_event == e);
18253a5a1b3Sopenharmony_ci    pa_assert(fd >= 0);
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci    do_call(c);
18553a5a1b3Sopenharmony_ci}
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_cistatic int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
18853a5a1b3Sopenharmony_ci    pa_assert(c);
18953a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
19053a5a1b3Sopenharmony_ci    pa_assert(sa);
19153a5a1b3Sopenharmony_ci    pa_assert(len > 0);
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci    pa_make_fd_nonblock(c->fd);
19453a5a1b3Sopenharmony_ci
19553a5a1b3Sopenharmony_ci    if (connect(c->fd, sa, len) < 0) {
19653a5a1b3Sopenharmony_ci#ifdef OS_IS_WIN32
19753a5a1b3Sopenharmony_ci        if (WSAGetLastError() != EWOULDBLOCK) {
19853a5a1b3Sopenharmony_ci            pa_log_debug("connect(): %d", WSAGetLastError());
19953a5a1b3Sopenharmony_ci#else
20053a5a1b3Sopenharmony_ci        if (errno != EINPROGRESS) {
20153a5a1b3Sopenharmony_ci            pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno), errno);
20253a5a1b3Sopenharmony_ci#endif
20353a5a1b3Sopenharmony_ci            return -1;
20453a5a1b3Sopenharmony_ci        }
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci        c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
20753a5a1b3Sopenharmony_ci    } else
20853a5a1b3Sopenharmony_ci        c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c);
20953a5a1b3Sopenharmony_ci
21053a5a1b3Sopenharmony_ci    return 0;
21153a5a1b3Sopenharmony_ci}
21253a5a1b3Sopenharmony_ci
21353a5a1b3Sopenharmony_cipa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
21453a5a1b3Sopenharmony_ci    struct sockaddr_in sa;
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci    pa_assert(m);
21753a5a1b3Sopenharmony_ci    pa_assert(port > 0);
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci    pa_zero(sa);
22053a5a1b3Sopenharmony_ci    sa.sin_family = AF_INET;
22153a5a1b3Sopenharmony_ci    sa.sin_port = htons(port);
22253a5a1b3Sopenharmony_ci    sa.sin_addr.s_addr = htonl(address);
22353a5a1b3Sopenharmony_ci
22453a5a1b3Sopenharmony_ci    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
22553a5a1b3Sopenharmony_ci}
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_cipa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
22853a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_UN_H
22953a5a1b3Sopenharmony_ci    struct sockaddr_un sa;
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_ci    pa_assert(m);
23253a5a1b3Sopenharmony_ci    pa_assert(filename);
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    pa_zero(sa);
23553a5a1b3Sopenharmony_ci    sa.sun_family = AF_UNIX;
23653a5a1b3Sopenharmony_ci    pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
23953a5a1b3Sopenharmony_ci#else /* HAVE_SYS_UN_H */
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    return NULL;
24253a5a1b3Sopenharmony_ci#endif /* HAVE_SYS_UN_H */
24353a5a1b3Sopenharmony_ci}
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_cistatic int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
24653a5a1b3Sopenharmony_ci    pa_assert(c);
24753a5a1b3Sopenharmony_ci    pa_assert(sa);
24853a5a1b3Sopenharmony_ci    pa_assert(salen);
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci    c->local = pa_socket_address_is_local(sa);
25153a5a1b3Sopenharmony_ci
25253a5a1b3Sopenharmony_ci    if ((c->fd = pa_socket_cloexec(sa->sa_family, SOCK_STREAM, 0)) < 0) {
25353a5a1b3Sopenharmony_ci        pa_log("socket(): %s", pa_cstrerror(errno));
25453a5a1b3Sopenharmony_ci        return -1;
25553a5a1b3Sopenharmony_ci    }
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
25853a5a1b3Sopenharmony_ci    if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
25953a5a1b3Sopenharmony_ci#else
26053a5a1b3Sopenharmony_ci    if (sa->sa_family == AF_INET)
26153a5a1b3Sopenharmony_ci#endif
26253a5a1b3Sopenharmony_ci        pa_make_tcp_socket_low_delay(c->fd);
26353a5a1b3Sopenharmony_ci    else
26453a5a1b3Sopenharmony_ci        pa_make_socket_low_delay(c->fd);
26553a5a1b3Sopenharmony_ci
26653a5a1b3Sopenharmony_ci    if (do_connect(c, sa, (socklen_t) salen) < 0)
26753a5a1b3Sopenharmony_ci        return -1;
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci    return 0;
27053a5a1b3Sopenharmony_ci}
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_cipa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
27353a5a1b3Sopenharmony_ci    pa_socket_client *c;
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci    pa_assert(m);
27653a5a1b3Sopenharmony_ci    pa_assert(sa);
27753a5a1b3Sopenharmony_ci    pa_assert(salen > 0);
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_ci    c = socket_client_new(m);
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    if (sockaddr_prepare(c, sa, salen) < 0)
28253a5a1b3Sopenharmony_ci        goto fail;
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci    return c;
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_cifail:
28753a5a1b3Sopenharmony_ci    pa_socket_client_unref(c);
28853a5a1b3Sopenharmony_ci    return NULL;
28953a5a1b3Sopenharmony_ci}
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_cistatic void socket_client_free(pa_socket_client *c) {
29253a5a1b3Sopenharmony_ci    pa_assert(c);
29353a5a1b3Sopenharmony_ci    pa_assert(c->mainloop);
29453a5a1b3Sopenharmony_ci
29553a5a1b3Sopenharmony_ci    free_events(c);
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_ci    if (c->fd >= 0)
29853a5a1b3Sopenharmony_ci        pa_close(c->fd);
29953a5a1b3Sopenharmony_ci
30053a5a1b3Sopenharmony_ci#ifdef HAVE_LIBASYNCNS
30153a5a1b3Sopenharmony_ci    if (c->asyncns_query)
30253a5a1b3Sopenharmony_ci        asyncns_cancel(c->asyncns, c->asyncns_query);
30353a5a1b3Sopenharmony_ci    if (c->asyncns)
30453a5a1b3Sopenharmony_ci        asyncns_free(c->asyncns);
30553a5a1b3Sopenharmony_ci    if (c->asyncns_io_event)
30653a5a1b3Sopenharmony_ci        c->mainloop->io_free(c->asyncns_io_event);
30753a5a1b3Sopenharmony_ci#endif
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_ci    pa_xfree(c);
31053a5a1b3Sopenharmony_ci}
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_civoid pa_socket_client_unref(pa_socket_client *c) {
31353a5a1b3Sopenharmony_ci    pa_assert(c);
31453a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(c) <= 0)
31753a5a1b3Sopenharmony_ci        socket_client_free(c);
31853a5a1b3Sopenharmony_ci}
31953a5a1b3Sopenharmony_ci
32053a5a1b3Sopenharmony_cipa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
32153a5a1b3Sopenharmony_ci    pa_assert(c);
32253a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
32353a5a1b3Sopenharmony_ci
32453a5a1b3Sopenharmony_ci    PA_REFCNT_INC(c);
32553a5a1b3Sopenharmony_ci    return c;
32653a5a1b3Sopenharmony_ci}
32753a5a1b3Sopenharmony_ci
32853a5a1b3Sopenharmony_civoid pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
32953a5a1b3Sopenharmony_ci    pa_assert(c);
33053a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
33153a5a1b3Sopenharmony_ci
33253a5a1b3Sopenharmony_ci    c->callback = on_connection;
33353a5a1b3Sopenharmony_ci    c->userdata = userdata;
33453a5a1b3Sopenharmony_ci}
33553a5a1b3Sopenharmony_ci
33653a5a1b3Sopenharmony_cipa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
33753a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
33853a5a1b3Sopenharmony_ci    struct sockaddr_in6 sa;
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_ci    pa_assert(m);
34153a5a1b3Sopenharmony_ci    pa_assert(address);
34253a5a1b3Sopenharmony_ci    pa_assert(port > 0);
34353a5a1b3Sopenharmony_ci
34453a5a1b3Sopenharmony_ci    pa_zero(sa);
34553a5a1b3Sopenharmony_ci    sa.sin6_family = AF_INET6;
34653a5a1b3Sopenharmony_ci    sa.sin6_port = htons(port);
34753a5a1b3Sopenharmony_ci    memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
34853a5a1b3Sopenharmony_ci
34953a5a1b3Sopenharmony_ci    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
35053a5a1b3Sopenharmony_ci
35153a5a1b3Sopenharmony_ci#else
35253a5a1b3Sopenharmony_ci    return NULL;
35353a5a1b3Sopenharmony_ci#endif
35453a5a1b3Sopenharmony_ci}
35553a5a1b3Sopenharmony_ci
35653a5a1b3Sopenharmony_ci#ifdef HAVE_LIBASYNCNS
35753a5a1b3Sopenharmony_ci
35853a5a1b3Sopenharmony_cistatic void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
35953a5a1b3Sopenharmony_ci    pa_socket_client *c = userdata;
36053a5a1b3Sopenharmony_ci    struct addrinfo *res = NULL;
36153a5a1b3Sopenharmony_ci    int ret;
36253a5a1b3Sopenharmony_ci
36353a5a1b3Sopenharmony_ci    pa_assert(m);
36453a5a1b3Sopenharmony_ci    pa_assert(c);
36553a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
36653a5a1b3Sopenharmony_ci    pa_assert(c->asyncns_io_event == e);
36753a5a1b3Sopenharmony_ci    pa_assert(fd >= 0);
36853a5a1b3Sopenharmony_ci
36953a5a1b3Sopenharmony_ci    if (asyncns_wait(c->asyncns, 0) < 0)
37053a5a1b3Sopenharmony_ci        goto fail;
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    if (!asyncns_isdone(c->asyncns, c->asyncns_query))
37353a5a1b3Sopenharmony_ci        return;
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res);
37653a5a1b3Sopenharmony_ci    c->asyncns_query = NULL;
37753a5a1b3Sopenharmony_ci
37853a5a1b3Sopenharmony_ci    if (ret != 0 || !res)
37953a5a1b3Sopenharmony_ci        goto fail;
38053a5a1b3Sopenharmony_ci
38153a5a1b3Sopenharmony_ci    if (res->ai_addr)
38253a5a1b3Sopenharmony_ci        if (sockaddr_prepare(c, res->ai_addr, res->ai_addrlen) < 0)
38353a5a1b3Sopenharmony_ci            goto fail;
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci    asyncns_freeaddrinfo(res);
38653a5a1b3Sopenharmony_ci
38753a5a1b3Sopenharmony_ci    m->io_free(c->asyncns_io_event);
38853a5a1b3Sopenharmony_ci    c->asyncns_io_event = NULL;
38953a5a1b3Sopenharmony_ci    return;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_cifail:
39253a5a1b3Sopenharmony_ci    m->io_free(c->asyncns_io_event);
39353a5a1b3Sopenharmony_ci    c->asyncns_io_event = NULL;
39453a5a1b3Sopenharmony_ci
39553a5a1b3Sopenharmony_ci    errno = EHOSTUNREACH;
39653a5a1b3Sopenharmony_ci    do_call(c);
39753a5a1b3Sopenharmony_ci    return;
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_ci}
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci#endif
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_cistatic void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
40453a5a1b3Sopenharmony_ci    pa_socket_client *c = userdata;
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ci    pa_assert(m);
40753a5a1b3Sopenharmony_ci    pa_assert(e);
40853a5a1b3Sopenharmony_ci    pa_assert(c);
40953a5a1b3Sopenharmony_ci
41053a5a1b3Sopenharmony_ci    if (c->fd >= 0) {
41153a5a1b3Sopenharmony_ci        pa_close(c->fd);
41253a5a1b3Sopenharmony_ci        c->fd = -1;
41353a5a1b3Sopenharmony_ci    }
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_ci    errno = ETIMEDOUT;
41653a5a1b3Sopenharmony_ci    do_call(c);
41753a5a1b3Sopenharmony_ci}
41853a5a1b3Sopenharmony_ci
41953a5a1b3Sopenharmony_cistatic void start_timeout(pa_socket_client *c, bool use_rtclock) {
42053a5a1b3Sopenharmony_ci    struct timeval tv;
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_ci    pa_assert(c);
42353a5a1b3Sopenharmony_ci    pa_assert(!c->timeout_event);
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci    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);
42653a5a1b3Sopenharmony_ci}
42753a5a1b3Sopenharmony_ci
42853a5a1b3Sopenharmony_cipa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, bool use_rtclock, const char*name, uint16_t default_port) {
42953a5a1b3Sopenharmony_ci    pa_socket_client *c = NULL;
43053a5a1b3Sopenharmony_ci    pa_parsed_address a;
43153a5a1b3Sopenharmony_ci    char *name_buf;
43253a5a1b3Sopenharmony_ci
43353a5a1b3Sopenharmony_ci    pa_assert(m);
43453a5a1b3Sopenharmony_ci    pa_assert(name);
43553a5a1b3Sopenharmony_ci
43653a5a1b3Sopenharmony_ci    a.path_or_host = NULL;
43753a5a1b3Sopenharmony_ci
43853a5a1b3Sopenharmony_ci    if (pa_is_ip6_address(name)) {
43953a5a1b3Sopenharmony_ci        size_t len = strlen(name);
44053a5a1b3Sopenharmony_ci        name_buf = pa_xmalloc(len + 3);
44153a5a1b3Sopenharmony_ci        memcpy(name_buf + 1, name, len);
44253a5a1b3Sopenharmony_ci        name_buf[0] = '[';
44353a5a1b3Sopenharmony_ci        name_buf[len + 1] = ']';
44453a5a1b3Sopenharmony_ci        name_buf[len + 2] = '\0';
44553a5a1b3Sopenharmony_ci    } else {
44653a5a1b3Sopenharmony_ci        name_buf = pa_xstrdup(name);
44753a5a1b3Sopenharmony_ci    }
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    if (pa_parse_address(name_buf, &a) < 0) {
45053a5a1b3Sopenharmony_ci        pa_log_warn("parsing address failed: %s", name_buf);
45153a5a1b3Sopenharmony_ci        goto finish;
45253a5a1b3Sopenharmony_ci    }
45353a5a1b3Sopenharmony_ci
45453a5a1b3Sopenharmony_ci    if (!a.port)
45553a5a1b3Sopenharmony_ci        a.port = default_port;
45653a5a1b3Sopenharmony_ci
45753a5a1b3Sopenharmony_ci    switch (a.type) {
45853a5a1b3Sopenharmony_ci        case PA_PARSED_ADDRESS_UNIX:
45953a5a1b3Sopenharmony_ci            if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
46053a5a1b3Sopenharmony_ci                start_timeout(c, use_rtclock);
46153a5a1b3Sopenharmony_ci            break;
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci        case PA_PARSED_ADDRESS_TCP4:  /* Fallthrough */
46453a5a1b3Sopenharmony_ci        case PA_PARSED_ADDRESS_TCP6:  /* Fallthrough */
46553a5a1b3Sopenharmony_ci        case PA_PARSED_ADDRESS_TCP_AUTO: {
46653a5a1b3Sopenharmony_ci            struct addrinfo hints;
46753a5a1b3Sopenharmony_ci            char port[12];
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci            pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
47053a5a1b3Sopenharmony_ci
47153a5a1b3Sopenharmony_ci            pa_zero(hints);
47253a5a1b3Sopenharmony_ci            if (a.type == PA_PARSED_ADDRESS_TCP4)
47353a5a1b3Sopenharmony_ci                hints.ai_family = PF_INET;
47453a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
47553a5a1b3Sopenharmony_ci            else if (a.type == PA_PARSED_ADDRESS_TCP6)
47653a5a1b3Sopenharmony_ci                hints.ai_family = PF_INET6;
47753a5a1b3Sopenharmony_ci#endif
47853a5a1b3Sopenharmony_ci            else
47953a5a1b3Sopenharmony_ci                hints.ai_family = PF_UNSPEC;
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci            hints.ai_socktype = SOCK_STREAM;
48253a5a1b3Sopenharmony_ci
48353a5a1b3Sopenharmony_ci#if defined(HAVE_LIBASYNCNS)
48453a5a1b3Sopenharmony_ci            {
48553a5a1b3Sopenharmony_ci                asyncns_t *asyncns;
48653a5a1b3Sopenharmony_ci
48753a5a1b3Sopenharmony_ci                if (!(asyncns = asyncns_new(1)))
48853a5a1b3Sopenharmony_ci                    goto finish;
48953a5a1b3Sopenharmony_ci
49053a5a1b3Sopenharmony_ci                c = socket_client_new(m);
49153a5a1b3Sopenharmony_ci                c->asyncns = asyncns;
49253a5a1b3Sopenharmony_ci                c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
49353a5a1b3Sopenharmony_ci                pa_assert_se(c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints));
49453a5a1b3Sopenharmony_ci                start_timeout(c, use_rtclock);
49553a5a1b3Sopenharmony_ci            }
49653a5a1b3Sopenharmony_ci#elif defined(HAVE_GETADDRINFO)
49753a5a1b3Sopenharmony_ci            {
49853a5a1b3Sopenharmony_ci                int ret;
49953a5a1b3Sopenharmony_ci                struct addrinfo *res = NULL;
50053a5a1b3Sopenharmony_ci
50153a5a1b3Sopenharmony_ci                ret = getaddrinfo(a.path_or_host, port, &hints, &res);
50253a5a1b3Sopenharmony_ci
50353a5a1b3Sopenharmony_ci                if (ret < 0 || !res)
50453a5a1b3Sopenharmony_ci                    goto finish;
50553a5a1b3Sopenharmony_ci
50653a5a1b3Sopenharmony_ci                if (res->ai_addr) {
50753a5a1b3Sopenharmony_ci                    if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
50853a5a1b3Sopenharmony_ci                        start_timeout(c, use_rtclock);
50953a5a1b3Sopenharmony_ci                }
51053a5a1b3Sopenharmony_ci
51153a5a1b3Sopenharmony_ci                freeaddrinfo(res);
51253a5a1b3Sopenharmony_ci            }
51353a5a1b3Sopenharmony_ci#else
51453a5a1b3Sopenharmony_ci            {
51553a5a1b3Sopenharmony_ci                struct hostent *host = NULL;
51653a5a1b3Sopenharmony_ci                struct sockaddr_in s;
51753a5a1b3Sopenharmony_ci
51853a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
51953a5a1b3Sopenharmony_ci                /* FIXME: PF_INET6 support */
52053a5a1b3Sopenharmony_ci                if (hints.ai_family == PF_INET6) {
52153a5a1b3Sopenharmony_ci                    pa_log_error("IPv6 is not supported on Windows");
52253a5a1b3Sopenharmony_ci                    goto finish;
52353a5a1b3Sopenharmony_ci                }
52453a5a1b3Sopenharmony_ci#endif
52553a5a1b3Sopenharmony_ci
52653a5a1b3Sopenharmony_ci                host = gethostbyname(a.path_or_host);
52753a5a1b3Sopenharmony_ci                if (!host) {
52853a5a1b3Sopenharmony_ci                    unsigned int addr = inet_addr(a.path_or_host);
52953a5a1b3Sopenharmony_ci                    if (addr != INADDR_NONE)
53053a5a1b3Sopenharmony_ci                        host = gethostbyaddr((char*)&addr, 4, AF_INET);
53153a5a1b3Sopenharmony_ci                }
53253a5a1b3Sopenharmony_ci
53353a5a1b3Sopenharmony_ci                if (!host)
53453a5a1b3Sopenharmony_ci                    goto finish;
53553a5a1b3Sopenharmony_ci
53653a5a1b3Sopenharmony_ci                pa_zero(s);
53753a5a1b3Sopenharmony_ci                s.sin_family = AF_INET;
53853a5a1b3Sopenharmony_ci                memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr));
53953a5a1b3Sopenharmony_ci                s.sin_port = htons(a.port);
54053a5a1b3Sopenharmony_ci
54153a5a1b3Sopenharmony_ci                if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
54253a5a1b3Sopenharmony_ci                    start_timeout(c, use_rtclock);
54353a5a1b3Sopenharmony_ci            }
54453a5a1b3Sopenharmony_ci#endif /* HAVE_LIBASYNCNS */
54553a5a1b3Sopenharmony_ci        }
54653a5a1b3Sopenharmony_ci    }
54753a5a1b3Sopenharmony_ci
54853a5a1b3Sopenharmony_cifinish:
54953a5a1b3Sopenharmony_ci    pa_xfree(name_buf);
55053a5a1b3Sopenharmony_ci    pa_xfree(a.path_or_host);
55153a5a1b3Sopenharmony_ci    return c;
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_ci}
55453a5a1b3Sopenharmony_ci
55553a5a1b3Sopenharmony_ci/* Return non-zero when the target sockaddr is considered
55653a5a1b3Sopenharmony_ci   local. "local" means UNIX socket or TCP socket on localhost. Other
55753a5a1b3Sopenharmony_ci   local IP addresses are not considered local. */
55853a5a1b3Sopenharmony_cibool pa_socket_client_is_local(pa_socket_client *c) {
55953a5a1b3Sopenharmony_ci    pa_assert(c);
56053a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(c) >= 1);
56153a5a1b3Sopenharmony_ci
56253a5a1b3Sopenharmony_ci    return c->local;
56353a5a1b3Sopenharmony_ci}
564