@ -50,6 +50,8 @@ ULTRAVNC_REPEATER_LOOP=1 or ULTRAVNC_REPEATER_LOOP=BG, the latter
forks into the background . Set ULTRAVNC_REPEATER_PIDFILE to a file
forks into the background . Set ULTRAVNC_REPEATER_PIDFILE to a file
to store the master pid in .
to store the master pid in .
Set ULTRAVNC_REPEATER_NO_RFB = 1 to disable sending "RFB 000.000" to
the client . Then this program acts as general TCP rendezvous tool .
Examples:
Examples:
@ -83,7 +85,7 @@ my $looppid = '';
my $ pidfile = '' ;
my $ pidfile = '' ;
#
#
sub get_out {
sub get_out {
print STDERR "$_[0]:\t$$ looppid=$looppid\n" ;
lprint ( "$_[0]:\t$$ looppid=$looppid" ) ;
if ( $ looppid ) {
if ( $ looppid ) {
kill 'TERM' , $ looppid ;
kill 'TERM' , $ looppid ;
fsleep ( 0.2 ) ;
fsleep ( 0.2 ) ;
@ -93,6 +95,10 @@ sub get_out {
exit 0 ;
exit 0 ;
}
}
sub lprint {
print STDERR scalar ( localtime ) , ": " , @ _ , "\n" ;
}
# These are overridden in actual server thread:
# These are overridden in actual server thread:
#
#
$ SIG { INT } = \ & get_out ;
$ SIG { INT } = \ & get_out ;
@ -108,7 +114,7 @@ sub open_pidfile {
close PID ;
close PID ;
$ pidfile = $ pf ;
$ pidfile = $ pf ;
} else {
} else {
print STDERR "could not open pidfile: $pf - $! - continuing... \n ";
lprint ( "could not open pidfile: $pf - $! - continuing... ") ;
}
}
delete $ ENV { ULTRAVNC_REPEATER_PIDFILE } ;
delete $ ENV { ULTRAVNC_REPEATER_PIDFILE } ;
}
}
@ -150,7 +156,7 @@ if (exists $ENV{ULTRAVNC_REPEATER_LOOP}) {
open_pidfile ( ) ;
open_pidfile ( ) ;
}
}
print STDERR "ultravnc_repeater.pl: starting service at " , scalar ( localtime ) , " master-pid=$$\n" ;
lprint ( "ultravnc_repeater.pl: starting service. master-pid=$$" ) ;
while ( 1 ) {
while ( 1 ) {
$ looppid = fork ;
$ looppid = fork ;
if ( ! defined $ looppid ) {
if ( ! defined $ looppid ) {
@ -161,7 +167,7 @@ if (exists $ENV{ULTRAVNC_REPEATER_LOOP}) {
exec $ 0 , @ ARGV ;
exec $ 0 , @ ARGV ;
exit 1 ;
exit 1 ;
}
}
print STDERR "ultravnc_repeater.pl: re-starting service at " , scalar ( localtime ) , " master-pid=$$\n" ;
lprint ( "ultravnc_repeater.pl: re-starting service. master-pid=$$" ) ;
sleep 1 ;
sleep 1 ;
}
}
exit 0 ;
exit 0 ;
@ -184,7 +190,7 @@ eval "use IO::Socket::INET6;";
$ have_inet6 = 1 if $@ eq "" ;
$ have_inet6 = 1 if $@ eq "" ;
print "perl module IO::Socket::INET6 not available: no IPv6 support.\n" if ! $ have_inet6 ;
print "perl module IO::Socket::INET6 not available: no IPv6 support.\n" if ! $ have_inet6 ;
my $ prog = 'ultravnc_repeater .pl ';
my $ prog = 'ultravnc_repeater ';
my % ID ;
my % ID ;
my $ refuse = 0 ;
my $ refuse = 0 ;
@ -196,7 +202,7 @@ if (@ARGV && $ARGV[0] =~ /-h/) {
}
}
if ( @ ARGV && $ ARGV [ 0 ] eq '-r' ) {
if ( @ ARGV && $ ARGV [ 0 ] eq '-r' ) {
$ refuse = 1 ;
$ refuse = 1 ;
print "enabling refuse mode (-r).\n" ;
lprint ( "enabling refuse mode (-r)." ) ;
shift ;
shift ;
}
}
@ -285,15 +291,16 @@ my $SOCK1 = '';
my $ SOCK2 = '' ;
my $ SOCK2 = '' ;
my $ CURR = '' ;
my $ CURR = '' ;
print "watching for IPv4 connections on $client_port/client\n" if $ client_listen ;
lprint ( "$prog: starting up. pid: $$" ) ;
print "watching for IPv4 connections on $server_port/server\n" if $ server_listen ;
lprint ( "watching for IPv4 connections on $client_port/client." ) if $ client_listen ;
print "watching for IPv6 connections on $client_port/client\n" if $ client_listen6 ;
lprint ( "watching for IPv4 connections on $server_port/server." ) if $ server_listen ;
print "watching for IPv6 connections on $server_port/server\n" if $ server_listen6 ;
lprint ( "watching for IPv6 connections on $client_port/client." ) if $ client_listen6 ;
lprint ( "watching for IPv6 connections on $server_port/server." ) if $ server_listen6 ;
my $ alarm_sock = '' ;
my $ alarm_sock = '' ;
my $ got_alarm = 0 ;
my $ got_alarm = 0 ;
sub alarm_handler {
sub alarm_handler {
print "$prog: got sig alarm.\n" ;
lprint ( "$prog: got sig alarm." ) ;
if ( $ alarm_sock ne '' ) {
if ( $ alarm_sock ne '' ) {
close $ alarm_sock ;
close $ alarm_sock ;
}
}
@ -303,24 +310,28 @@ sub alarm_handler {
while ( my @ ready = $ select - > can_read ( ) ) {
while ( my @ ready = $ select - > can_read ( ) ) {
foreach my $ fh ( @ ready ) {
foreach my $ fh ( @ ready ) {
if ( $ fh == $ client_listen || $ fh == $ client_listen6 ) {
if ( ( $ client_listen && $ fh == $ client_listen ) || ( $ client_listen6 && $ fh == $ client_listen6 ) ) {
print "new vnc client connecting at " , scalar ( localtime ) , "\n" ;
lprint ( "new vnc client connecting." ) ;
} elsif ( $ fh == $ server_listen || $ fh == $ server_listen6 ) {
} elsif ( ( $ server_listen && $ fh == $ server_listen ) || ( $ server_listen6 && $ fh == $ server_listen6 ) ) {
print "new vnc server connecting at " , scalar ( localtime ) , "\n" ;
lprint ( "new vnc server connecting." ) ;
}
}
my $ sock = $ fh - > accept ( ) ;
my $ sock = $ fh - > accept ( ) ;
if ( ! $ sock ) {
if ( ! $ sock ) {
print "$prog: accept $!\n" ;
lprint ( "$prog: accept $!" ) ;
next ;
next ;
}
}
if ( $ fh == $ client_listen || $ fh == $ client_listen6 ) {
if ( ( $ client_listen && $ fh == $ client_listen ) || ( $ client_listen6 && $ fh == $ client_listen6 ) ) {
my $ str = "RFB 000.000\n" ;
if ( exists $ ENV { ULTRAVNC_REPEATER_NO_RFB } && $ ENV { ULTRAVNC_REPEATER_NO_RFB } ) {
my $ len = length $ str ;
lprint ( "ULTRAVNC_REPEATER_NO_RFB: not sending RFB 000.000" ) ;
my $ n = syswrite ( $ sock , $ str , $ len , 0 ) ;
} else {
if ( $ n != $ len ) {
my $ str = "RFB 000.000\n" ;
print "$prog: bad $str write: $n != $len $!\n" ;
my $ len = length $ str ;
close $ sock ;
my $ n = syswrite ( $ sock , $ str , $ len , 0 ) ;
if ( $ n != $ len ) {
lprint ( "$prog: bad $str write: $n != $len $!" ) ;
close $ sock ;
}
}
}
}
}
@ -336,15 +347,15 @@ while (my @ready = $select->can_read()) {
alarm ( 0 ) ;
alarm ( 0 ) ;
if ( $ got_alarm ) {
if ( $ got_alarm ) {
print "$prog: read timed out: $!\n" ;
lprint ( "$prog: read timed out: $!" ) ;
} elsif ( ! defined $ n ) {
} elsif ( ! defined $ n ) {
print "$prog: read error: $!\n" ;
lprint ( "$prog: read error: $!" ) ;
} elsif ( $ repeater_bufsize > 0 && $ n != $ size ) {
} elsif ( $ repeater_bufsize > 0 && $ n != $ size ) {
print "$prog: short read $n != $size $!\n" ;
lprint ( "$prog: short read $n != $size $!" ) ;
close $ sock ;
close $ sock ;
} elsif ( $ fh == $ client_listen || $ fh == $ client_listen6 ) {
} elsif ( ( $ client_listen && $ fh == $ client_listen ) || ( $ client_listen6 && $ fh == $ client_listen6 ) ) {
do_new_client ( $ sock , $ buf ) ;
do_new_client ( $ sock , $ buf ) ;
} elsif ( $ fh == $ server_listen || $ fh == $ server_listen6 ) {
} elsif ( ( $ server_listen && $ fh == $ server_listen ) || ( $ server_listen6 && $ fh == $ server_listen6 ) ) {
do_new_server ( $ sock , $ buf ) ;
do_new_server ( $ sock , $ buf ) ;
}
}
}
}
@ -355,33 +366,42 @@ sub do_new_client {
if ( $ buf =~ /^ID:(\w+)/ ) {
if ( $ buf =~ /^ID:(\w+)/ ) {
my $ id = $ 1 ;
my $ id = $ 1 ;
if ( exists $ ID { $ id } && exists $ ID { $ id } { client } && $ ID { $ id } { client } eq "0" ) {
if ( ! established ( $ ID { $ id } { sock } ) ) {
lprint ( "server socket for ID:$id is no longer established, closing it." ) ;
close $ ID { $ id } { sock } ;
delete $ ID { $ id } ;
} else {
lprint ( "server socket for ID:$id is still established." ) ;
}
}
if ( exists $ ID { $ id } ) {
if ( exists $ ID { $ id } ) {
if ( $ ID { $ id } { client } ) {
if ( $ ID { $ id } { client } ) {
my $ ref = $ refuse ;
my $ ref = $ refuse ;
if ( $ ref && ! established ( $ ID { $ id } { sock } ) ) {
if ( $ ref && ! established ( $ ID { $ id } { sock } ) ) {
print "socket for ID:$id is no longer established, closing it.\n" ;
lprint ( "socket for ID:$id is no longer established, closing it. ") ;
$ ref = 0 ;
$ ref = 0 ;
}
}
if ( $ ref ) {
if ( $ ref ) {
print "refusing extra vnc client for ID:$id\n" ;
lprint ( "refusing extra vnc client for ID:$id." ) ;
close $ sock ;
close $ sock ;
return ;
return ;
} else {
} else {
print "closing and deleting previous vnc client with ID:$id\n" ;
lprint ( "closing and deleting previous vnc client with ID:$id." ) ;
close $ ID { $ id } { sock } ;
close $ ID { $ id } { sock } ;
print "storing new vnc client with ID:$id\n" ;
lprint ( "storing new vnc client with ID:$id." ) ;
$ ID { $ id } { client } = 1 ;
$ ID { $ id } { client } = 1 ;
$ ID { $ id } { sock } = $ sock ;
$ ID { $ id } { sock } = $ sock ;
}
}
} else {
} else {
print "hooking up new vnc client with existing vnc server for ID:$id\n" ;
lprint ( "hooking up new vnc client with existing vnc server for ID:$id." ) ;
my $ sock2 = $ ID { $ id } { sock } ;
my $ sock2 = $ ID { $ id } { sock } ;
delete $ ID { $ id } ;
delete $ ID { $ id } ;
hookup ( $ sock , $ sock2 , "ID:$id" ) ;
hookup ( $ sock , $ sock2 , "ID:$id" ) ;
}
}
} else {
} else {
print "storing new vnc client with ID:$id\n" ;
lprint ( "storing new vnc client with ID:$id." ) ;
$ ID { $ id } { client } = 1 ;
$ ID { $ id } { client } = 1 ;
$ ID { $ id } { sock } = $ sock ;
$ ID { $ id } { sock } = $ sock ;
}
}
@ -400,32 +420,32 @@ sub do_new_client {
}
}
if ( $ port < 0 ) {
if ( $ port < 0 ) {
my $ pnew = - $ port ;
my $ pnew = - $ port ;
print "resetting port from $port to $pnew\n" ;
lprint ( "resetting port from $port to $pnew." ) ;
$ port = $ pnew ;
$ port = $ pnew ;
} elsif ( $ port < 200 ) {
} elsif ( $ port < 200 ) {
my $ pnew = $ port + 5900 ;
my $ pnew = $ port + 5900 ;
print "resetting port from $port to $pnew\n" ;
lprint ( "resetting port from $port to $pnew." ) ;
$ port = $ pnew ;
$ port = $ pnew ;
}
}
print "making vnc client connection directly to vnc server host='$host' port='$port'\n" ;
lprint ( "making vnc client connection directly to vnc server host='$host' port='$port'." ) ;
my $ sock2 = IO::Socket::INET - > new (
my $ sock2 = IO::Socket::INET - > new (
PeerAddr = > $ host ,
PeerAddr = > $ host ,
PeerPort = > $ port ,
PeerPort = > $ port ,
Proto = > "tcp"
Proto = > "tcp"
) ;
) ;
if ( ! $ sock2 && $ have_inet6 ) {
if ( ! $ sock2 && $ have_inet6 ) {
print "IPv4 connect error: $!, trying IPv6 ...\n" ;
lprint ( "IPv4 connect error: $!, trying IPv6 ..." ) ;
eval { $ sock2 = IO::Socket::INET6 - > new (
eval { $ sock2 = IO::Socket::INET6 - > new (
PeerAddr = > $ host ,
PeerAddr = > $ host ,
PeerPort = > $ port ,
PeerPort = > $ port ,
Proto = > "tcp"
Proto = > "tcp"
) ; } ;
) ; } ;
print "IPv6 connect error: $!\n" if ! $ sock2 ;
lprint ( "IPv6 connect error: $!" ) if ! $ sock2 ;
} else {
} else {
print "IPv4 connect error: $!\n" if ! $ sock2 ;
lprint ( "IPv4 connect error: $!" ) if ! $ sock2 ;
}
}
if ( ! $ sock2 ) {
if ( ! $ sock2 ) {
print "failed to connect to $host:$port\n" ;
lprint ( "failed to connect to $host:$port." ) ;
close $ sock ;
close $ sock ;
return ;
return ;
}
}
@ -439,48 +459,79 @@ sub do_new_server {
if ( $ buf =~ /^ID:(\w+)/ ) {
if ( $ buf =~ /^ID:(\w+)/ ) {
my $ id = $ 1 ;
my $ id = $ 1 ;
my $ store = 1 ;
my $ store = 1 ;
if ( exists $ ID { $ id } && exists $ ID { $ id } { client } && $ ID { $ id } { client } eq "1" ) {
if ( ! established ( $ ID { $ id } { sock } ) ) {
lprint ( "client socket for ID:$id is no longer established, closing it." ) ;
close $ ID { $ id } { sock } ;
delete $ ID { $ id } ;
} else {
lprint ( "client socket for ID:$id is still established." ) ;
}
}
if ( exists $ ID { $ id } ) {
if ( exists $ ID { $ id } ) {
if ( ! $ ID { $ id } { client } ) {
if ( ! $ ID { $ id } { client } ) {
my $ ref = $ refuse ;
my $ ref = $ refuse ;
if ( $ ref && ! established ( $ ID { $ id } { sock } ) ) {
if ( $ ref && ! established ( $ ID { $ id } { sock } ) ) {
print "socket for ID:$id is no longer established, closing it.\n" ;
lprint ( "socket for ID:$id is no longer established, closing it. ") ;
$ ref = 0 ;
$ ref = 0 ;
}
}
if ( $ ref ) {
if ( $ ref ) {
print "refusing extra vnc server for ID:$id\n" ;
lprint ( "refusing extra vnc server for ID:$id." ) ;
close $ sock ;
close $ sock ;
return ;
return ;
} else {
} else {
print "closing and deleting previous vnc server with ID:$id\n" ;
lprint ( "closing and deleting previous vnc server with ID:$id." ) ;
close $ ID { $ id } { sock } ;
close $ ID { $ id } { sock } ;
print "storing new vnc server with ID:$id\n" ;
lprint ( "storing new vnc server with ID:$id." ) ;
$ ID { $ id } { client } = 0 ;
$ ID { $ id } { client } = 0 ;
$ ID { $ id } { sock } = $ sock ;
$ ID { $ id } { sock } = $ sock ;
}
}
} else {
} else {
print "hooking up new vnc server with existing vnc client for ID:$id\n" ;
lprint ( "hooking up new vnc server with existing vnc client for ID:$id." ) ;
my $ sock2 = $ ID { $ id } { sock } ;
my $ sock2 = $ ID { $ id } { sock } ;
delete $ ID { $ id } ;
delete $ ID { $ id } ;
hookup ( $ sock , $ sock2 , "ID:$id" ) ;
hookup ( $ sock , $ sock2 , "ID:$id" ) ;
}
}
} else {
} else {
print "storing new vnc server with ID:$id\n" ;
lprint ( "storing new vnc server with ID:$id." ) ;
$ ID { $ id } { client } = 0 ;
$ ID { $ id } { client } = 0 ;
$ ID { $ id } { sock } = $ sock ;
$ ID { $ id } { sock } = $ sock ;
}
}
} else {
} else {
print "invalid ID:NNNNN string for vnc server: $buf \n ";
lprint ( "invalid ID:NNNNN string for vnc server: $buf ") ;
close $ sock ;
close $ sock ;
return ;
return ;
}
}
}
}
sub established {
sub established {
my $ fh = shift ;
return established_linux_proc ( $ fh ) ;
# not working:
my $ est = 1 ;
my $ str = "Z" ;
my $ res ;
#$res = recv($fh, $str, 1, MSG_PEEK | MSG_DONTWAIT);
if ( defined ( $ res ) ) {
lprint ( "established OK: $! '$str'." ) ;
$ est = 1 ;
} else {
# would check for EAGAIN here to decide ...
lprint ( "established err: $! '$str'." ) ;
$ est = 1 ;
}
return $ est ;
}
sub established_linux_proc {
# hack for Linux to see if remote side has gone away:
# hack for Linux to see if remote side has gone away:
my $ fh = shift ;
my $ fh = shift ;
# if we can't figure things out, we return true.
# if we can't figure things out, we must return true.
if ( $ uname !~ /Linux/ ) {
if ( $ uname !~ /Linux/ ) {
return 1 ;
return 1 ;
}
}
@ -549,7 +600,7 @@ sub established {
}
}
sub handler {
sub handler {
print STDERR "$prog\[$$/$CURR]: got SIGTERM.\n" ;
lprint ( "\[$$/$CURR] got SIGTERM." ) ;
close $ SOCK1 if $ SOCK1 ;
close $ SOCK1 if $ SOCK1 ;
close $ SOCK2 if $ SOCK2 ;
close $ SOCK2 if $ SOCK2 ;
exit ;
exit ;
@ -561,7 +612,7 @@ sub hookup {
my $ worker = fork ( ) ;
my $ worker = fork ( ) ;
if ( ! defined $ worker ) {
if ( ! defined $ worker ) {
print "failed to fork worker: $!\n" ;
lprint ( "failed to fork worker: $!" ) ;
close $ sock1 ;
close $ sock1 ;
close $ sock2 ;
close $ sock2 ;
return ;
return ;
@ -604,10 +655,10 @@ sub xfer {
my $ len = sysread ( $ in , $ buf , 8192 ) ;
my $ len = sysread ( $ in , $ buf , 8192 ) ;
if ( ! defined ( $ len ) ) {
if ( ! defined ( $ len ) ) {
next if $! =~ /^Interrupted/ ;
next if $! =~ /^Interrupted/ ;
print STDERR "$prog\[$$/$CURR]: $!\n" ;
lprint ( "\[$$/$CURR] $!" ) ;
last ;
last ;
} elsif ( $ len == 0 ) {
} elsif ( $ len == 0 ) {
print STDERR "$prog\[$$/$CURR]: Input is EOF.\n" ;
lprint ( "\[$$/$CURR] Input is EOF." ) ;
last ;
last ;
}
}
my $ offset = 0 ;
my $ offset = 0 ;
@ -615,7 +666,7 @@ sub xfer {
while ( $ len ) {
while ( $ len ) {
my $ written = syswrite ( $ out , $ buf , $ len , $ offset ) ;
my $ written = syswrite ( $ out , $ buf , $ len , $ offset ) ;
if ( ! defined $ written ) {
if ( ! defined $ written ) {
print STDERR "$prog\[$$/$CURR]: Output is EOF. $!\n" ;
lprint ( "\[$$/$CURR] Output is EOF. $!" ) ;
$ quit = 1 ;
$ quit = 1 ;
last ;
last ;
}
}
@ -626,7 +677,7 @@ sub xfer {
}
}
close ( $ out ) ;
close ( $ out ) ;
close ( $ in ) ;
close ( $ in ) ;
print STDERR "$prog\[$$/$CURR]: finished xfer.\n" ;
lprint ( "\[$$/$CURR] finished xfer." ) ;
}
}
sub xfer_both {
sub xfer_both {
@ -637,7 +688,7 @@ sub xfer_both {
my $ child = fork ( ) ;
my $ child = fork ( ) ;
if ( ! defined $ child ) {
if ( ! defined $ child ) {
print STDERR "$prog\[$$/$CURR] failed to fork: $!\n" ;
lprint ( "$prog\[$$/$CURR] failed to fork: $!" ) ;
return ;
return ;
}
}
@ -645,30 +696,30 @@ sub xfer_both {
$ SIG { INT } = "handler" ;
$ SIG { INT } = "handler" ;
if ( $ child ) {
if ( $ child ) {
print STDERR "$prog parent[$$/$CURR] 1 -> 2\n" ;
lprint ( "[$$/$CURR] parent 1 -> 2." ) ;
xfer ( $ sock1 , $ sock2 ) ;
xfer ( $ sock1 , $ sock2 ) ;
select ( undef , undef , undef , 0.25 ) ;
select ( undef , undef , undef , 0.25 ) ;
if ( kill 0 , $ child ) {
if ( kill 0 , $ child ) {
select ( undef , undef , undef , 0.9 ) ;
select ( undef , undef , undef , 0.9 ) ;
if ( kill 0 , $ child ) {
if ( kill 0 , $ child ) {
print STDERR "$prog\[$$/$CURR]: kill TERM child $child\n" ;
lprint ( "\[$$/$CURR] kill TERM child $child" ) ;
kill "TERM" , $ child ;
kill "TERM" , $ child ;
} else {
} else {
print STDERR "$prog\[$$/$CURR]: child $child gone.\n" ;
lprint ( "\[$$/$CURR] child $child gone." ) ;
}
}
}
}
} else {
} else {
select ( undef , undef , undef , 0.05 ) ;
select ( undef , undef , undef , 0.05 ) ;
print STDERR "$prog child [$$/$CURR] 2 -> 1\n" ;
lprint ( "[$$/$CURR] child 2 -> 1." ) ;
xfer ( $ sock2 , $ sock1 ) ;
xfer ( $ sock2 , $ sock1 ) ;
select ( undef , undef , undef , 0.25 ) ;
select ( undef , undef , undef , 0.25 ) ;
if ( kill 0 , $ parent ) {
if ( kill 0 , $ parent ) {
select ( undef , undef , undef , 0.8 ) ;
select ( undef , undef , undef , 0.8 ) ;
if ( kill 0 , $ parent ) {
if ( kill 0 , $ parent ) {
print STDERR "$prog\[$$/$CURR]: kill TERM parent $parent\n" ;
lprint ( "\[$$/$CURR] kill TERM parent $parent." ) ;
kill "TERM" , $ parent ;
kill "TERM" , $ parent ;
} else {
} else {
print STDERR "$prog\[$$/$CURR]: parent $parent gone.\n" ;
lprint ( "\[$$/$CURR] parent $parent gone." ) ;
}
}
}
}
}
}