* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2002-2023 OpenVPN 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
* 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
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
-#elif defined(_MSC_VER)
-#include "config-msvc.h"
#endif
#include "syshead.h"
#include "gremlin.h"
#include "plugin.h"
#include "ps.h"
+#include "run_command.h"
#include "manage.h"
#include "misc.h"
#include "manage.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
*/
/*
* Functions related to the translation of DNS names to IP addresses.
*/
+static int
+get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname,
+ void *network, unsigned int *netbits,
+ int resolve_retry_seconds, struct signal_info *sig_info,
+ int msglevel)
+{
+ char *endp, *sep, *var_host = NULL;
+ struct addrinfo *ai = NULL;
+ unsigned long bits;
+ uint8_t max_bits;
+ int ret = -1;
+
+ if (!hostname)
+ {
+ msg(M_NONFATAL, "Can't resolve null hostname!");
+ goto out;
+ }
+
+ /* assign family specific default values */
+ switch (af)
+ {
+ case AF_INET:
+ bits = 0;
+ max_bits = sizeof(in_addr_t) * 8;
+ break;
+
+ case AF_INET6:
+ bits = 64;
+ max_bits = sizeof(struct in6_addr) * 8;
+ break;
+
+ default:
+ msg(M_WARN,
+ "Unsupported AF family passed to getaddrinfo for %s (%d)",
+ hostname, af);
+ goto out;
+ }
+
+ /* we need to modify the hostname received as input, but we don't want to
+ * touch it directly as it might be a constant string.
+ *
+ * Therefore, we clone the string here and free it at the end of the
+ * function */
+ var_host = strdup(hostname);
+ if (!var_host)
+ {
+ msg(M_NONFATAL | M_ERRNO,
+ "Can't allocate hostname buffer for getaddrinfo");
+ goto out;
+ }
+
+ /* check if this hostname has a /bits suffix */
+ sep = strchr(var_host, '/');
+ if (sep)
+ {
+ bits = strtoul(sep + 1, &endp, 10);
+ if ((*endp != '\0') || (bits > max_bits))
+ {
+ msg(msglevel, "IP prefix '%s': invalid '/bits' spec (%s)", hostname,
+ sep + 1);
+ goto out;
+ }
+ *sep = '\0';
+ }
+
+ ret = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, var_host, NULL,
+ resolve_retry_seconds, sig_info, af, &ai);
+ if ((ret == 0) && network)
+ {
+ struct in6_addr *ip6;
+ in_addr_t *ip4;
+
+ switch (af)
+ {
+ case AF_INET:
+ ip4 = network;
+ *ip4 = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
+
+ if (flags & GETADDR_HOST_ORDER)
+ {
+ *ip4 = ntohl(*ip4);
+ }
+ break;
+
+ case AF_INET6:
+ ip6 = network;
+ *ip6 = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+ break;
+
+ default:
+ /* can't get here because 'af' was previously checked */
+ msg(M_WARN,
+ "Unsupported AF family for %s (%d)", var_host, af);
+ goto out;
+ }
+ }
+
+ if (netbits)
+ {
+ *netbits = bits;
+ }
+
+ /* restore '/' separator, if any */
+ if (sep)
+ {
+ *sep = '/';
+ }
+out:
+ freeaddrinfo(ai);
+ free(var_host);
+
+ return ret;
+}
-/*
- * 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 signal_info *sig_info)
{
- struct addrinfo *ai;
+ in_addr_t addr;
int status;
- status = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, hostname, NULL,
- resolve_retry_seconds, signal_received, AF_INET, &ai);
+
+ status = get_addr_generic(AF_INET, flags, hostname, &addr, NULL,
+ resolve_retry_seconds, sig_info,
+ M_WARN);
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;
+ return addr;
}
else
{
}
}
+bool
+get_ipv6_addr(const char *hostname, struct in6_addr *network,
+ unsigned int *netbits, int msglevel)
+{
+ if (get_addr_generic(AF_INET6, GETADDR_RESOLVE, hostname, network, netbits,
+ 0, NULL, msglevel) < 0)
+ {
+ return false;
+ }
+
+ return true; /* parsing OK, values set */
+}
+
static inline bool
streqnull(const char *a, const char *b)
{
/* 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);
+ status = do_preresolve_host(c, remote, ce->remote_port,
+ ce->af, flags);
if (status != 0)
{
goto err;
{
flags |= GETADDR_PASSIVE;
flags &= ~GETADDR_RANDOMIZE;
- status = do_preresolve_host(c, ce->local, ce->local_port, ce->af, flags);
+ status = do_preresolve_host(c, ce->local, ce->local_port,
+ ce->af, flags);
if (status != 0)
{
goto err;
const char *hostname,
const char *servname,
int resolve_retry_seconds,
- volatile int *signal_received,
+ struct signal_info *sig_info,
int ai_family,
struct addrinfo **res)
{
struct addrinfo hints;
int status;
- int sigrec = 0;
+ struct signal_info sigrec = {0};
int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
struct gc_arena gc = gc_new();
const char *print_hostname;
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;
}
if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
- && !signal_received)
+ && !sig_info)
{
- signal_received = &sigrec;
+ sig_info = &sigrec;
}
/* try numeric ipv6 addr first */
const char *fmt;
int level = 0;
+ if (hostname && (flags & GETADDR_RANDOMIZE))
+ {
+ hostname = hostname_randomize(hostname, &gc);
+ }
+
+ if (hostname)
+ {
+ print_hostname = hostname;
+ }
+ else
+ {
+ print_hostname = "undefined";
+ }
+
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.)";
+ 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));
+ print_hostname, print_servname, gai_strerror(status));
goto done;
}
while (true)
{
#ifndef _WIN32
+ /* force resolv.conf reload */
res_init();
#endif
/* try hostname lookup */
hints.ai_flags &= ~AI_NUMERICHOST;
- dmsg(D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d",
+ 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)
+ if (sig_info)
{
- get_signal(signal_received);
- if (*signal_received) /* were we interrupted by a signal? */
+ get_signal(&sig_info->signal_received);
+ if (sig_info->signal_received) /* were we interrupted by a signal? */
{
- if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
+ /* why are we overwriting SIGUSR1 ? */
+ if (signal_reset(sig_info, SIGUSR1) == SIGUSR1) /* ignore SIGUSR1 */
{
- msg(level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
- *signal_received = 0;
+ msg(level,
+ "RESOLVE: Ignored SIGUSR1 signal received during "
+ "DNS resolution attempt");
}
else
{
goto done;
}
- openvpn_sleep(fail_wait_interval);
+ management_sleep(fail_wait_interval);
}
ASSERT(res);
else
{
/* IP address parse succeeded */
+ if (flags & GETADDR_RANDOMIZE)
+ {
+ msg(M_WARN,
+ "WARNING: ignoring --remote-random-hostname because the "
+ "hostname is an IP address");
+ }
}
done:
- if (signal_received && *signal_received)
+ if (sig_info && sig_info->signal_received)
{
int level = 0;
if (flags & GETADDR_FATAL_ON_SIGNAL)
}
static int
-socket_get_sndbuf(int sd)
+socket_get_sndbuf(socket_descriptor_t sd)
{
-#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF)
+#if defined(SOL_SOCKET) && defined(SO_SNDBUF)
int val;
socklen_t len;
}
static void
-socket_set_sndbuf(int sd, int size)
+socket_set_sndbuf(socket_descriptor_t sd, int size)
{
-#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF)
+#if defined(SOL_SOCKET) && defined(SO_SNDBUF)
if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof(size)) != 0)
{
msg(M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size);
}
static int
-socket_get_rcvbuf(int sd)
+socket_get_rcvbuf(socket_descriptor_t sd)
{
-#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF)
+#if defined(SOL_SOCKET) && defined(SO_RCVBUF)
int val;
socklen_t len;
}
static bool
-socket_set_rcvbuf(int sd, int size)
+socket_set_rcvbuf(socket_descriptor_t sd, int size)
{
-#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF)
+#if defined(SOL_SOCKET) && defined(SO_RCVBUF)
if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof(size)) != 0)
{
msg(M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size);
}
static void
-socket_set_buffers(int fd, const struct socket_buffer_size *sbs)
+socket_set_buffers(socket_descriptor_t fd, const struct socket_buffer_size *sbs)
{
if (sbs)
{
*/
static bool
-socket_set_tcp_nodelay(int sd, int state)
+socket_set_tcp_nodelay(socket_descriptor_t sd, int state)
{
-#if defined(_WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY))
+#if defined(_WIN32) || (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);
dmsg(D_OSBUF, "Socket flags: TCP_NODELAY=%d succeeded", state);
return true;
}
-#else /* if defined(_WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) */
+#else /* if defined(_WIN32) || (defined(IPPROTO_TCP) && defined(TCP_NODELAY)) */
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)
+socket_set_mark(socket_descriptor_t sd, int mark)
{
#if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK
if (mark && setsockopt(sd, SOL_SOCKET, SO_MARK, (void *) &mark, sizeof(mark)) != 0)
}
static bool
-socket_set_flags(int sd, unsigned int sockflags)
+socket_set_flags(socket_descriptor_t sd, unsigned int sockflags)
{
- if (sockflags & SF_TCP_NODELAY)
+ /* SF_TCP_NODELAY doesn't make sense for dco-win */
+ if ((sockflags & SF_TCP_NODELAY) && (!(sockflags & SF_DCO_WIN)))
{
return socket_set_tcp_nodelay(sd, 1);
}
{
if (ls && socket_defined(ls->sd))
{
- return socket_set_flags(ls->sd, ls->sockflags = sockflags);
+ ls->sockflags |= sockflags;
+ return socket_set_flags(ls->sd, ls->sockflags);
}
else
{
}
/*
- * SOCKET INITALIZATION CODE.
+ * SOCKET INITIALIZATION CODE.
* Create a TCP/UDP socket
*/
{
ASSERT(0);
}
+ /* Set af field of sock->info, so it always reflects the address family
+ * of the created socket */
+ sock->info.af = addr->ai_family;
+
/* 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);
+#if defined(TARGET_LINUX)
+ if (sock->bind_dev)
+ {
+ msg(M_INFO, "Using bind-dev %s", sock->bind_dev);
+ if (setsockopt(sock->sd, SOL_SOCKET, SO_BINDTODEVICE, sock->bind_dev, strlen(sock->bind_dev) + 1) != 0)
+ {
+ msg(M_WARN|M_ERRNO, "WARN: setsockopt SO_BINDTODEVICE=%s failed", sock->bind_dev);
+ }
+
+ }
+#endif
+
bind_local(sock, addr->ai_family);
}
static void
protect_fd_nonlocal(int fd, const struct sockaddr *addr)
{
+ if (!management)
+ {
+ msg(M_FATAL, "Required management interface not available.");
+ }
+
/* pass socket FD to management interface to pass on to VPNService API
* as "protected socket" (exempt from being routed into tunnel)
*/
ASSERT(local);
msg(M_INFO, "Listening for incoming TCP connection on %s",
print_sockaddr(local->ai_addr, &gc));
- if (listen(sd, 1))
+ if (listen(sd, 32))
{
msg(M_ERR, "TCP: listen() failed");
}
CLEAR(*act);
-#ifdef HAVE_GETPEERNAME
if (nowait)
{
new_sd = getpeername(sd, &act->dest.addr.sa, &remote_len);
new_sd = sd;
}
}
-#else /* ifdef HAVE_GETPEERNAME */
- 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 (!socket_defined(new_sd))
{
- msg(D_LINK_ERRORS | M_ERRNO, "TCP: accept(%d) failed", sd);
+ msg(D_LINK_ERRORS | M_ERRNO, "TCP: accept(%d) failed", (int)sd);
}
/* only valid if we have remote_len_af!=0 */
else if (remote_len_af && remote_len != remote_len_af)
gc_free(&gc);
}
-static int
+static socket_descriptor_t
socket_listen_accept(socket_descriptor_t sd,
struct link_socket_actual *act,
const char *remote_dynamic,
struct gc_arena gc = gc_new();
/* struct openvpn_sockaddr *remote = &act->dest; */
struct openvpn_sockaddr remote_verify = act->dest;
- int new_sd = SOCKET_UNDEFINED;
+ socket_descriptor_t new_sd = SOCKET_UNDEFINED;
CLEAR(*act);
socket_do_listen(sd, local, do_listen, true);
if (status <= 0)
{
- openvpn_sleep(1);
+ management_sleep(1);
continue;
}
break;
}
}
- openvpn_sleep(1);
+ management_sleep(1);
}
if (!nowait && openvpn_close_socket(sd))
return new_sd;
}
-/* older mingw versions and WinXP do not have this define,
- * but Vista and up support the functionality - just define it here
- */
-#ifdef _WIN32
-#ifndef IPV6_V6ONLY
-#define IPV6_V6ONLY 27
-#endif
-#endif
void
socket_bind(socket_descriptor_t sd,
struct addrinfo *local,
}
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",
+ msg(M_FATAL | M_ERRNO, "%s: Socket bind failed on local address %s",
prefix,
- print_sockaddr_ex(local->ai_addr, ":", PS_SHOW_PORT, &gc),
- strerror_ts(errnum, &gc));
+ print_sockaddr_ex(local->ai_addr, ":", PS_SHOW_PORT, &gc));
}
gc_free(&gc);
}
protect_fd_nonlocal(sd, remote);
#endif
-#ifdef CONNECT_NONBLOCK
set_nonblock(sd);
status = connect(sd, remote, af_addr_size(remote->sa_family));
if (status)
struct pollfd fds[1];
fds[0].fd = sd;
fds[0].events = POLLOUT;
- status = poll(fds, 1, 0);
+ status = poll(fds, 1, (connect_timeout > 0) ? 1000 : 0);
#else
fd_set writes;
struct timeval tv;
FD_ZERO(&writes);
openvpn_fd_set(sd, &writes);
- tv.tv_sec = 0;
+ tv.tv_sec = (connect_timeout > 0) ? 1 : 0;
tv.tv_usec = 0;
status = select(sd + 1, NULL, &writes, NULL, &tv);
#endif
break;
}
- openvpn_sleep(1);
+ management_sleep(0);
continue;
}
}
}
}
-#else /* ifdef CONNECT_NONBLOCK */
- status = connect(sd, remote, af_addr_size(remote->sa_family));
- if (status)
- {
- status = openvpn_errno();
- }
-#endif /* ifdef CONNECT_NONBLOCK */
return status;
}
}
-void
+static void
socket_connect(socket_descriptor_t *sd,
const struct sockaddr *dest,
const int connect_timeout,
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)
if (status)
{
- msg(D_LINK_ERRORS,
- "TCP: connect to %s failed: %s",
- print_sockaddr(dest, &gc),
- strerror_ts(status, &gc));
+ msg(D_LINK_ERRORS, "TCP: connect to %s failed: %s",
+ print_sockaddr(dest, &gc), strerror(status));
openvpn_close_socket(*sd);
*sd = SOCKET_UNDEFINED;
- sig_info->signal_received = SIGUSR1;
- sig_info->source = SIG_SOURCE_CONNECTION_FAILED;
+ register_signal(sig_info, SIGUSR1, "connection-failed");
}
else
{
gc_free(&gc);
}
+/*
+ * Stream buffer handling prototypes -- stream_buf is a helper class
+ * to assist in the packetization of stream transport protocols
+ * such as TCP.
+ */
+
+static void
+stream_buf_init(struct stream_buf *sb, struct buffer *buf,
+ const unsigned int sockflags, const int proto);
+
+static void
+stream_buf_close(struct stream_buf *sb);
+
+static bool
+stream_buf_added(struct stream_buf *sb, int length_added);
+
/* For stream protocols, allocate a buffer to build up packet.
* Called after frame has been finalized. */
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);
+ overlapped_io_init(&sock->reads, frame, FALSE);
+ overlapped_io_init(&sock->writes, frame, TRUE);
sock->rw_handle.read = sock->reads.overlapped.hEvent;
sock->rw_handle.write = sock->writes.overlapped.hEvent;
#endif
sock->sockflags,
sock->info.proto);
#else
- alloc_buf_sock_tun(&sock->stream_buf_data,
- frame,
- false,
- FRAME_HEADROOM_MARKER_READ_STREAM);
+ alloc_buf_sock_tun(&sock->stream_buf_data, frame);
stream_buf_init(&sock->stream_buf,
&sock->stream_buf_data,
}
}
-/*
- * 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)
{
resolve_remote(struct link_socket *sock,
int phase,
const char **remote_dynamic,
- volatile int *signal_received)
+ struct signal_info *sig_info)
{
+ volatile int *signal_received = sig_info ? &sig_info->signal_received : NULL;
struct gc_arena gc = gc_new();
/* resolve remote address if undefined */
if (status)
{
status = openvpn_getaddrinfo(flags, sock->remote_host, sock->remote_port,
- retry, signal_received, sock->info.af, &ai);
+ retry, sig_info, 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",
+ 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 && *signal_received)
{
- if (*signal_received)
- {
- goto done;
- }
+ goto done;
}
if (status!=0)
{
if (signal_received)
{
- *signal_received = SIGUSR1;
+ /* potential overwrite of signal */
+ register_signal(sig_info, SIGUSR1, "socks-resolve-failure");
}
goto done;
}
}
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 mtu_discover_type,
- int rcvbuf,
- int sndbuf,
- int mark,
- struct event_timeout *server_poll_timeout,
- unsigned int sockflags)
+link_socket_init_phase1(struct context *c, int mode)
{
+ struct link_socket *sock = c->c2.link_socket;
+ struct options *o = &c->options;
ASSERT(sock);
- sock->local_host = local_host;
- sock->local_port = local_port;
+ const char *remote_host = o->ce.remote;
+ const char *remote_port = o->ce.remote_port;
+
+ sock->local_host = o->ce.local;
+ sock->local_port = o->ce.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->mtu_discover_type = mtu_discover_type;
+ sock->dns_cache = c->c1.dns_cache;
+ sock->http_proxy = c->c1.http_proxy;
+ sock->socks_proxy = c->c1.socks_proxy;
+ sock->bind_local = o->ce.bind_local;
+ sock->resolve_retry_seconds = o->resolve_retry_seconds;
+ sock->mtu_discover_type = o->ce.mtu_discover_type;
#ifdef ENABLE_DEBUG
- sock->gremlin = gremlin;
+ sock->gremlin = o->gremlin;
#endif
- sock->socket_buffer_sizes.rcvbuf = rcvbuf;
- sock->socket_buffer_sizes.sndbuf = sndbuf;
-
- sock->sockflags = sockflags;
- sock->mark = mark;
+ sock->socket_buffer_sizes.rcvbuf = o->rcvbuf;
+ sock->socket_buffer_sizes.sndbuf = o->sndbuf;
- 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->server_poll_timeout = server_poll_timeout;
+ sock->sockflags = o->sockflags;
+#if PORT_SHARE
+ if (o->port_share_host && o->port_share_port)
+ {
+ sock->sockflags |= SF_PORT_SHARE;
+ }
+#endif
+ sock->mark = o->mark;
+ sock->bind_dev = o->bind_dev;
+
+ sock->info.proto = o->ce.proto;
+ sock->info.af = o->ce.af;
+ sock->info.remote_float = o->ce.remote_float;
+ sock->info.lsa = &c->c1.link_socket_addr;
+ sock->info.bind_ipv6_only = o->ce.bind_ipv6_only;
+ sock->info.ipchange_command = o->ipchange;
+ sock->info.plugins = c->plugins;
+ sock->server_poll_timeout = &c->c2.server_poll_interval;
sock->mode = mode;
if (mode == LS_MODE_TCP_ACCEPT_FROM)
{
- ASSERT(accept_from);
+ ASSERT(c->c2.accept_from);
ASSERT(sock->info.proto == PROTO_TCP_SERVER);
- ASSERT(!sock->inetd);
- sock->sd = accept_from->sd;
+ sock->sd = c->c2.accept_from->sd;
+ /* inherit (possibly guessed) info AF from parent context */
+ sock->info.af = c->c2.accept_from->info.af;
}
/* are we running in HTTP proxy mode? */
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;
+ sock->remote_host = c->c1.http_proxy->options.server;
+ sock->remote_port = c->c1.http_proxy->options.port;
/* the OpenVPN server we will use the proxy to connect to */
sock->proxy_dest_host = remote_host;
/* 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;
+ sock->remote_host = c->c1.socks_proxy->server;
+ sock->remote_port = c->c1.socks_proxy->port;
/* the OpenVPN server we will use the proxy to connect to */
sock->proxy_dest_host = remote_host;
}
}
- /* 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;
- set_cloexec(sock->sd); /* not created by create_socket*() */
- }
- else if (mode != LS_MODE_TCP_ACCEPT_FROM)
+ if (mode != LS_MODE_TCP_ACCEPT_FROM)
{
if (sock->bind_local)
{
}
}
-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 /* ifdef HAVE_GETSOCKNAME */
- msg(M_WARN, "inetd(%s): this OS does not provide the getsockname() "
- "function, using AF_INET",
- proto2ascii(sock->info.proto, false));
-#endif /* ifdef HAVE_GETSOCKNAME */
- 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)
{
#if EXTENDED_SOCKET_ERROR_CAPABILITY
/* if the OS supports it, enable extended error passing on the socket */
- set_sock_extended_error_passing(sock->sd);
+ set_sock_extended_error_passing(sock->sd, sock->info.af);
#endif
}
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)
+ 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,
ASSERT(cur);
msg(msglevel, "%s link local (bound): %s",
proto2ascii(sock->info.proto, sock->info.af, true),
- print_sockaddr(cur->ai_addr,&gc));
+ print_sockaddr(cur->ai_addr, &gc));
}
else
{
static void
phase2_tcp_server(struct link_socket *sock, const char *remote_dynamic,
- volatile int *signal_received)
+ struct signal_info *sig_info)
{
+ volatile int *signal_received = sig_info ? &sig_info->signal_received : NULL;
switch (sock->mode)
{
case LS_MODE_DEFAULT:
false);
if (!socket_defined(sock->sd))
{
- *signal_received = SIGTERM;
+ register_signal(sig_info, SIGTERM, "socket-undefiled");
return;
}
tcp_connection_established(&sock->info.lsa->actual);
sock->proxy_dest_port,
sock->server_poll_timeout,
&sock->stream_buf.residual,
- &sig_info->signal_received);
+ sig_info);
}
else if (sock->socks_proxy)
{
sock->sd,
sock->proxy_dest_host,
sock->proxy_dest_port,
- &sig_info->signal_received);
+ sig_info);
}
if (proxy_retry)
{
sock->ctrl_sd,
sock->sd,
&sock->socks_relay.dest,
- &sig_info->signal_received);
+ sig_info);
if (sig_info->signal_received)
{
sock->info.lsa->remote_list = NULL;
}
- resolve_remote(sock, 1, NULL, &sig_info->signal_received);
+ resolve_remote(sock, 1, NULL, sig_info);
}
+#if defined(_WIN32)
+static void
+create_socket_dco_win(struct context *c, struct link_socket *sock,
+ struct signal_info *sig_info)
+{
+ if (!c->c1.tuntap)
+ {
+ struct tuntap *tt;
+ ALLOC_OBJ(tt, struct tuntap);
+
+ *tt = create_dco_handle(c->options.dev_node, &c->gc);
+
+ /* Ensure we can "safely" cast the handle to a socket */
+ static_assert(sizeof(sock->sd) == sizeof(tt->hand), "HANDLE and SOCKET size differs");
+
+ c->c1.tuntap = tt;
+ }
+
+ dco_create_socket(c->c1.tuntap->hand,
+ sock->info.lsa->current_remote,
+ sock->bind_local, sock->info.lsa->bind_local,
+ get_server_poll_remaining_time(sock->server_poll_timeout),
+ sig_info);
+
+ sock->sockflags |= SF_DCO_WIN;
+
+ if (sig_info->signal_received)
+ {
+ return;
+ }
+
+ sock->sd = (SOCKET)c->c1.tuntap->hand;
+ linksock_print_addr(sock);
+}
+#endif /* if defined(_WIN32) */
+
/* finalize socket initialization */
void
-link_socket_init_phase2(struct link_socket *sock,
- const struct frame *frame,
- struct signal_info *sig_info)
+link_socket_init_phase2(struct context *c)
{
+ struct link_socket *sock = c->c2.link_socket;
+ const struct frame *frame = &c->c2.frame;
+ struct signal_info *sig_info = c->sig;
+
const char *remote_dynamic = NULL;
- int sig_save = 0;
+ struct signal_info sig_save = {0};
ASSERT(sock);
ASSERT(sig_info);
if (sig_info->signal_received)
{
- sig_save = sig_info->signal_received;
- sig_info->signal_received = 0;
+ sig_save = *sig_info;
+ sig_save.signal_received = signal_reset(sig_info, 0);
}
/* initialize buffers */
remote_dynamic = sock->remote_host;
}
- /* were we started by inetd or xinetd? */
- if (sock->inetd)
+ /* Second chance to resolv/create socket */
+ resolve_remote(sock, 2, &remote_dynamic, sig_info);
+
+ /* If a valid remote has been found, create the socket with its addrinfo */
+ if (sock->info.lsa->current_remote)
{
- phase2_inetd(sock, frame, remote_dynamic, &sig_info->signal_received);
- if (sig_info->signal_received)
+#if defined(_WIN32)
+ if (dco_enabled(&c->options))
{
+ create_socket_dco_win(c, sock, sig_info);
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)
+ else
+#endif
{
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));
- sock->info.af = sock->info.lsa->bind_local->ai_family;
- }
+ /* 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 */
- create_socket(sock, sock->info.lsa->bind_local);
+ 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));
+ sock->info.af = sock->info.lsa->bind_local->ai_family;
}
- }
- /* 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;
+ create_socket(sock, sock->info.lsa->bind_local);
}
+ }
- if (sig_info->signal_received)
- {
- goto done;
- }
+ /* Socket still undefined, give a warning and abort connection */
+ if (sock->sd == SOCKET_UNDEFINED)
+ {
+ msg(M_WARN, "Could not determine IPv4/IPv6 protocol");
+ register_signal(sig_info, SIGUSR1, "Could not determine IPv4/IPv6 protocol");
+ 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);
+ if (sig_info->signal_received)
+ {
+ goto done;
+ }
- }
- else if (sock->info.proto == PROTO_UDP && sock->socks_proxy)
- {
- phase2_socks_client(sock, sig_info);
- }
+ if (sock->info.proto == PROTO_TCP_SERVER)
+ {
+ phase2_tcp_server(sock, remote_dynamic, sig_info);
+ }
+ 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);
- }
+ if (sock->sd != -1)
+ {
+ protect_fd_nonlocal(sock->sd, &sock->info.lsa->actual.dest.addr.sa);
+ }
#endif
- if (sig_info->signal_received)
- {
- goto done;
- }
+ if (sig_info->signal_received)
+ {
+ goto done;
}
phase2_set_socket_flags(sock);
linksock_print_addr(sock);
done:
- if (sig_save)
+ if (sig_save.signal_received)
{
- if (!sig_info->signal_received)
+ /* Always restore the saved signal -- register/throw_signal will handle priority */
+ if (sig_save.source == SIG_SOURCE_HARD && sig_info == &siginfo_static)
+ {
+ throw_signal(sig_save.signal_received);
+ }
+ else
{
- sig_info->signal_received = sig_save;
+ register_signal(sig_info, sig_save.signal_received, sig_save.signal_text);
}
}
}
}
}
-/* 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)
{
}
void
-link_socket_connection_initiated(const struct buffer *buf,
- struct link_socket_info *info,
+link_socket_connection_initiated(struct link_socket_info *info,
const struct link_socket_actual *act,
const char *common_name,
struct env_set *es)
{
msg(M_WARN, "WARNING: ipchange plugin call failed");
}
- argv_reset(&argv);
+ argv_free(&argv);
}
/* Process --ipchange option */
setenv_str(es, "script_type", "ipchange");
ipchange_fmt(true, &argv, info, &gc);
openvpn_run_script(&argv, es, 0, "--ipchange");
- argv_reset(&argv);
+ argv_free(&argv);
}
gc_free(&gc);
"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_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));
+ msg(D_LINK_ERRORS, "or from peer address: %s",
+ print_sockaddr_ex(ai->ai_addr, ":", PS_SHOW_PORT, &gc));
}
break;
}
* by now just ignore it
*
* For --remote entries with multiple addresses this
- * only return the actual endpoint we have sucessfully connected to
+ * only return the actual endpoint we have successfully connected to
*/
if (lsa->actual.dest.addr.sa.sa_family != AF_INET)
{
* for PF_INET6 routes over PF_INET6 endpoints
*
* For --remote entries with multiple addresses this
- * only return the actual endpoint we have sucessfully connected to
+ * only return the actual endpoint we have successfully connected to
*/
if (lsa->actual.dest.addr.sa.sa_family != AF_INET6)
{
sb->len = -1;
}
-void
+static void
stream_buf_init(struct stream_buf *sb,
struct buffer *buf,
const unsigned int sockflags,
return !sock->stream_buf.residual_fully_formed;
}
-bool
+static bool
stream_buf_added(struct stream_buf *sb,
int length_added)
{
}
}
-void
+static void
stream_buf_close(struct stream_buf *sb)
{
free_buf(&sb->residual);
if (status!=0)
{
- buf_printf(&out,"[nameinfo() err: %s]",gai_strerror(status));
+ buf_printf(&out, "[nameinfo() err: %s]", gai_strerror(status));
return BSTR(&out);
}
{
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
+ char ifname[IF_NAMESIZE] = "[undef]";
+
if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
{
switch (act->dest.addr.sa.sa_family)
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);
+ char *out = gc_malloc(INET_ADDRSTRLEN, true, 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));
+ inet_ntop(AF_INET, &ia, out, INET_ADDRSTRLEN);
}
- return BSTR(&out);
+ return out;
}
/*
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 */
+ char *out = gc_malloc(INET6_ADDRSTRLEN, true, gc);
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 );
+ inet_ntop(AF_INET6, &a6, out, INET6_ADDRSTRLEN);
}
- return BSTR(&out);
+ return out;
+}
+
+/*
+ * Convert an in_port_t in host byte order to a string
+ */
+const char *
+print_in_port_t(in_port_t port, struct gc_arena *gc)
+{
+ struct buffer buffer = alloc_buf_gc(8, gc);
+ buf_printf(&buffer, "%hu", port);
+ return BSTR(&buffer);
}
#ifndef UINT8_MAX
{
char name_buf[256];
- char buf[128];
+ char buf[INET6_ADDRSTRLEN];
switch (addr->addr.sa.sa_family)
{
case AF_INET:
openvpn_snprintf(name_buf, sizeof(name_buf), "%s", name_prefix);
}
- setenv_str(es, name_buf, inet_ntoa(addr->addr.in4.sin_addr));
+ inet_ntop(AF_INET, &addr->addr.in4.sin_addr, buf, sizeof(buf));
+ setenv_str(es, name_buf, buf);
if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
{
memcpy(&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12],
sizeof(ia.s_addr));
openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
- openvpn_snprintf(buf, sizeof(buf), "%s", inet_ntoa(ia) );
+ inet_ntop(AF_INET, &ia, buf, sizeof(buf));
}
else
{
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);
+ inet_ntop(AF_INET6, &addr->addr.in6.sin6_addr, buf, sizeof(buf));
}
setenv_str(es, name_buf, buf);
/* Indexed by PROTO_x */
static const struct proto_names proto_names[] = {
- {"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE},
+ {"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},
+ {"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},
+ {"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},
+ {"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;
-}
-
int
ascii2proto(const char *proto_name)
{
*
* IPv6 and IPv4 protocols are comptabile but OpenVPN
* has always sent UDPv4, TCPv4 over the wire. Keep these
- * strings for backward compatbility
+ * strings for backward compatibility
*/
const char *
proto_remote(int proto, bool remote)
if (!sock->stream_buf.residual_fully_formed)
{
+ /* with Linux-DCO, we sometimes try to access a socket that is
+ * already installed in the kernel and has no valid file descriptor
+ * anymore. This is a bug.
+ * Handle by resetting client instance instead of crashing.
+ */
+ if (sock->sd == SOCKET_UNDEFINED)
+ {
+ msg(M_INFO, "BUG: link_socket_read_tcp(): sock->sd==-1, reset client instance" );
+ sock->stream_reset = true; /* reset client instance */
+ return buf->len = 0; /* nothing to read */
+ }
+
#ifdef _WIN32
- len = socket_finalize(sock->sd, &sock->reads, buf, NULL);
+ sockethandle_t sh = { .s = sock->sd };
+ len = sockethandle_finalize(sh, &sock->reads, buf, NULL);
#else
struct buffer frag;
stream_buf_get_next(&sock->stream_buf, &frag);
#if ENABLE_IP_PKTINFO
-/* make the buffer large enough to handle ancilliary socket data for
+/* make the buffer large enough to handle ancillary socket data for
* both IPv4 and IPv6 destination addresses, plus padding (see RFC 2292)
*/
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
{
struct iovec iov;
uint8_t pktinfo_buf[PKTINFO_BUF_SIZE];
- struct msghdr mesg;
+ struct msghdr mesg = {0};
socklen_t fromlen = sizeof(from->dest.addr);
+ ASSERT(sock->sd >= 0); /* can't happen */
+
iov.iov_base = BPTR(buf);
iov.iov_len = buf_forward_capacity_total(buf);
mesg.msg_iov = &iov;
socklen_t fromlen = sizeof(from->dest.addr);
socklen_t expectedlen = af_addr_size(sock->info.af);
addr_zero_host(&from->dest);
+
+ ASSERT(sock->sd >= 0); /* can't happen */
+
#if ENABLE_IP_PKTINFO
/* Both PROTO_UDPv4 and PROTO_UDPv6 */
if (sock->info.proto == PROTO_UDP && sock->sockflags & SF_USE_IP_PKTINFO)
#ifdef _WIN32
+static int
+socket_get_last_error(const struct link_socket *sock)
+{
+ if (socket_is_dco_win(sock))
+ {
+ return GetLastError();
+ }
+
+ return WSAGetLastError();
+}
+
int
socket_recv_queue(struct link_socket *sock, int maxsize)
{
}
/* Win32 docs say it's okay to allocate the wsabuf on the stack */
- wsabuf[0].buf = BPTR(&sock->reads.buf);
+ wsabuf[0].buf = BSTR(&sock->reads.buf);
wsabuf[0].len = maxsize ? maxsize : BLEN(&sock->reads.buf);
/* check for buffer overflow */
ASSERT(ResetEvent(sock->reads.overlapped.hEvent));
sock->reads.flags = 0;
- if (proto_is_udp(sock->info.proto))
+ if (socket_is_dco_win(sock))
+ {
+ status = ReadFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len,
+ &sock->reads.size, &sock->reads.overlapped);
+ /* Readfile status is inverted from WSARecv */
+ status = !status;
+ }
+ else if (proto_is_udp(sock->info.proto))
{
sock->reads.addr_defined = true;
sock->reads.addrlen = sizeof(sock->reads.addr6);
}
else
{
- status = WSAGetLastError();
+ status = socket_get_last_error(sock);
if (status == WSA_IO_PENDING) /* operation queued? */
{
sock->reads.iostate = IOSTATE_QUEUED;
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].buf = BSTR(&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))
+ if (socket_is_dco_win(sock))
+ {
+ status = WriteFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len,
+ &sock->writes.size, &sock->writes.overlapped);
+
+ /* WriteFile status is inverted from WSASendTo */
+ status = !status;
+
+ }
+ else if (proto_is_udp(sock->info.proto))
{
/* set destination address for UDP writes */
sock->writes.addr_defined = true;
}
else
{
- status = WSAGetLastError();
- if (status == WSA_IO_PENDING) /* operation queued? */
+ status = socket_get_last_error(sock);
+ /* both status code have the identical value */
+ if (status == WSA_IO_PENDING || status == ERROR_IO_PENDING) /* operation queued? */
{
sock->writes.iostate = IOSTATE_QUEUED;
sock->writes.status = status;
return sock->writes.iostate;
}
+/* Returns the number of bytes successfully read */
int
-socket_finalize(SOCKET s,
- struct overlapped_io *io,
- struct buffer *buf,
- struct link_socket_actual *from)
+sockethandle_finalize(sockethandle_t sh,
+ 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
- );
+ status = SocketHandleGetOverlappedResult(sh, io);
if (status)
{
/* successful return for a queued operation */
io->iostate = IOSTATE_INITIAL;
ASSERT(ResetEvent(io->overlapped.hEvent));
- dmsg(D_WIN32_IO, "WIN32 I/O: Socket Completion success [%d]", ret);
+ dmsg(D_WIN32_IO, "WIN32 I/O: Completion success [%d]", ret);
}
else
{
/* error during a queued operation */
ret = -1;
- if (WSAGetLastError() != WSA_IO_INCOMPLETE)
+ if (SocketHandleGetLastError(sh) != ERROR_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");
+ msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Completion error");
}
}
break;
if (io->status)
{
/* error return for a non-queued operation */
- WSASetLastError(io->status);
+ SocketHandleSetLastError(sh, io->status);
ret = -1;
- msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Socket Completion non-queued error");
+ msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Completion non-queued error");
}
else
{
*buf = io->buf;
}
ret = io->size;
- dmsg(D_WIN32_IO, "WIN32 I/O: Socket Completion non-queued success [%d]", ret);
+ dmsg(D_WIN32_IO, "WIN32 I/O: Completion non-queued success [%d]", ret);
}
break;
case IOSTATE_INITIAL: /* were we called without proper queueing? */
- WSASetLastError(WSAEINVAL);
+ SocketHandleSetInvalError(sh);
ret = -1;
- dmsg(D_WIN32_IO, "WIN32 I/O: Socket Completion BAD STATE");
+ dmsg(D_WIN32_IO, "WIN32 I/O: Completion BAD STATE");
break;
default:
}
/* return from address if requested */
- if (from)
+ if (!sh.is_handle && 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
+ /* in this func we don't 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
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",
+ msg(M_FATAL | M_ERRNO,
+ "%s: Socket bind[%d] failed on unix domain socket %s",
prefix,
(int)sd,
- sockaddr_unix_name(local, "NULL"),
- strerror_ts(errnum, &gc));
+ sockaddr_unix_name(local, "NULL"));
}
-#ifdef HAVE_UMASK
umask(orig_umask);
-#endif
-
gc_free(&gc);
}
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