diff options
Diffstat (limited to 'x11vnc/misc/connect_switch')
-rwxr-xr-x | x11vnc/misc/connect_switch | 696 |
1 files changed, 0 insertions, 696 deletions
diff --git a/x11vnc/misc/connect_switch b/x11vnc/misc/connect_switch deleted file mode 100755 index 08376c6..0000000 --- a/x11vnc/misc/connect_switch +++ /dev/null @@ -1,696 +0,0 @@ -#!/usr/bin/perl -# -# Copyright (c) 2006-2010 by Karl J. Runge <runge@karlrunge.com> -# -# connect_switch is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or (at -# your option) any later version. -# -# connect_switch 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 connect_switch; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA -# or see <http://www.gnu.org/licenses/>. -# -# -# connect_switch: -# -# A kludge script that sits between web clients and a mod_ssl (https) -# enabled apache webserver. -# -# If an incoming web client connection makes a proxy CONNECT request -# it is handled directly by this script (apache is not involved). -# Otherwise, all other connections are forwarded to the apache webserver. -# -# This can be useful for VNC redirection using an existing https (port -# 443) webserver, thereby not requiring a 2nd (non-https) port open on -# the firewall for the CONNECT requests. -# -# It does not seem possible (to me) to achieve this entirely within apache -# because the CONNECT request appears to be forwarded encrypted to -# the remote host and so the SSL dies immediately. -# -# It can also be used to redirect ANY protocol, e.g. SSH, not just VNC. -# See CONNECT_SWITCH_APPLY_VNC_OFFSET=0 to disable VNC 5900 shift. -# -# Note: There is no need to use this script for a non-ssl apache webserver -# port because mod_proxy works fine for doing the switching all inside -# apache (see ProxyRequests and AllowCONNECT parameters). -# -# -# Apache configuration: -# -# The mod_ssl configuration is often in a file named ssl.conf. In the -# simplest case you change something like this: -# -# From: -# -# Listen 443 -# -# <VirtualHost _default_:443> -# ... -# </VirtualHost> -# -# To: -# -# Listen 127.0.0.1:443 -# -# <VirtualHost _default_:443> -# ... -# </VirtualHost> -# -# (i.e. just change the Listen directive). -# -# If you have mod_ssl listening on a different internal port, you do -# not need to specify the localhost Listen address. -# -# It is probably a good idea to set $listen_host below to the known -# IP address you want the service to listen on (to avoid localhost where -# apache is listening). -# - -#################################################################### -# NOTE: For more info on configuration settings, read below for -# all of the CONNECT_SWITCH_* env. var. parameters. -#################################################################### - - -#################################################################### -# Allow env vars to also be specified on cmdline: -# -foreach my $arg (@ARGV) { - if ($arg =~ /^(CONNECT_SWITCH.*?)=(.*)$/) { - $ENV{$1} = $2; - } -} - -# Set up logging: -# -if (exists $ENV{CONNECT_SWITCH_LOGFILE}) { - close STDOUT; - if (!open(STDOUT, ">>$ENV{CONNECT_SWITCH_LOGFILE}")) { - die "connect_switch: $ENV{CONNECT_SWITCH_LOGFILE} $!\n"; - } - close STDERR; - open(STDERR, ">&STDOUT"); -} -select(STDERR); $| = 1; -select(STDOUT); $| = 1; - -# interrupt handler: -# -my $looppid = ''; -my $pidfile = ''; -my $listen_sock = ''; # declared here for get_out() -# -sub get_out { - print STDERR "$_[0]:\t$$ looppid=$looppid\n"; - close $listen_sock if $listen_sock; - if ($looppid) { - kill 'TERM', $looppid; - fsleep(0.2); - } - unlink $pidfile if $pidfile; - exit 0; -} -$SIG{INT} = \&get_out; -$SIG{TERM} = \&get_out; - -# pidfile: -# -sub open_pidfile { - if (exists $ENV{CONNECT_SWITCH_PIDFILE}) { - my $pf = $ENV{CONNECT_SWITCH_PIDFILE}; - if (open(PID, ">$pf")) { - print PID "$$\n"; - close PID; - $pidfile = $pf; - } else { - print STDERR "could not open pidfile: $pf - $! - continuing...\n"; - } - delete $ENV{CONNECT_SWITCH_PIDFILE}; - } -} - -#################################################################### -# Set CONNECT_SWITCH_LOOP=1 to have this script create an outer loop -# restarting itself if it ever exits. Set CONNECT_SWITCH_LOOP=BG to -# do this in the background as a daemon. - -if (exists $ENV{CONNECT_SWITCH_LOOP}) { - my $csl = $ENV{CONNECT_SWITCH_LOOP}; - if ($csl ne 'BG' && $csl ne '1') { - die "connect_switch: invalid CONNECT_SWITCH_LOOP.\n"; - } - if ($csl eq 'BG') { - # go into bg as "daemon": - setpgrp(0, 0); - my $pid = fork(); - if (! defined $pid) { - die "connect_switch: $!\n"; - } elsif ($pid) { - wait; - exit 0; - } - if (fork) { - exit 0; - } - setpgrp(0, 0); - close STDIN; - if (! $ENV{CONNECT_SWITCH_LOGFILE}) { - close STDOUT; - close STDERR; - } - } - delete $ENV{CONNECT_SWITCH_LOOP}; - - if (exists $ENV{CONNECT_SWITCH_PIDFILE}) { - open_pidfile(); - } - - print STDERR "connect_switch: starting service at ", scalar(localtime), " master-pid=$$\n"; - while (1) { - $looppid = fork; - if (! defined $looppid) { - sleep 10; - } elsif ($looppid) { - wait; - } else { - exec $0; - exit 1; - } - print STDERR "connect_switch: re-starting service at ", scalar(localtime), " master-pid=$$\n"; - sleep 1; - } - exit 0; -} -if (exists $ENV{CONNECT_SWITCH_PIDFILE}) { - open_pidfile(); -} - - -############################################################################ -# The defaults for hosts and ports (you can override them below if needed): -# -# Look below for these environment variables that let you set the various -# parameters without needing to edit this script: -# -# CONNECT_SWITCH_LISTEN -# CONNECT_SWITCH_HTTPD -# CONNECT_SWITCH_ALLOWED -# CONNECT_SWITCH_ALLOW_FILE -# CONNECT_SWITCH_VERBOSE -# CONNECT_SWITCH_APPLY_VNC_OFFSET -# CONNECT_SWITCH_VNC_OFFSET -# CONNECT_SWITCH_LISTEN_IPV6 -# CONNECT_SWITCH_BUFSIZE -# CONNECT_SWITCH_LOGFILE -# CONNECT_SWITCH_PIDFILE -# CONNECT_SWITCH_MAX_CONNECTIONS -# -# You can also set these on the cmdline: -# connect_switch CONNECT_SWITCH_LISTEN=X CONNECT_SWITCH_ALLOW_FILE=Y ... -# - -# By default we will use hostname and assume it resolves: -# -my $hostname = `hostname`; -chomp $hostname; - -my $listen_host = $hostname; -my $listen_port = 443; - -# Let user override listening situation, e.g. multihomed: -# -if (exists $ENV{CONNECT_SWITCH_LISTEN}) { - # - # E.g. CONNECT_SWITCH_LISTEN=192.168.0.32:443 - # - $listen_host = ''; - $listen_port = ''; - if ($ENV{CONNECT_SWITCH_LISTEN} =~ /^(.*):(\d+)$/) { - ($listen_host, $listen_port) = ($1, $2); - } -} - -my $httpd_host = 'localhost'; -my $httpd_port = 443; - -if (exists $ENV{CONNECT_SWITCH_HTTPD}) { - # - # E.g. CONNECT_SWITCH_HTTPD=127.0.0.1:443 - # - $httpd_host = ''; - $httpd_port = ''; - if ($ENV{CONNECT_SWITCH_HTTPD} =~ /^(.*):(\d+)$/) { - ($httpd_host, $httpd_port) = ($1, $2); - } -} - -my $bufsize = 8192; -if (exists $ENV{CONNECT_SWITCH_BUFSIZE}) { - # - # E.g. CONNECT_SWITCH_BUFSIZE=32768 - # - $bufsize = $ENV{CONNECT_SWITCH_BUFSIZE}; -} - - -############################################################################ -# You can/should override the host/port settings here: -# -#$listen_host = '23.45.67.89'; # set to your interface IP number. -#$listen_port = 555; # and/or nonstandard port. -#$httpd_host = 'somehost'; # maybe you redir https to another machine. -#$httpd_port = 666; # and/or nonstandard port. - -# You must set the allowed host:port CONNECT redirection list. -# Only these host:port pairs will be redirected to. -# Port ranges are allowed too: host:5900-5930. -# If there is one entry named ALL all connections are allow. -# You must supply something, default is deny. -# -my @allowed = qw( - machine1:5915 - machine2:5900 -); - -if (exists $ENV{CONNECT_SWITCH_ALLOWED}) { - # - # E.g. CONNECT_SWITCH_ALLOWED=machine1:5915,machine2:5900 - # - @allowed = split(/,/, $ENV{CONNECT_SWITCH_ALLOWED}); -} - -# Or you could also use an external "allow file". -# They get added to the @allowed list. -# The file is re-read for each new connection. -# -# Format of $allow_file: -# -# host1 vncdisp -# host2 vncdisp -# -# where, e.g. vncdisp = 15 => port 5915, say -# -# joesbox 15 -# fredsbox 15 -# rupert 1 - -# For examply, mine is: -# -my $allow_file = '/dist/apache/2.0/conf/vnc.hosts'; -$allow_file = ''; - -if (exists $ENV{CONNECT_SWITCH_ALLOW_FILE}) { - # E.g. CONNECT_SWITCH_ALLOW_FILE=/usr/local/etc/allow.txt - $allow_file = $ENV{CONNECT_SWITCH_ALLOW_FILE}; -} - -# Set to 1 to re-map to vnc port, e.g. 'hostname 15' to 'hostname 5915' -# i.e. assume a port 0 <= port < 200 is actually a VNC display -# and add 5900 to it. Set to 0 to not do the mapping. -# Note that negative ports, e.g. 'joesbox -22' go directly to -port. -# -my $apply_vnc_offset = 1; -my $vnc_offset = 5900; - -if (exists $ENV{CONNECT_SWITCH_APPLY_VNC_OFFSET}) { - # - # E.g. CONNECT_SWITCH_APPLY_VNC_OFFSET=0 - # - $apply_vnc_offset = $ENV{CONNECT_SWITCH_APPLY_VNC_OFFSET}; -} -if (exists $ENV{CONNECT_SWITCH_VNC_OFFSET}) { - # - # E.g. CONNECT_SWITCH_VNC_OFFSET=6000 - # - $vnc_offset = $ENV{CONNECT_SWITCH_VNC_OFFSET}; -} - -# Set to 1 or higher for more info output: -# -my $verbose = 0; - -if (exists $ENV{CONNECT_SWITCH_VERBOSE}) { - # - # E.g. CONNECT_SWITCH_VERBOSE=1 - # - $verbose = $ENV{CONNECT_SWITCH_VERBOSE}; -} - -# zero means loop forever, positive value means exit after handling that -# many connections. -# -my $cmax = 0; -if (exists $ENV{CONNECT_SWITCH_MAX_CONNECTIONS}) { - $cmax = $ENV{CONNECT_SWITCH_MAX_CONNECTIONS}; -} - - -#=========================================================================== -# No need for any changes below here. -#=========================================================================== - -use IO::Socket::INET; -use strict; -use warnings; - -# Test for INET6 support: -# -my $have_inet6 = 0; -eval "use IO::Socket::INET6;"; -$have_inet6 = 1 if $@ eq ""; - -my $killpid = 1; - -setpgrp(0, 0); - -if (exists $ENV{CONNECT_SWITCH_LISTEN_IPV6}) { - # note we leave out LocalAddr. - my $cmd = ' - $listen_sock = IO::Socket::INET6->new( - Listen => 10, - LocalPort => $listen_port, - ReuseAddr => 1, - Domain => AF_INET6, - Proto => "tcp" - ); - '; - eval $cmd; - die "$@\n" if $@; -} else { - $listen_sock = IO::Socket::INET->new( - Listen => 10, - LocalAddr => $listen_host, - LocalPort => $listen_port, - ReuseAddr => 1, - Proto => "tcp" - ); -} - -if (! $listen_sock) { - die "connect_switch: $!\n"; -} - -my $current_fh1 = ''; -my $current_fh2 = ''; - -my $conn = 0; - -while (1) { - $conn++; - if ($cmax > 0 && $conn > $cmax) { - print STDERR "last connection ($cmax)\n" if $verbose; - last; - } - print STDERR "listening for connection: $conn\n" if $verbose; - my ($client, $ip) = $listen_sock->accept(); - if (! $client) { - fsleep(0.5); - next; - } - print STDERR "conn: $conn -- ", $client->peerhost(), " at ", scalar(localtime), "\n" if $verbose; - - my $pid = fork(); - if (! defined $pid) { - die "connect_switch: $!\n"; - } elsif ($pid) { - wait; - next; - } else { - close $listen_sock; - if (fork) { - exit 0; - } - setpgrp(0, 0); - handle_conn($client); - } -} - -exit 0; - -sub handle_conn { - my $client = shift; - - my $start = time(); - - my @allow = @allowed; - - # read allow file. Note we read it for every connection - # to allow the admin to modify it w/o restarting us. - # better way would be to read in parent and check mtime. - # - if ($allow_file && -f $allow_file) { - if (open(ALLOW, "<$allow_file")) { - while (<ALLOW>) { - next if /^\s*#/; - next if /^\s*$/; - chomp; - my ($host, $dpy) = split(' ', $_); - next if ! defined $host; - next if ! defined $dpy; - if ($dpy < 0) { - $dpy = -$dpy; - } elsif ($apply_vnc_offset) { - $dpy += $vnc_offset if $dpy < 200; - } - push @allow, "$host:$dpy"; - } - close(ALLOW); - } else { - warn "$allow_file: $!\n"; - } - } - - # Read the first 7 bytes of connection, see if it is 'CONNECT' - # - my $str = ''; - my $N = 0; - my $isconn = 1; - for (my $i = 0; $i < 7; $i++) { - my $b; - sysread($client, $b, 1); - $str .= $b; - $N++; - print STDERR "read: '$str'\n" if $verbose > 1; - my $cstr = substr('CONNECT', 0, $i+1); - if ($str ne $cstr) { - $isconn = 0; - last; - } - } - - my $sock = ''; - - if ($isconn) { - # it is CONNECT, read rest of HTTP header: - # - while ($str !~ /\r\n\r\n/) { - my $b; - sysread($client, $b, 1); - $str .= $b; - } - print STDERR "read: $str\n" if $verbose > 1; - - # get http version and host:port - # - my $ok = 0; - my $hostport = ''; - my $http_vers = '1.0'; - if ($str =~ /^CONNECT\s+(\S+)\s+HTTP\/(\S+)/) { - $hostport = $1; - $http_vers = $2; - my $h = ''; - my $p = ''; - - if ($hostport =~ /^(.*):(\d+)$/) { - ($h, $p) = ($1, $2); - } - if ($p =~ /^\d+$/) { - # check allowed host list: - foreach my $hp (@allow) { - if ($hp eq 'ALL') { - $ok = 1; - } - if ($hp eq $hostport) { - $ok = 1; - } - if ($hp =~ /^(.*):(\d+)-(\d+)$/) { - my $ahost = $1; - my $pmin = $2; - my $pmax = $3; - if ($h eq $ahost) { - if ($p >= $pmin && $p <= $pmax) { - $ok = 1; - } - } - } - last if $ok; - } - } - } - - my $msg_1 = "HTTP/$http_vers 200 Connection Established\r\n" - . "Proxy-agent: connect_switch v0.2\r\n\r\n"; - my $msg_2 = "HTTP/$http_vers 502 Bad Gateway\r\n" - . "Connection: close\r\n\r\n"; - - if (! $ok) { - # disallowed. drop with message. - # - syswrite($client, $msg_2, length($msg_2)); - close $client; - exit 0; - } - - my $host = ''; - my $port = ''; - - if ($hostport =~ /^(.*):(\d+)$/) { - ($host, $port) = ($1, $2); - } - - print STDERR "connecting to: $host:$port\n" if $verbose; - - $sock = IO::Socket::INET->new( - PeerAddr => $host, - PeerPort => $port, - Proto => "tcp" - ); - print STDERR "connect to host='$host' port='$port' failed: $!\n" if !$sock; - if (! $sock && $have_inet6) { - eval {$sock = IO::Socket::INET6->new( - PeerAddr => $host, - PeerPort => $port, - Proto => "tcp" - );}; - print STDERR "connect to host='$host' port='$port' failed: $! (ipv6)\n" if !$sock; - } - my $msg; - - # send the connect proxy reply: - # - if ($sock) { - $msg = $msg_1; - } else { - $msg = $msg_2; - } - syswrite($client, $msg, length($msg)); - $str = ''; - } else { - # otherwise, redirect to apache for normal https: - # - print STDERR "connecting to: $httpd_host:$httpd_port\n" if $verbose; - $sock = IO::Socket::INET->new( - PeerAddr => $httpd_host, - PeerPort => $httpd_port, - Proto => "tcp" - ); - if (! $sock && $have_inet6) { - eval {$sock = IO::Socket::INET6->new( - PeerAddr => $httpd_host, - PeerPort => $httpd_port, - Proto => "tcp" - );}; - } - } - - if (! $sock) { - close $client; - die "connect_switch: $!\n"; - } - - # get ready for xfer phase: - # - $current_fh1 = $client; - $current_fh2 = $sock; - - $SIG{TERM} = sub {print STDERR "got sigterm\[$$]\n" if $verbose; close $current_fh1; close $current_fh2; exit 0}; - - my $parent = $$; - if (my $child = fork()) { - xfer($sock, $client, 'S->C'); - if ($killpid) { - fsleep(0.5); - kill 'TERM', $child; - } - } else { - # write those first bytes if not CONNECT: - # - if ($str ne '' && $N > 0) { - syswrite($sock, $str, $N); - } - xfer($client, $sock, 'C->S'); - if ($killpid) { - fsleep(0.75); - kill 'TERM', $parent; - } - } - if ($verbose > 1) { - my $dt = time() - $start; - print STDERR "duration\[$$]: $dt seconds. ", scalar(localtime), "\n"; - } - exit 0; -} - -sub xfer { - my($in, $out, $lab) = @_; - my ($RIN, $WIN, $EIN, $ROUT); - $RIN = $WIN = $EIN = ""; - $ROUT = ""; - vec($RIN, fileno($in), 1) = 1; - vec($WIN, fileno($in), 1) = 1; - $EIN = $RIN | $WIN; - my $buf; - - while (1) { - my $nf = 0; - while (! $nf) { - $nf = select($ROUT=$RIN, undef, undef, undef); - } - my $len = sysread($in, $buf, $bufsize); - if (! defined($len)) { - next if $! =~ /^Interrupted/; - print STDERR "connect_switch\[$lab/$conn/$$]: $!\n"; - last; - } elsif ($len == 0) { - print STDERR "connect_switch\[$lab/$conn/$$]: " - . "Input is EOF.\n"; - last; - } - - if (0) { - # very verbose debugging of data: - syswrite(STDERR , "\n$lab: ", 6); - syswrite(STDERR , $buf, $len); - } - - my $offset = 0; - my $quit = 0; - while ($len) { - my $written = syswrite($out, $buf, $len, $offset); - if (! defined $written) { - print STDERR "connect_switch\[$lab/$conn/$$]: " - . "Output is EOF. $!\n"; - $quit = 1; - last; - } - $len -= $written; - $offset += $written; - } - last if $quit; - } - close($in); - close($out); -} - -sub fsleep { - my ($time) = @_; - select(undef, undef, undef, $time) if $time; -} |