1 #!/bin/sh -- # A comment mentioning perl 2eval 'exec perl -S $0 ${1+"$@"}' 3 if 0; 4# 5# Here is the remote x11vnc command. 6# Modify to your needs, required to have %DISP item that expands to X display 7# and the -bg option to go into the background. 8# 9$x11vnc_cmd = "x11vnc -localhost -nap -q -bg -display %DISP"; 10 11# 12# We will redir local ports to these remote ports hoping the remote 13# x11vnc selects one of them: 14# 15@tunnel_ports = qw(5900 5901 5902 5903 5904); 16 17# 18# We need to specify the encoding preferences since vncviewer will 19# mistakeningly prefer "raw" encoding for local connection. required to 20# have %VNC_ITEM to expand to localhost:<port> 21 22# One really needs an -encodings option otherwise the vncviewer will 23# prefer 'raw' which is very slow. 24# 25$viewer_cmd = "vncviewer -encodings 'copyrect tight zrle hextile zlib corre rre' %VNC_DISP"; 26$sleep_time = 15; 27 28if ($ENV{USER} eq 'runge') { 29 # my personal kludges: 30 $viewer_cmd =~ s/vncviewer/vncviewerz/; # for tight 31 $x11vnc_cmd .= ' -rfbauth .vnc/passwd'; # I always want rfbauth 32} 33 34chop($Program = `basename $0`); 35 36$Usage = <<"END"; 37 38$Program: wrapper to tunnel vncviewer <-> x11vnc VNC traffic through a ssh 39 encrypted tunnel port redirection. 40 41Usage: $Program <options> <remote-Xdisplay> 42 43Options: 44 -l <user> ssh login as remote user <user> 45 46 -rfbauth <remote-auth-file> this option is passed to the remote 47 x11vnc command for passwd file. 48 49Notes: 50 51Example: $Program snoopy:0 52 53END 54 55LOOP: 56while (@ARGV) { 57 $_ = shift; 58 CASE: { 59 /^-display$/ && ($remote_xdisplay = shift, last CASE); 60 /^-rfbauth$/ && ($x11vnc_cmd .= ' -rfbauth ' . shift, last CASE); 61 /^-l$/ && ($remote_user = ' -l ' . shift, last CASE); 62 /^--$/ && (last LOOP); # -- means end of switches 63 /^-(-.*)$/ && (unshift(@ARGV, $1), last CASE); 64 /^(-h|-help)$/ && ((print STDOUT $Usage), exit 0, last CASE); 65 if ( /^-(..+)$/ ) { # split bundled switches: 66 local($y, $x) = ($1, ''); 67 (unshift(@ARGV, $y), last CASE) if $y =~ /^-/; 68 foreach $x (reverse(split(//, $y))) { unshift(@ARGV,"-$x") }; 69 last CASE; 70 } 71 /^-/ && ((print STDERR "Invalid arg: $_\n$Usage"), exit 1, last CASE); 72 unshift(@ARGV,$_); 73 last LOOP; 74 } 75} 76 77select(STDERR); $| = 1; 78select(STDOUT); $| = 1; 79 80# Determine the remote X display to connect to: 81$remote_xdisplay = shift if $remote_xdisplay eq ''; 82if ($remote_xdisplay !~ /:/) { 83 $remote_xdisplay .= ':0'; # assume they mean :0 over there. 84} 85if ($remote_xdisplay =~ /:/) { 86 $host = $`; 87 $disp = ':' . $'; 88} else { 89 die "bad X display: $remote_xdisplay, must be <host>:<display>\n"; 90} 91 92# 93# Get list of local ports in use so we can avoid them: 94# (tested on Linux and Solaris) 95# 96open(NETSTAT, "netstat -an|") || die "netstat -an: $!"; 97while (<NETSTAT>) { 98 chomp ($line = $_); 99 next unless $line =~ /(ESTABLISHED|LISTEN|WAIT2?)\s*$/; 100 $line =~ s/^\s*//; 101 $line =~ s/^tcp[\s\d]*//; 102 $line =~ s/\s.*$//; 103 $line =~ s/^.*\D//; 104 if ($line !~ /^\d+$/) { 105 die "bad netstat line: $line from $_"; 106 } 107 $used_port{$line} = 1; 108} 109close(NETSTAT); 110 111# 112# Now match up free local ports with the desired remote ports 113# (note that the remote ones could be in use but that won't stop 114# the ssh with port redirs from succeeding) 115# 116$lport = 5900; 117$cnt = 0; 118foreach $rport (@tunnel_ports) { 119 while ($used_port{$lport}) { 120 $lport++; 121 $cnt++; 122 die "too hard to find local ports 5900-$lport" if $cnt > 200; 123 } 124 $port_map{$rport} = $lport; 125 $lport++; 126} 127 128$redir = ''; 129foreach $rport (@tunnel_ports) { 130 $redir .= " -L $port_map{$rport}:localhost:$rport"; 131} 132 133# 134# Have ssh put the command in the bg, then we look for PORT= in the 135# tmp file. The sleep at the end is to give us enough time to connect 136# thru the port redir, otherwise ssh will exit before we can connect. 137# 138 139# This is the x11vnc cmd for the remote side: 140$cmd = $x11vnc_cmd; 141$cmd =~ s/%DISP/$disp/; 142 143# 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$ssh_cmd =~ s/ / /g; 146print STDERR "running ssh command:\n\n$ssh_cmd\n\n"; 147 148# 149# Run ssh and redir into a tmp file (assumes ssh will use /dev/tty 150# for password/passphrase dialog) 151# 152$tmp = "/tmp/rx.$$"; 153system("$ssh_cmd > $tmp"); 154 155# Now watch for the PORT=XXXX message: 156$sleep = 0; 157$rport = ''; 158print STDERR "\nWaiting for x11vnc to indicate its port .."; 159while ($sleep < $sleep_time + 10) { 160 print STDERR "."; 161 sleep(1); 162 $sleep++; 163 if (`cat $tmp` =~ /PORT=(\d+)/) { 164 $rport = $1; 165 # wait 1 more second for output: 166 sleep(1); 167 if (`cat $tmp` =~ /PORT=(\d+)/) { 168 $rport = $1; 169 } 170 last; 171 } 172} 173print STDERR "\n"; 174 175if (! $rport) { 176 print STDERR `cat $tmp`; 177 unlink($tmp); 178 die "could not determine remote port.\n"; 179} 180unlink($tmp); 181 182# Find the remote to local mapping: 183$lport = $port_map{$rport}; 184print STDERR "remote port is: $rport (corresponds to port $lport here)\n"; 185if (! $lport) { 186 die "could not determine local port redir.\n"; 187} 188 189# Apply the special casing vncviewer does for 5900 <= port < 6000 190if ($lport < 6000 && $lport >= 5900) { 191 $lport = $lport - 5900; 192} 193 194# Finally, run the viewer. 195$cmd = $viewer_cmd; 196$cmd =~ s/%VNC_DISP/localhost:$lport/; 197 198print STDERR "running vncviewer command:\n\n$cmd\n\n"; 199system($cmd); 200