mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-23 17:53:49 +08:00
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
This commit is contained in:
parent
34a507c9ab
commit
6add6b2fe7
@ -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.
|
||||
|
@ -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 \
|
||||
|
13
configure.ac
13
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"
|
||||
|
@ -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 */
|
||||
|
40
error.c
40
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 ();
|
||||
|
5
error.h
5
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);
|
||||
|
5
event.h
5
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
|
||||
|
32
fdmisc.c
32
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 */
|
||||
bool
|
||||
set_cloexec_action (int fd)
|
||||
{
|
||||
#ifndef WIN32
|
||||
if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
|
||||
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)
|
||||
{
|
||||
#ifndef WIN32
|
||||
if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
|
||||
if (!set_cloexec_action (fd))
|
||||
msg (M_ERR, "Set FD_CLOEXEC flag on file descriptor failed");
|
||||
#endif
|
||||
}
|
||||
|
5
fdmisc.h
5
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);
|
||||
|
30
forward.c
30
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;
|
||||
}
|
||||
|
42
init.c
42
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;
|
||||
|
18
openvpn.8
18
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
|
||||
|
@ -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);
|
||||
|
||||
|
34
options.c
34
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);
|
||||
|
@ -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;
|
||||
|
783
ps.c
Normal file
783
ps.c
Normal file
@ -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
|
57
ps.h
Normal file
57
ps.h
Normal file
@ -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
|
@ -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];
|
||||
|
@ -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);
|
||||
|
||||
|
31
socket.c
31
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);
|
||||
|
||||
|
29
socket.h
29
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)
|
||||
{
|
||||
|
6
ssl.c
6
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 */
|
||||
|
3
ssl.h
3
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 */
|
||||
|
@ -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?
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user