#!/usr/bin/perl #!/usr/local/bin/perl # # vncxfer: A CGI script to set up TCP redirection between a VNC Viewer # and VNC Server using the Web Server as a relay point. # # Copy this to a place on a website where CGI scripts will be run. # e.g. http://somesite.com/cgi-bin/vncxfer # # One user goes to the the above URL and chooses a session name and then # clicks "Submit". That starts up a helper program on the web server to # act as a relay. # # The other user does the same, using the SAME session name. # # The page that is returned to them tells them the port number and VNC # Host:Display to use in their viewer or VNC server. # # The VNC Server makes a reverse VNC connection to the relay point. # e.g. somesite:com:5950 # # The VNC Viewer makes a normal VNC connection to the relay point. # e.g. vncviewer somesite:com:50 # # Unfortunately most Web servers will not allow this sort of thing: # # 1) They forbid running CGI programs that run as daemons that listen # on TCP ports for incoming connections. # # 2) The firewall configuration of the webserver disallows connections # to the "session" ports, e.g. 5950, etc. # # James B - modified to work with mongoose, as vncxfer.pl # Put here a list of ports to use for transfer sessions. The Web server's # firewall configuration MUST NOT block this port(s). # my @allowed_ports = qw( 5955 5956 5957 5958 5959 5960 ); # Maximum time in seconds to wait for both the VNC Viewer # and VNC Server to connect: # my $max_wait = 300; # The form action="..." program, it may need to be "/cgi-bin/vncxfer", etc. # my $program = "vncxfer.pl"; ########################################################################## # no config needed below here. use POSIX qw(setsid); my $oldsockets = 0; if (! $oldsockets) { use IO::Socket::INET; } my $oldfh = 'LISTEN000'; if (@ARGV && $ARGV[0] =~ /^vncxfer:/) { # Started in session helper mode: helper($ARGV[0]); exit 0; } # Parse CGI request: # my $request; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $request, $ENV{'CONTENT_LENGTH'}); } elsif ($ENV{'REQUEST_METHOD'} eq "GET" ) { $request = $ENV{'QUERY_STRING'}; } else { $request = $ARGV[0]; } my %request = &url_decode(split(/[&=]/, $request)); # html for top and bottom: # my $htop = "\n"; my $hbot = "\n"; select(STDERR); $| = 1; select(STDOUT); $| = 1; # content-type: # print STDOUT "Content-Type: text/html\r\n"; # see if session is set: # my $session = ''; if (exists $request{session}) { $session = $request{session}; } $session =~ s/\W//g; # word characters only. if ($session eq '') { # No session, show the create-a-session form: print STDOUT "\r\n"; print_form(); exit 0; } # determine hostnames and IP addresses: # my $server = $ENV{HTTP_HOST}; $server =~ s/:\d+//g; my ($nm, $aliases, $type, $len, @addrs) = gethostbyname($server); my $server_ip = join('.', unpack('C4', $addrs[0])); my $sport = $ENV{SERVER_PORT}; # find an existing session, or start one up: # my $port = find_or_start_session($session); if ($port ne '') { # print some http header lines indicating the port: # my $disp = $port - 5900; print STDOUT "VNC-Host-Display: $server:$disp\r\n"; print STDOUT "VNC-IP-Display: $server_ip:$disp\r\n"; print STDOUT "VNC-Host-Port: $server:$port\r\n"; print STDOUT "VNC-IP-Port: $server_ip:$port\r\n"; print STDOUT "\r\n"; # print connection info to the browser: # print STDOUT <VNC Transfer (vncxfer)

Your VNC session name is: $session
Your VNC session port is: $port
Both VNC Viewer and Server must connect within $max_wait seconds.

For the VNC Viewer, use either of these as VNC Host displays:

   $server:$disp

   $server_ip:$disp

E.g.:

        vncviewer $server_ip:$disp
 

For the VNC Server, it needs to do a reverse connection to either of these:

   $server:$port

   $server_ip:$port

E.g.:

        x11vnc -connect $server_ip:$port
$hbot END } else { print STDOUT "\r\n"; # whoops: # print STDOUT <VNC Transfer (vncxfer)

A Free VNC Port could not be found or the helper could not be started! $hbot END } exit 0; ####################################################################### sub url_decode { foreach (@_) { tr/+/ /; s/%(..)/pack("c",hex($1))/ge; } @_; } # starting form: # sub print_form { print STDOUT < VNC Transfer (vncxfer)

To start a VNC Transfer agent for you, enter a name for your session:

Your Session Name:

This name will be used by the other VNC party to identify the session and connect with you. END } sub do_listen { my $port = shift; if (! $oldsockets) { my $sock = IO::Socket::INET->new( Listen => 2, LocalPort => $port, Proto => "tcp" ); return $sock; } else { my $AF_INET = 2; my $SOCK_STREAM = 1; my $SockAddr = 'S n a4 x8'; my ($n1, $a1, $proto) = getprotobyname('tcp'); my $us = pack($SockAddr, $AF_INET, $port, "\0\0\0\0"); my $listen = ++$oldfh; my $package = caller; $listen =~ s/^[^']+$/$package'$&/; if (! socket($listen, $AF_INET, $SOCK_STREAM, $proto)) { return ''; } if (! bind($listen, $us)) { return ''; } if (! listen($listen, 2)) { return ''; } return $listen; } } sub find_or_start_session { my $session = shift; chdir("/tmp"); # we scrape ps(1) output to find an existing session: # my $ps = "ps"; if (-f "/usr/ucb/ps") { $ps = "/usr/ucb/ps"; } open(PS, "env COLUMNS=256 $ps wwwwwwwwaux |"); while () { if (/\bvncxfer:(\w+):(\d+)/) { my $s = $1; my $p = $2; if ($s eq $session) { return $p; } } } close PS; # no session, so we will start it. # look for a free port: # my $use_port = ''; foreach my $p (@allowed_ports) { my $sock = do_listen($p); if ($sock) { $use_port = $p; close $sock; last; } } if ($use_port eq '') { # all in use. return ''; } # start the helper in the background: # my $pf = fork(); if (! defined $pf) { return ''; } elsif ($pf) { sleep 2; wait; } else { close STDIN; close STDOUT; close STDERR; POSIX::setsid(); sleep 1; system("$ENV{'DOCUMENT_ROOT'}$ENV{'SCRIPT_NAME'} 'vncxfer:$session:$use_port' &"); exit 0; } sleep 2; return $use_port; } # SIGALRM timeout handler: # sub timeout { close LOG; exit 0; } sub helper { my $tag = shift; # set this to somefile for logging: #open(LOG, ">>/tmp/vncxfer.log"); open(LOG, ">/dev/null"); select(LOG); $| = 1; close(STDERR); open(STDERR, ">&LOG"); # split the vncxfer:session:port string: # my ($name, $session, $port) = split(/:/, $tag, 3); # check port: # if ($port !~ /^\d+$/) { print LOG "bad port $tag\n"; close LOG; exit 1; } # try to listen on port: # my $sock = do_listen($port); if (! $sock) { print LOG "bad sock $tag\n"; close LOG; exit 1; } # set timeout handler: # $SIG{ARLM} = \&timeout; alarm($max_wait); # wait for first connection: # my ($client1, $ip1, $client2, $ip2); if (! $oldsockets) { ($client1, $ip1) = $sock->accept(); } else { $client1 = ++$oldfh; my $package = caller; $client1 =~ s/^[^']+$/$package'$&/; $ip1 = accept($client1, $sock); } print LOG "1st connection $tag $ip1\n"; # wait for second connection: # if (! $oldsockets) { ($client2, $ip2) = $sock->accept(); } else { $client2 = ++$oldfh; my $package = caller; $client2 =~ s/^[^']+$/$package'$&/; $ip2 = accept($client2, $sock); } print LOG "2nd connection $tag $ip2\n"; alarm(0); $SIG{ALRM} = 'DEFAULT'; # no need to listen anymore: # close($sock); # fork a child to handle I/O one direction, and # we do the other direction: # my $parent = $$; my $child = fork; if (! defined $child) { exit 1; } if ($child) { print LOG "pproxy parent\[$$] STDIN -> socket\n"; xfer($client1, $client2); select(undef, undef, undef, 0.25); if (kill 0, $child) { select(undef, undef, undef, 1.5); kill "TERM", $child; } } else { print LOG "pproxy child \[$$] socket -> STDOUT\n"; xfer($client2, $client1); select(undef, undef, undef, 0.25); if (kill 0, $parent) { select(undef, undef, undef, 1.5); kill "TERM", $parent; } } exit; } # utlity to transfer bytes from one socket to another: # taken from PPROXY in ss_vncviewer. # sub xfer { my($in, $out) = @_; $RIN = $WIN = $EIN = ""; $ROUT = ""; vec($RIN, fileno($in), 1) = 1; vec($WIN, fileno($in), 1) = 1; $EIN = $RIN | $WIN; while (1) { my $nf = 0; while (! $nf) { $nf = select($ROUT=$RIN, undef, undef, undef); } my $len = sysread($in, $buf, 8192); if (! defined($len)) { next if $! =~ /^Interrupted/; print STDERR "pproxy\[$$]: $!\n"; last; } elsif ($len == 0) { print STDERR "pproxy\[$$]: Input is EOF.\n"; last; } my $offset = 0; my $quit = 0; while ($len) { my $written = syswrite($out, $buf, $len, $offset); if (! defined $written) { print STDERR "pproxy\[$$]: Output is EOF. $!\n"; $quit = 1; last; } $len -= $written; $offset += $written; } last if $quit; } close($in); close($out); }