diff options
Diffstat (limited to 'ics-openvpn-stripped/main/openvpn/src/openvpn/socket.c')
-rw-r--r-- | ics-openvpn-stripped/main/openvpn/src/openvpn/socket.c | 3530 |
1 files changed, 3530 insertions, 0 deletions
diff --git a/ics-openvpn-stripped/main/openvpn/src/openvpn/socket.c b/ics-openvpn-stripped/main/openvpn/src/openvpn/socket.c new file mode 100644 index 00000000..9e6bd10c --- /dev/null +++ b/ics-openvpn-stripped/main/openvpn/src/openvpn/socket.c @@ -0,0 +1,3530 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "socket.h" +#include "fdmisc.h" +#include "misc.h" +#include "gremlin.h" +#include "plugin.h" +#include "ps.h" +#include "manage.h" +#include "misc.h" +#include "manage.h" +#include "openvpn.h" + +#include "memdbg.h" + +const int proto_overhead[] = { /* indexed by PROTO_x */ + 0, + IPv4_UDP_HEADER_SIZE, /* IPv4 */ + IPv4_TCP_HEADER_SIZE, + IPv4_TCP_HEADER_SIZE, + IPv6_UDP_HEADER_SIZE, /* IPv6 */ + IPv6_TCP_HEADER_SIZE, + IPv6_TCP_HEADER_SIZE, + IPv6_TCP_HEADER_SIZE, +}; + +/* + * Convert sockflags/getaddr_flags into getaddr_flags + */ +static unsigned int +sf2gaf(const unsigned int getaddr_flags, + const unsigned int sockflags) +{ + if (sockflags & SF_HOST_RANDOMIZE) + return getaddr_flags | GETADDR_RANDOMIZE; + else + return getaddr_flags; +} + +/* + * Functions related to the translation of DNS names to IP addresses. + */ + +static const char* +h_errno_msg(int h_errno_err) +{ + switch (h_errno_err) + { + case HOST_NOT_FOUND: + return "[HOST_NOT_FOUND] The specified host is unknown."; + case NO_DATA: + return "[NO_DATA] The requested name is valid but does not have an IP address."; + case NO_RECOVERY: + return "[NO_RECOVERY] A non-recoverable name server error occurred."; + case TRY_AGAIN: + return "[TRY_AGAIN] A temporary error occurred on an authoritative name server."; + } + return "[unknown h_errno value]"; +} + +/* + * Translate IP addr or hostname to in_addr_t. + * If resolve error, try again for + * resolve_retry_seconds seconds. + */ +in_addr_t +getaddr (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + volatile int *signal_received) +{ + struct addrinfo *ai; + int status; + status = openvpn_getaddrinfo (flags & ~GETADDR_HOST_ORDER, hostname, NULL, + resolve_retry_seconds, signal_received, AF_INET, &ai); + if(status==0) { + struct in_addr ia; + if(succeeded) + *succeeded=true; + ia = ((struct sockaddr_in*)ai->ai_addr)->sin_addr; + freeaddrinfo(ai); + return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr; + } else { + if(succeeded) + *succeeded =false; + return 0; + } +} + +static inline bool +streqnull (const char* a, const char* b) +{ + if (a == NULL && b == NULL) + return true; + else if (a == NULL || b == NULL) + return false; + else + return streq (a, b); +} + +/* + get_cached_dns_entry return 0 on success and -1 + otherwise. (like getaddrinfo) + */ +static int +get_cached_dns_entry (struct cached_dns_entry* dns_cache, + const char* hostname, + const char* servname, + int ai_family, + int resolve_flags, + struct addrinfo **ai) +{ + struct cached_dns_entry *ph; + int flags; + + /* Only use flags that are relevant for the structure */ + flags = resolve_flags & GETADDR_CACHE_MASK; + + for (ph = dns_cache; ph ; ph = ph->next) + { + if (streqnull (ph->hostname, hostname) && + streqnull (ph->servname, servname) && + ph->ai_family == ai_family && + ph->flags == flags) + { + *ai = ph->ai; + return 0; + } + } + return -1; +} + + +static int +do_preresolve_host (struct context *c, + const char *hostname, + const char *servname, + const int af, + const int flags) +{ + struct addrinfo *ai; + int status; + + if (get_cached_dns_entry(c->c1.dns_cache, + hostname, + servname, + af, + flags, + &ai) == 0 ) + { + /* entry already cached, return success */ + return 0; + } + + status = openvpn_getaddrinfo (flags, hostname, servname, + c->options.resolve_retry_seconds, NULL, + af, &ai); + if (status == 0) + { + struct cached_dns_entry *ph; + + ALLOC_OBJ_CLEAR_GC (ph, struct cached_dns_entry, &c->gc); + ph->ai = ai; + ph->hostname = hostname; + ph->servname = servname; + ph->flags = flags & GETADDR_CACHE_MASK; + + if (!c->c1.dns_cache) + c->c1.dns_cache = ph; + else + { + struct cached_dns_entry *prev = c->c1.dns_cache; + while (prev->next) + prev = prev->next; + prev->next = ph; + } + + gc_addspecial (ai, &gc_freeaddrinfo_callback, &c->gc); + + } + return status; +} + +void +do_preresolve (struct context *c) +{ + int i; + struct connection_list *l = c->options.connection_list; + const unsigned int preresolve_flags = GETADDR_RESOLVE| + GETADDR_UPDATE_MANAGEMENT_STATE| + GETADDR_MENTION_RESOLVE_RETRY| + GETADDR_FATAL; + + + for (i = 0; i < l->len; ++i) + { + int status; + const char *remote; + int flags = preresolve_flags; + + struct connection_entry* ce = c->options.connection_list->array[i]; + + if (proto_is_dgram(ce->proto)) + flags |= GETADDR_DATAGRAM; + + if (c->options.sockflags & SF_HOST_RANDOMIZE) + flags |= GETADDR_RANDOMIZE; + + if (c->options.ip_remote_hint) + remote = c->options.ip_remote_hint; + else + remote = ce->remote; + + /* HTTP remote hostname does not need to be resolved */ + if (! ce->http_proxy_options) + { + status = do_preresolve_host (c, remote, ce->remote_port, ce->af, flags); + if (status != 0) + goto err; + } + + /* Preresolve proxy */ + if (ce->http_proxy_options) + { + status = do_preresolve_host (c, + ce->http_proxy_options->server, + ce->http_proxy_options->port, + ce->af, + preresolve_flags); + + if (status != 0) + goto err; + } + + if (ce->socks_proxy_server) + { + status = do_preresolve_host (c, + ce->socks_proxy_server, + ce->socks_proxy_port, + ce->af, + flags); + if (status != 0) + goto err; + } + + if (ce->bind_local) + { + flags |= GETADDR_PASSIVE; + flags &= ~GETADDR_RANDOMIZE; + status = do_preresolve_host (c, ce->local, ce->local_port, ce->af, flags); + if (status != 0) + goto err; + + } + + } + return; + + err: + throw_signal_soft (SIGHUP, "Preresolving failed"); +} + +/* + * Translate IPv4/IPv6 addr or hostname into struct addrinfo + * If resolve error, try again for resolve_retry_seconds seconds. + */ +int +openvpn_getaddrinfo (unsigned int flags, + const char *hostname, + const char *servname, + int resolve_retry_seconds, + volatile int *signal_received, + int ai_family, + struct addrinfo **res) +{ + struct addrinfo hints; + int status; + int sigrec = 0; + int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS; + struct gc_arena gc = gc_new (); + const char *print_hostname; + const char *print_servname; + + ASSERT(res); + +#if defined(HAVE_RES_INIT) + res_init (); +#endif + + ASSERT (hostname || servname); + ASSERT (!(flags & GETADDR_HOST_ORDER)); + + if (hostname && (flags & GETADDR_RANDOMIZE)) + hostname = hostname_randomize(hostname, &gc); + + if(hostname) + print_hostname = hostname; + else + print_hostname = "undefined"; + + if(servname) + print_servname = servname; + else + print_servname = ""; + + if (flags & GETADDR_MSG_VIRT_OUT) + msglevel |= M_MSG_VIRT_OUT; + + if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL)) + && !signal_received) + signal_received = &sigrec; + + /* try numeric ipv6 addr first */ + CLEAR(hints); + hints.ai_family = ai_family; + hints.ai_flags = AI_NUMERICHOST; + + if(flags & GETADDR_PASSIVE) + hints.ai_flags |= AI_PASSIVE; + + if(flags & GETADDR_DATAGRAM) + hints.ai_socktype = SOCK_DGRAM; + else + hints.ai_socktype = SOCK_STREAM; + + status = getaddrinfo(hostname, servname, &hints, res); + + if (status != 0) /* parse as numeric address failed? */ + { + const int fail_wait_interval = 5; /* seconds */ + /* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 */ + int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : + ((resolve_retry_seconds + 4)/ fail_wait_interval); + const char *fmt; + int level = 0; + + fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s)"; + if ((flags & GETADDR_MENTION_RESOLVE_RETRY) + && !resolve_retry_seconds) + fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s) (I would have retried this name query if you had specified the --resolv-retry option.)"; + + if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL) + { + msg (msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)", + print_hostname,print_servname, gai_strerror(status)); + goto done; + } + +#ifdef ENABLE_MANAGEMENT + if (flags & GETADDR_UPDATE_MANAGEMENT_STATE) + { + if (management) + management_set_state (management, + OPENVPN_STATE_RESOLVE, + NULL, + (in_addr_t)0, + (in_addr_t)0); + } +#endif + + /* + * Resolve hostname + */ + while (true) + { + /* try hostname lookup */ + hints.ai_flags &= ~AI_NUMERICHOST; + dmsg (D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d", + flags, hints.ai_family, hints.ai_socktype); + status = getaddrinfo(hostname, servname, &hints, res); + + if (signal_received) + { + get_signal (signal_received); + if (*signal_received) /* were we interrupted by a signal? */ + { + if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */ + { + msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt"); + *signal_received = 0; + } + else + { + if (0 == status) { + ASSERT(res); + freeaddrinfo(*res); + res = NULL; + } + goto done; + } + } + } + + /* success? */ + if (0 == status) + break; + + /* resolve lookup failed, should we + continue or fail? */ + level = msglevel; + if (resolve_retries > 0) + level = D_RESOLVE_ERRORS; + + msg (level, + fmt, + print_hostname, + print_servname, + gai_strerror(status)); + + if (--resolve_retries <= 0) + goto done; + + openvpn_sleep (fail_wait_interval); + } + + ASSERT(res); + + /* hostname resolve succeeded */ + + /* + * Do not choose an IP Addresse by random or change the order * + * of IP addresses, doing so will break RFC 3484 address selection * + */ + } + else + { + /* IP address parse succeeded */ + } + + done: + if (signal_received && *signal_received) + { + int level = 0; + if (flags & GETADDR_FATAL_ON_SIGNAL) + level = M_FATAL; + else if (flags & GETADDR_WARN_ON_SIGNAL) + level = M_WARN; + msg (level, "RESOLVE: signal received during DNS resolution attempt"); + } + + gc_free (&gc); + return status; +} + +/* + * We do our own inet_aton because the glibc function + * isn't very good about error checking. + */ +int +openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr) +{ + unsigned int a, b, c, d; + + CLEAR (*addr); + if (sscanf (dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) + { + if (a < 256 && b < 256 && c < 256 && d < 256) + { + addr->s_addr = htonl (a<<24 | b<<16 | c<<8 | d); + return OIA_IP; /* good dotted quad */ + } + } + if (string_class (dotted_quad, CC_DIGIT|CC_DOT, 0)) + return OIA_ERROR; /* probably a badly formatted dotted quad */ + else + return OIA_HOSTNAME; /* probably a hostname */ +} + +bool +ip_addr_dotted_quad_safe (const char *dotted_quad) +{ + /* verify non-NULL */ + if (!dotted_quad) + return false; + + /* verify length is within limits */ + if (strlen (dotted_quad) > 15) + return false; + + /* verify that all chars are either numeric or '.' and that no numeric + substring is greater than 3 chars */ + { + int nnum = 0; + const char *p = dotted_quad; + int c; + + while ((c = *p++)) + { + if (c >= '0' && c <= '9') + { + ++nnum; + if (nnum > 3) + return false; + } + else if (c == '.') + { + nnum = 0; + } + else + return false; + } + } + + /* verify that string will convert to IP address */ + { + struct in_addr a; + return openvpn_inet_aton (dotted_quad, &a) == OIA_IP; + } +} + +bool +ipv6_addr_safe (const char *ipv6_text_addr) +{ + /* verify non-NULL */ + if (!ipv6_text_addr) + return false; + + /* verify length is within limits */ + if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN ) + return false; + + /* verify that string will convert to IPv6 address */ + { + struct in6_addr a6; + return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1; + } +} + +static bool +dns_addr_safe (const char *addr) +{ + if (addr) + { + const size_t len = strlen (addr); + return len > 0 && len <= 255 && string_class (addr, CC_ALNUM|CC_DASH|CC_DOT, 0); + } + else + return false; +} + +bool +ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn) +{ + if (ip_addr_dotted_quad_safe (addr)) + return true; + else if (allow_fqdn) + return dns_addr_safe (addr); + else + return false; +} + +bool +mac_addr_safe (const char *mac_addr) +{ + /* verify non-NULL */ + if (!mac_addr) + return false; + + /* verify length is within limits */ + if (strlen (mac_addr) > 17) + return false; + + /* verify that all chars are either alphanumeric or ':' and that no + alphanumeric substring is greater than 2 chars */ + { + int nnum = 0; + const char *p = mac_addr; + int c; + + while ((c = *p++)) + { + if ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + { + ++nnum; + if (nnum > 2) + return false; + } + else if (c == ':') + { + nnum = 0; + } + else + return false; + } + } + + /* error-checking is left to script invoked in lladdr.c */ + return true; +} + +static int +socket_get_sndbuf (int sd) +{ +#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF) + int val; + socklen_t len; + + len = sizeof (val); + if (getsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &val, &len) == 0 + && len == sizeof (val)) + return val; +#endif + return 0; +} + +static void +socket_set_sndbuf (int sd, int size) +{ +#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF) + if (size > 0 && size < SOCKET_SND_RCV_BUF_MAX) + { + if (setsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof (size)) != 0) + { + msg (M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size); + } + } +#endif +} + +static int +socket_get_rcvbuf (int sd) +{ +#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF) + int val; + socklen_t len; + + len = sizeof (val); + if (getsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &val, &len) == 0 + && len == sizeof (val)) + return val; +#endif + return 0; +} + +static bool +socket_set_rcvbuf (int sd, int size) +{ +#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF) + if (size > 0 && size < SOCKET_SND_RCV_BUF_MAX) + { + if (setsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof (size)) != 0) + { + msg (M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size); + return false; + } + } + return true; +#endif +} + +static void +socket_set_buffers (int fd, const struct socket_buffer_size *sbs) +{ + if (sbs) + { + const int sndbuf_old = socket_get_sndbuf (fd); + const int rcvbuf_old = socket_get_rcvbuf (fd); + + if (sbs->sndbuf) + socket_set_sndbuf (fd, sbs->sndbuf); + + if (sbs->rcvbuf) + socket_set_rcvbuf (fd, sbs->rcvbuf); + + msg (D_OSBUF, "Socket Buffers: R=[%d->%d] S=[%d->%d]", + rcvbuf_old, + socket_get_rcvbuf (fd), + sndbuf_old, + socket_get_sndbuf (fd)); + } +} + +/* + * Set other socket options + */ + +static bool +socket_set_tcp_nodelay (int sd, int state) +{ +#if defined(WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) + if (setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof (state)) != 0) + { + msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state); + return false; + } + else + { + dmsg (D_OSBUF, "Socket flags: TCP_NODELAY=%d succeeded", state); + return true; + } +#else + msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed (No kernel support)", state); + return false; +#endif +} + +static inline void +socket_set_mark (int sd, int mark) +{ +#if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK + if (mark && setsockopt (sd, SOL_SOCKET, SO_MARK, &mark, sizeof (mark)) != 0) + msg (M_WARN, "NOTE: setsockopt SO_MARK=%d failed", mark); +#endif +} + +static bool +socket_set_flags (int sd, unsigned int sockflags) +{ + if (sockflags & SF_TCP_NODELAY) + return socket_set_tcp_nodelay (sd, 1); + else + return true; +} + +bool +link_socket_update_flags (struct link_socket *ls, unsigned int sockflags) +{ + if (ls && socket_defined (ls->sd)) + return socket_set_flags (ls->sd, ls->sockflags = sockflags); + else + return false; +} + +void +link_socket_update_buffer_sizes (struct link_socket *ls, int rcvbuf, int sndbuf) +{ + if (ls && socket_defined (ls->sd)) + { + ls->socket_buffer_sizes.sndbuf = sndbuf; + ls->socket_buffer_sizes.rcvbuf = rcvbuf; + socket_set_buffers (ls->sd, &ls->socket_buffer_sizes); + } +} + +/* + * SOCKET INITALIZATION CODE. + * Create a TCP/UDP socket + */ + +socket_descriptor_t +create_socket_tcp (struct addrinfo* addrinfo) +{ + socket_descriptor_t sd; + + ASSERT (addrinfo); + ASSERT (addrinfo->ai_socktype == SOCK_STREAM); + + if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) + msg (M_ERR, "Cannot create TCP socket"); + +#ifndef WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */ + /* set SO_REUSEADDR on socket */ + { + int on = 1; + if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof (on)) < 0) + msg (M_ERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP socket"); + } +#endif + + return sd; +} + +static socket_descriptor_t +create_socket_udp (struct addrinfo* addrinfo, const unsigned int flags) +{ + socket_descriptor_t sd; + + ASSERT (addrinfo); + ASSERT (addrinfo->ai_socktype == SOCK_DGRAM); + + if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) + msg (M_ERR, "UDP: Cannot create UDP/UDP6 socket"); +#if ENABLE_IP_PKTINFO + else if (flags & SF_USE_IP_PKTINFO) + { + int pad = 1; + if(addrinfo->ai_family == AF_INET) + { +#ifdef IP_PKTINFO + if (setsockopt (sd, SOL_IP, IP_PKTINFO, + (void*)&pad, sizeof(pad)) < 0) + msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO"); +#elif defined(IP_RECVDSTADDR) + if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR, + (void*)&pad, sizeof(pad)) < 0) + msg(M_ERR, "UDP: failed setsockopt for IP_RECVDSTADDR"); +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + } + else if (addrinfo->ai_family == AF_INET6 ) + { +#ifndef IPV6_RECVPKTINFO /* Some older Darwin platforms require this */ + if (setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, + (void*)&pad, sizeof(pad)) < 0) +#else + if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (void*)&pad, sizeof(pad)) < 0) +#endif + msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO"); + } + } +#endif + return sd; +} + +static void bind_local (struct link_socket *sock, const sa_family_t ai_family) +{ + /* bind to local address/port */ + if (sock->bind_local) + { + if (sock->socks_proxy && sock->info.proto == PROTO_UDP) + socket_bind (sock->ctrl_sd, sock->info.lsa->bind_local, + ai_family, "SOCKS", false); + else + socket_bind (sock->sd, sock->info.lsa->bind_local, + ai_family, + "TCP/UDP", sock->info.bind_ipv6_only); + } +} + +static void +create_socket (struct link_socket* sock, struct addrinfo* addr) +{ + if (addr->ai_protocol == IPPROTO_UDP || addr->ai_socktype == SOCK_DGRAM) + { + sock->sd = create_socket_udp (addr, sock->sockflags); + sock->sockflags |= SF_GETADDRINFO_DGRAM; + + /* Assume that control socket and data socket to the socks proxy + * are using the same IP family */ + if (sock->socks_proxy) + { + /* Construct a temporary addrinfo to create the socket, + * currently resolve two remote addresses is not supported, + * TODO: Rewrite the whole resolve_remote */ + struct addrinfo addrinfo_tmp = *addr; + addrinfo_tmp.ai_socktype = SOCK_STREAM; + addrinfo_tmp.ai_protocol = IPPROTO_TCP; + sock->ctrl_sd = create_socket_tcp (&addrinfo_tmp); + } + } + else if (addr->ai_protocol == IPPROTO_TCP || addr->ai_socktype == SOCK_STREAM) + { + sock->sd = create_socket_tcp (addr); + } + else + { + ASSERT (0); + } + /* set socket buffers based on --sndbuf and --rcvbuf options */ + socket_set_buffers (sock->sd, &sock->socket_buffer_sizes); + + /* set socket to --mark packets with given value */ + socket_set_mark (sock->sd, sock->mark); + + bind_local (sock, addr->ai_family); +} + +#ifdef TARGET_ANDROID +static void protect_fd_nonlocal (int fd, const struct sockaddr* addr) +{ + /* pass socket FD to management interface to pass on to VPNService API + * as "protected socket" (exempt from being routed into tunnel) + */ + if (addr_local (addr)) { + msg(M_DEBUG, "Address is local, not protecting socket fd %d", fd); + return; + } + + msg(M_DEBUG, "Protecting socket fd %d", fd); + management->connection.fdtosend = fd; + management_android_control (management, "PROTECTFD", __func__); +} +#endif + +/* + * Functions used for establishing a TCP stream connection. + */ +static void +socket_do_listen (socket_descriptor_t sd, + const struct addrinfo *local, + bool do_listen, + bool do_set_nonblock) +{ + struct gc_arena gc = gc_new (); + if (do_listen) + { + ASSERT(local); + msg (M_INFO, "Listening for incoming TCP connection on %s", + print_sockaddr (local->ai_addr, &gc)); + if (listen (sd, 1)) + msg (M_ERR, "TCP: listen() failed"); + } + + /* set socket to non-blocking mode */ + if (do_set_nonblock) + set_nonblock (sd); + + gc_free (&gc); +} + +socket_descriptor_t +socket_do_accept (socket_descriptor_t sd, + struct link_socket_actual *act, + const bool nowait) +{ + /* af_addr_size WILL return 0 in this case if AFs other than AF_INET + * are compiled because act is empty here. + * could use getsockname() to support later remote_len check + */ + socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family); + socklen_t remote_len = sizeof(act->dest.addr); + socket_descriptor_t new_sd = SOCKET_UNDEFINED; + + CLEAR (*act); + +#ifdef HAVE_GETPEERNAME + if (nowait) + { + new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len); + + if (!socket_defined (new_sd)) + msg (D_LINK_ERRORS | M_ERRNO, "TCP: getpeername() failed"); + else + new_sd = sd; + } +#else + if (nowait) + msg (M_WARN, "TCP: this OS does not provide the getpeername() function"); +#endif + else + { + new_sd = accept (sd, &act->dest.addr.sa, &remote_len); + } + +#if 0 /* For debugging only, test the effect of accept() failures */ + { + static int foo = 0; + ++foo; + if (foo & 1) + new_sd = -1; + } +#endif + + if (!socket_defined (new_sd)) + { + msg (D_LINK_ERRORS | M_ERRNO, "TCP: accept(%d) failed", sd); + } + /* only valid if we have remote_len_af!=0 */ + else if (remote_len_af && remote_len != remote_len_af) + { + msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len); + openvpn_close_socket (new_sd); + new_sd = SOCKET_UNDEFINED; + } + return new_sd; +} + +static void +tcp_connection_established (const struct link_socket_actual *act) +{ + struct gc_arena gc = gc_new (); + msg (M_INFO, "TCP connection established with %s", + print_link_socket_actual (act, &gc)); + gc_free (&gc); +} + +static int +socket_listen_accept (socket_descriptor_t sd, + struct link_socket_actual *act, + const char *remote_dynamic, + const struct addrinfo *local, + bool do_listen, + bool nowait, + volatile int *signal_received) +{ + struct gc_arena gc = gc_new (); + /* struct openvpn_sockaddr *remote = &act->dest; */ + struct openvpn_sockaddr remote_verify = act->dest; + int new_sd = SOCKET_UNDEFINED; + + CLEAR (*act); + socket_do_listen (sd, local, do_listen, true); + + while (true) + { + int status; + fd_set reads; + struct timeval tv; + + FD_ZERO (&reads); + FD_SET (sd, &reads); + tv.tv_sec = 0; + tv.tv_usec = 0; + + status = select (sd + 1, &reads, NULL, NULL, &tv); + + get_signal (signal_received); + if (*signal_received) + { + gc_free (&gc); + return sd; + } + + if (status < 0) + msg (D_LINK_ERRORS | M_ERRNO, "TCP: select() failed"); + + if (status <= 0) + { + openvpn_sleep (1); + continue; + } + + new_sd = socket_do_accept (sd, act, nowait); + + if (socket_defined (new_sd)) + { + struct addrinfo* ai = NULL; + if(remote_dynamic) + openvpn_getaddrinfo(0, remote_dynamic, NULL, 1, NULL, + remote_verify.addr.sa.sa_family, &ai); + + if(ai && !addrlist_match(&remote_verify, ai)) + { + msg (M_WARN, + "TCP NOTE: Rejected connection attempt from %s due to --remote setting", + print_link_socket_actual (act, &gc)); + if (openvpn_close_socket (new_sd)) + msg (M_ERR, "TCP: close socket failed (new_sd)"); + freeaddrinfo(ai); + } + else + { + if(ai) + freeaddrinfo(ai); + break; + } + } + openvpn_sleep (1); + } + + if (!nowait && openvpn_close_socket (sd)) + msg (M_ERR, "TCP: close socket failed (sd)"); + + tcp_connection_established (act); + + gc_free (&gc); + return new_sd; +} + +void +socket_bind (socket_descriptor_t sd, + struct addrinfo *local, + int ai_family, + const char *prefix, + bool ipv6only) +{ + struct gc_arena gc = gc_new (); + + /* FIXME (schwabe) + * getaddrinfo for the bind address might return multiple AF_INET/AF_INET6 + * entries for the requested protocol. + * For example if an address has multiple A records + * What is the correct way to deal with it? + */ + + struct addrinfo* cur; + + ASSERT(local); + + + /* find the first addrinfo with correct ai_family */ + for (cur = local; cur; cur=cur->ai_next) + { + if(cur->ai_family == ai_family) + break; + } + if (!cur) + msg (M_FATAL, "%s: Socket bind failed: Addr to bind has no %s record", + prefix, addr_family_name(ai_family)); + + if (ai_family == AF_INET6) + { + int v6only = ipv6only ? 1: 0; /* setsockopt must have an "int" */ + + msg (M_INFO, "setsockopt(IPV6_V6ONLY=%d)", v6only); + if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))) + { + msg (M_NONFATAL|M_ERRNO, "Setting IPV6_V6ONLY=%d failed", v6only); + } + } + if (bind (sd, cur->ai_addr, cur->ai_addrlen)) + { + const int errnum = openvpn_errno (); + msg (M_FATAL, "%s: Socket bind failed on local address %s: %s", + prefix, + print_sockaddr_ex (local->ai_addr, ":", PS_SHOW_PORT, &gc), + strerror_ts (errnum, &gc)); + } + gc_free (&gc); +} + +int +openvpn_connect (socket_descriptor_t sd, + const struct sockaddr *remote, + int connect_timeout, + volatile int *signal_received) +{ + int status = 0; + +#ifdef TARGET_ANDROID + protect_fd_nonlocal(sd, remote); +#endif + +#ifdef CONNECT_NONBLOCK + set_nonblock (sd); + status = connect (sd, remote, af_addr_size(remote->sa_family)); + if (status) + status = openvpn_errno (); + if ( +#ifdef WIN32 + status == WSAEWOULDBLOCK +#else + status == EINPROGRESS +#endif + ) + { + while (true) + { + fd_set writes; + struct timeval tv; + + FD_ZERO (&writes); + FD_SET (sd, &writes); + tv.tv_sec = 0; + tv.tv_usec = 0; + + status = select (sd + 1, NULL, &writes, NULL, &tv); + + if (signal_received) + { + get_signal (signal_received); + if (*signal_received) + { + status = 0; + break; + } + } + if (status < 0) + { + status = openvpn_errno (); + break; + } + if (status <= 0) + { + if (--connect_timeout < 0) + { + status = ETIMEDOUT; + break; + } + openvpn_sleep (1); + continue; + } + + /* got it */ + { + int val = 0; + socklen_t len; + + len = sizeof (val); + if (getsockopt (sd, SOL_SOCKET, SO_ERROR, (void *) &val, &len) == 0 + && len == sizeof (val)) + status = val; + else + status = openvpn_errno (); + break; + } + } + } +#else + status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); + if (status) + status = openvpn_errno (); +#endif + + return status; +} + +void set_actual_address (struct link_socket_actual* actual, struct addrinfo* ai) +{ + CLEAR (*actual); + ASSERT (ai); + + if (ai->ai_family == AF_INET) + actual->dest.addr.in4 = + *((struct sockaddr_in*) ai->ai_addr); + else if (ai->ai_family == AF_INET6) + actual->dest.addr.in6 = + *((struct sockaddr_in6*) ai->ai_addr); + else + ASSERT(0); + +} + +void +socket_connect (socket_descriptor_t* sd, + const struct sockaddr* dest, + const int connect_timeout, + struct signal_info* sig_info) +{ + struct gc_arena gc = gc_new (); + int status; + +#ifdef CONNECT_NONBLOCK + msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]", + print_sockaddr (dest, &gc)); +#else + msg (M_INFO, "Attempting to establish TCP connection with %s", + print_sockaddr (dest, &gc)); +#endif + +#ifdef ENABLE_MANAGEMENT + if (management) + management_set_state (management, + OPENVPN_STATE_TCP_CONNECT, + NULL, + (in_addr_t)0, + (in_addr_t)0); +#endif + + /* Set the actual address */ + status = openvpn_connect (*sd, dest, connect_timeout, &sig_info->signal_received); + + get_signal (&sig_info->signal_received); + if (sig_info->signal_received) + goto done; + + if (status) { + + msg (D_LINK_ERRORS, + "TCP: connect to %s failed: %s", + print_sockaddr (dest, &gc), + strerror_ts (status, &gc)); + + openvpn_close_socket (*sd); + *sd = SOCKET_UNDEFINED; + sig_info->signal_received = SIGUSR1; + sig_info->source = SIG_SOURCE_CONNECTION_FAILED; + } else { + msg (M_INFO, "TCP connection established with %s", + print_sockaddr (dest, &gc)); + } + + done: + gc_free (&gc); +} + +/* For stream protocols, allocate a buffer to build up packet. + Called after frame has been finalized. */ + +static void +socket_frame_init (const struct frame *frame, struct link_socket *sock) +{ +#ifdef WIN32 + overlapped_io_init (&sock->reads, frame, FALSE, false); + overlapped_io_init (&sock->writes, frame, TRUE, false); + sock->rw_handle.read = sock->reads.overlapped.hEvent; + sock->rw_handle.write = sock->writes.overlapped.hEvent; +#endif + + if (link_socket_connection_oriented (sock)) + { +#ifdef WIN32 + stream_buf_init (&sock->stream_buf, + &sock->reads.buf_init, + sock->sockflags, + sock->info.proto); +#else + alloc_buf_sock_tun (&sock->stream_buf_data, + frame, + false, + FRAME_HEADROOM_MARKER_READ_STREAM); + + stream_buf_init (&sock->stream_buf, + &sock->stream_buf_data, + sock->sockflags, + sock->info.proto); +#endif + } +} + +/* + * Adjust frame structure based on a Path MTU value given + * to us by the OS. + */ +void +frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto) +{ + frame_set_mtu_dynamic (frame, pmtu - datagram_overhead (proto), SET_MTU_UPPER_BOUND); +} + +static void +resolve_bind_local (struct link_socket *sock, const sa_family_t af) +{ + struct gc_arena gc = gc_new (); + + /* resolve local address if undefined */ + if (!sock->info.lsa->bind_local) + { + int flags = GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | + GETADDR_FATAL | GETADDR_PASSIVE; + int status; + + if(proto_is_dgram(sock->info.proto)) + flags |= GETADDR_DATAGRAM; + + /* will return AF_{INET|INET6}from local_host */ + status = get_cached_dns_entry (sock->dns_cache, + sock->local_host, + sock->local_port, + af, + flags, + &sock->info.lsa->bind_local); + + if (status) + status = openvpn_getaddrinfo(flags, sock->local_host, sock->local_port, 0, + NULL, af, &sock->info.lsa->bind_local); + + if(status !=0) { + msg (M_FATAL, "getaddrinfo() failed for local \"%s:%s\": %s", + sock->local_host, sock->local_port, + gai_strerror(status)); + } + } + + gc_free (&gc); +} + +static void +resolve_remote (struct link_socket *sock, + int phase, + const char **remote_dynamic, + volatile int *signal_received) +{ + struct gc_arena gc = gc_new (); + + /* resolve remote address if undefined */ + if (!sock->info.lsa->remote_list) + { + if (sock->remote_host) + { + unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags); + int retry = 0; + int status = -1; + struct addrinfo* ai; + if (proto_is_dgram(sock->info.proto)) + flags |= GETADDR_DATAGRAM; + + if (sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE) + { + if (phase == 2) + flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL); + retry = 0; + } + else if (phase == 1) + { + if (sock->resolve_retry_seconds) + { + retry = 0; + } + else + { + flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY); + retry = 0; + } + } + else if (phase == 2) + { + if (sock->resolve_retry_seconds) + { + flags |= GETADDR_FATAL; + retry = sock->resolve_retry_seconds; + } + else + { + ASSERT (0); + } + } + else + { + ASSERT (0); + } + + + status = get_cached_dns_entry (sock->dns_cache, + sock->remote_host, + sock->remote_port, + sock->info.af, + flags, &ai); + if (status) + status = openvpn_getaddrinfo (flags, sock->remote_host, sock->remote_port, + retry, signal_received, sock->info.af, &ai); + + if(status == 0) { + sock->info.lsa->remote_list = ai; + sock->info.lsa->current_remote = ai; + + dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", + flags, + phase, + retry, + signal_received ? *signal_received : -1, + status); + } + if (signal_received) + { + if (*signal_received) + goto done; + } + if (status!=0) + { + if (signal_received) + *signal_received = SIGUSR1; + goto done; + } + } + } + + /* should we re-use previous active remote address? */ + if (link_socket_actual_defined (&sock->info.lsa->actual)) + { + msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s", + print_link_socket_actual (&sock->info.lsa->actual, &gc)); + if (remote_dynamic) + *remote_dynamic = NULL; + } + else + { + CLEAR (sock->info.lsa->actual); + if(sock->info.lsa->current_remote) + { + set_actual_address (&sock->info.lsa->actual, + sock->info.lsa->current_remote); + } + } + + done: + gc_free (&gc); +} + + + +struct link_socket * +link_socket_new (void) +{ + struct link_socket *sock; + + ALLOC_OBJ_CLEAR (sock, struct link_socket); + sock->sd = SOCKET_UNDEFINED; + sock->ctrl_sd = SOCKET_UNDEFINED; + return sock; +} + +void +link_socket_init_phase1 (struct link_socket *sock, + const char *local_host, + const char *local_port, + const char *remote_host, + const char *remote_port, + struct cached_dns_entry *dns_cache, + int proto, + sa_family_t af, + bool bind_ipv6_only, + int mode, + const struct link_socket *accept_from, + struct http_proxy_info *http_proxy, + struct socks_proxy_info *socks_proxy, +#ifdef ENABLE_DEBUG + int gremlin, +#endif + bool bind_local, + bool remote_float, + int inetd, + struct link_socket_addr *lsa, + const char *ipchange_command, + const struct plugin_list *plugins, + int resolve_retry_seconds, + int connect_timeout, + int mtu_discover_type, + int rcvbuf, + int sndbuf, + int mark, + unsigned int sockflags) +{ + ASSERT (sock); + + sock->local_host = local_host; + sock->local_port = local_port; + sock->remote_host = remote_host; + sock->remote_port = remote_port; + sock->dns_cache = dns_cache; + sock->http_proxy = http_proxy; + sock->socks_proxy = socks_proxy; + sock->bind_local = bind_local; + sock->inetd = inetd; + sock->resolve_retry_seconds = resolve_retry_seconds; + sock->connect_timeout = connect_timeout; + sock->mtu_discover_type = mtu_discover_type; + +#ifdef ENABLE_DEBUG + sock->gremlin = gremlin; +#endif + + sock->socket_buffer_sizes.rcvbuf = rcvbuf; + sock->socket_buffer_sizes.sndbuf = sndbuf; + + sock->sockflags = sockflags; + sock->mark = mark; + + sock->info.proto = proto; + sock->info.af = af; + sock->info.remote_float = remote_float; + sock->info.lsa = lsa; + sock->info.bind_ipv6_only = bind_ipv6_only; + sock->info.ipchange_command = ipchange_command; + sock->info.plugins = plugins; + + sock->mode = mode; + if (mode == LS_MODE_TCP_ACCEPT_FROM) + { + ASSERT (accept_from); + ASSERT (sock->info.proto == PROTO_TCP_SERVER); + ASSERT (!sock->inetd); + sock->sd = accept_from->sd; + } + + if (false) + ; + /* are we running in HTTP proxy mode? */ + else if (sock->http_proxy) + { + ASSERT (sock->info.proto == PROTO_TCP_CLIENT); + ASSERT (!sock->inetd); + + /* the proxy server */ + sock->remote_host = http_proxy->options.server; + sock->remote_port = http_proxy->options.port; + + /* the OpenVPN server we will use the proxy to connect to */ + sock->proxy_dest_host = remote_host; + sock->proxy_dest_port = remote_port; + } + /* or in Socks proxy mode? */ + else if (sock->socks_proxy) + { + ASSERT (!sock->inetd); + + /* the proxy server */ + sock->remote_host = socks_proxy->server; + sock->remote_port = socks_proxy->port; + + /* the OpenVPN server we will use the proxy to connect to */ + sock->proxy_dest_host = remote_host; + sock->proxy_dest_port = remote_port; + } + else + { + sock->remote_host = remote_host; + sock->remote_port = remote_port; + } + + /* bind behavior for TCP server vs. client */ + if (sock->info.proto == PROTO_TCP_SERVER) + { + if (sock->mode == LS_MODE_TCP_ACCEPT_FROM) + sock->bind_local = false; + else + sock->bind_local = true; + } + + /* were we started by inetd or xinetd? */ + if (sock->inetd) + { + ASSERT (sock->info.proto != PROTO_TCP_CLIENT); + ASSERT (socket_defined (inetd_socket_descriptor)); + sock->sd = inetd_socket_descriptor; + } + else if (mode != LS_MODE_TCP_ACCEPT_FROM) + { + if (sock->bind_local) { + resolve_bind_local (sock, sock->info.af); + } + resolve_remote (sock, 1, NULL, NULL); + } +} + +static +void phase2_inetd (struct link_socket* sock, const struct frame *frame, + const char *remote_dynamic, volatile int *signal_received) +{ + bool remote_changed = false; + + if (sock->info.proto == PROTO_TCP_SERVER) { + /* AF_INET as default (and fallback) for inetd */ + sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET; +#ifdef HAVE_GETSOCKNAME + { + /* inetd: hint family type for dest = local's */ + struct openvpn_sockaddr local_addr; + socklen_t addrlen = sizeof(local_addr); + if (getsockname (sock->sd, &local_addr.addr.sa, &addrlen) == 0) { + sock->info.lsa->actual.dest.addr.sa.sa_family = local_addr.addr.sa.sa_family; + dmsg (D_SOCKET_DEBUG, "inetd(%s): using sa_family=%d from getsockname(%d)", + proto2ascii(sock->info.proto, sock->info.af, false), + local_addr.addr.sa.sa_family, sock->sd); + } else + msg (M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET", + proto2ascii(sock->info.proto, sock->info.af, false), sock->sd); + } +#else + msg (M_WARN, "inetd(%s): this OS does not provide the getsockname() " + "function, using AF_INET", + proto2ascii(sock->info.proto, false)); +#endif + sock->sd = + socket_listen_accept (sock->sd, + &sock->info.lsa->actual, + remote_dynamic, + sock->info.lsa->bind_local, + false, + sock->inetd == INETD_NOWAIT, + signal_received); + + } + ASSERT (!remote_changed); +} + +static void +phase2_set_socket_flags (struct link_socket* sock) +{ + /* set misc socket parameters */ + socket_set_flags (sock->sd, sock->sockflags); + + /* set socket to non-blocking mode */ + set_nonblock (sock->sd); + + /* set socket file descriptor to not pass across execs, so that + scripts don't have access to it */ + set_cloexec (sock->sd); + + if (socket_defined (sock->ctrl_sd)) + set_cloexec (sock->ctrl_sd); + + /* set Path MTU discovery options on the socket */ + set_mtu_discover_type (sock->sd, sock->mtu_discover_type); + +#if EXTENDED_SOCKET_ERROR_CAPABILITY + /* if the OS supports it, enable extended error passing on the socket */ + set_sock_extended_error_passing (sock->sd); +#endif +} + + +static void +linksock_print_addr (struct link_socket *sock) +{ + struct gc_arena gc = gc_new (); + const int msglevel = (sock->mode == LS_MODE_TCP_ACCEPT_FROM) ? D_INIT_MEDIUM : M_INFO; + + /* print local address */ + if (sock->inetd) + msg (msglevel, "%s link local: [inetd]", proto2ascii (sock->info.proto, sock->info.af, true)); + else if (sock->bind_local) + { + sa_family_t ai_family = sock->info.lsa->actual.dest.addr.sa.sa_family; + /* Socket is always bound on the first matching address, + * For bound sockets with no remote addr this is the element of + * the list */ + struct addrinfo *cur; + for (cur = sock->info.lsa->bind_local; cur; cur=cur->ai_next) + { + if(!ai_family || ai_family == cur->ai_family) + break; + } + ASSERT (cur); + msg (msglevel, "%s link local (bound): %s", + proto2ascii (sock->info.proto, sock->info.af, true), + print_sockaddr(cur->ai_addr,&gc)); + } + else + msg (msglevel, "%s link local: (not bound)", + proto2ascii (sock->info.proto, sock->info.af, true)); + + /* print active remote address */ + msg (msglevel, "%s link remote: %s", + proto2ascii (sock->info.proto, sock->info.af, true), + print_link_socket_actual_ex (&sock->info.lsa->actual, + ":", + PS_SHOW_PORT_IF_DEFINED, + &gc)); + gc_free(&gc); +} + +static void +phase2_tcp_server (struct link_socket *sock, const char *remote_dynamic, + volatile int *signal_received) +{ + switch (sock->mode) + { + case LS_MODE_DEFAULT: + sock->sd = socket_listen_accept (sock->sd, + &sock->info.lsa->actual, + remote_dynamic, + sock->info.lsa->bind_local, + true, + false, + signal_received); + break; + case LS_MODE_TCP_LISTEN: + socket_do_listen (sock->sd, + sock->info.lsa->bind_local, + true, + false); + break; + case LS_MODE_TCP_ACCEPT_FROM: + sock->sd = socket_do_accept (sock->sd, + &sock->info.lsa->actual, + false); + if (!socket_defined (sock->sd)) + { + *signal_received = SIGTERM; + return; + } + tcp_connection_established (&sock->info.lsa->actual); + break; + default: + ASSERT (0); + } +} + + +static void +phase2_tcp_client (struct link_socket *sock, struct signal_info *sig_info) +{ + bool proxy_retry = false; + do { + socket_connect (&sock->sd, + sock->info.lsa->current_remote->ai_addr, + sock->connect_timeout, + sig_info); + + if (sig_info->signal_received) + return; + + if (false) + ; + else if (sock->http_proxy) + { + proxy_retry = establish_http_proxy_passthru (sock->http_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + &sock->stream_buf.residual, + &sig_info->signal_received); + } + else if (sock->socks_proxy) + { + establish_socks_proxy_passthru (sock->socks_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + &sig_info->signal_received); + } + if (proxy_retry) + { + openvpn_close_socket (sock->sd); + sock->sd = create_socket_tcp (sock->info.lsa->current_remote); + } + + } while (proxy_retry); + +} + +static void +phase2_socks_client (struct link_socket *sock, struct signal_info *sig_info) +{ + socket_connect (&sock->ctrl_sd, + sock->info.lsa->current_remote->ai_addr, + sock->connect_timeout, + sig_info); + + if (sig_info->signal_received) + return; + + establish_socks_proxy_udpassoc (sock->socks_proxy, + sock->ctrl_sd, + sock->sd, + &sock->socks_relay.dest, + &sig_info->signal_received); + + if (sig_info->signal_received) + return; + + sock->remote_host = sock->proxy_dest_host; + sock->remote_port = sock->proxy_dest_port; + + addr_zero_host(&sock->info.lsa->actual.dest); + if (sock->info.lsa->remote_list) + { + freeaddrinfo(sock->info.lsa->remote_list); + sock->info.lsa->current_remote = NULL; + sock->info.lsa->remote_list = NULL; + } + + resolve_remote (sock, 1, NULL, &sig_info->signal_received); +} + +/* finalize socket initialization */ +void +link_socket_init_phase2 (struct link_socket *sock, + const struct frame *frame, + struct signal_info *sig_info) +{ + const char *remote_dynamic = NULL; + int sig_save = 0; + + ASSERT (sock); + + if (sig_info && sig_info->signal_received) + { + sig_save = sig_info->signal_received; + sig_info->signal_received = 0; + } + + /* initialize buffers */ + socket_frame_init (frame, sock); + + /* + * Pass a remote name to connect/accept so that + * they can test for dynamic IP address changes + * and throw a SIGUSR1 if appropriate. + */ + if (sock->resolve_retry_seconds) + remote_dynamic = sock->remote_host; + + /* were we started by inetd or xinetd? */ + if (sock->inetd) + { + phase2_inetd (sock, frame, remote_dynamic, &sig_info->signal_received); + if (sig_info && sig_info->signal_received) + goto done; + + } + else + { + /* Second chance to resolv/create socket */ + resolve_remote (sock, 2, &remote_dynamic, &sig_info->signal_received); + + /* If a valid remote has been found, create the socket with its addrinfo */ + if (sock->info.lsa->current_remote) + create_socket (sock, sock->info.lsa->current_remote); + + /* If socket has not already been created create it now */ + if (sock->sd == SOCKET_UNDEFINED) + { + /* If we have no --remote and have still not figured out the + * protocol family to use we will use the first of the bind */ + + if (sock->bind_local && !sock->remote_host && sock->info.lsa->bind_local) + { + /* Warn if this is because neither v4 or v6 was specified + * and we should not connect a remote */ + if (sock->info.af == AF_UNSPEC) + msg (M_WARN, "Could not determine IPv4/IPv6 protocol. Using %s", + addr_family_name(sock->info.lsa->bind_local->ai_family)); + + create_socket (sock, sock->info.lsa->bind_local); + } + } + + /* Socket still undefined, give a warning and abort connection */ + if (sock->sd == SOCKET_UNDEFINED) + { + msg (M_WARN, "Could not determine IPv4/IPv6 protocol"); + sig_info->signal_received = SIGUSR1; + goto done; + } + + if (sig_info && sig_info->signal_received) + goto done; + + if (sock->info.proto == PROTO_TCP_SERVER) + { + phase2_tcp_server (sock, remote_dynamic, + &sig_info->signal_received); + } + else if (sock->info.proto == PROTO_TCP_CLIENT) + { + phase2_tcp_client (sock, sig_info); + + } + else if (sock->info.proto == PROTO_UDP && sock->socks_proxy) + { + phase2_socks_client (sock, sig_info); + } +#ifdef TARGET_ANDROID + if (sock->sd != -1) + protect_fd_nonlocal (sock->sd, &sock->info.lsa->actual.dest.addr.sa); +#endif + if (sig_info && sig_info->signal_received) + goto done; + } + + phase2_set_socket_flags(sock); + linksock_print_addr(sock); + + done: + if (sig_save && sig_info) + { + if (!sig_info->signal_received) + sig_info->signal_received = sig_save; + } +} + +void +link_socket_close (struct link_socket *sock) +{ + if (sock) + { +#ifdef ENABLE_DEBUG + const int gremlin = GREMLIN_CONNECTION_FLOOD_LEVEL (sock->gremlin); +#else + const int gremlin = 0; +#endif + + if (socket_defined (sock->sd)) + { +#ifdef WIN32 + close_net_event_win32 (&sock->listen_handle, sock->sd, 0); +#endif + if (!gremlin) + { + msg (D_LOW, "TCP/UDP: Closing socket"); + if (openvpn_close_socket (sock->sd)) + msg (M_WARN | M_ERRNO, "TCP/UDP: Close Socket failed"); + } + sock->sd = SOCKET_UNDEFINED; +#ifdef WIN32 + if (!gremlin) + { + overlapped_io_close (&sock->reads); + overlapped_io_close (&sock->writes); + } +#endif + } + + if (socket_defined (sock->ctrl_sd)) + { + if (openvpn_close_socket (sock->ctrl_sd)) + msg (M_WARN | M_ERRNO, "TCP/UDP: Close Socket (ctrl_sd) failed"); + sock->ctrl_sd = SOCKET_UNDEFINED; + } + + stream_buf_close (&sock->stream_buf); + free_buf (&sock->stream_buf_data); + if (!gremlin) + free (sock); + } +} + +/* for stream protocols, allow for packet length prefix */ +void +socket_adjust_frame_parameters (struct frame *frame, int proto) +{ + if (link_socket_proto_connection_oriented (proto)) + frame_add_to_extra_frame (frame, sizeof (packet_size_type)); +} + +void +setenv_trusted (struct env_set *es, const struct link_socket_info *info) +{ + setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT); +} + +static void +ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc) +{ + const char *host = print_sockaddr_ex (&info->lsa->actual.dest.addr.sa, " ", PS_SHOW_PORT , gc); + if (include_cmd) + argv_printf (argv, "%sc %s", + info->ipchange_command, + host); + else + argv_printf (argv, "%s", host); + +} + +void +link_socket_connection_initiated (const struct buffer *buf, + struct link_socket_info *info, + const struct link_socket_actual *act, + const char *common_name, + struct env_set *es) +{ + struct gc_arena gc = gc_new (); + + info->lsa->actual = *act; /* Note: skip this line for --force-dest */ + setenv_trusted (es, info); + info->connection_established = true; + + /* Print connection initiated message, with common name if available */ + { + struct buffer out = alloc_buf_gc (256, &gc); + if (common_name) + buf_printf (&out, "[%s] ", common_name); + buf_printf (&out, "Peer Connection Initiated with %s", print_link_socket_actual (&info->lsa->actual, &gc)); + msg (M_INFO, "%s", BSTR (&out)); + } + + /* set environmental vars */ + setenv_str (es, "common_name", common_name); + + /* Process --ipchange plugin */ + if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE)) + { + struct argv argv = argv_new (); + ipchange_fmt (false, &argv, info, &gc); + if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS) + msg (M_WARN, "WARNING: ipchange plugin call failed"); + argv_reset (&argv); + } + + /* Process --ipchange option */ + if (info->ipchange_command) + { + struct argv argv = argv_new (); + setenv_str (es, "script_type", "ipchange"); + ipchange_fmt (true, &argv, info, &gc); + openvpn_run_script (&argv, es, 0, "--ipchange"); + argv_reset (&argv); + } + + gc_free (&gc); +} + +void +link_socket_bad_incoming_addr (struct buffer *buf, + const struct link_socket_info *info, + const struct link_socket_actual *from_addr) +{ + struct gc_arena gc = gc_new (); + struct addrinfo* ai; + + switch(from_addr->dest.addr.sa.sa_family) + { + case AF_INET: + case AF_INET6: + msg (D_LINK_ERRORS, + "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)", + print_link_socket_actual (from_addr, &gc), + (int)from_addr->dest.addr.sa.sa_family, + print_sockaddr_ex (info->lsa->remote_list->ai_addr,":" ,PS_SHOW_PORT, &gc)); + /* print additional remote addresses */ + for(ai=info->lsa->remote_list->ai_next;ai;ai=ai->ai_next) { + msg(D_LINK_ERRORS,"or from peer address: %s", + print_sockaddr_ex(ai->ai_addr,":",PS_SHOW_PORT, &gc)); + } + break; + } + buf->len = 0; + gc_free (&gc); +} + +void +link_socket_bad_outgoing_addr (void) +{ + dmsg (D_READ_WRITE, "TCP/UDP: No outgoing address to send packet"); +} + +in_addr_t +link_socket_current_remote (const struct link_socket_info *info) +{ + const struct link_socket_addr *lsa = info->lsa; + +/* + * This logic supports "redirect-gateway" semantic, which + * makes sense only for PF_INET routes over PF_INET endpoints + * + * Maybe in the future consider PF_INET6 endpoints also ... + * by now just ignore it + * + * For --remote entries with multiple addresses this + * only return the actual endpoint we have sucessfully connected to + */ + if (lsa->actual.dest.addr.sa.sa_family != AF_INET) + return IPV4_INVALID_ADDR; + + if (link_socket_actual_defined (&lsa->actual)) + return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr); + else if (lsa->current_remote) + return ntohl (((struct sockaddr_in*)lsa->current_remote->ai_addr) + ->sin_addr.s_addr); + else + return 0; +} + +/* + * Return a status string describing socket state. + */ +const char * +socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (64, gc); + if (s) + { + if (rwflags & EVENT_READ) + { + buf_printf (&out, "S%s", + (s->rwflags_debug & EVENT_READ) ? "R" : "r"); +#ifdef WIN32 + buf_printf (&out, "%s", + overlapped_io_state_ascii (&s->reads)); +#endif + } + if (rwflags & EVENT_WRITE) + { + buf_printf (&out, "S%s", + (s->rwflags_debug & EVENT_WRITE) ? "W" : "w"); +#ifdef WIN32 + buf_printf (&out, "%s", + overlapped_io_state_ascii (&s->writes)); +#endif + } + } + else + { + buf_printf (&out, "S?"); + } + return BSTR (&out); +} + +/* + * Stream buffer functions, used to packetize a TCP + * stream connection. + */ + +static inline void +stream_buf_reset (struct stream_buf *sb) +{ + dmsg (D_STREAM_DEBUG, "STREAM: RESET"); + sb->residual_fully_formed = false; + sb->buf = sb->buf_init; + buf_reset (&sb->next); + sb->len = -1; +} + +void +stream_buf_init (struct stream_buf *sb, + struct buffer *buf, + const unsigned int sockflags, + const int proto) +{ + sb->buf_init = *buf; + sb->maxlen = sb->buf_init.len; + sb->buf_init.len = 0; + sb->residual = alloc_buf (sb->maxlen); + sb->error = false; +#if PORT_SHARE + sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCP_SERVER)) + ? PS_ENABLED + : PS_DISABLED; +#endif + stream_buf_reset (sb); + + dmsg (D_STREAM_DEBUG, "STREAM: INIT maxlen=%d", sb->maxlen); +} + +static inline void +stream_buf_set_next (struct stream_buf *sb) +{ + /* set up 'next' for next i/o read */ + sb->next = sb->buf; + sb->next.offset = sb->buf.offset + sb->buf.len; + sb->next.len = (sb->len >= 0 ? sb->len : sb->maxlen) - sb->buf.len; + dmsg (D_STREAM_DEBUG, "STREAM: SET NEXT, buf=[%d,%d] next=[%d,%d] len=%d maxlen=%d", + sb->buf.offset, sb->buf.len, + sb->next.offset, sb->next.len, + sb->len, sb->maxlen); + ASSERT (sb->next.len > 0); + ASSERT (buf_safe (&sb->buf, sb->next.len)); +} + +static inline void +stream_buf_get_final (struct stream_buf *sb, struct buffer *buf) +{ + dmsg (D_STREAM_DEBUG, "STREAM: GET FINAL len=%d", + buf_defined (&sb->buf) ? sb->buf.len : -1); + ASSERT (buf_defined (&sb->buf)); + *buf = sb->buf; +} + +static inline void +stream_buf_get_next (struct stream_buf *sb, struct buffer *buf) +{ + dmsg (D_STREAM_DEBUG, "STREAM: GET NEXT len=%d", + buf_defined (&sb->next) ? sb->next.len : -1); + ASSERT (buf_defined (&sb->next)); + *buf = sb->next; +} + +bool +stream_buf_read_setup_dowork (struct link_socket* sock) +{ + if (sock->stream_buf.residual.len && !sock->stream_buf.residual_fully_formed) + { + ASSERT (buf_copy (&sock->stream_buf.buf, &sock->stream_buf.residual)); + ASSERT (buf_init (&sock->stream_buf.residual, 0)); + sock->stream_buf.residual_fully_formed = stream_buf_added (&sock->stream_buf, 0); + dmsg (D_STREAM_DEBUG, "STREAM: RESIDUAL FULLY FORMED [%s], len=%d", + sock->stream_buf.residual_fully_formed ? "YES" : "NO", + sock->stream_buf.residual.len); + } + + if (!sock->stream_buf.residual_fully_formed) + stream_buf_set_next (&sock->stream_buf); + return !sock->stream_buf.residual_fully_formed; +} + +bool +stream_buf_added (struct stream_buf *sb, + int length_added) +{ + dmsg (D_STREAM_DEBUG, "STREAM: ADD length_added=%d", length_added); + if (length_added > 0) + sb->buf.len += length_added; + + /* if length unknown, see if we can get the length prefix from + the head of the buffer */ + if (sb->len < 0 && sb->buf.len >= (int) sizeof (packet_size_type)) + { + packet_size_type net_size; + +#if PORT_SHARE + if (sb->port_share_state == PS_ENABLED) + { + if (!is_openvpn_protocol (&sb->buf)) + { + msg (D_STREAM_ERRORS, "Non-OpenVPN client protocol detected"); + sb->port_share_state = PS_FOREIGN; + sb->error = true; + return false; + } + else + sb->port_share_state = PS_DISABLED; + } +#endif + + ASSERT (buf_read (&sb->buf, &net_size, sizeof (net_size))); + sb->len = ntohps (net_size); + + if (sb->len < 1 || sb->len > sb->maxlen) + { + msg (M_WARN, "WARNING: Bad encapsulated packet length from peer (%d), which must be > 0 and <= %d -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attempting restart...]", sb->len, sb->maxlen); + stream_buf_reset (sb); + sb->error = true; + return false; + } + } + + /* is our incoming packet fully read? */ + if (sb->len > 0 && sb->buf.len >= sb->len) + { + /* save any residual data that's part of the next packet */ + ASSERT (buf_init (&sb->residual, 0)); + if (sb->buf.len > sb->len) + ASSERT (buf_copy_excess (&sb->residual, &sb->buf, sb->len)); + dmsg (D_STREAM_DEBUG, "STREAM: ADD returned TRUE, buf_len=%d, residual_len=%d", + BLEN (&sb->buf), + BLEN (&sb->residual)); + return true; + } + else + { + dmsg (D_STREAM_DEBUG, "STREAM: ADD returned FALSE (have=%d need=%d)", sb->buf.len, sb->len); + stream_buf_set_next (sb); + return false; + } +} + +void +stream_buf_close (struct stream_buf* sb) +{ + free_buf (&sb->residual); +} + +/* + * The listen event is a special event whose sole purpose is + * to tell us that there's a new incoming connection on a + * TCP socket, for use in server mode. + */ +event_t +socket_listen_event_handle (struct link_socket *s) +{ +#ifdef WIN32 + if (!defined_net_event_win32 (&s->listen_handle)) + init_net_event_win32 (&s->listen_handle, FD_ACCEPT, s->sd, 0); + return &s->listen_handle; +#else + return s->sd; +#endif +} + +/* + * Format IP addresses in ascii + */ + +const char * +print_sockaddr_ex (const struct sockaddr *sa, + const char* separator, + const unsigned int flags, + struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (128, gc); + bool addr_is_defined; + char hostaddr[NI_MAXHOST] = ""; + char servname[NI_MAXSERV] = ""; + int status; + + socklen_t salen; + switch(sa->sa_family) + { + case AF_INET: + buf_puts (&out, "[AF_INET]"); + salen = sizeof (struct sockaddr_in); + addr_is_defined = ((struct sockaddr_in*) sa)->sin_addr.s_addr != 0; + break; + case AF_INET6: + buf_puts (&out, "[AF_INET6]"); + salen = sizeof (struct sockaddr_in6); + addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*) sa)->sin6_addr); + break; + case AF_UNSPEC: + return "[AF_UNSPEC]"; + default: + ASSERT(0); + } + + status = getnameinfo(sa, salen, hostaddr, sizeof (hostaddr), + servname, sizeof(servname), NI_NUMERICHOST | NI_NUMERICSERV); + + if(status!=0) { + buf_printf(&out,"[nameinfo() err: %s]",gai_strerror(status)); + return BSTR(&out); + } + + if (!(flags & PS_DONT_SHOW_ADDR)) + { + if (addr_is_defined) + buf_puts (&out, hostaddr); + else + buf_puts (&out, "[undef]"); + } + + if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED)) + { + if (separator) + buf_puts (&out, separator); + + buf_puts (&out, servname); + } + + return BSTR (&out); +} + +const char * +print_link_socket_actual (const struct link_socket_actual *act, struct gc_arena *gc) +{ + return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc); +} + +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 16 +#endif + +const char * +print_link_socket_actual_ex (const struct link_socket_actual *act, + const char *separator, + const unsigned int flags, + struct gc_arena *gc) +{ + if (act) + { + char ifname[IF_NAMESIZE] = "[undef]"; + struct buffer out = alloc_buf_gc (128, gc); + buf_printf (&out, "%s", print_sockaddr_ex (&act->dest.addr.sa, separator, flags, gc)); +#if ENABLE_IP_PKTINFO + if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act)) + { + switch(act->dest.addr.sa.sa_family) + { + case AF_INET: + { + struct openvpn_sockaddr sa; + CLEAR (sa); + sa.addr.in4.sin_family = AF_INET; +#ifdef IP_PKTINFO + sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst; + if_indextoname(act->pi.in4.ipi_ifindex, ifname); +#elif defined(IP_RECVDSTADDR) + sa.addr.in4.sin_addr = act->pi.in4; + ifname[0]=0; +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + buf_printf (&out, " (via %s%%%s)", + print_sockaddr_ex (&sa.addr.sa, separator, 0, gc), + ifname); + } + break; + case AF_INET6: + { + struct sockaddr_in6 sin6; + char buf[INET6_ADDRSTRLEN] = "[undef]"; + CLEAR(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = act->pi.in6.ipi6_addr; + if_indextoname(act->pi.in6.ipi6_ifindex, ifname); + if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6), + buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0) + buf_printf (&out, " (via %s%%%s)", buf, ifname); + else + buf_printf (&out, " (via [getnameinfo() err]%%%s)", ifname); + } + break; + } + } +#endif + return BSTR (&out); + } + else + return "[NULL]"; +} + +/* + * Convert an in_addr_t in host byte order + * to an ascii dotted quad. + */ +const char * +print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc) +{ + struct in_addr ia; + struct buffer out = alloc_buf_gc (64, gc); + + if (addr || !(flags & IA_EMPTY_IF_UNDEF)) + { + CLEAR (ia); + ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl (addr); + + buf_printf (&out, "%s", inet_ntoa (ia)); + } + return BSTR (&out); +} + +/* + * Convert an in6_addr in host byte order + * to an ascii representation of an IPv6 address + */ +const char * +print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (64, gc); + char tmp_out_buf[64]; /* inet_ntop wants pointer to buffer */ + + if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 || + !(flags & IA_EMPTY_IF_UNDEF)) + { + inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1); + buf_printf (&out, "%s", tmp_out_buf ); + } + return BSTR (&out); +} + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif + +/* add some offset to an ipv6 address + * (add in steps of 8 bits, taking overflow into next round) + */ +struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add ) +{ + int i; + + for( i=15; i>=0 && add > 0 ; i-- ) + { + register int carry; + register uint32_t h; + + h = (unsigned char) base.s6_addr[i]; + base.s6_addr[i] = (h+add) & UINT8_MAX; + + /* using explicit carry for the 8-bit additions will catch + * 8-bit and(!) 32-bit overruns nicely + */ + carry = ((h & 0xff) + (add & 0xff)) >> 8; + add = (add>>8) + carry; + } + return base; +} + +/* set environmental variables for ip/port in *addr */ +void +setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const unsigned int flags) +{ + char name_buf[256]; + + char buf[128]; + switch(addr->addr.sa.sa_family) + { + case AF_INET: + if (flags & SA_IP_PORT) + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); + else + openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix); + + setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr)); + + if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port) + { + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); + setenv_int (es, name_buf, ntohs (addr->addr.in4.sin_port)); + } + break; + case AF_INET6: + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip6", name_prefix); + getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6), + buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); + setenv_str (es, name_buf, buf); + + if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port) + { + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); + setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port)); + } + break; + } +} + +void +setenv_in_addr_t (struct env_set *es, const char *name_prefix, in_addr_t addr, const unsigned int flags) +{ + if (addr || !(flags & SA_SET_IF_NONZERO)) + { + struct openvpn_sockaddr si; + CLEAR (si); + si.addr.in4.sin_family = AF_INET; + si.addr.in4.sin_addr.s_addr = htonl (addr); + setenv_sockaddr (es, name_prefix, &si, flags); + } +} + +void +setenv_link_socket_actual (struct env_set *es, + const char *name_prefix, + const struct link_socket_actual *act, + const unsigned int flags) +{ + setenv_sockaddr (es, name_prefix, &act->dest, flags); +} + +/* + * Convert protocol names between index and ascii form. + */ + +struct proto_names { + const char *short_form; + const char *display_form; + sa_family_t proto_af; + int proto; +}; + +/* Indexed by PROTO_x */ +static const struct proto_names proto_names[] = { + {"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE}, + /* try IPv4 and IPv6 (client), bind dual-stack (server) */ + {"udp", "UDP", AF_UNSPEC, PROTO_UDP}, + {"tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER}, + {"tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT}, + {"tcp", "TCP", AF_UNSPEC, PROTO_TCP}, + /* force IPv4 */ + {"udp4", "UDPv4", AF_INET, PROTO_UDP}, + {"tcp4-server","TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER}, + {"tcp4-client","TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT}, + {"tcp4", "TCPv4", AF_INET, PROTO_TCP}, + /* force IPv6 */ + {"udp6" ,"UDPv6", AF_INET6, PROTO_UDP}, + {"tcp6-server","TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER}, + {"tcp6-client","TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT}, + {"tcp6" ,"TCPv6", AF_INET6, PROTO_TCP}, +}; + +bool +proto_is_net(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto != PROTO_NONE; +} +bool +proto_is_dgram(int proto) +{ + return proto_is_udp(proto); +} + +bool +proto_is_udp(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto == PROTO_UDP; +} + +bool +proto_is_tcp(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER || proto == PROTO_TCP_CLIENT; +} + +int +ascii2proto (const char* proto_name) +{ + int i; + for (i = 0; i < SIZE (proto_names); ++i) + if (!strcmp (proto_name, proto_names[i].short_form)) + return proto_names[i].proto; + return -1; +} + +sa_family_t +ascii2af (const char* proto_name) +{ + int i; + for (i = 0; i < SIZE (proto_names); ++i) + if (!strcmp (proto_name, proto_names[i].short_form)) + return proto_names[i].proto_af; + return 0; +} + +const char * +proto2ascii (int proto, sa_family_t af, bool display_form) +{ + unsigned int i; + for (i = 0; i < SIZE (proto_names); ++i) + { + if(proto_names[i].proto_af == af && proto_names[i].proto == proto) + { + if(display_form) + return proto_names[i].display_form; + else + return proto_names[i].short_form; + } + } + + return "[unknown protocol]"; +} + +const char * +proto2ascii_all (struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (256, gc); + int i; + + for (i = 0; i < SIZE (proto_names); ++i) + { + if (i) + buf_printf(&out, " "); + buf_printf(&out, "[%s]", proto_names[i].short_form); + } + return BSTR (&out); +} + +const char * +addr_family_name (int af) +{ + switch (af) + { + case AF_INET: return "AF_INET"; + case AF_INET6: return "AF_INET6"; + } + return "AF_UNSPEC"; +} + +/* + * Given a local proto, return local proto + * if !remote, or compatible remote proto + * if remote. + * + * This is used for options compatibility + * checking. + * + * IPv6 and IPv4 protocols are comptabile but OpenVPN + * has always sent UDPv4, TCPv4 over the wire. Keep these + * strings for backward compatbility + */ +const char* +proto_remote (int proto, bool remote) +{ + ASSERT (proto >= 0 && proto < PROTO_N); + if (proto == PROTO_UDP) + return "UDPv4"; + + if ( (remote && proto == PROTO_TCP_CLIENT) || + (!remote && proto == PROTO_TCP_SERVER)) + return "TCPv4_SERVER"; + if ( (remote && proto == PROTO_TCP_SERVER) || + (!remote && proto == PROTO_TCP_CLIENT)) + return "TCPv4_CLIENT"; + + ASSERT (0); + return ""; /* Make the compiler happy */ +} + +/* + * Bad incoming address lengths that differ from what + * we expect are considered to be fatal errors. + */ +void +bad_address_length (int actual, int expected) +{ + msg (M_FATAL, "ERROR: received strange incoming packet with an address length of %d -- we only accept address lengths of %d.", + actual, + expected); +} + +/* + * Socket Read Routines + */ + +int +link_socket_read_tcp (struct link_socket *sock, + struct buffer *buf) +{ + int len = 0; + + if (!sock->stream_buf.residual_fully_formed) + { +#ifdef WIN32 + len = socket_finalize (sock->sd, &sock->reads, buf, NULL); +#else + struct buffer frag; + stream_buf_get_next (&sock->stream_buf, &frag); + len = recv (sock->sd, BPTR (&frag), BLEN (&frag), MSG_NOSIGNAL); +#endif + + if (!len) + sock->stream_reset = true; + if (len <= 0) + return buf->len = len; + } + + if (sock->stream_buf.residual_fully_formed + || stream_buf_added (&sock->stream_buf, len)) /* packet complete? */ + { + stream_buf_get_final (&sock->stream_buf, buf); + stream_buf_reset (&sock->stream_buf); + return buf->len; + } + else + return buf->len = 0; /* no error, but packet is still incomplete */ +} + +#ifndef WIN32 + +#if ENABLE_IP_PKTINFO + +#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */ +struct openvpn_in4_pktinfo +{ + struct cmsghdr cmsghdr; +#ifdef HAVE_IN_PKTINFO + struct in_pktinfo pi4; +#elif defined(IP_RECVDSTADDR) + struct in_addr pi4; +#endif +}; +struct openvpn_in6_pktinfo +{ + struct cmsghdr cmsghdr; + struct in6_pktinfo pi6; +}; + +union openvpn_pktinfo { + struct openvpn_in4_pktinfo msgpi4; + struct openvpn_in6_pktinfo msgpi6; +}; +#pragma pack() + +static socklen_t +link_socket_read_udp_posix_recvmsg (struct link_socket *sock, + struct buffer *buf, + int maxsize, + struct link_socket_actual *from) +{ + struct iovec iov; + union openvpn_pktinfo opi; + struct msghdr mesg; + socklen_t fromlen = sizeof (from->dest.addr); + + iov.iov_base = BPTR (buf); + iov.iov_len = maxsize; + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; + mesg.msg_name = &from->dest.addr; + mesg.msg_namelen = fromlen; + mesg.msg_control = &opi; + mesg.msg_controllen = sizeof opi; + buf->len = recvmsg (sock->sd, &mesg, 0); + if (buf->len >= 0) + { + struct cmsghdr *cmsg; + fromlen = mesg.msg_namelen; + cmsg = CMSG_FIRSTHDR (&mesg); + if (cmsg != NULL + && CMSG_NXTHDR (&mesg, cmsg) == NULL +#ifdef IP_PKTINFO + && cmsg->cmsg_level == SOL_IP + && cmsg->cmsg_type == IP_PKTINFO +#elif defined(IP_RECVDSTADDR) + && cmsg->cmsg_level == IPPROTO_IP + && cmsg->cmsg_type == IP_RECVDSTADDR +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo)) + { +#ifdef IP_PKTINFO + struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); + from->pi.in4.ipi_ifindex = pkti->ipi_ifindex; + from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst; +#elif defined(IP_RECVDSTADDR) + from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg); +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + } + else if (cmsg != NULL + && CMSG_NXTHDR (&mesg, cmsg) == NULL + && cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO + && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo)) + { + struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg); + from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex; + from->pi.in6.ipi6_addr = pkti6->ipi6_addr; + } + else if (cmsg != NULL) + { + msg(M_WARN, "CMSG received that cannot be parsed (cmsg_level=%d, cmsg_type=%d, cmsg=len=%d)", (int)cmsg->cmsg_level, (int)cmsg->cmsg_type, (int)cmsg->cmsg_len ); + } + } + + return fromlen; +} +#endif + +int +link_socket_read_udp_posix (struct link_socket *sock, + struct buffer *buf, + int maxsize, + struct link_socket_actual *from) +{ + socklen_t fromlen = sizeof (from->dest.addr); + socklen_t expectedlen = af_addr_size(sock->info.af); + addr_zero_host(&from->dest); + ASSERT (buf_safe (buf, maxsize)); +#if ENABLE_IP_PKTINFO + /* Both PROTO_UDPv4 and PROTO_UDPv6 */ + if (sock->info.proto == PROTO_UDP && sock->sockflags & SF_USE_IP_PKTINFO) + fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from); + else +#endif + buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0, + &from->dest.addr.sa, &fromlen); + if (buf->len >= 0 && expectedlen && fromlen != expectedlen) + bad_address_length (fromlen, expectedlen); + return buf->len; +} + +#endif + +/* + * Socket Write Routines + */ + +int +link_socket_write_tcp (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + packet_size_type len = BLEN (buf); + dmsg (D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset); + ASSERT (len <= sock->stream_buf.maxlen); + len = htonps (len); + ASSERT (buf_write_prepend (buf, &len, sizeof (len))); +#ifdef WIN32 + return link_socket_write_win32 (sock, buf, to); +#else + return link_socket_write_tcp_posix (sock, buf, to); +#endif +} + +#if ENABLE_IP_PKTINFO + +size_t +link_socket_write_udp_posix_sendmsg (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + struct iovec iov; + struct msghdr mesg; + struct cmsghdr *cmsg; + union openvpn_pktinfo opi; + + iov.iov_base = BPTR (buf); + iov.iov_len = BLEN (buf); + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; + switch (to->dest.addr.sa.sa_family) + { + case AF_INET: + { + mesg.msg_name = &to->dest.addr.sa; + mesg.msg_namelen = sizeof (struct sockaddr_in); + mesg.msg_control = &opi; + mesg.msg_flags = 0; +#ifdef HAVE_IN_PKTINFO + mesg.msg_controllen = sizeof (struct openvpn_in4_pktinfo); + cmsg = CMSG_FIRSTHDR (&mesg); + cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + { + struct in_pktinfo *pkti; + pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); + pkti->ipi_ifindex = to->pi.in4.ipi_ifindex; + pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst; + pkti->ipi_addr.s_addr = 0; + } +#elif defined(IP_RECVDSTADDR) + ASSERT( CMSG_SPACE(sizeof (struct in_addr)) <= sizeof(opi) ); + mesg.msg_controllen = CMSG_SPACE(sizeof (struct in_addr)); + cmsg = CMSG_FIRSTHDR (&mesg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_RECVDSTADDR; + *(struct in_addr *) CMSG_DATA (cmsg) = to->pi.in4; +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + break; + } + case AF_INET6: + { + struct in6_pktinfo *pkti6; + mesg.msg_name = &to->dest.addr.sa; + mesg.msg_namelen = sizeof (struct sockaddr_in6); + mesg.msg_control = &opi; + mesg.msg_controllen = sizeof (struct openvpn_in6_pktinfo); + mesg.msg_flags = 0; + cmsg = CMSG_FIRSTHDR (&mesg); + cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg); + pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex; + pkti6->ipi6_addr = to->pi.in6.ipi6_addr; + break; + } + default: ASSERT(0); + } + return sendmsg (sock->sd, &mesg, 0); +} + +#endif + +/* + * Win32 overlapped socket I/O functions. + */ + +#ifdef WIN32 + +int +socket_recv_queue (struct link_socket *sock, int maxsize) +{ + if (sock->reads.iostate == IOSTATE_INITIAL) + { + WSABUF wsabuf[1]; + int status; + + /* reset buf to its initial state */ + if (proto_is_udp(sock->info.proto)) + { + sock->reads.buf = sock->reads.buf_init; + } + else if (proto_is_tcp(sock->info.proto)) + { + stream_buf_get_next (&sock->stream_buf, &sock->reads.buf); + } + else + { + ASSERT (0); + } + + /* Win32 docs say it's okay to allocate the wsabuf on the stack */ + wsabuf[0].buf = BPTR (&sock->reads.buf); + wsabuf[0].len = maxsize ? maxsize : BLEN (&sock->reads.buf); + + /* check for buffer overflow */ + ASSERT (wsabuf[0].len <= BLEN (&sock->reads.buf)); + + /* the overlapped read will signal this event on I/O completion */ + ASSERT (ResetEvent (sock->reads.overlapped.hEvent)); + sock->reads.flags = 0; + + if (proto_is_udp(sock->info.proto)) + { + sock->reads.addr_defined = true; + if (sock->info.af == AF_INET) + sock->reads.addrlen = sizeof (sock->reads.addr); + else + sock->reads.addrlen = sizeof (sock->reads.addr6); + status = WSARecvFrom( + sock->sd, + wsabuf, + 1, + &sock->reads.size, + &sock->reads.flags, + (struct sockaddr *) &sock->reads.addr, + &sock->reads.addrlen, + &sock->reads.overlapped, + NULL); + } + else if (proto_is_tcp(sock->info.proto)) + { + sock->reads.addr_defined = false; + status = WSARecv( + sock->sd, + wsabuf, + 1, + &sock->reads.size, + &sock->reads.flags, + &sock->reads.overlapped, + NULL); + } + else + { + status = 0; + ASSERT (0); + } + + if (!status) /* operation completed immediately? */ + { + int addrlen = af_addr_size(sock->info.lsa->local.addr.sa.sa_family); + if (sock->reads.addr_defined && sock->reads.addrlen != addrlen) + bad_address_length (sock->reads.addrlen, addrlen); + sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; + + /* since we got an immediate return, we must signal the event object ourselves */ + ASSERT (SetEvent (sock->reads.overlapped.hEvent)); + sock->reads.status = 0; + + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Receive immediate return [%d,%d]", + (int) wsabuf[0].len, + (int) sock->reads.size); + } + else + { + status = WSAGetLastError (); + if (status == WSA_IO_PENDING) /* operation queued? */ + { + sock->reads.iostate = IOSTATE_QUEUED; + sock->reads.status = status; + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Receive queued [%d]", + (int) wsabuf[0].len); + } + else /* error occurred */ + { + struct gc_arena gc = gc_new (); + ASSERT (SetEvent (sock->reads.overlapped.hEvent)); + sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; + sock->reads.status = status; + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Receive error [%d]: %s", + (int) wsabuf[0].len, + strerror_win32 (status, &gc)); + gc_free (&gc); + } + } + } + return sock->reads.iostate; +} + +int +socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct link_socket_actual *to) +{ + if (sock->writes.iostate == IOSTATE_INITIAL) + { + WSABUF wsabuf[1]; + int status; + + /* make a private copy of buf */ + sock->writes.buf = sock->writes.buf_init; + sock->writes.buf.len = 0; + ASSERT (buf_copy (&sock->writes.buf, buf)); + + /* Win32 docs say it's okay to allocate the wsabuf on the stack */ + wsabuf[0].buf = BPTR (&sock->writes.buf); + wsabuf[0].len = BLEN (&sock->writes.buf); + + /* the overlapped write will signal this event on I/O completion */ + ASSERT (ResetEvent (sock->writes.overlapped.hEvent)); + sock->writes.flags = 0; + + if (proto_is_udp(sock->info.proto)) + { + /* set destination address for UDP writes */ + sock->writes.addr_defined = true; + if (sock->info.af == AF_INET6) + { + sock->writes.addr6 = to->dest.addr.in6; + sock->writes.addrlen = sizeof (sock->writes.addr6); + } + else + { + sock->writes.addr = to->dest.addr.in4; + sock->writes.addrlen = sizeof (sock->writes.addr); + } + + status = WSASendTo( + sock->sd, + wsabuf, + 1, + &sock->writes.size, + sock->writes.flags, + (struct sockaddr *) &sock->writes.addr, + sock->writes.addrlen, + &sock->writes.overlapped, + NULL); + } + else if (proto_is_tcp(sock->info.proto)) + { + /* destination address for TCP writes was established on connection initiation */ + sock->writes.addr_defined = false; + + status = WSASend( + sock->sd, + wsabuf, + 1, + &sock->writes.size, + sock->writes.flags, + &sock->writes.overlapped, + NULL); + } + else + { + status = 0; + ASSERT (0); + } + + if (!status) /* operation completed immediately? */ + { + sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN; + + /* since we got an immediate return, we must signal the event object ourselves */ + ASSERT (SetEvent (sock->writes.overlapped.hEvent)); + + sock->writes.status = 0; + + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Send immediate return [%d,%d]", + (int) wsabuf[0].len, + (int) sock->writes.size); + } + else + { + status = WSAGetLastError (); + if (status == WSA_IO_PENDING) /* operation queued? */ + { + sock->writes.iostate = IOSTATE_QUEUED; + sock->writes.status = status; + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Send queued [%d]", + (int) wsabuf[0].len); + } + else /* error occurred */ + { + struct gc_arena gc = gc_new (); + ASSERT (SetEvent (sock->writes.overlapped.hEvent)); + sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN; + sock->writes.status = status; + + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Send error [%d]: %s", + (int) wsabuf[0].len, + strerror_win32 (status, &gc)); + + gc_free (&gc); + } + } + } + return sock->writes.iostate; +} + +int +socket_finalize (SOCKET s, + struct overlapped_io *io, + struct buffer *buf, + struct link_socket_actual *from) +{ + int ret = -1; + BOOL status; + + switch (io->iostate) + { + case IOSTATE_QUEUED: + status = WSAGetOverlappedResult( + s, + &io->overlapped, + &io->size, + FALSE, + &io->flags + ); + if (status) + { + /* successful return for a queued operation */ + if (buf) + *buf = io->buf; + ret = io->size; + io->iostate = IOSTATE_INITIAL; + ASSERT (ResetEvent (io->overlapped.hEvent)); + + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Completion success [%d]", ret); + } + else + { + /* error during a queued operation */ + ret = -1; + if (WSAGetLastError() != WSA_IO_INCOMPLETE) + { + /* if no error (i.e. just not finished yet), then DON'T execute this code */ + io->iostate = IOSTATE_INITIAL; + ASSERT (ResetEvent (io->overlapped.hEvent)); + msg (D_WIN32_IO | M_ERRNO, "WIN32 I/O: Socket Completion error"); + } + } + break; + + case IOSTATE_IMMEDIATE_RETURN: + io->iostate = IOSTATE_INITIAL; + ASSERT (ResetEvent (io->overlapped.hEvent)); + if (io->status) + { + /* error return for a non-queued operation */ + WSASetLastError (io->status); + ret = -1; + msg (D_WIN32_IO | M_ERRNO, "WIN32 I/O: Socket Completion non-queued error"); + } + else + { + /* successful return for a non-queued operation */ + if (buf) + *buf = io->buf; + ret = io->size; + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Completion non-queued success [%d]", ret); + } + break; + + case IOSTATE_INITIAL: /* were we called without proper queueing? */ + WSASetLastError (WSAEINVAL); + ret = -1; + dmsg (D_WIN32_IO, "WIN32 I/O: Socket Completion BAD STATE"); + break; + + default: + ASSERT (0); + } + + /* return from address if requested */ + if (from) + { + if (ret >= 0 && io->addr_defined) + { + /* TODO(jjo): streamline this mess */ + /* in this func we dont have relevant info about the PF_ of this + * endpoint, as link_socket_actual will be zero for the 1st received packet + * + * Test for inets PF_ possible sizes + */ + switch (io->addrlen) + { + case sizeof(struct sockaddr_in): + case sizeof(struct sockaddr_in6): + /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 + * under WIN32*/ + case sizeof(struct sockaddr_in6)-4: + break; + default: + bad_address_length (io->addrlen, af_addr_size(io->addr.sin_family)); + } + + switch (io->addr.sin_family) + { + case AF_INET: + from->dest.addr.in4 = io->addr; + break; + case AF_INET6: + from->dest.addr.in6 = io->addr6; + break; + } + } + else + CLEAR (from->dest.addr); + } + + if (buf) + buf->len = ret; + return ret; +} + +#endif /* WIN32 */ + +/* + * Socket event notification + */ + +unsigned int +socket_set (struct link_socket *s, + struct event_set *es, + unsigned int rwflags, + void *arg, + unsigned int *persistent) +{ + if (s) + { + if ((rwflags & EVENT_READ) && !stream_buf_read_setup (s)) + { + ASSERT (!persistent); + rwflags &= ~EVENT_READ; + } + +#ifdef WIN32 + if (rwflags & EVENT_READ) + socket_recv_queue (s, 0); +#endif + + /* if persistent is defined, call event_ctl only if rwflags has changed since last call */ + if (!persistent || *persistent != rwflags) + { + event_ctl (es, socket_event_handle (s), rwflags, arg); + if (persistent) + *persistent = rwflags; + } + + s->rwflags_debug = rwflags; + } + return rwflags; +} + +void +sd_close (socket_descriptor_t *sd) +{ + if (sd && socket_defined (*sd)) + { + openvpn_close_socket (*sd); + *sd = SOCKET_UNDEFINED; + } +} + +#if UNIX_SOCK_SUPPORT + +/* + * code for unix domain sockets + */ + +const char * +sockaddr_unix_name (const struct sockaddr_un *local, const char *null) +{ + if (local && local->sun_family == PF_UNIX) + return local->sun_path; + else + return null; +} + +socket_descriptor_t +create_socket_unix (void) +{ + socket_descriptor_t sd; + + if ((sd = socket (PF_UNIX, SOCK_STREAM, 0)) < 0) + msg (M_ERR, "Cannot create unix domain socket"); + return sd; +} + +void +socket_bind_unix (socket_descriptor_t sd, + struct sockaddr_un *local, + const char *prefix) +{ + struct gc_arena gc = gc_new (); + +#ifdef HAVE_UMASK + const mode_t orig_umask = umask (0); +#endif + + if (bind (sd, (struct sockaddr *) local, sizeof (struct sockaddr_un))) + { + const int errnum = openvpn_errno (); + msg (M_FATAL, "%s: Socket bind[%d] failed on unix domain socket %s: %s", + prefix, + (int)sd, + sockaddr_unix_name (local, "NULL"), + strerror_ts (errnum, &gc)); + } + +#ifdef HAVE_UMASK + umask (orig_umask); +#endif + + gc_free (&gc); +} + +socket_descriptor_t +socket_accept_unix (socket_descriptor_t sd, + struct sockaddr_un *remote) +{ + socklen_t remote_len = sizeof (struct sockaddr_un); + socket_descriptor_t ret; + + CLEAR (*remote); + ret = accept (sd, (struct sockaddr *) remote, &remote_len); + return ret; +} + +int +socket_connect_unix (socket_descriptor_t sd, + struct sockaddr_un *remote) +{ + int status = connect (sd, (struct sockaddr *) remote, sizeof (struct sockaddr_un)); + if (status) + status = openvpn_errno (); + return status; +} + +void +sockaddr_unix_init (struct sockaddr_un *local, const char *path) +{ + local->sun_family = PF_UNIX; + strncpynt (local->sun_path, path, sizeof (local->sun_path)); +} + +void +socket_delete_unix (const struct sockaddr_un *local) +{ + const char *name = sockaddr_unix_name (local, NULL); +#ifdef HAVE_UNLINK + if (name && strlen (name)) + unlink (name); +#endif +} + +bool +unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int *gid) +{ +#ifdef HAVE_GETPEEREID + uid_t u; + gid_t g; + if (getpeereid (sd, &u, &g) == -1) + return false; + if (uid) + *uid = u; + if (gid) + *gid = g; + return true; +#elif defined(SO_PEERCRED) + struct ucred peercred; + socklen_t so_len = sizeof(peercred); + if (getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) == -1) + return false; + if (uid) + *uid = peercred.uid; + if (gid) + *gid = peercred.gid; + return true; +#else + return false; +#endif +} + +#endif |