• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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