summaryrefslogtreecommitdiffstats
path: root/x11vnc/misc/rx11vnc.pl
blob: cf4b437359da79405e630a1c1b7efd4781dcf5f2 (plain)
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
144
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
  #!/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 -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);