| #!/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): |
| $ssh_cmd = "ssh -t -f $remote_user $redir $host '$cmd; echo END; sleep $sleep_time'"; |
| $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); |