aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2006-02-16 18:12:24 +0000
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2006-02-16 18:12:24 +0000
commit6add6b2fe78c549d174729869e26cee917e31d5f (patch)
tree7b0786b12c40853bd9742d61e07417ade430f3d2
parentAdded "bytecount" command to management interface. (diff)
downloadopenvpn-6add6b2fe78c549d174729869e26cee917e31d5f.tar.xz
Added --port-share option for allowing OpenVPN and HTTPS
server to share the same port number. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@893 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r--ChangeLog4
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac13
-rw-r--r--errlevel.h2
-rw-r--r--error.c40
-rw-r--r--error.h5
-rw-r--r--event.h5
-rw-r--r--fdmisc.c32
-rw-r--r--fdmisc.h5
-rw-r--r--forward.c30
-rw-r--r--init.c42
-rw-r--r--openvpn.818
-rw-r--r--openvpn.c2
-rw-r--r--options.c34
-rw-r--r--options.h4
-rw-r--r--ps.c783
-rw-r--r--ps.h57
-rw-r--r--reliable.c6
-rw-r--r--reliable.h3
-rw-r--r--socket.c31
-rw-r--r--socket.h29
-rw-r--r--ssl.c6
-rw-r--r--ssl.h3
-rw-r--r--syshead.h9
24 files changed, 1122 insertions, 42 deletions
diff --git a/ChangeLog b/ChangeLog
index e38de18..496ac8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,8 +3,10 @@ Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
$Id$
-2006.xx.xx -- Version 2.1-beta9
+2006.02.16 -- Version 2.1-beta9
+* Added --port-share option for allowing OpenVPN and HTTPS
+ server to share the same port number.
* Added --management-client option to connect as a client
to management GUI app rather than be connected to as a
server.
diff --git a/Makefile.am b/Makefile.am
index 424b167..85c0c4f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -79,6 +79,7 @@ openvpn_SOURCES = \
pool.c pool.h \
proto.c proto.h \
proxy.c proxy.h \
+ ps.c ps.h \
push.c push.h \
reliable.c reliable.h \
route.c route.h \
diff --git a/configure.ac b/configure.ac
index 47c9bea..3a1f96b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,7 +25,7 @@ dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.50)
-AC_INIT([OpenVPN], [2.1_beta8b], [openvpn-users@lists.sourceforge.net], [openvpn])
+AC_INIT([OpenVPN], [2.1_beta8c], [openvpn-users@lists.sourceforge.net], [openvpn])
AM_CONFIG_HEADER(config.h)
AC_CONFIG_SRCDIR(syshead.h)
@@ -101,6 +101,12 @@ AC_ARG_ENABLE(multihome,
[MULTIHOME="yes"]
)
+AC_ARG_ENABLE(port-share,
+ [ --disable-port-share Disable TCP server port-share support (--port-share)],
+ [PORT_SHARE="$enableval"],
+ [PORT_SHARE="yes"]
+)
+
AC_ARG_ENABLE(debug,
[ --disable-debug Disable debugging support (disable gremlin and verb 7+ messages)],
[DEBUG="$enableval"],
@@ -636,6 +642,11 @@ if test "$FRAGMENT" = "yes"; then
AC_DEFINE(ENABLE_FRAGMENT, 1, [Enable internal fragmentation support])
fi
+dnl enable --port-share
+if test "$PORT_SHARE" = "yes"; then
+ AC_DEFINE(ENABLE_PORT_SHARE, 1, [Enable TCP Server port sharing])
+fi
+
dnl enable strict compiler warnings
if test "$STRICT" = "yes"; then
CFLAGS="$CFLAGS -Wall -Wpointer-arith -Wsign-compare -Wno-unused-parameter -Wno-unused-function"
diff --git a/errlevel.h b/errlevel.h
index cbe8336..426a4ac 100644
--- a/errlevel.h
+++ b/errlevel.h
@@ -93,6 +93,7 @@
#define D_SCHED_EXIT LOGLEV(3, 42, 0) /* show arming of scheduled exit */
#define D_ROUTE_QUOTA LOGLEV(3, 43, 0) /* show route quota exceeded messages */
#define D_OSBUF LOGLEV(3, 44, 0) /* show socket/tun/tap buffer sizes */
+#define D_PS_PROXY LOGLEV(3, 45, 0) /* messages related to --port-share option */
#define D_SHOW_PARMS LOGLEV(4, 50, 0) /* show all parameters on program initiation */
#define D_SHOW_OCC LOGLEV(4, 51, 0) /* show options compatibility string */
@@ -132,6 +133,7 @@
#define D_ALIGN_DEBUG LOGLEV(7, 70, M_DEBUG) /* show verbose struct alignment info */
#define D_PACKET_TRUNC_DEBUG LOGLEV(7, 70, M_DEBUG) /* PACKET_TRUNCATION_CHECK verbose */
#define D_PING LOGLEV(7, 70, M_DEBUG) /* PING send/receive messages */
+#define D_PS_PROXY_DEBUG LOGLEV(7, 70, M_DEBUG) /* port share proxy debug */
#define D_HANDSHAKE_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show detailed description of each handshake */
#define D_TLS_DEBUG_MED LOGLEV(8, 70, M_DEBUG) /* limited info from tls_session routines */
diff --git a/error.c b/error.c
index 6191cb3..de23b3d 100644
--- a/error.c
+++ b/error.c
@@ -41,6 +41,7 @@
#include "perf.h"
#include "status.h"
#include "integer.h"
+#include "ps.h"
#ifdef USE_CRYPTO
#include <openssl/err.h>
@@ -88,6 +89,15 @@ static char *pgmname_syslog; /* GLOBAL */
/* If non-null, messages should be written here (used for debugging only) */
static FILE *msgfp; /* GLOBAL */
+/* If true, we forked from main OpenVPN process */
+static bool forked; /* GLOBAL */
+
+void
+msg_forked (void)
+{
+ forked = true;
+}
+
bool
set_debug_level (const int level, const unsigned int flags)
{
@@ -270,21 +280,22 @@ void x_msg (const unsigned int flags, const char *format, ...)
prefix_sep = prefix = "";
/* virtual output capability used to copy output to management subsystem */
- {
- const struct virtual_output *vo = msg_get_virtual_output ();
- if (vo)
- {
- openvpn_snprintf (m2, ERR_BUF_SIZE, "%s%s%s",
- prefix,
- prefix_sep,
- m1);
- virtual_output_print (vo, flags, m2);
- }
- }
+ if (!forked)
+ {
+ const struct virtual_output *vo = msg_get_virtual_output ();
+ if (vo)
+ {
+ openvpn_snprintf (m2, ERR_BUF_SIZE, "%s%s%s",
+ prefix,
+ prefix_sep,
+ m1);
+ virtual_output_print (vo, flags, m2);
+ }
+ }
if (!(flags & M_MSG_VIRT_OUT))
{
- if (use_syslog && !std_redir)
+ if (use_syslog && !std_redir && !forked)
{
#if SYSLOG_CAPABILITY
syslog (level, "%s%s%s",
@@ -674,6 +685,11 @@ openvpn_exit (const int status)
plugin_abort ();
#endif
+#if PORT_SHARE
+ if (port_share)
+ port_share_abort (port_share);
+#endif
+
#ifdef ABORT_ON_ERROR
if (status == OPENVPN_EXIT_STATUS_ERROR)
abort ();
diff --git a/error.h b/error.h
index 62926cc..148ca61 100644
--- a/error.h
+++ b/error.h
@@ -196,7 +196,7 @@ FILE *msg_fp(void);
void assert_failed (const char *filename, int line);
#ifdef ENABLE_DEBUG
-void crash (void); // force a segfault (debugging only)
+void crash (void); /* force a segfault (debugging only) */
#endif
/* Inline functions */
@@ -207,6 +207,9 @@ check_debug_level (unsigned int level)
return (level & M_DEBUG_LEVEL) <= x_debug_level;
}
+/* Call if we forked */
+void msg_forked (void);
+
/* syslog output */
void open_syslog (const char *pgmname, bool stdio_to_null);
diff --git a/event.h b/event.h
index 314ef4c..4449a0a 100644
--- a/event.h
+++ b/event.h
@@ -33,9 +33,9 @@
* rwflags passed to event_ctl and returned by
* struct event_set_return.
*/
+#define EVENT_UNDEF 4
#define EVENT_READ (1<<0)
#define EVENT_WRITE (1<<1)
-
/*
* Initialization flags passed to event_set_init
*/
@@ -98,7 +98,8 @@ struct event_set *event_set_init (int *maxevents, unsigned int flags);
static inline void
event_free (struct event_set *es)
{
- (*es->func.free)(es);
+ if (es)
+ (*es->func.free)(es);
}
static inline void
diff --git a/fdmisc.c b/fdmisc.c
index 8c8b91e..eb7a6fc 100644
--- a/fdmisc.c
+++ b/fdmisc.c
@@ -36,25 +36,43 @@
#include "memdbg.h"
/* Set a file descriptor to non-blocking */
-void
-set_nonblock (int fd)
+bool
+set_nonblock_action (int fd)
{
#ifdef WIN32
u_long arg = 1;
if (ioctlsocket (fd, FIONBIO, &arg))
- msg (M_SOCKERR, "Set socket to non-blocking mode failed");
+ return false;
#else
if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
- msg (M_ERR, "Set file descriptor to non-blocking mode failed");
+ return false;
#endif
+ return true;
}
/* Set a file descriptor to not be passed across execs */
-void
-set_cloexec (int fd)
+bool
+set_cloexec_action (int fd)
{
#ifndef WIN32
if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
- msg (M_ERR, "Set FD_CLOEXEC flag on file descriptor failed");
+ return false;
#endif
+ return true;
+}
+
+/* Set a file descriptor to non-blocking */
+void
+set_nonblock (int fd)
+{
+ if (!set_nonblock_action (fd))
+ msg (M_SOCKERR, "Set socket to non-blocking mode failed");
+}
+
+/* Set a file descriptor to not be passed across execs */
+void
+set_cloexec (int fd)
+{
+ if (!set_cloexec_action (fd))
+ msg (M_ERR, "Set FD_CLOEXEC flag on file descriptor failed");
}
diff --git a/fdmisc.h b/fdmisc.h
index 5019cc2..fa2cac9 100644
--- a/fdmisc.h
+++ b/fdmisc.h
@@ -22,5 +22,10 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "basic.h"
+
+bool set_nonblock_action (int fd);
+bool set_cloexec_action (int fd);
+
void set_nonblock (int fd);
void set_cloexec (int fd);
diff --git a/forward.c b/forward.c
index be041b9..5fec532 100644
--- a/forward.c
+++ b/forward.c
@@ -36,6 +36,7 @@
#include "gremlin.h"
#include "mss.h"
#include "event.h"
+#include "ps.h"
#include "memdbg.h"
@@ -640,18 +641,31 @@ read_incoming_link (struct context *c)
if (socket_connection_reset (c->c2.link_socket, status))
{
- /* received a disconnect from a connection-oriented protocol */
- if (c->options.inetd)
+#if PORT_SHARE
+ if (port_share && socket_foreign_protocol_detected (c->c2.link_socket))
{
+ const struct buffer *fbuf = socket_foreign_protocol_head (c->c2.link_socket);
+ const int sd = socket_foreign_protocol_sd (c->c2.link_socket);
+ port_share_redirect (port_share, fbuf, sd);
c->sig->signal_received = SIGTERM;
- msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status);
+ c->sig->signal_text = "port-share-redirect";
}
else
- {
- c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */
- msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status);
- }
- c->sig->signal_text = "connection-reset";
+#endif
+ {
+ /* received a disconnect from a connection-oriented protocol */
+ if (c->options.inetd)
+ {
+ c->sig->signal_received = SIGTERM;
+ msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status);
+ }
+ else
+ {
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */
+ msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status);
+ }
+ c->sig->signal_text = "connection-reset";
+ }
perf_pop ();
return;
}
diff --git a/init.c b/init.c
index b1ef034..c5b3a84 100644
--- a/init.c
+++ b/init.c
@@ -39,6 +39,7 @@
#include "pool.h"
#include "gremlin.h"
#include "pkcs11.h"
+#include "ps.h"
#include "memdbg.h"
@@ -191,6 +192,32 @@ context_gc_free (struct context *c)
gc_free (&c->gc);
}
+#if PORT_SHARE
+
+static void
+close_port_share (void)
+{
+ if (port_share)
+ {
+ port_share_close (port_share);
+ port_share = NULL;
+ }
+}
+
+static void
+init_port_share (struct context *c)
+{
+ if (!port_share && (c->options.port_share_host && c->options.port_share_port))
+ {
+ port_share = port_share_open (c->options.port_share_host,
+ c->options.port_share_port);
+ if (port_share == NULL)
+ msg (M_FATAL, "Fatal error: Port sharing failed");
+ }
+}
+
+#endif
+
bool
init_static (void)
{
@@ -274,6 +301,10 @@ uninit_static (void)
pkcs11_terminate ();
#endif
+#if PORT_SHARE
+ close_port_share ();
+#endif
+
#if defined(MEASURE_TLS_HANDSHAKE_STATS) && defined(USE_CRYPTO) && defined(USE_SSL)
show_tls_performance_stats ();
#endif
@@ -1490,6 +1521,11 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.renegotiate_seconds = options->renegotiate_seconds;
to.single_session = options->single_session;
+ /* should we not xmit any packets until we get an initial
+ response from client? */
+ if (to.server && options->proto == PROTO_TCPv4_SERVER)
+ to.xmit_hold = true;
+
#ifdef ENABLE_OCC
to.disable_occ = !options->occ;
#endif
@@ -2659,6 +2695,12 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int
open_plugins (c, false, OPENVPN_PLUGIN_INIT_POST_UID_CHANGE);
#endif
+#if PORT_SHARE
+ /* share OpenVPN port with foreign (such as HTTPS) server */
+ if (c->first_time && (c->mode == CM_P2P || c->mode == CM_TOP))
+ init_port_share (c);
+#endif
+
/* Check for signals */
if (IS_SIG (c))
goto sig;
diff --git a/openvpn.8 b/openvpn.8
index 6a92cac..8f29469 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -217,6 +217,7 @@ openvpn \- secure IP tunnel daemon.
[\ \fB\-\-pkcs12\fR\ \fIfile\fR\ ]
[\ \fB\-\-plugin\fR\ \fImodule\-pathname\ init\-string\fR\ ]
[\ \fB\-\-port\fR\ \fIport\fR\ ]
+[\ \fB\-\-port\-share\fR\ \fIhost\ port\fR\ ]
[\ \fB\-\-proto\fR\ \fIp\fR\ ]
[\ \fB\-\-pull\fR\ ]
[\ \fB\-\-push\-reset\fR\ ]
@@ -2971,6 +2972,23 @@ authentication, use
the authenticated username as the common name,
rather than the common name from the client cert.
.\"*********************************************************
+.TP
+.B --port-share host port
+When run in TCP server mode, share the OpenVPN port with
+another application, such as an HTTPS server. If OpenVPN
+senses a connection to its port which is using a non-OpenVPN
+protocol, it will proxy the connection to the server at
+.B host:port.
+Currently only designed to work with HTTPS,
+though it would be theoretically possible to extend to
+other protocols such as ssh.
+
+Currently only implemented
+on Linux, though porting to BSDs should be straightforward.
+The reason for the non-portability is that the current implementation
+uses sendmsg and recvmsg for passing file descriptors between
+processes.
+.\"*********************************************************
.SS Client Mode
Use client mode when connecting to an OpenVPN server
which has
diff --git a/openvpn.c b/openvpn.c
index eff610a..496e505 100644
--- a/openvpn.c
+++ b/openvpn.c
@@ -192,7 +192,7 @@ main (int argc, char *argv[])
if (!open_management (&c))
break;
#endif
-
+
/* set certain options as environmental variables */
setenv_settings (c.es, &c.options);
diff --git a/options.c b/options.c
index 2c9dbcb..d882434 100644
--- a/options.c
+++ b/options.c
@@ -363,6 +363,10 @@ static const char usage_message[] =
"--connect-freq n s : Allow a maximum of n new connections per s seconds.\n"
"--max-clients n : Allow a maximum of n simultaneously connected clients.\n"
"--max-routes-per-client n : Allow a maximum of n internal routes per client.\n"
+#if PORT_SHARE
+ "--port-share host port : When run in TCP mode, proxy incoming HTTPS sessions\n"
+ " to a web server at host:port.\n"
+#endif
#endif
"\n"
"Client options (when connecting to a multi-client server):\n"
@@ -918,6 +922,10 @@ show_p2mp_parms (const struct options *o)
SHOW_BOOL (username_as_common_name)
SHOW_STR (auth_user_pass_verify_script);
SHOW_BOOL (auth_user_pass_verify_script_via_file);
+#if PORT_SHARE
+ SHOW_STR (port_share_host);
+ SHOW_INT (port_share_port);
+#endif
#endif /* P2MP_SERVER */
SHOW_BOOL (client);
@@ -1594,6 +1602,10 @@ options_postprocess (struct options *options, bool first_time)
msg (M_USAGE, "--pull cannot be used with --mode server");
if (!(options->proto == PROTO_UDPv4 || options->proto == PROTO_TCPv4_SERVER))
msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+#if PORT_SHARE
+ if ((options->port_share_host || options->port_share_port) && options->proto != PROTO_TCPv4_SERVER)
+ msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server)");
+#endif
if (!options->tls_server)
msg (M_USAGE, "--mode server requires --tls-server");
if (options->remote_list)
@@ -1682,6 +1694,11 @@ options_postprocess (struct options *options, bool first_time)
msg (M_USAGE, "--username-as-common-name requires --mode server");
if (options->auth_user_pass_verify_script)
msg (M_USAGE, "--auth-user-pass-verify requires --mode server");
+#if PORT_SHARE
+ if (options->port_share_host || options->port_share_port)
+ msg (M_USAGE, "--port-share requires TCP server mode (--mode server --proto tcp-server)");
+#endif
+
}
#endif /* P2MP_SERVER */
@@ -4234,6 +4251,23 @@ add_option (struct options *options,
msg (msglevel, "--tcp-queue-limit parameter must be > 0");
options->tcp_queue_limit = tcp_queue_limit;
}
+#if PORT_SHARE
+ else if (streq (p[0], "port-share") && p[1] && p[2])
+ {
+ int port;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ port = atoi (p[2]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "port number associated with --port-share directive is out of range");
+ goto err;
+ }
+
+ options->port_share_host = p[1];
+ options->port_share_port = port;
+ }
+#endif
else if (streq (p[0], "client-to-client"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
diff --git a/options.h b/options.h
index 9f8f08a..4bf1139 100644
--- a/options.h
+++ b/options.h
@@ -344,6 +344,10 @@ struct options
bool username_as_common_name;
const char *auth_user_pass_verify_script;
bool auth_user_pass_verify_script_via_file;
+#if PORT_SHARE
+ char *port_share_host;
+ int port_share_port;
+#endif
#endif
bool client;
diff --git a/ps.c b/ps.c
new file mode 100644
index 0000000..97aaf59
--- /dev/null
+++ b/ps.c
@@ -0,0 +1,783 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@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 WIN32
+#include "config-win32.h"
+#else
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#if PORT_SHARE
+
+#include "event.h"
+#include "socket.h"
+#include "fdmisc.h"
+#include "ps.h"
+
+#include "memdbg.h"
+
+struct port_share *port_share = NULL; /* GLOBAL */
+
+/* size of i/o buffers */
+#define PROXY_CONNECTION_BUFFER_SIZE 1500
+
+/* Command codes for foreground -> background communication */
+#define COMMAND_REDIRECT 10
+#define COMMAND_EXIT 11
+
+/* Response codes for background -> foreground communication */
+#define RESPONSE_INIT_SUCCEEDED 20
+#define RESPONSE_INIT_FAILED 21
+
+/* A foreign (non-OpenVPN) connection we are proxying,
+ usually HTTPS */
+struct proxy_connection {
+ bool defined;
+ struct proxy_connection *next;
+ struct proxy_connection *counterpart;
+ struct buffer buf;
+ bool buffer_initial;
+ int rwflags;
+ int sd;
+};
+
+/* used for passing fds between processes */
+union fdmsg {
+ struct cmsghdr h;
+ char buf[CMSG_SPACE(sizeof(socket_descriptor_t))];
+};
+
+#if 0
+static const char *
+headc (const struct buffer *buf)
+{
+ static char foo[16];
+ strncpy (foo, BSTR(buf), 15);
+ foo[15] = 0;
+ return foo;
+}
+#endif
+
+static void
+close_socket_if_defined (const socket_descriptor_t sd)
+{
+ if (socket_defined (sd))
+ openvpn_close_socket (sd);
+}
+
+/*
+ * Close most of parent's fds.
+ * Keep stdin/stdout/stderr, plus one
+ * other fd which is presumed to be
+ * our pipe back to parent.
+ * Admittedly, a bit of a kludge,
+ * but posix doesn't give us a kind
+ * of FD_CLOEXEC which will stop
+ * fds from crossing a fork().
+ */
+static void
+close_fds_except (int keep)
+{
+ socket_descriptor_t i;
+ closelog ();
+ for (i = 3; i <= 100; ++i)
+ {
+ if (i != keep)
+ openvpn_close_socket (i);
+ }
+}
+
+/*
+ * Usually we ignore signals, because our parent will
+ * deal with them.
+ */
+static void
+set_signals (void)
+{
+ signal (SIGTERM, SIG_DFL);
+
+ signal (SIGINT, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+ signal (SIGUSR2, SIG_IGN);
+ signal (SIGPIPE, SIG_IGN);
+}
+
+/*
+ * Socket read/write functions.
+ */
+
+static int
+recv_control (const socket_descriptor_t fd)
+{
+ unsigned char c;
+ const ssize_t size = read (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return c;
+ else
+ {
+ return -1;
+ }
+}
+
+static int
+send_control (const socket_descriptor_t fd, int code)
+{
+ unsigned char c = (unsigned char) code;
+ const ssize_t size = write (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return (int) size;
+ else
+ return -1;
+}
+
+static void
+port_share_sendmsg (const socket_descriptor_t sd,
+ const char command,
+ const struct buffer *head,
+ const socket_descriptor_t sd_send)
+{
+ if (socket_defined (sd))
+ {
+ struct msghdr mesg;
+ union fdmsg cmsg;
+ struct cmsghdr* h;
+ struct iovec iov[2];
+ socket_descriptor_t sd_null[2] = { SOCKET_UNDEFINED, SOCKET_UNDEFINED };
+ char cmd;
+ ssize_t status;
+
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: sendmsg sd=%d len=%d",
+ sd_send,
+ head ? BLEN(head) : -1);
+
+ CLEAR (mesg);
+
+ cmd = command;
+
+ iov[0].iov_base = &cmd;
+ iov[0].iov_len = sizeof (cmd);
+ mesg.msg_iovlen = 1;
+
+ if (head)
+ {
+ iov[1].iov_base = BPTR (head);
+ iov[1].iov_len = BLEN (head);
+ mesg.msg_iovlen = 2;
+ }
+
+ mesg.msg_iov = iov;
+
+ mesg.msg_control = cmsg.buf;
+ mesg.msg_controllen = sizeof (union fdmsg);
+ mesg.msg_flags = 0;
+
+ h = CMSG_FIRSTHDR(&mesg);
+ h->cmsg_level = SOL_SOCKET;
+ h->cmsg_type = SCM_RIGHTS;
+ h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t));
+
+ if (socket_defined (sd_send))
+ {
+ *((socket_descriptor_t*)CMSG_DATA(h)) = sd_send;
+ }
+ else
+ {
+ socketpair (PF_UNIX, SOCK_DGRAM, 0, sd_null);
+ *((socket_descriptor_t*)CMSG_DATA(h)) = sd_null[0];
+ }
+
+ status = sendmsg (sd, &mesg, MSG_NOSIGNAL);
+ if (status == -1)
+ msg (M_WARN, "PORT SHARE: sendmsg failed (unable to communicate with background process)");
+
+ close_socket_if_defined (sd_null[0]);
+ close_socket_if_defined (sd_null[1]);
+ }
+}
+
+static int
+pc_list_len (struct proxy_connection *pc)
+{
+ int count = 0;
+ while (pc)
+ {
+ ++count;
+ pc = pc->next;
+ }
+ return count;
+}
+
+/* mark a proxy entry and its counterpart for close */
+static void
+proxy_entry_mark_for_close (struct proxy_connection *pc, struct event_set *es)
+{
+ if (pc->defined)
+ {
+ struct proxy_connection *cp = pc->counterpart;
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: delete sd=%d", pc->sd);
+ if (socket_defined (pc->sd))
+ {
+ if (es)
+ event_del (es, pc->sd);
+ openvpn_close_socket (pc->sd);
+ pc->sd = SOCKET_UNDEFINED;
+ }
+ free_buf (&pc->buf);
+ pc->buffer_initial = false;
+ pc->rwflags = 0;
+ pc->counterpart = NULL;
+ pc->defined = false;
+ if (cp && cp->defined && cp->counterpart == pc)
+ proxy_entry_mark_for_close (cp, es);
+ }
+}
+
+static void
+proxy_list_housekeeping (struct proxy_connection **list)
+{
+ if (list)
+ {
+ struct proxy_connection *prev = NULL;
+ struct proxy_connection *pc = *list;
+
+ while (pc)
+ {
+ struct proxy_connection *next = pc->next;
+ if (!pc->defined)
+ {
+ free (pc);
+ if (prev)
+ prev->next = next;
+ else
+ *list = next;
+ }
+ else
+ prev = pc;
+ pc = next;
+ }
+ }
+}
+
+static void
+proxy_list_close (struct proxy_connection **list)
+{
+ if (list)
+ {
+ struct proxy_connection *pc = *list;
+ while (pc)
+ {
+ proxy_entry_mark_for_close (pc, NULL);
+ pc = pc->next;
+ }
+ proxy_list_housekeeping (list);
+ }
+}
+
+static void
+sock_addr_set (struct openvpn_sockaddr *osaddr,
+ const in_addr_t addr,
+ const int port)
+{
+ CLEAR (*osaddr);
+ osaddr->sa.sin_family = AF_INET;
+ osaddr->sa.sin_addr.s_addr = htonl (addr);
+ osaddr->sa.sin_port = htons (port);
+}
+
+static inline void
+proxy_connection_io_requeue (struct proxy_connection *pc, const int rwflags_new, struct event_set *es)
+{
+ if (pc->rwflags != rwflags_new)
+ {
+ event_ctl (es, pc->sd, rwflags_new, (void*)pc);
+ pc->rwflags = rwflags_new;
+ }
+}
+
+static bool
+proxy_entry_new (struct proxy_connection **list,
+ struct event_set *es,
+ const in_addr_t server_addr,
+ const int server_port,
+ const socket_descriptor_t sd_client,
+ struct buffer *initial_data)
+{
+ struct openvpn_sockaddr osaddr;
+ socket_descriptor_t sd_server;
+ int status;
+ struct proxy_connection *pc;
+ struct proxy_connection *cp;
+
+ /* connect to port share server */
+ sock_addr_set (&osaddr, server_addr, server_port);
+ sd_server = create_socket_tcp ();
+ status = openvpn_connect (sd_server, &osaddr, 5, NULL);
+ if (status)
+ {
+ msg (M_WARN, "PORT SHARE PROXY: connect to port-share server failed");
+ openvpn_close_socket (sd_server);
+ return false;
+ }
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: connect to port-share server succeeded");
+
+ set_nonblock (sd_client);
+ set_nonblock (sd_server);
+
+ /* allocate 2 new proxy_connection objects */
+ ALLOC_OBJ_CLEAR (pc, struct proxy_connection);
+ ALLOC_OBJ_CLEAR (cp, struct proxy_connection);
+
+ /* client object */
+ pc->defined = true;
+ pc->next = cp;
+ pc->counterpart = cp;
+ pc->buf = *initial_data;
+ pc->buffer_initial = true;
+ pc->rwflags = EVENT_UNDEF;
+ pc->sd = sd_client;
+
+ /* server object */
+ cp->defined = true;
+ cp->next = *list;
+ cp->counterpart = pc;
+ cp->buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ cp->buffer_initial = false;
+ cp->rwflags = EVENT_UNDEF;
+ cp->sd = sd_server;
+
+ /* add to list */
+ *list = pc;
+
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: NEW CONNECTION [c=%d s=%d]", sd_client, sd_server);
+
+ /* set initial i/o states */
+ proxy_connection_io_requeue (pc, EVENT_READ, es);
+ proxy_connection_io_requeue (cp, EVENT_READ|EVENT_WRITE, es);
+
+ return true;
+}
+
+static bool
+control_message_from_parent (const socket_descriptor_t sd_control,
+ struct proxy_connection **list,
+ struct event_set *es,
+ const in_addr_t server_addr,
+ const int server_port)
+{
+ struct buffer buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ struct msghdr mesg;
+ union fdmsg cmsg;
+ struct cmsghdr* h;
+ struct iovec iov[2];
+ char command = 0;
+ ssize_t status;
+ int ret = true;
+
+ CLEAR (mesg);
+
+ iov[0].iov_base = &command;
+ iov[0].iov_len = sizeof (command);
+ iov[1].iov_base = BPTR (&buf);
+ iov[1].iov_len = BCAP (&buf);
+ mesg.msg_iov = iov;
+ mesg.msg_iovlen = 2;
+
+ mesg.msg_control = cmsg.buf;
+ mesg.msg_controllen = sizeof (union fdmsg);
+ mesg.msg_flags = 0;
+
+ h = CMSG_FIRSTHDR(&mesg);
+ h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t));
+ h->cmsg_level = SOL_SOCKET;
+ h->cmsg_type = SCM_RIGHTS;
+ *((socket_descriptor_t*)CMSG_DATA(h)) = SOCKET_UNDEFINED;
+
+ status = recvmsg (sd_control, &mesg, MSG_NOSIGNAL);
+ if (status != -1)
+ {
+ if ( h == NULL
+ || h->cmsg_len != CMSG_LEN(sizeof(socket_descriptor_t))
+ || h->cmsg_level != SOL_SOCKET
+ || h->cmsg_type != SCM_RIGHTS )
+ {
+ ret = false;
+ }
+ else
+ {
+ const socket_descriptor_t received_fd = *((socket_descriptor_t*)CMSG_DATA(h));
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED sd=%d", received_fd);
+
+ if (status >= 2 && command == COMMAND_REDIRECT)
+ {
+ buf.len = status - 1;
+ if (proxy_entry_new (list,
+ es,
+ server_addr,
+ server_port,
+ received_fd,
+ &buf))
+ {
+ CLEAR (buf); /* we gave the buffer to proxy_entry_new */
+ }
+ else
+ {
+ openvpn_close_socket (received_fd);
+ }
+ }
+ else if (status >= 1 && command == COMMAND_EXIT)
+ {
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED COMMAND_EXIT");
+ openvpn_close_socket (received_fd); /* null socket */
+ ret = false;
+ }
+ }
+ }
+ free_buf (&buf);
+ return ret;
+}
+
+/* proxy_connection_io_xfer return values */
+
+#define IOSTAT_EAGAIN_ON_READ 0
+#define IOSTAT_EAGAIN_ON_WRITE 1
+#define IOSTAT_ERROR 2
+
+/* forward data from pc to pc->counterpart */
+static int
+proxy_connection_io_xfer (struct proxy_connection *pc)
+{
+ while (true)
+ {
+ if (!BLEN (&pc->buf))
+ {
+ /* recv data from socket */
+ ssize_t status = recv (pc->sd, BPTR(&pc->buf), BCAP(&pc->buf), MSG_NOSIGNAL);
+ if (status == -1)
+ {
+ return (errno == EAGAIN) ? IOSTAT_EAGAIN_ON_READ : IOSTAT_ERROR;
+ }
+ else
+ {
+ if (!status)
+ return IOSTAT_ERROR;
+ pc->buf.len = status;
+ }
+ }
+
+ if (BLEN (&pc->buf))
+ {
+ /* send data to counterpart socket */
+ ssize_t status = send (pc->counterpart->sd, BPTR(&pc->buf), BLEN(&pc->buf), MSG_NOSIGNAL);
+ if (status == -1)
+ {
+ const int e = errno;
+ return (e == EAGAIN) ? IOSTAT_EAGAIN_ON_WRITE : IOSTAT_ERROR;
+ }
+ else
+ {
+ if (status != pc->buf.len)
+ return IOSTAT_ERROR;
+ pc->buf.len = 0;
+ }
+
+ /* successful send */
+ if (pc->buffer_initial)
+ {
+ free_buf (&pc->buf);
+ pc->buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ pc->buffer_initial = false;
+ }
+ }
+ }
+ return IOSTAT_ERROR;
+}
+
+static inline bool
+proxy_connection_io_status (const int status, int *rwflags_pc, int *rwflags_cp)
+{
+ switch (status)
+ {
+ case IOSTAT_EAGAIN_ON_READ:
+ *rwflags_pc |= EVENT_READ;
+ *rwflags_cp &= ~EVENT_WRITE;
+ return true;
+ case IOSTAT_EAGAIN_ON_WRITE:
+ *rwflags_pc &= ~EVENT_READ;
+ *rwflags_cp |= EVENT_WRITE;
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+proxy_connection_io_dispatch (struct proxy_connection *pc,
+ const int rwflags,
+ struct event_set *es)
+{
+ struct proxy_connection *cp = pc->counterpart;
+ int status;
+ int rwflags_pc = pc->rwflags;
+ int rwflags_cp = cp->rwflags;
+
+ if (rwflags & EVENT_READ)
+ {
+ status = proxy_connection_io_xfer (pc);
+ if (!proxy_connection_io_status (status, &rwflags_pc, &rwflags_cp))
+ goto bad;
+ }
+ if (rwflags & EVENT_WRITE)
+ {
+ status = proxy_connection_io_xfer (cp);
+ if (!proxy_connection_io_status (status, &rwflags_cp, &rwflags_pc))
+ goto bad;
+ }
+ proxy_connection_io_requeue (pc, rwflags_pc, es);
+ proxy_connection_io_requeue (cp, rwflags_cp, es);
+
+ return true;
+
+ bad:
+ proxy_entry_mark_for_close (pc, es);
+ return false;
+}
+
+static void
+port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descriptor_t sd_control)
+{
+ if (send_control (sd_control, RESPONSE_INIT_SUCCEEDED) >= 0)
+ {
+ void *sd_control_marker = (void *)1;
+ int maxevents = 256;
+ struct event_set *es;
+ struct event_set_return esr[64];
+ struct proxy_connection *list = NULL;
+ time_t last_housekeeping = 0;
+
+ msg (D_PS_PROXY, "PORT SHARE PROXY: proxy starting");
+
+ es = event_set_init (&maxevents, 0);
+ event_ctl (es, sd_control, EVENT_READ, sd_control_marker);
+ while (true)
+ {
+ int n_events;
+ struct timeval tv;
+ time_t current;
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ n_events = event_wait (es, &tv, esr, SIZE(esr));
+ current = time(NULL);
+ if (n_events > 0)
+ {
+ int i;
+ for (i = 0; i < n_events; ++i)
+ {
+ const struct event_set_return *e = &esr[i];
+ if (e->arg == sd_control_marker)
+ {
+ if (!control_message_from_parent (sd_control, &list, es, hostaddr, port))
+ goto done;
+ }
+ else
+ {
+ struct proxy_connection *pc = (struct proxy_connection *)e->arg;
+ if (pc->defined)
+ proxy_connection_io_dispatch (pc, e->rwflags, es);
+ }
+ }
+ }
+ else if (n_events < 0)
+ {
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: event_wait failed");
+ }
+ if (current > last_housekeeping)
+ {
+ proxy_list_housekeeping (&list);
+ last_housekeeping = current;
+ }
+ }
+
+ done:
+ proxy_list_close (&list);
+ event_free (es);
+ }
+ msg (D_PS_PROXY, "PORT SHARE PROXY: proxy exiting");
+}
+
+struct port_share *
+port_share_open (const char *host, const int port)
+{
+ pid_t pid;
+ socket_descriptor_t fd[2];
+ in_addr_t hostaddr;
+ struct port_share *ps;
+
+ ALLOC_OBJ_CLEAR (ps, struct port_share);
+
+ /*
+ * Get host's IP address
+ */
+ hostaddr = getaddr (GETADDR_RESOLVE|GETADDR_HOST_ORDER|GETADDR_FATAL, host, 0, NULL, NULL);
+
+ /*
+ * Make a socket for foreground and background processes
+ * to communicate.
+ */
+ if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
+ {
+ msg (M_WARN, "PORT SHARE: socketpair call failed");
+ goto error;
+ }
+
+ /*
+ * Fork off background proxy process.
+ */
+ pid = fork ();
+
+ if (pid)
+ {
+ int status;
+
+ /*
+ * Foreground Process
+ */
+
+ ps->background_pid = pid;
+
+ /* close our copy of child's socket */
+ openvpn_close_socket (fd[1]);
+
+ /* don't let future subprocesses inherit child socket */
+ set_cloexec (fd[0]);
+
+ /* wait for background child process to initialize */
+ status = recv_control (fd[0]);
+ if (status == RESPONSE_INIT_SUCCEEDED)
+ {
+ ps->foreground_fd = fd[0];
+ return ps;
+ }
+ }
+ else
+ {
+ /*
+ * Background Process
+ */
+
+ /* Ignore most signals (the parent will receive them) */
+ set_signals ();
+
+ /* Let msg know that we forked */
+ msg_forked ();
+
+ /* close all parent fds except our socket back to parent */
+ close_fds_except (fd[1]);
+
+ /* no blocking on control channel back to parent */
+ set_nonblock (fd[1]);
+
+ /* execute the event loop */
+ port_share_proxy (hostaddr, port, fd[1]);
+
+ openvpn_close_socket (fd[1]);
+
+ exit (0);
+ return 0; /* NOTREACHED */
+ }
+
+ error:
+ port_share_close (ps);
+ return NULL;
+}
+
+void
+port_share_close (struct port_share *ps)
+{
+ if (ps)
+ {
+ if (ps->foreground_fd >= 0)
+ {
+ /* tell background process to exit */
+ port_share_sendmsg (ps->foreground_fd, COMMAND_EXIT, NULL, SOCKET_UNDEFINED);
+
+ /* wait for background process to exit */
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: waiting for background process to exit");
+ if (ps->background_pid > 0)
+ waitpid (ps->background_pid, NULL, 0);
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: background process exited");
+
+ openvpn_close_socket (ps->foreground_fd);
+ ps->foreground_fd = -1;
+ }
+
+ free (ps);
+ }
+}
+
+void
+port_share_abort (struct port_share *ps)
+{
+ if (ps)
+ {
+ /* tell background process to exit */
+ if (ps->foreground_fd >= 0)
+ {
+ send_control (ps->foreground_fd, COMMAND_EXIT);
+ openvpn_close_socket (ps->foreground_fd);
+ ps->foreground_fd = -1;
+ }
+ }
+}
+
+bool
+is_openvpn_protocol (const struct buffer *buf)
+{
+ const unsigned char *p = BSTR (buf);
+ const int len = BLEN (buf);
+ if (len >= 3)
+ {
+ return p[0] == 0
+ && p[1] >= 14
+ && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
+ }
+ else if (len >= 2)
+ {
+ return p[0] == 0 && p[1] >= 14;
+ }
+ else
+ return true;
+}
+
+void
+port_share_redirect (struct port_share *ps, const struct buffer *head, socket_descriptor_t sd)
+{
+ if (ps)
+ port_share_sendmsg (ps->foreground_fd, COMMAND_REDIRECT, head, sd);
+}
+
+#endif
diff --git a/ps.h b/ps.h
new file mode 100644
index 0000000..65c4c97
--- /dev/null
+++ b/ps.h
@@ -0,0 +1,57 @@
+/*
+ * 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-2005 OpenVPN Solutions LLC <info@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
+ */
+
+#ifndef PS_H
+#define PS_H
+
+#if PORT_SHARE
+
+#include "basic.h"
+#include "buffer.h"
+#include "ssl.h"
+
+typedef void (*post_fork_cleanup_func_t)(void *arg);
+
+struct port_share {
+ /* Foreground's socket to background process */
+ socket_descriptor_t foreground_fd;
+
+ /* Process ID of background process */
+ pid_t background_pid;
+};
+
+extern struct port_share *port_share;
+
+struct port_share *port_share_open (const char *host,
+ const int port);
+
+void port_share_close (struct port_share *ps);
+void port_share_abort (struct port_share *ps);
+
+bool is_openvpn_protocol (const struct buffer *buf);
+
+void port_share_redirect (struct port_share *ps, const struct buffer *head, socket_descriptor_t sd);
+
+#endif
+#endif
diff --git a/reliable.c b/reliable.c
index ca83b2b..01e1d0a 100644
--- a/reliable.c
+++ b/reliable.c
@@ -221,12 +221,13 @@ reliable_ack_print (struct buffer *buf, bool verbose, struct gc_arena *gc)
*/
void
-reliable_init (struct reliable *rel, int buf_size, int offset, int array_size)
+reliable_init (struct reliable *rel, int buf_size, int offset, int array_size, bool hold)
{
int i;
CLEAR (*rel);
ASSERT (array_size > 0 && array_size <= RELIABLE_CAPACITY);
+ rel->hold = hold;
rel->size = array_size;
rel->offset = offset;
for (i = 0; i < rel->size; ++i)
@@ -465,7 +466,7 @@ reliable_can_send (const struct reliable *rel)
reliable_print_ids (rel, &gc));
gc_free (&gc);
- return n_current > 0;
+ return n_current > 0 && !rel->hold;
}
/* return a unique point-in-time to trigger retry */
@@ -530,6 +531,7 @@ reliable_schedule_now (struct reliable *rel)
{
int i;
dmsg (D_REL_DEBUG, "ACK reliable_schedule_now");
+ rel->hold = false;
for (i = 0; i < rel->size; ++i)
{
struct reliable_entry *e = &rel->array[i];
diff --git a/reliable.h b/reliable.h
index d498f64..405bedd 100644
--- a/reliable.h
+++ b/reliable.h
@@ -96,6 +96,7 @@ struct reliable
interval_t initial_timeout;
packet_id_type packet_id;
int offset;
+ bool hold; /* don't xmit until reliable_schedule_now is called */
struct reliable_entry array[RELIABLE_CAPACITY];
};
@@ -108,7 +109,7 @@ reliable_set_timeout (struct reliable *rel, interval_t timeout)
rel->initial_timeout = timeout;
}
-void reliable_init (struct reliable *rel, int buf_size, int offset, int array_size);
+void reliable_init (struct reliable *rel, int buf_size, int offset, int array_size, bool hold);
void reliable_free (struct reliable *rel);
diff --git a/socket.c b/socket.c
index 38b0c15..0b866bd 100644
--- a/socket.c
+++ b/socket.c
@@ -36,6 +36,7 @@
#include "misc.h"
#include "gremlin.h"
#include "plugin.h"
+#include "ps.h"
#include "memdbg.h"
@@ -739,11 +740,14 @@ openvpn_connect (socket_descriptor_t sd,
status = select (sd + 1, NULL, &writes, NULL, &tv);
- get_signal (signal_received);
- if (*signal_received)
+ if (signal_received)
{
- status = 0;
- break;
+ get_signal (signal_received);
+ if (*signal_received)
+ {
+ status = 0;
+ break;
+ }
}
if (status < 0)
{
@@ -1666,6 +1670,9 @@ stream_buf_init (struct stream_buf *sb,
sb->buf_init.len = 0;
sb->residual = alloc_buf (sb->maxlen);
sb->error = false;
+#if PORT_SHARE
+ sb->port_share_state = PS_ENABLED;
+#endif
stream_buf_reset (sb);
dmsg (D_STREAM_DEBUG, "STREAM: INIT maxlen=%d", sb->maxlen);
@@ -1735,6 +1742,22 @@ stream_buf_added (struct stream_buf *sb,
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 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);
diff --git a/socket.h b/socket.h
index 79f06b5..82b3626 100644
--- a/socket.h
+++ b/socket.h
@@ -130,6 +130,12 @@ struct stream_buf
bool error; /* if true, fatal TCP error has occurred,
requiring that connection be restarted */
+#if PORT_SHARE
+# define PS_DISABLED 0
+# define PS_ENABLED 1
+# define PS_FOREIGN 2
+ int port_share_state;
+#endif
};
/*
@@ -540,6 +546,29 @@ link_socket_actual_match (const struct link_socket_actual *a1, const struct link
return addr_port_match (&a1->dest, &a2->dest);
}
+#if PORT_SHARE
+
+static inline bool
+socket_foreign_protocol_detected (const struct link_socket *sock)
+{
+ return link_socket_connection_oriented (sock)
+ && sock->stream_buf.port_share_state == PS_FOREIGN;
+}
+
+static inline const struct buffer *
+socket_foreign_protocol_head (const struct link_socket *sock)
+{
+ return &sock->stream_buf.buf;
+}
+
+static inline int
+socket_foreign_protocol_sd (const struct link_socket *sock)
+{
+ return sock->sd;
+}
+
+#endif
+
static inline bool
socket_connection_reset (const struct link_socket *sock, int status)
{
diff --git a/ssl.c b/ssl.c
index 5f8b5d1..42fd904 100644
--- a/ssl.c
+++ b/ssl.c
@@ -1791,9 +1791,11 @@ key_state_init (struct tls_session *session, struct key_state *ks)
ks->plaintext_write_buf = alloc_buf (PLAINTEXT_BUFFER_SIZE);
ks->ack_write_buf = alloc_buf (BUF_SIZE (&session->opt->frame));
reliable_init (ks->send_reliable, BUF_SIZE (&session->opt->frame),
- FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_SEND_BUFFERS);
+ FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_SEND_BUFFERS,
+ session->opt->xmit_hold);
reliable_init (ks->rec_reliable, BUF_SIZE (&session->opt->frame),
- FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_REC_BUFFERS);
+ FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_REC_BUFFERS,
+ false);
reliable_set_timeout (ks->send_reliable, session->opt->packet_timeout);
/* init packet ID tracker */
diff --git a/ssl.h b/ssl.h
index afebb8e..5c71611 100644
--- a/ssl.h
+++ b/ssl.h
@@ -384,6 +384,9 @@ struct tls_options
/* true if we are a TLS server, client otherwise */
bool server;
+ /* if true, don't xmit until first packet from peer is received */
+ bool xmit_hold;
+
#ifdef ENABLE_OCC
/* local and remote options strings
that must match between client and server */
diff --git a/syshead.h b/syshead.h
index bc6ad1b..950af53 100644
--- a/syshead.h
+++ b/syshead.h
@@ -396,6 +396,15 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
+ * HTTPS port sharing capability
+ */
+#if defined(ENABLE_PORT_SHARE) && P2MP_SERVER && defined(SCM_RIGHTS) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
+#define PORT_SHARE 1
+#else
+#define PORT_SHARE 0
+#endif
+
+/*
* Do we have a plug-in capability?
*/
#if defined(USE_LIBDL) || defined(USE_LOAD_LIBRARY)