@@ -15,4 +15,5 @@ MAINTAINERCLEANFILES = \
include_HEADERS = \
openvpn-plugin.h \
+ openvpn-transport.h \
openvpn-msg.h
@@ -129,7 +129,8 @@ extern "C" {
#define OPENVPN_PLUGIN_TLS_FINAL 10
#define OPENVPN_PLUGIN_ENABLE_PF 11
#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12
-#define OPENVPN_PLUGIN_N 13
+#define OPENVPN_PLUGIN_TRANSPORT 13
+#define OPENVPN_PLUGIN_N 14
/*
* Build a mask out of a set of plug-in types.
@@ -852,6 +853,34 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_select_initialization_
OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_min_version_required_v1)
(void);
+/*
+ * FUNCTION: openvpn_plugin_get_vtab_v1
+ *
+ * This is only used for TRANSPORT plugins presently. It is called to
+ * retrieve a vtable structure to be used for binding virtual sockets
+ * which use the transport provided by the plugin. The selector is an
+ * OPENVPN_VTAB constant. *size_out must be set to the size of the
+ * structure returned.
+ *
+ * REQUIRED: only for TRANSPORT plugins
+ *
+ * RETURN VALUE
+ *
+ * A pointer to a vtable of the requested type, or NULL if no
+ * such vtable is provided by this plugin.
+ */
+
+enum {
+ /* Return type: struct openvpn_transport_bind_vtab1 *
+ Plugin should provide OPENVPN_PLUGIN_TRANSPORT at open time.
+ */
+ OPENVPN_VTAB_TRANSPORT_BIND_V1 = 0x54726e31 /* 'Trn1' */
+};
+
+
+OPENVPN_PLUGIN_DEF void *OPENVPN_PLUGIN_FUNC(openvpn_plugin_get_vtab_v1)
+ (int selector, size_t *size_out);
+
/*
* Deprecated functions which are still supported for backward compatibility.
*/
new file mode 100644
@@ -0,0 +1,240 @@
+/*
+ * Transport API handling code
+ *
+ * Copyright (C) 2018 Robin Tarsiger <rtt@dasyatidae.com>
+ *
+ * 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
+ */
+
+
+#ifndef OPENVPN_TRANSPORT_H_
+#define OPENVPN_TRANSPORT_H_
+
+/* PLATFORM: only POSIX-y platforms or Win32 here */
+
+#ifdef _WIN32
+
+/* Win32 */
+#define OPENVPN_TRANSPORT_PLATFORM_WIN32
+#include <stdbool.h>
+#include <windows.h>
+#include <winsock2.h>
+
+/* On Windows, platform-native events to wait on are provided to OpenVPN core as
+ * pairs of system events, normally corresponding to one potentially queued I/O
+ * operation in each direction. The read event is waited on if read events are
+ * requested, and the write event is waited on if write events are
+ * requested. Events need not be distinct, but usually will be. Two event
+ * handles must always be provided; neither is permitted to be NULL. */
+typedef const struct openvpn_transport_win32_event_pair {
+ HANDLE read;
+ HANDLE write;
+} *openvpn_transport_native_event_t;
+
+/* Windows doesn't have socklen_t; it uses int. */
+typedef int openvpn_transport_socklen_t;
+
+#else /* ifdef _WIN32 */
+
+/* POSIX-y */
+#define OPENVPN_TRANSPORT_PLATFORM_POSIX
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+/* On POSIX-y platforms, platform-native events to wait on are provided to
+ * OpenVPN core as file descriptors. Readiness for read and write are defined
+ * the same way as in poll, epoll, and similar system APIs. The file descriptor
+ * may be of any type which can be waited on. */
+typedef int openvpn_transport_native_event_t;
+
+/* Alias natural socklen_t definition. */
+typedef socklen_t openvpn_transport_socklen_t;
+
+#endif
+
+/* rwflags values are a bitwise OR of zero or more of these values, indicating
+ * what types of events are being requested or returned for a given object. */
+#define OPENVPN_TRANSPORT_EVENT_READ (1<<0)
+#define OPENVPN_TRANSPORT_EVENT_WRITE (1<<1)
+
+/* Handle to an object that can accumulate requests to wait on platform-native
+ * event sources. Use the functions in the vtable to operate on it. */
+typedef struct openvpn_transport_event_set_handle *openvpn_transport_event_set_handle_t;
+
+struct openvpn_transport_event_set_vtab {
+ /* Request to be notified when ev becomes ready in any of the ways specified
+ * by the bitmask rwflags. Incoming notifications will have arg passed
+ * through as-is. See above for the definition of native events on different
+ * platforms.
+ *
+ * Note that arg must be a distinct, owned pointer and not NULL; see
+ * update_event below. */
+ void (*set_event)(openvpn_transport_event_set_handle_t handle,
+ openvpn_transport_native_event_t ev, unsigned rwflags,
+ void *arg);
+};
+
+/* Implementation extends this structure with state. Implementation is normally
+ * provided by OpenVPN core; see request_event in struct
+ * openvpn_transport_vtab. */
+struct openvpn_transport_event_set_handle {
+ const struct openvpn_transport_event_set_vtab *vtab;
+};
+
+/* Handle to a virtual datagram socket, non-connection-oriented. */
+typedef struct openvpn_transport_socket *openvpn_transport_socket_t;
+
+/* Implementation extends this structure with state. Implementation is normally
+ * provided by a plugin providing an indirect socket mechanism. */
+struct openvpn_transport_socket {
+ const struct openvpn_transport_socket_vtab1 *vtab;
+};
+
+/* Handle to opaque per-connection parameters. */
+typedef void *openvpn_transport_args_t;
+
+/* Virtual socket implementations should expect (and consumers must
+ * provide) vtable calls in the following order:
+ *
+ * - bind must occur first, to create the virtual socket.
+ * - request_event is followed by zero or more update_event calls, and then a
+ * pump call, before any further request_event calls occur.
+ * - recvfrom/sendto may occur at any time between bind and close, including
+ * interleaved with the event request cycle above.
+ * - close must occur last.
+ *
+ * Error reporting is platform-native: errno on POSIX-y systems, or
+ * Winsock errors on Windows systems.
+ */
+
+struct openvpn_transport_bind_vtab1 {
+ /* Producer should set to 0, consumer should ignore. May indicate
+ * extended functions in the future. */
+ unsigned features;
+
+ /* Parse connection-specific arguments as provided by a
+ * "transport-plugin" configuration line. argv[0] is normally the
+ * plugin shared object pathname. argc is the total number of
+ * valid strings in argv; argv[argc] is also NULL. plugin_handle
+ * is an openvpn_plugin_handle_t. Memory for argv and its strings
+ * is borrowed and may not be retained by the plugin. Any syntax
+ * checking of text arguments should be done in this function.
+ *
+ * The value returned points to either opaque, valid parameters,
+ * or an error value. The distinction between the two is defined
+ * by the plugin, and can only be evaluated via the argerror
+ * function below.
+ *
+ * This function pointer is allowed to be NULL. In this case,
+ * argerror and freeargs must also be NULL, a nonempty
+ * connection-specific argument list will be rejected with an
+ * error, and the value of args in the subsequent bind call will
+ * always be NULL.
+ */
+ openvpn_transport_args_t (*parseargs)(void *plugin_handle,
+ const char *const *argv,
+ int argc);
+
+ /* If args is an error value, as defined by this plugin, then
+ * return a string describing the error. The string must remain
+ * valid until the next call to argerror or freeargs on the same
+ * value of args. If args is not an error value, return NULL.
+ * This function pointer must be NULL if and only if parseargs is
+ * NULL. */
+ const char *(*argerror)(openvpn_transport_args_t args);
+
+ /* Destroy any resources associated with args, which may be any
+ * return value of parseargs: either parsed parameters or an error
+ * value. This function pointer must be NULL if and only if
+ * parseargs is NULL. */
+ void (*freeargs)(openvpn_transport_args_t args);
+
+ /* Bind a new virtual socket to addr/len, given a plugin handle
+ * and any connection-specific parameters. addr must not be NULL.
+ * plugin_handle is actually of type openvpn_plugin_handle_t.
+ * args is any value returned by parseargs for which argerror
+ * would return NULL; however, it is still borrowed, and the
+ * caller may call freeargs after bind while the socket is still
+ * in use. */
+ openvpn_transport_socket_t (*bind)(void *plugin_handle,
+ openvpn_transport_args_t args,
+ const struct sockaddr *addr,
+ openvpn_transport_socklen_t len);
+};
+
+struct openvpn_transport_socket_vtab1 {
+ /* Producer should set to 0, consumer should ignore. May indicate
+ * extended functions in the future. */
+ unsigned features;
+
+ /* Given the bitmask rwflags, request that event_set be provided with all
+ * native events that should be waited on such that whenever this virtual
+ * socket may become ready in a way specified by rwflags, one of the
+ * native events will become ready. This function should call
+ *
+ * event_set->vtab->set_event(event_set, ...)
+ *
+ * zero or more times for this purpose.
+ *
+ * The state of event_set should be assumed not to persist between calls to
+ * request_event; every native event must be provided every time. Currently,
+ * only one native event may be supplied (i.e., one call above). */
+ void (*request_event)(openvpn_transport_socket_t handle,
+ openvpn_transport_event_set_handle_t event_set,
+ unsigned rwflags);
+
+ /* Indicate to the virtual socket that a native event for which arg was
+ * provided to a set_event call above became ready in a manner indicated by
+ * the bitmask rwflags. This function _must_ test for whether arg
+ * corresponds to an actual requested event from this virtual socket.
+ *
+ * If arg corresponds to a requested event, update_event does any necessary
+ * internal state updates and _returns true_ to consume the event.
+ *
+ * If arg does not correspond to a requested event, update_event
+ * does nothing and _returns false_. */
+ bool (*update_event)(openvpn_transport_socket_t handle, void *arg,
+ unsigned rwflags);
+
+ /* Perform any pending processing that can be performed
+ * immediately, and return a bitmask of rwflags indicating whether
+ * this virtual socket is ready to receive/send more packets. */
+ unsigned (*pump)(openvpn_transport_socket_t handle);
+
+ /* Receive a packet into buf/len, storing the address into addr/(*addrlen)
+ * and updating *addrlen to match. Returns -1 on error, or the number of
+ * bytes received. Must not block; signals an error if there is nothing to
+ * receive. */
+ ssize_t (*recvfrom)(openvpn_transport_socket_t handle,
+ void *buf, size_t len,
+ struct sockaddr *addr,
+ openvpn_transport_socklen_t *addrlen);
+
+ /* Send a packet from buf/len to the address addr/addrlen. Returns -1 on
+ * error, or the number of bytes sent. Must not block; signals an error if
+ * there is no room to send. */
+ ssize_t (*sendto)(openvpn_transport_socket_t handle,
+ const void *buf, size_t len,
+ const struct sockaddr *addr,
+ openvpn_transport_socklen_t addrlen);
+
+ /* Destroy this virtual socket and free all resources allocated for it. The
+ * virtual socket must not be used afterward. */
+ void (*close)(openvpn_transport_socket_t handle);
+};
+
+#endif
@@ -119,6 +119,7 @@ openvpn_SOURCES = \
status.c status.h \
syshead.h \
tls_crypt.c tls_crypt.h \
+ transport.c transport.h \
tun.c tun.h \
win32.h win32.c \
cryptoapi.h cryptoapi.c
@@ -119,6 +119,9 @@ plugin_type_name(const int type)
case OPENVPN_PLUGIN_ROUTE_PREDOWN:
return "PLUGIN_ROUTE_PREDOWN";
+ case OPENVPN_PLUGIN_TRANSPORT:
+ return "PLUGIN_TRANSPORT";
+
default:
return "PLUGIN_???";
}
@@ -295,6 +298,7 @@ plugin_init_item(struct plugin *p, const struct plugin_option *o)
PLUGIN_SYM(client_destructor, "openvpn_plugin_client_destructor_v1", 0);
PLUGIN_SYM(min_version_required, "openvpn_plugin_min_version_required_v1", 0);
PLUGIN_SYM(initialization_point, "openvpn_plugin_select_initialization_point_v1", 0);
+ PLUGIN_SYM(get_vtab, "openvpn_plugin_get_vtab_v1", 0);
if (!p->open1 && !p->open2 && !p->open3)
{
@@ -76,6 +76,7 @@ struct plugin {
openvpn_plugin_client_destructor_v1 client_destructor;
openvpn_plugin_min_version_required_v1 min_version_required;
openvpn_plugin_select_initialization_point_v1 initialization_point;
+ openvpn_plugin_get_vtab_v1 get_vtab;
openvpn_plugin_handle_t plugin_handle;
};
@@ -572,6 +572,9 @@ enum proto_num {
PROTO_TCP,
PROTO_TCP_SERVER,
PROTO_TCP_CLIENT,
+#ifdef ENABLE_PLUGIN
+ PROTO_INDIRECT,
+#endif
PROTO_N
};
new file mode 100644
@@ -0,0 +1,303 @@
+/*
+ * Transport API handling code
+ *
+ * Copyright (C) 2018 Robin Tarsiger <rtt@dasyatidae.com>
+ *
+ * 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"
+
+#ifdef ENABLE_PLUGIN
+
+#include "error.h"
+#include "event.h"
+#include "plugin.h"
+#include "socket.h"
+#include "transport.h"
+#include "openvpn-transport.h"
+
+bool
+transport_prepare(const struct plugin_list *plugins,
+ const char **transport_plugin_argv,
+ struct openvpn_transport_bind_vtab1 **vtabp,
+ openvpn_plugin_handle_t *handlep,
+ openvpn_transport_args_t *argsp)
+{
+ const char *expected_so_pathname = transport_plugin_argv[0];
+ int argc = 0;
+
+ while (transport_plugin_argv[argc])
+ {
+ argc++;
+ }
+
+ for (int i = 0; i < plugins->common->n; i++)
+ {
+ struct plugin *p = &plugins->common->plugins[i];
+ if (p->so_pathname && !strcmp(p->so_pathname, expected_so_pathname))
+ {
+ /* Pathname matches; this is the plugin requested. */
+ if (!(p->plugin_type_mask
+ & OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TRANSPORT)))
+ {
+ msg(M_FATAL,
+ "INDIRECT: plugin %s does not indicate TRANSPORT functionality",
+ p->so_pathname);
+ }
+
+ size_t size;
+ struct openvpn_transport_bind_vtab1 *vtab = NULL;
+ if (p->get_vtab)
+ {
+ vtab = p->get_vtab(OPENVPN_VTAB_TRANSPORT_BIND_V1, &size);
+ }
+
+ if (!vtab)
+ {
+ msg(M_FATAL,
+ "INDIRECT: plugin %s has no TRANSPORT_BIND_V1 table",
+ p->so_pathname);
+ }
+
+ /* Sanity checks on the vtable. */
+ if (!(size == sizeof(*vtab) && vtab->bind
+ && ((vtab->parseargs && vtab->argerror && vtab->freeargs)
+ || (!vtab->parseargs && !vtab->argerror
+ && !vtab->freeargs))))
+ {
+ msg(M_FATAL,
+ "INDIRECT: plugin %s returned a faulty TRANSPORT_BIND_V1 table",
+ p->so_pathname);
+ }
+
+ openvpn_transport_args_t args = NULL;
+ if (vtab->parseargs)
+ {
+ args = vtab->parseargs(p->plugin_handle, transport_plugin_argv,
+ argc);
+
+ const char *argerror = vtab->argerror(args);
+ if (argerror)
+ {
+ msg(M_FATAL,
+ "INDIRECT: invalid arguments to transport-plugin %s: %s",
+ p->so_pathname, argerror);
+ }
+ }
+
+ *vtabp = vtab;
+ *handlep = p->plugin_handle;
+ *argsp = args;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+openvpn_transport_socket_t
+transport_bind(const struct plugin_list *plugins,
+ const char **transport_plugin_argv, sa_family_t ai_family,
+ struct addrinfo *bind_addresses)
+{
+ openvpn_plugin_handle_t handle;
+ openvpn_transport_args_t args;
+ openvpn_transport_socket_t indirect;
+ struct openvpn_transport_bind_vtab1 *vtab;
+ struct addrinfo *cur = NULL;
+ struct openvpn_sockaddr zero;
+
+ if (!transport_prepare(plugins, transport_plugin_argv, &vtab, &handle,
+ &args))
+ {
+ msg(M_FATAL, "INDIRECT: Socket bind failed: provider plugin not found");
+ }
+
+ /* Partially replicates the functionality of socket_bind. No bind_ipv6_only
+ * or other such options, presently.
+ */
+ if (bind_addresses)
+ {
+ for (cur = bind_addresses; cur; cur = cur->ai_next)
+ {
+ if (cur->ai_family == ai_family)
+ {
+ break;
+ }
+ }
+
+ if (!cur)
+ {
+ msg(M_FATAL, "INDIRECT: Socket bind failed: Addr to bind has no %s record",
+ addr_family_name(ai_family));
+ }
+ }
+
+ if (cur)
+ {
+ indirect = vtab->bind(handle, args, cur->ai_addr, cur->ai_addrlen);
+ }
+ else if (ai_family == AF_UNSPEC)
+ {
+ msg(M_ERR, "INDIRECT: cannot bind with unspecified address family");
+ }
+ else
+ {
+ memset(&zero, 0, sizeof(zero));
+ zero.addr.sa.sa_family = ai_family;
+ addr_zero_host(&zero);
+ indirect = vtab->bind(handle, args, &zero.addr.sa, af_addr_size(ai_family));
+ }
+
+ if (!indirect)
+ {
+ msg(M_ERR, "INDIRECT: Socket bind failed");
+ }
+
+ if (vtab->freeargs)
+ {
+ vtab->freeargs(args);
+ }
+
+ return indirect;
+}
+
+struct encapsulated_event_set
+{
+ struct openvpn_transport_event_set_handle handle;
+ struct event_set *real;
+};
+
+#if EVENT_READ == OPENVPN_TRANSPORT_EVENT_READ \
+ && EVENT_WRITE == OPENVPN_TRANSPORT_EVENT_WRITE
+#define TRANSPORT_EVENT_BITS_IDENTICAL 1
+#else
+#define TRANSPORT_EVENT_BITS_IDENTICAL 0
+#endif
+
+static inline unsigned
+translate_rwflags_in(unsigned vrwflags)
+{
+#if TRANSPORT_EVENT_BITS_IDENTICAL
+ return vrwflags;
+#else
+ unsigned rwflags = 0;
+ if (vrwflags & OPENVPN_TRANSPORT_EVENT_READ)
+ {
+ rwflags |= EVENT_READ;
+ }
+ if (vrwflags & OPENVPN_TRANSPORT_EVENT_WRITE)
+ {
+ rwflags |= EVENT_WRITE;
+ }
+ return rwflags;
+#endif
+}
+
+static inline unsigned
+translate_rwflags_out(unsigned rwflags)
+{
+#if TRANSPORT_EVENT_BITS_IDENTICAL
+ return rwflags;
+#else
+ unsigned vrwflags = 0;
+ if (rwflags & EVENT_READ)
+ {
+ vrwflags |= OPENVPN_TRANSPORT_EVENT_READ;
+ }
+ if (rwflags & EVENT_WRITE)
+ {
+ vrwflags |= OPENVPN_TRANSPORT_EVENT_WRITE;
+ }
+ return vrwflags;
+#endif
+}
+
+static void
+encapsulated_event_set_set_event(openvpn_transport_event_set_handle_t handle,
+ openvpn_transport_native_event_t vev,
+ unsigned vrwflags, void *arg)
+{
+ unsigned rwflags = translate_rwflags_in(vrwflags);
+ event_t ev;
+#ifdef _WIN32
+ struct rw_handle rw;
+ rw.read = vev->read;
+ rw.write = vev->write;
+ ev = &rw;
+#else
+ ev = vev;
+#endif
+
+ struct event_set *es = ((struct encapsulated_event_set *) handle)->real;
+ /* If rwflags == 0, we do nothing, because this is always one-shot mode. */
+ if (rwflags != 0)
+ {
+ event_ctl(es, ev, rwflags, arg);
+ }
+}
+
+static const struct openvpn_transport_event_set_vtab encapsulated_event_set_vtab = {
+ encapsulated_event_set_set_event
+};
+
+unsigned
+transport_pump(openvpn_transport_socket_t indirect,
+ struct event_set_return *esr, int *esrlen)
+{
+ int i = 0;
+ while (i < *esrlen)
+ {
+ unsigned vrwflags = translate_rwflags_out(esr[i].rwflags);
+ if (indirect->vtab->update_event(indirect, esr[i].arg, vrwflags))
+ {
+ /* Consume the event; move the last one in place of it. */
+ if (i != *esrlen - 1)
+ {
+ memcpy(&esr[i], &esr[*esrlen-1], sizeof(*esr));
+ }
+ (*esrlen)--;
+ }
+ else
+ {
+ /* Don't consume the event; move to the next one. */
+ i++;
+ }
+ }
+
+ return translate_rwflags_in(indirect->vtab->pump(indirect));
+}
+
+void
+transport_request_events(openvpn_transport_socket_t indirect,
+ struct event_set *es, unsigned rwflags)
+{
+ unsigned vrwflags = translate_rwflags_out(rwflags);
+ struct encapsulated_event_set encapsulated_es;
+ encapsulated_es.handle.vtab = &encapsulated_event_set_vtab;
+ encapsulated_es.real = es;
+ indirect->vtab->request_event(indirect, &encapsulated_es.handle, vrwflags);
+}
+
+#endif /* ENABLE_PLUGIN */
new file mode 100644
@@ -0,0 +1,94 @@
+/*
+ * Transport API handling code
+ *
+ * Copyright (C) 2018 Robin Tarsiger <rtt@dasyatidae.com>
+ *
+ * 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
+ */
+
+
+#ifndef OPENVPN_TRANSPORT_H
+#define OPENVPN_TRANSPORT_H
+
+#ifdef ENABLE_PLUGIN
+
+#include "plugin.h"
+#include "openvpn-transport.h"
+
+/* Given a list of plugins and an argument list for a desired
+ * transport plugin instance, prepare to bind new link sockets using
+ * that transport plugin and args. If all succeeds, return true, and:
+ *
+ * *vtabp is set to the vtable by which to bind new link sockets.
+ * *handlep is set to the plugin handle to use in bind calls.
+ * *argsp is set to the args value to use in bind calls. It is
+ * the caller's responsibility to call freeargs on it later.
+ *
+ * Otherwise, return false, and the values of all of the above are
+ * undefined. None of the output pointers may be NULL.
+ */
+bool transport_prepare(const struct plugin_list *plugins,
+ const char **transport_plugin_argv,
+ struct openvpn_transport_bind_vtab1 **vtabp,
+ openvpn_plugin_handle_t *handlep,
+ openvpn_transport_args_t *argsp);
+
+/* Bind a virtual socket given an address family and list of potential
+ * bind addresses. bind_addresses may be NULL, in which case an
+ * unspecified address of the correct family is used. The virtual
+ * socket comes from a transport plugin in the list of plugins which
+ * matches transport_plugin_argv, which is used for any
+ * connection-specific parameters the plugin may require.
+ *
+ * Raises a fatal error if the socket cannot be bound.
+ */
+openvpn_transport_socket_t
+transport_bind(const struct plugin_list *plugins,
+ const char **transport_plugin_argv, sa_family_t ai_family,
+ struct addrinfo *bind_addresses);
+
+/* Mutates esr/esrlen to consume events. */
+unsigned transport_pump(openvpn_transport_socket_t vsocket,
+ struct event_set_return *esr, int *esrlen);
+
+void transport_request_events(openvpn_transport_socket_t indirect,
+ struct event_set *es, unsigned rwflags);
+
+/* NOTE: transport_write and transport_read implicitly downcast from a
+ * ssize_t to an int on return. Various link_socket_* functions
+ * already do this, under the assumption that the return values will
+ * always fit in an int, because the requested lengths always fit in
+ * an int, otherwise the buffer structure would already be corrupted.
+ */
+
+static inline int
+transport_write(openvpn_transport_socket_t indirect,
+ struct buffer *buf, struct sockaddr *addr, socklen_t addrlen)
+{
+ return indirect->vtab->sendto(indirect, BPTR(buf), BLEN(buf), addr,
+ addrlen);
+}
+
+static inline int
+transport_read(openvpn_transport_socket_t indirect,
+ struct buffer *buf, struct sockaddr *addr, socklen_t *addrlen)
+{
+ return indirect->vtab->recvfrom(indirect, BPTR(buf),
+ buf_forward_capacity(buf), addr, addrlen);
+}
+
+#endif /* ENABLE_PLUGIN */
+
+#endif /* !OPENVPN_TRANSPORT_H */