]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Added --port-share option for allowing OpenVPN and HTTPS
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Thu, 16 Feb 2006 18:12:24 +0000 (18:12 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Thu, 16 Feb 2006 18:12:24 +0000 (18:12 +0000)
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

24 files changed:
ChangeLog
Makefile.am
configure.ac
errlevel.h
error.c
error.h
event.h
fdmisc.c
fdmisc.h
forward.c
init.c
openvpn.8
openvpn.c
options.c
options.h
ps.c [new file with mode: 0644]
ps.h [new file with mode: 0644]
reliable.c
reliable.h
socket.c
socket.h
ssl.c
ssl.h
syshead.h

index e38de180bf4ae1b4db0da14d9076b38535522dfa..496ac8d4b855968406a8af12c8fde8cd7f0bc765 100644 (file)
--- 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.
index 424b167d05ed9a65d447845c291eee93d9a149aa..85c0c4f1c8a737399aa427c2ff8c4a67bd8b542e 100644 (file)
@@ -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 \
index 47c9bea0ba11380fbed287ce5e2612c436487dde..3a1f96b328f53408c13e40dbe821bf97f641cef6 100644 (file)
@@ -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"
index cbe833673e451daed656ab9549f4ff36577d0bc6..426a4ac8007ed6ec909964b573b518c64be4a3e9 100644 (file)
@@ -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 */
 #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 6191cb3936f893f95de6a670187e4c155377071b..de23b3d17a35b417e84028c087ca595742edd4c9 100644 (file)
--- 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 62926ccfb14495dde22a81088b4483d58536d9ce..148ca61f90b814392bece7081d9a474795b040fc 100644 (file)
--- 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 314ef4cac1a524b253d3470a233eeb8add11d802..4449a0a31e57e39788571d2c51ea8ff905e72f0b 100644 (file)
--- 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
index 8c8b91e17399f18342a9a4c24a02d6fab22c4179..eb7a6fcd2990a36b077de3e191241ebb372baf38 100644 (file)
--- a/fdmisc.c
+++ b/fdmisc.c
 #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");
 }
index 5019cc292783c88edd8341698e821caae1103b0a..fa2cac9568f7e83259bc09aee3c995d2e6d1c0fc 100644 (file)
--- a/fdmisc.h
+++ b/fdmisc.h
  *  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);
index be041b994d192074a376a02fbf21e5980c61abf8..5fec5327a026f57015b2508298764fe7e72efc95 100644 (file)
--- 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 b1ef03433a6bb471c957c8d731b0435798c54125..c5b3a84fb10b8538ba242702d02309bb9edd8482 100644 (file)
--- 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;
index 6a92cac2a12d7d6c12130aa7df580c064d46ef7a..8f29469f8865e17630473c118ccac74211283a61 100644 (file)
--- 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
index eff610ab19eef0263c75b61ef78e5b162c64945c..496e5059ac657f3aaf1293be1af37eb97a3eec14 100644 (file)
--- 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);
 
index 2c9dbcbb4ac094c2e52908acdd8476085459e078..d882434c085c7dbe7a50aae4582c24667922045a 100644 (file)
--- 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);
index 9f8f08a79f8a3610748fa7f4acd243bdf56ca5a8..4bf11397ccd7b7ee92e94f944a7f902a3f9007a7 100644 (file)
--- 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 (file)
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 (file)
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
index ca83b2b8ac1c8cd486cd56411589be3d7ee3c531..01e1d0ae16f7523674a6daac46023fad31d27b12 100644 (file)
@@ -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];
index d498f64744bcdacb5d49bb2bd2d54dbf7e784aa0..405bedd91465e3224e71810a9e47ab1594753cab 100644 (file)
@@ -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);
 
index 38b0c1597dcac07a70d574599acb5f3cbecbfc4f..0b866bddf8b49d7efc874fc26602a1dda5569e0d 100644 (file)
--- 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);
 
index 79f06b5a24a9ba2f3f3320e155a36e7d5ac65e11..82b3626218ea985b493286baa2f61a9f02d18a85 100644 (file)
--- 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 5f8b5d11b4c6bf0b0ba8a4a3c1bcf510be30180b..42fd904c583bf69725c152bfd68cd60379e165f9 100644 (file)
--- 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 afebb8e3edf76947b250fefcfd5a41a28e07da1c..5c71611f34042706ce5bb93d6239a0a414d92091 100644 (file)
--- 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 */
index bc6ad1b22ca2dbc1bd874c90f26e41cf738ee1a0..950af5331a67063aa0f15073fb8652b75934a8bb 100644 (file)
--- a/syshead.h
+++ b/syshead.h
@@ -395,6 +395,15 @@ socket_defined (const socket_descriptor_t sd)
 #define P2MP_SERVER 0
 #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?
  */