rx11vnc.pl 5.05 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  #!/bin/sh -- # A comment mentioning perl
eval 'exec perl -S $0 ${1+"$@"}'
        if 0;
#
# Here is the remote x11vnc command.
# Modify to your needs, required to have %DISP item that expands to X display
# and the -bg option to go into the background.
#
$x11vnc_cmd = "x11vnc -localhost -nap -q -bg -display %DISP";

#
# We will redir local ports to these remote ports hoping the remote
# x11vnc selects one of them:
#
@tunnel_ports = qw(5900 5901 5902 5903 5904);

#
# We need to specify the encoding preferences since vncviewer will
# mistakeningly prefer "raw" encoding for local connection.  required to
# have %VNC_ITEM to expand to localhost:<port>

# One really needs an -encodings option otherwise the vncviewer will
# prefer 'raw' which is very slow.
#
$viewer_cmd = "vncviewer -encodings 'copyrect tight zrle hextile zlib corre rre' %VNC_DISP";
$sleep_time = 15;

if ($ENV{USER} eq 'runge') {
	# my personal kludges:
	$viewer_cmd =~ s/vncviewer/vncviewerz/;	# for tight
	$x11vnc_cmd .= ' -rfbauth .vnc/passwd';	# I always want rfbauth
}

chop($Program = `basename $0`);

$Usage = <<"END";

$Program: wrapper to tunnel vncviewer <-> x11vnc VNC traffic through a ssh
	encrypted tunnel port redirection.

Usage: $Program <options> <remote-Xdisplay>

Options:
	-l <user>			ssh login as remote user <user>

	-rfbauth <remote-auth-file>	this option is passed to the remote
					x11vnc command for passwd file.

Notes:

Example: $Program snoopy:0

END

LOOP:	
while (@ARGV) {
    $_ = shift;
    CASE: {
	/^-display$/ && ($remote_xdisplay = shift, last CASE);
	/^-rfbauth$/ && ($x11vnc_cmd .= ' -rfbauth ' . shift, last CASE);
	/^-l$/ && ($remote_user = ' -l ' . shift, last CASE);
	/^--$/ && (last LOOP);	# -- means end of switches
	/^-(-.*)$/ && (unshift(@ARGV, $1), last CASE);
	/^(-h|-help)$/ && ((print STDOUT $Usage), exit 0, last CASE);
	if ( /^-(..+)$/ ) {	# split bundled switches:
		local($y, $x) = ($1, '');
		(unshift(@ARGV, $y), last CASE) if $y =~ /^-/;
		foreach $x (reverse(split(//, $y))) { unshift(@ARGV,"-$x") };
		last CASE;
	}
	/^-/ && ((print STDERR "Invalid arg: $_\n$Usage"), exit 1, last CASE);
	unshift(@ARGV,$_);
	last LOOP;
    }
}

select(STDERR); $| = 1;
select(STDOUT); $| = 1;

# Determine the remote X display to connect to:
$remote_xdisplay = shift if $remote_xdisplay eq '';
if ($remote_xdisplay !~ /:/) {
	$remote_xdisplay .= ':0';	# assume they mean :0 over there.
}
if ($remote_xdisplay =~ /:/) {
	$host = $`;
	$disp = ':' . $';
} else {
	die "bad X display: $remote_xdisplay, must be <host>:<display>\n";
}

#
# Get list of local ports in use so we can avoid them: 
# (tested on Linux and Solaris)
#
open(NETSTAT, "netstat -an|") || die "netstat -an: $!";
while (<NETSTAT>) {
	chomp ($line = $_);
	next unless $line =~ /(ESTABLISHED|LISTEN|WAIT2?)\s*$/;
	$line =~ s/^\s*//;
	$line =~ s/^tcp[\s\d]*//;
	$line =~ s/\s.*$//;
	$line =~ s/^.*\D//;
	if ($line !~ /^\d+$/) {
		die "bad netstat line: $line from $_"; 
	}
	$used_port{$line} = 1;
}
close(NETSTAT);

#
# Now match up free local ports with the desired remote ports
# (note that the remote ones could be in use but that won't stop
# the ssh with port redirs from succeeding)
#
$lport = 5900;
$cnt = 0;
foreach $rport (@tunnel_ports) {
	while ($used_port{$lport}) {
		$lport++;
		$cnt++;
		die "too hard to find local ports 5900-$lport" if $cnt > 200;
	}
	$port_map{$rport} = $lport;
	$lport++;
}

$redir = '';
foreach $rport (@tunnel_ports) {
	$redir .= " -L $port_map{$rport}:localhost:$rport";
}

#
# Have ssh put the command in the bg, then we look for PORT= in the
# tmp file.  The sleep at the end is to give us enough time to connect
# thru the port redir, otherwise ssh will exit before we can connect.
#

# This is the x11vnc cmd for the remote side:
$cmd = $x11vnc_cmd;
$cmd =~ s/%DISP/$disp/;

# This is the ssh cmd for the local side (this machine):
144
$ssh_cmd = "ssh -t -f $remote_user $redir $host '$cmd; echo END; sleep $sleep_time'";
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
$ssh_cmd =~ s/  / /g;
print STDERR "running ssh command:\n\n$ssh_cmd\n\n";

#
# Run ssh and redir into a tmp file (assumes ssh will use /dev/tty
# for password/passphrase dialog)
#
$tmp = "/tmp/rx.$$";
system("$ssh_cmd > $tmp");

# Now watch for the PORT=XXXX message:
$sleep = 0;
$rport = '';
print STDERR "\nWaiting for x11vnc to indicate its port ..";
while ($sleep < $sleep_time + 10) {
	print STDERR ".";
	sleep(1);
	$sleep++;
	if (`cat $tmp` =~ /PORT=(\d+)/) {
		$rport = $1;
		# wait 1 more second for output:
		sleep(1);
		if (`cat $tmp` =~ /PORT=(\d+)/) {
			$rport = $1;
		}
		last;
	}
}
print STDERR "\n";

if (! $rport) {
	print STDERR `cat $tmp`;
	unlink($tmp);
	die "could not determine remote port.\n";
}
unlink($tmp);

# Find the remote to local mapping:
$lport = $port_map{$rport};
print STDERR "remote port is: $rport (corresponds to port $lport here)\n";
if (! $lport) {
	die "could not determine local port redir.\n";
}

# Apply the special casing vncviewer does for 5900 <= port < 6000
if ($lport < 6000 && $lport >= 5900) {
	$lport = $lport - 5900;
}

# Finally, run the viewer.
$cmd = $viewer_cmd;
$cmd =~ s/%VNC_DISP/localhost:$lport/;

print STDERR "running vncviewer command:\n\n$cmd\n\n";
system($cmd);