#!/bin/sh
#
# ss_vncviewer:  wrapper for vncviewer to use an stunnel SSL tunnel
#                or an SSH tunnel.
#
# Copyright (c) 2006-2009 by Karl J. Runge <runge@karlrunge.com>
#
# ss_vncviewer is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
# 
# ss_vncviewer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with ss_vncviewer; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
# or see <http://www.gnu.org/licenses/>.
# 
# 
# You must have stunnel(8) installed on the system and in your PATH
# (however, see the -ssh option below, in which case you will need ssh(1)
# installed)  Note: stunnel is usually installed in an "sbin" subdirectory.
#
# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." 
# already running as the VNC server on the remote machine.
# (or use stunnel on the server side for any other VNC server)
#
#
# Usage: ss_vncviewer [cert-args] host:display <vncviewer-args>
#
# e.g.:  ss_vncviewer snoopy:0
#        ss_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile"
#
# [cert-args] can be:
#
#	-verify /path/to/cacert.pem		
#	-mycert /path/to/mycert.pem		
#	-crl    /path/to/my_crl.pem  (or directory)
#	-proxy  host:port
#
# -verify specifies a CA cert PEM file (or a self-signed one) for
#         authenticating the VNC server.
#
# -mycert specifies this client's cert+key PEM file for the VNC server to
#	  authenticate this client. 
#
# -proxy  try host:port as a Web proxy to use the CONNECT method
#         to reach the VNC server (e.g. your firewall requires a proxy).
#
#         For the "double proxy" case use -proxy host1:port1,host2:port2
#         (the first CONNECT is done through host1:port1 to host2:port2
#         and then a 2nd CONNECT to the destination VNC server.)
#
#         Use socks://host:port, socks4://host:port, or socks5://host,port
#         to force usage of a SOCKS proxy.  Also repeater://host:port and
#         sslrepeater://host:port.
#
# -showcert  Only fetch the certificate using the 'openssl s_client'
#            command (openssl(1) must in installed).  On ssvnc 1.0.27 and
#            later the bundled command 'ultravnc_dsm_helper' is used.
#
#    See http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-ca for details on
#    SSL certificates with VNC.
#
# A few other args (not related to SSL and certs):
#
# -2nd    Run the vncviewer a 2nd time if the first connections fails.
#
# -ssh    Use ssh instead of stunnel SSL.  ssh(1) must be installed and you
#         must be able to log into the remote machine via ssh.
#
#         In this case "host:display" may be of the form "user@host:display"
#         where "user@host" is used for the ssh login (see ssh(1) manpage).
#
#         If -proxy is supplied it can be of the forms: "gwhost" "gwhost:port"
#         "user@gwhost" or "user@gwhost:port".  "gwhost" is an incoming ssh 
#         gateway machine (the VNC server is not running there), an ssh -L
#         redir is used to "host" in "host:display" from "gwhost". Any "user@"
#         part must be in the -proxy string (not in "host:display").
#
#         Under -proxy use "gwhost:port" if connecting to any ssh port
#         other than the default (22).  (even for the non-gateway case,
#         -proxy must be used to specify a non-standard ssh port)
#
#         A "double ssh" can be specified via a -proxy string with the two
#         hosts separated by a comma:
#
#             [user1@]host1[:port1],[user2@]host2[:port2]
#
#         in which case a ssh to host1 and thru it via a -L redir a 2nd
#         ssh is established to host2.  
#
#         Examples:
#
#         ss_vncviewer -ssh bob@bobs-home.net:0
#         ss_vncviewer -ssh -sshcmd 'x11vnc -localhost' bob@bobs-home.net:0
#
#         ss_vncviewer -ssh -proxy fred@mygate.com:2022 mymachine:0
#         ss_vncviewer -ssh -proxy bob@bobs-home.net:2222 localhost:0
#
#         ss_vncviewer -ssh -proxy fred@gw-host,fred@peecee localhost:0
#
# -sshcmd cmd   Run "cmd" via ssh instead of the default "sleep 15"
#               e.g. -sshcmd 'x11vnc -display :0 -localhost -rfbport 5900'
#
# -sshargs "args"  pass "args" to the ssh process, e.g. -L/-R port redirs.
#
# -sshssl Tunnel the SSL connection thru a SSH connection.  The tunnel as
#         under -ssh is set up and the SSL connection goes thru it.  Use
#         this if you want to have and end-to-end SSL connection but must
#         go thru a SSH gateway host (e.g. not the vnc server).  Or use
#         this if you need to tunnel additional services via -R and -L 
#         (see -sshargs above).
#
#         ss_vncviewer -sshssl -proxy fred@mygate.com mymachine:0
#
# -listen (or -reverse) set up a reverse connection.
#
# -alpha  turn on cursor alphablending hack if you are using the
#         enhanced tightvnc vncviewer.
#
# -grab   turn on XGrabServer hack if you are using the enhanced tightvnc
#         vncviewer (e.g. for fullscreen mode in some windowmanagers like
#         fvwm that do not otherwise work in fullscreen mode)
#
#
# set VNCVIEWERCMD to whatever vncviewer command you want to use.
#
VNCIPCMD=${VNCVIEWERCMD:-vncip}
VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer}
if [ "X$SSVNC_TURBOVNC" != "X" ]; then
	if echo "$VNCVIEWERCMD" | grep '\.turbovnc' > /dev/null; then
		:
	else
		if type "$VNCVIEWERCMD.turbovnc" > /dev/null 2>/dev/null; then
			VNCVIEWERCMD="$VNCVIEWERCMD.turbovnc"
		fi
	fi
fi
#
# Same for STUNNEL, e.g. set it to /path/to/stunnel or stunnel4, etc.
#

# turn on verbose debugging output
if [ "X$SS_DEBUG" != "X" -a "X$SS_DEBUG" != "X0" ]; then
	set -xv 
fi

PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH

localhost="localhost"
if uname | grep Darwin >/dev/null; then
	localhost="127.0.0.1"
fi

# work out which stunnel to use (debian installs as stunnel4)
stunnel_set_here=""
if [ "X$STUNNEL" = "X" ]; then
	check_stunnel=1
	if [ "X$SSVNC_BASEDIRNAME" != "X" ]; then
		if [ -x "$SSVNC_BASEDIRNAME/stunnel" ]; then
			type stunnel > /dev/null 2>&1
			if [ $? = 0 ]; then
				# found ours
				STUNNEL=stunnel
				check_stunnel=0
			fi
		fi
	fi
	if [ "X$check_stunnel" = "X1" ]; then
		type stunnel4 > /dev/null 2>&1
		if [ $? = 0 ]; then
			STUNNEL=stunnel4
		else
			STUNNEL=stunnel
		fi
	fi
	stunnel_set_here=1
fi

help() {
	tail -n +2 "$0" | sed -e '/^$/ q'
}

secondtry=""
gotalpha=""
use_ssh=""
use_sshssl=""
direct_connect=""
ssh_sleep=15

# sleep longer in -listen mode:
if echo "$*" | grep '.*-listen' > /dev/null; then
	ssh_sleep=1800
fi


ssh_cmd=""
# env override of ssh_cmd:
if [ "X$SS_VNCVIEWER_SSH_CMD" != "X" ]; then
	ssh_cmd="$SS_VNCVIEWER_SSH_CMD"
fi

ssh_args=""
showcert=""
reverse=""

ciphers=""
anondh="ALL:RC4+RSA:+SSLv2:@STRENGTH"
anondh_set=""
stunnel_debug="6"
if [ "X$SS_DEBUG" != "X" -o "X$SSVNC_VENCRYPT_DEBUG" != "X" -o "X$SSVNC_STUNNEL_DEBUG" != "X" ]; then
	stunnel_debug="7"
fi

if [ "X$1" = "X-viewerflavor" ]; then
	# special case, try to guess which viewer:
	#
	if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then
		echo "unknown"
		exit 0
	fi
	if echo "$VNCVIEWERCMD" | grep -i chicken.of > /dev/null; then
		echo "cotvnc"
		exit 0
	fi
	if echo "$VNCVIEWERCMD" | grep -i ultra > /dev/null; then
		echo "ultravnc"
		exit 0
	fi
	# OK, run it for help output...
	str=`$VNCVIEWERCMD -h 2>&1 | head -n 5`
	if echo "$str" | grep -i 'TightVNC.viewer' > /dev/null; then
		echo "tightvnc"
	elif echo "$str" | grep -i 'VNC viewer version 3' > /dev/null; then
		echo "realvnc3"
	elif echo "$str" | grep -i 'VNC viewer .*Edition 4' > /dev/null; then
		echo "realvnc4"
	elif echo "$str" | grep -i 'RealVNC.Ltd' > /dev/null; then
		echo "realvnc4"
	else
		echo "unknown"
	fi
	exit 0
fi
if [ "X$1" = "X-viewerhelp" ]; then
	$VNCVIEWERCMD -h 2>&1
	exit 0
fi

# grab our cmdline options:
while [ "X$1" != "X" ]
do
    case $1 in 
	"-verify")	shift; verify="$1"
                ;;
	"-mycert")	shift; mycert="$1"
                ;;
	"-crl")		shift; crl="$1"
                ;;
	"-proxy")	shift; proxy="$1"
                ;;
	"-ssh")		use_ssh=1
                ;;
	"-sshssl")	use_ssh=1
			use_sshssl=1
                ;;
	"-sshcmd")	shift; ssh_cmd="$1"
                ;;
	"-sshargs")	shift; ssh_args="$1"
                ;;
	"-anondh")	ciphers="ciphers=$anondh"
			ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1
			export ULTRAVNC_DSM_HELPER_SHOWCERT_ADH
			anondh_set=1
                ;;
	"-ciphers")	shift; ciphers="ciphers=$1"
                ;;
	"-alpha")	gotalpha=1
                ;;
	"-showcert")	showcert=1
                ;;
	"-listen")	reverse=1
                ;;
	"-reverse")	reverse=1
                ;;
	"-2nd")		secondtry=1
                ;;
	"-grab")	VNCVIEWER_GRAB_SERVER=1; export VNCVIEWER_GRAB_SERVER
                ;;
	"-x11cursor")	VNCVIEWER_X11CURSOR=1; export VNCVIEWER_X11CURSOR
                ;;
	"-rawlocal")	VNCVIEWER_RAWLOCAL=1; export VNCVIEWER_RAWLOCAL
                ;;
	"-scale")	shift; SSVNC_SCALE="$1"; export SSVNC_SCALE
                ;;
	"-onelisten")	SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE
                ;;
	"-sendclipboard")	VNCVIEWER_SEND_CLIPBOARD=1; export VNCVIEWER_SEND_CLIPBOARD
                ;;
	"-sendalways")	VNCVIEWER_SEND_ALWAYS=1; export VNCVIEWER_SEND_ALWAYS
                ;;
	"-recvtext")	shift; VNCVIEWER_RECV_TEXT="$1"; export VNCVIEWER_RECV_TEXT
                ;;
	"-escape")	shift; VNCVIEWER_ESCAPE="$1"; export VNCVIEWER_ESCAPE
                ;;
	"-ssvnc_encodings")	shift; VNCVIEWER_ENCODINGS="$1"; export VNCVIEWER_ENCODINGS
                ;;
	"-ssvnc_extra_opts")	shift; VNCVIEWERCMD_EXTRA_OPTS="$1"; export VNCVIEWERCMD_EXTRA_OPTS
                ;;
	"-rfbversion")	shift; VNCVIEWER_RFBVERSION="$1"; export VNCVIEWER_RFBVERSION
                ;;
	"-nobell")	VNCVIEWER_NOBELL=1; export VNCVIEWER_NOBELL
                ;;
	"-popupfix")	VNCVIEWER_POPUP_FIX=1; export VNCVIEWER_POPUP_FIX
                ;;
	"-realvnc4")	VNCVIEWER_IS_REALVNC4=1; export VNCVIEWER_IS_REALVNC4
                ;;
	"-h"*)	help; exit 0
                ;;
	"--h"*)	help; exit 0
                ;;
	*)	break
                ;;
    esac
    shift
done

# maxconn is something we added to stunnel, this disables it:
if [ "X$SS_VNCVIEWER_NO_MAXCONN" != "X" ]; then
	STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'`
elif echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then
	STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'`
elif [ "X$reverse" != "X" ]; then
	STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'`
else
	# new way (our patches).  other than the above, we set these:
	if [ "X$SKIP_STUNNEL_ONCE" = "X" ]; then
		STUNNEL_ONCE=1; export STUNNEL_ONCE
	fi
	if [ "X$SKIP_STUNNEL_MAX_CLIENTS" = "X" ]; then
		STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS
	fi
fi
# always set this one:
if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then
	STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG
fi

# this is the -t ssh option (gives better keyboard response thru SSH tunnel)
targ="-t"
if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then
	targ=""
fi

# set the alpha blending env. hack: 
if [ "X$gotalpha" = "X1" ]; then
	VNCVIEWER_ALPHABLEND=1
	export VNCVIEWER_ALPHABLEND
else
	NO_ALPHABLEND=1
	export NO_ALPHABLEND
fi

if [ "X$reverse" != "X" ]; then
	ssh_sleep=1800
	if [ "X$proxy" != "X" ]; then
		# check proxy usage under reverse connection:
		if [ "X$use_ssh" = "X" -a "X$use_sshssl" = "X" ]; then
			echo ""
			if echo "$proxy" | egrep -i "(repeater|vencrypt)://" > /dev/null; then
				:
			else
				echo "*Warning*: SSL -listen and a Web proxy does not make sense."
				sleep 2
			fi
		elif echo "$proxy" | grep "," > /dev/null; then
			:
		else
			echo ""
			echo "*Warning*: -listen and a single proxy/gateway does not make sense."
			sleep 2
		fi

		# we now try to PPROXY_LOOP_THYSELF, set this var to disable that.
		#SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE
	fi
fi
if [ "X$ssh_cmd" = "X" ]; then
	# if no remote ssh cmd, sleep a bit:
	ssh_cmd="sleep $ssh_sleep"
fi

# this should be a host:display:
#
orig="$1"
shift

dL="-L"
if uname -sr | egrep 'SunOS 5\.[5-8]' > /dev/null; then
	dL="-h"
fi

have_uvnc_dsm_helper_showcert=""
if [ "X$showcert" = "X1" -a "X$SSVNC_USE_S_CLIENT" = "X" -a "X$reverse" = "X" ]; then
	if type ultravnc_dsm_helper >/dev/null 2>&1; then
		if ultravnc_dsm_helper -help 2>&1 | grep -w showcert >/dev/null; then
			have_uvnc_dsm_helper_showcert=1
		fi
	fi
fi
have_uvnc_dsm_helper_ipv6=""
if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
	if type ultravnc_dsm_helper >/dev/null 2>&1; then
		if ultravnc_dsm_helper -help 2>&1 | grep -iw ipv6 >/dev/null; then
			have_uvnc_dsm_helper_ipv6=1
		fi
	fi
fi

rchk() {
	# a kludge to set $RANDOM if we are not bash:
	if [ "X$BASH_VERSION" = "X" ]; then
		RANDOM=`date +%S``sh -c 'echo $$'``ps -elf 2>&1 | sum 2>&1 | awk '{print $1}'`
	fi
}
rchk

# a portable, but not absolutely safe, tmp file creator
mytmp() {
	tf=$1
	if type mktemp > /dev/null 2>&1; then
		# if we have mktemp(1), use it:
		tf2="$tf.XXXXXX"
		tf2=`mktemp "$tf2"`
		if [ "X$tf2" != "X" -a -f "$tf2" ]; then
			if [ "X$DEBUG_MKTEMP" != "X" ]; then
				echo "mytmp-mktemp: $tf2" 1>&2
			fi
			echo "$tf2"
			return
		fi
	fi
	# fallback to multiple cmds:
	rm -rf "$tf" || exit 1
	if [ -d "$tf" ]; then
		echo "tmp file $tf still exists as a directory."
		exit 1
	elif [ $dL "$tf" ]; then
		echo "tmp file $tf still exists as a symlink."
		exit 1
	elif [ -f "$tf" ]; then
		echo "tmp file $tf still exists."
		exit 1
	fi
	touch "$tf" || exit 1
	chmod 600 "$tf" || exit 1
	rchk
	if [ "X$DEBUG_MKTEMP" != "X" ]; then
		echo "mytmp-touch: $tf" 1>&2
	fi
	echo "$tf"
}

# set up special case of ultravnc single click III mode:
if echo "$proxy" | egrep "^sslrepeater://" > /dev/null; then
	pstr=`echo "$proxy" | sed -e 's,sslrepeater://,,'`
	pstr1=`echo "$pstr" | sed -e 's/+.*$//'`
	pstr2=`echo "$pstr" | sed -e 's/^[^+]*+//'`
	SSVNC_REPEATER="SCIII=$pstr2"; export SSVNC_REPEATER
	orig=$pstr1
	echo
	echo "reset: SSVNC_REPEATER=$SSVNC_REPEATER orig=$orig proxy=''"
	proxy=""
fi
if echo "$proxy" | egrep "vencrypt://" > /dev/null; then
	vtmp="/tmp/ss_handshake${RANDOM}.$$.txt"
	vtmp=`mytmp "$vtmp"`
	SSVNC_PREDIGESTED_HANDSHAKE="$vtmp"
	export SSVNC_PREDIGESTED_HANDSHAKE
	if [ "X$SSVNC_USE_OURS" = "X" ]; then
		NEED_VENCRYPT_VIEWER_BRIDGE=1
	fi
fi
if [ "X$SSVNC_USE_OURS" = "X" ]; then
	VNCVIEWERCMD_EXTRA_OPTS=""
fi


# check -ssh and -mycert/-verify conflict:
if [ "X$use_ssh" = "X1" -a "X$use_sshssl" = "X" ]; then
	if [ "X$mycert" != "X" -o "X$verify" != "X" ]; then
		echo "-mycert and -verify cannot be used in -ssh mode" 
		exit 1
	fi
fi

# direct mode Vnc:// means show no warnings.
# direct mode vnc:// will show warnings.
if echo "$orig" | grep '^V[Nn][Cc]://' > /dev/null; then
	SSVNC_NO_ENC_WARN=1
	export SSVNC_NO_ENC_WARN
	orig=`echo "$orig" | sed -e 's/^...:/vnc:/'`
fi

# interprest the pseudo URL proto:// strings:
if echo "$orig" | grep '^vnc://' > /dev/null; then
	orig=`echo "$orig" | sed -e 's,vnc://,,'`
	verify=""
	mycert=""
	crl=""
	use_ssh=""
	use_sshssl=""
	direct_connect=1
elif echo "$orig" | grep '^vncs://' > /dev/null; then
	orig=`echo "$orig" | sed -e 's,vncs://,,'`
elif echo "$orig" | grep '^vncssl://' > /dev/null; then
	orig=`echo "$orig" | sed -e 's,vncssl://,,'`
elif echo "$orig" | grep '^vnc+ssl://' > /dev/null; then
	orig=`echo "$orig" | sed -e 's,vnc.ssl://,,'`
elif echo "$orig" | grep '^vncssh://' > /dev/null; then
	orig=`echo "$orig" | sed -e 's,vncssh://,,'`
	use_ssh=1
elif echo "$orig" | grep '^vnc+ssh://' > /dev/null; then
	orig=`echo "$orig" | sed -e 's,vnc.ssh://,,'`
	use_ssh=1
fi

if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
        verify=""
        mycert=""
        crl=""
        use_ssh=""
        use_sshssl=""
        direct_connect=1
	if echo "$SSVNC_ULTRA_DSM" | grep 'noultra:' > /dev/null; then
		SSVNC_NO_ULTRA_DSM=1; export SSVNC_NO_ULTRA_DSM
	fi
fi

# rsh mode is an internal/secret thing only I use.
rsh=""
if echo "$orig" | grep '^rsh://' > /dev/null; then
	use_ssh=1
	rsh=1
	orig=`echo "$orig" | sed -e 's,rsh://,,'`
elif echo "$orig" | grep '^rsh:' > /dev/null; then
	use_ssh=1
	rsh=1
	orig=`echo "$orig" | sed -e 's,rsh:,,'`
fi

# play around with host:display port:
if echo "$orig" | grep ':' > /dev/null; then
	:
else
	# add or assume :0 if no ':'
	if [ "X$reverse" = "X" ]; then
		orig="$orig:0"
	elif [ "X$orig" = "X" ]; then
		orig=":0"
	fi
fi

# extract host and disp number:

# try to see if it is ipv6 address:
ipv6=0
if echo "$orig" | grep '\[' > /dev/null; then
	# ipv6 [fe80::219:dbff:fee5:3f92%eth1]:5900
	host=`echo "$orig" | sed -e 's/\].*$//' -e 's/\[//'`
	disp=`echo "$orig" | sed -e 's/^.*\]://'`
	ipv6=1
elif echo "$orig" | grep ':..*:' > /dev/null; then
	# ipv6 fe80::219:dbff:fee5:3f92%eth1:5900
	host=`echo "$orig" | sed -e 's/:[^:]*$//'`
	disp=`echo "$orig" | sed -e 's/^.*://'`
	ipv6=1
else
	# regular host:port
	host=`echo "$orig" | awk -F: '{print $1}'`
	disp=`echo "$orig" | awk -F: '{print $2}'`
fi

if [ "X$reverse" != "X" -a "X$STUNNEL_LISTEN" = "X" -a "X$host" != "X" ]; then
	STUNNEL_LISTEN=$host
	echo "set STUNNEL_LISTEN=$STUNNEL_LISTEN"
fi

if [ "X$host" = "X" ]; then
	host=$localhost
fi

if [ "X$SSVNC_IPV6" = "X0" ]; then
	# disable checking for it.
	ipv6=0
#elif [ "X$reverse" != "X" -a "X$ipv6" = "X1" ]; then
#	ipv6=0
elif [ "X$ipv6" = "X1" ]; then
	:
elif echo "$host" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then
	:
else
	# regular hostname, can't be sure...
	gout=""
	if type getent > /dev/null 2>/dev/null; then
		gout=`getent hosts "$host" 2>/dev/null`
	fi
	if echo "$gout" | grep ':.*:' > /dev/null; then
		if echo "$gout" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then
			:
		else
			echo "ipv6: "`echo "$gout" | grep ':.*:' | head -n 1`
			ipv6=1
		fi
	fi
	if [ "X$ipv6" = "X0" ]; then
		hout=""
		if type host > /dev/null 2>/dev/null; then
			host "$host" >/dev/null 2>&1
			host "$host" >/dev/null 2>&1
			hout=`host "$host" 2>/dev/null`
		fi
		if echo "$hout" | grep -i 'has ipv6 address' > /dev/null; then
			if echo "$hout" | grep -i 'has address' > /dev/null; then
				:
			else
				echo "ipv6: "`echo "$hout" | grep -i 'has ipv6 address' | head -n 1`
				ipv6=1
			fi
		fi
	fi
	if [ "X$ipv6" = "X0" ]; then
		dout=""
		if type dig > /dev/null 2>/dev/null; then
		dout=`dig -t any "$host" 2>/dev/null`
		fi
		if echo "$dout" | grep -i "^$host" | grep '[ 	]AAAA[ 	]' > /dev/null; then
			if echo "$dout" | grep -i "^$host" | grep '[ 	]A[ 	]' > /dev/null; then
				:
			else
				echo "ipv6: "`echo "$dout" | grep -i '[ 	]AAAA[ 	]' | head -n 1`
				ipv6=1
			fi
		fi
	fi
	if [ "X$ipv6" = "X0" ]; then
		sout=`env LOOKUP="$host" \
		      perl -e '	eval {use Socket};  exit 0 if $@;
				eval {use Socket6}; exit 0 if $@;
				@res = getaddrinfo($ENV{LOOKUP}, "daytime", AF_UNSPEC, SOCK_STREAM);
				$ipv4 = 0;
				$ipv6 = 0;
				$ip6 = "";
				while (scalar(@res) >= 5) {
					($family, $socktype, $proto, $saddr, $canon, @res) = @res;
					$ipv4 = 1 if $family == AF_INET;
					$ipv6 = 1 if $family == AF_INET6;
					if ($family == AF_INET6 && $ip6 eq "") {
						my ($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV);
						$ip6 = $host;
					}
				}
				if (! $ipv4 && $ipv6) {
					print "AF_INET6_ONLY: $ENV{LOOKUP}: $ip6\n";
				}
				exit 0;
			' 2>/dev/null`
		if echo "$sout" | grep AF_INET6_ONLY > /dev/null; then
			echo "$sout"
			ipv6=1
		fi
	fi
fi
if [ "X$ipv6" = "X1" ]; then
	echo "ipv6: addr=$host disp=$disp"
fi
if [ "X$disp" = "X" ]; then
	port=""	# probably -listen mode.
elif [ $disp -lt 0 ]; then
	# negative means use |n| without question:
	port=`expr 0 - $disp`
elif [ $disp -lt 200 ]; then
	# less than 200 means 5900+n
	if [ "X$reverse" = "X" ]; then
		port=`expr $disp + 5900`
	else
		port=`expr $disp + 5500`
	fi
else
	# otherwise use the number directly, e.g. 443, 2345
	port=$disp
fi

if [ "X$ipv6" = "X1" -a "X$direct_connect" = "X1" ]; then
	if [ "X$proxy" = "X" -a "X$reverse" = "X" ]; then
		if [ "X$SSVNC_ULTRA_DSM" != "X" -a "X$have_uvnc_dsm_helper_ipv6" = "X1" ]; then
			:
		elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then
			:
		elif [ "X$SSVNC_NO_IPV6_PROXY_DIRECT" != "X" ]; then
			:
		else
			proxy="ipv6://$host:$port"
			echo "direct connect: set proxy=$proxy"
		fi
	fi
fi

# (possibly) tell the vncviewer to only listen on lo: 
if [ "X$reverse" != "X" ]; then
	if [ "X$direct_connect" = "X" -o "X$proxy" != "X" -o "X$STUNNEL_LISTEN" != "X" ]; then
		VNCVIEWER_LISTEN_LOCALHOST=1
		export VNCVIEWER_LISTEN_LOCALHOST
	fi
fi

# try to find an open listening port via netstat(1):
inuse=""
if uname | grep Linux > /dev/null; then
	inuse=`netstat -ant | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*://'`
elif uname | grep SunOS > /dev/null; then
	inuse=`netstat -an -f inet -P tcp | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $1}' | sed 's/^.*\.//'`
elif uname | egrep -i 'bsd|darwin' > /dev/null; then
	inuse=`netstat -ant -f inet | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*\.//'`
# add others...
fi

# this is a crude attempt for unique ports tags, etc.
date_sec=`date +%S`

# these are special cases of no vnc, e.g. sleep or xmessage.
# these are for using ssvnc as a general port redirector.
if echo "$VNCVIEWERCMD" | grep '^sleep[ 	][ 	]*[0-9][0-9]*' > /dev/null; then
	if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then
		p=`echo "$VNCVIEWERCMD" | awk '{print $3}'`
		if [ "X$p" != "X" ]; then
			SS_VNCVIEWER_LISTEN_PORT=$p
		fi
	fi
	p2=`echo "$VNCVIEWERCMD" | awk '{print $2}'`
	VNCVIEWERCMD="eval sleep $p2; echo Local "
elif echo "$VNCVIEWERCMD" | grep '^xmessage[ 	][ 	]*[0-9][0-9]*' > /dev/null; then
	if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then
		p=`echo "$VNCVIEWERCMD" | awk '{print $2}'`
		SS_VNCVIEWER_LISTEN_PORT=$p
	fi
fi

# utility to find a free port to listen on.
findfree() {
	try0=$1
	try=$try0
	use0=""

	if [ "X$SS_VNCVIEWER_LISTEN_PORT" != "X" ]; then
		echo "$SS_VNCVIEWER_LISTEN_PORT"
		return	
	fi
	if [ $try -ge 6000 ]; then
		fmax=`expr $try + 1000`
	else
		fmax=6000
	fi

	while [ $try -lt $fmax ]
	do
		if [ "X$inuse" = "X" ]; then
			break
		fi
		if echo "$inuse" | grep -w $try > /dev/null; then
			:
		else
			use0=$try
			break
		fi
		try=`expr $try + 1`
	done
	if [ "X$use0" = "X" ]; then
		use0=`expr $date_sec + $try0`
	fi

	echo $use0
}

# utility for exiting; kills some helper processes,
# removes files, etc.
final() {
	echo ""
	if [ "X$tmp_cfg" != "X" ]; then
		rm -f $tmp_cfg
	fi
	if [ "X$SS_VNCVIEWER_RM" != "X" ]; then
		rm -f $SS_VNCVIEWER_RM 2>/dev/null
	fi
	if [ "X$tcert" != "X" ]; then
		rm -f $tcert
	fi
	if [ "X$pssh" != "X" ]; then
		echo "Terminating background ssh process"
		echo kill -TERM "$pssh"
		kill -TERM "$pssh" 2>/dev/null
		sleep 1
		kill -KILL "$pssh" 2>/dev/null
		pssh=""
	fi
	if [ "X$stunnel_pid" != "X" ]; then
		echo "Terminating background stunnel process"
		echo kill -TERM "$stunnel_pid"
		kill -TERM "$stunnel_pid" 2>/dev/null
		sleep 1
		kill -KILL "$stunnel_pid" 2>/dev/null
		stunnel_pid=""
	fi
	if [ "X$dsm_pid" != "X" ]; then
		echo "Terminating background ultravnc_dsm_helper process"
		echo kill -TERM "$dsm_pid"
		kill -TERM "$dsm_pid" 2>/dev/null
		sleep 1
		kill -KILL "$dsm_pid" 2>/dev/null
		stunnel_pid=""
	fi
	if [ "X$tail_pid" != "X" ]; then
		kill -TERM $tail_pid
	fi
	if [ "X$tail_pid2" != "X" ]; then
		kill -TERM $tail_pid2
	fi
}

if [ "X$reverse" = "X" ]; then
	# normal connections try 5930-5999:
	if [ "X$showcert" = "X" ]; then
		use=`findfree 5930`
	else
		# move away from normal place for (possibly many) -showcert
		pstart=`date +%S`
		pstart=`expr 6130 + $pstart + $pstart`
		use=`findfree $pstart`
	fi
	if [ $use -ge 5900 ]; then
		N=`expr $use - 5900`
	else
		N=$use
	fi
else
	# reverse connections:
	p2=`expr $port + 30`
	use=`findfree $p2`
	if [ $use -ge 5500 ]; then
		N=`expr $use - 5500`
	else
		N=$use
	fi
fi

# this is for my special use of ss_vncip -> vncip viewer.
if echo "$0" | grep vncip > /dev/null; then
	VNCVIEWERCMD="$VNCIPCMD"
fi

if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then
	:
elif [ "X$VNCVIEWERCMD_EXTRA_OPTS" != "X" ]; then
	VNCVIEWERCMD="$VNCVIEWERCMD $VNCVIEWERCMD_EXTRA_OPTS"
fi

# trick for the undocumented rsh://host:port method.
rsh_setup() {
	if echo "$ssh_host" | grep '@' > /dev/null; then
		ul=`echo "$ssh_host" | awk -F@ '{print $1}'`
		ul="-l $ul"
		ssh_host=`echo "$ssh_host" | awk -F@ '{print $2}'`
	else
		ul=""
	fi
	ssh_cmd=`echo "$ssh_cmd" | sed -e 's/ -localhost/ /g'`
}

# trick for the undocumented rsh://host:port method.
rsh_viewer() {
	trap "final" 0 2 15
	if [ "X$PORT" = "X" ]; then
		exit 1
	elif [ $PORT -ge 5900 ]; then
		vdpy=`expr $PORT - 5900`
	else
		vdpy=":$PORT"
	fi
	stty sane
	echo "$VNCVIEWERCMD" "$@" $ssh_host:$vdpy
	echo ""
	$VNCVIEWERCMD "$@" $ssh_host:$vdpy
	if [ $? != 0 ]; then
		sleep 2
		$VNCVIEWERCMD "$@" $ssh_host:$vdpy
	fi
}

check_perl() {
	if type "$1" > /dev/null 2>&1; then
		:
	elif [ ! -x "$1" ]; then
		echo ""
		echo "*******************************************************"
		echo "** Problem finding the Perl command '$1': **"
		echo ""
		type "perl"
		echo ""
		echo "** Perhaps you need to install the Perl package. **"
		echo "*******************************************************"
		echo ""
		sleep 5
	fi
}

# this is the PPROXY tool.  used only here for now... 
pcode() {
	tf=$1
	PPROXY_PROXY=$proxy; export PPROXY_PROXY
	PPROXY_DEST="$host:$port"; export PPROXY_DEST
	check_perl /usr/bin/perl

	cod='#!/usr/bin/perl

# A hack to glue stunnel to a Web or SOCKS proxy, UltraVNC repeater for
# client connections.
# Also acts as a VeNCrypt bridge (by redirecting to stunnel.)

use IO::Socket::INET;

my $have_inet6 = "";
eval "use IO::Socket::INET6;";
$have_inet6 = 1 if $@ eq "";

#my $have_sock6 = "";
#eval "use Socket; use Socket6;";
#$have_sock6 = 1 if $@ eq "";

if (exists $ENV{PPROXY_LOOP_THYSELF}) {
	# used for reverse vnc, run a repeating outer loop.
	print STDERR "PPROXY_LOOP: $ENV{PPROXY_LOOP_THYSELF}\n";
	my $rm = $ENV{PPROXY_REMOVE};
	my $lp = $ENV{PPROXY_LOOP_THYSELF};
	delete $ENV{PPROXY_REMOVE};
	delete $ENV{PPROXY_LOOP_THYSELF};
	$ENV{PPROXY_LOOP_THYSELF_MASTER} = $$;
	my $pid = $$;
	my $dbg = 0;
	my $c = 0;
	use POSIX ":sys_wait_h";
	while (1) {
		$pid = fork();
		last if ! defined $pid;
		if ($pid eq "0") {
			last;
		}
		$c++;
		print STDERR "\nPPROXY_LOOP: pid=$$ child=$pid count=$c\n";
		while (1) {
			waitpid(-1, WNOHANG);
			fsleep(0.25);
			if (! kill 0, $pid) {
				print STDERR "PPROXY_LOOP: child=$pid gone.\n";
				last;
			}
			print STDERR "PPROXY_LOOP: child=$pid alive.\n" if $dbg;
			if (! -f $lp) {
				print STDERR "PPROXY_LOOP: flag file $lp gone, killing $pid\n";
				kill TERM, $pid;
				fsleep(0.1);
				wait;
				last;
			}
			print STDERR "PPROXY_LOOP: file exists $lp\n" if $dbg;
		}
		last if ! -f $lp;
		fsleep(0.25);
	}
	if ($pid ne "0") {
		unlink($0) if $rm;
		exit 0;
	}
}

if (exists $ENV{PPROXY_SLEEP} && $ENV{PPROXY_SLEEP} > 0) {
	print STDERR "PPROXY_PID: $$\n";
	sleep $ENV{PPROXY_SLEEP};
}

foreach my $var (qw(
		PPROXY_DEST
		PPROXY_KILLPID
		PPROXY_LISTEN
		PPROXY_PROXY
		PPROXY_REMOVE
		PPROXY_REPEATER
		PPROXY_REVERSE
		PPROXY_SLEEP
		PPROXY_SOCKS
		PPROXY_VENCRYPT
		PPROXY_VENCRYPT_VIEWER_BRIDGE
    )) {
	if (0 || $ENV{SS_DEBUG} || $ENV{SSVNC_VENCRYPT_DEBUG}) {
		print STDERR "$var: $ENV{$var}\n";
	}
} 

if ($ENV{PPROXY_SOCKS} ne "" && $ENV{PPROXY_PROXY} !~ m,^socks5?://,i) {
	if ($ENV{PPROXY_SOCKS} eq "5") {
		$ENV{PPROXY_PROXY} = "socks5://$ENV{PPROXY_PROXY}";
	} else {
		$ENV{PPROXY_PROXY} = "socks://$ENV{PPROXY_PROXY}";
	}
}

my $rfbSecTypeAnonTls  = 18;
my $rfbSecTypeVencrypt = 19;

my $rfbVencryptPlain        = 256;
my $rfbVencryptTlsNone      = 257;
my $rfbVencryptTlsVnc       = 258;
my $rfbVencryptTlsPlain     = 259;
my $rfbVencryptX509None     = 260;
my $rfbVencryptX509Vnc      = 261;
my $rfbVencryptX509Plain    = 262;

my $handshake_file = "";
if (exists $ENV{SSVNC_PREDIGESTED_HANDSHAKE})  {
	$handshake_file = $ENV{SSVNC_PREDIGESTED_HANDSHAKE};
}

my $have_gettimeofday = 0;
eval "use Time::HiRes;";
if ($@ eq "") {
	$have_gettimeofday = 1;
}
sub gettime {
	my $t = "0.0";
	if ($have_gettimeofday) {
		$t = Time::HiRes::gettimeofday();
	}
	return $t;
}

my $listen_handle = "";
my $sock = "";
my $parent = $$;

my $initial_data = "";

if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) {
	my ($from, $to) = split(/,/, $ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE});
	do_vencrypt_viewer_bridge($from, $to);
	exit 0;
}

my ($first, $second, $third) = split(/,/, $ENV{PPROXY_PROXY}, 3);
my ($mode_1st, $mode_2nd, $mode_3rd) = ("", "", "");

($first, $mode_1st) = url_parse($first);

my ($proxy_host, $proxy_port) = ($first, "");
if ($proxy_host =~ /^(.*):(\d+)$/) {
	$proxy_host = $1;
	$proxy_port = $2;
}
my $connect = $ENV{PPROXY_DEST};

if ($second ne "") {
	($second, $mode_2nd) = url_parse($second);
}

if ($third ne "") {
	($third, $mode_3rd) = url_parse($third);
}


print STDERR "\n";
print STDERR "PPROXY v0.4: a tool for Web, SOCKS, and UltraVNC proxies and for\n";
print STDERR "PPROXY v0.4: IPv6 and VNC VeNCrypt bridging.\n";
print STDERR "proxy_host:       $proxy_host\n";
print STDERR "proxy_port:       $proxy_port\n";
print STDERR "proxy_connect:    $connect\n";
print STDERR "pproxy_params:    $ENV{PPROXY_PROXY}\n";
print STDERR "pproxy_listen:    $ENV{PPROXY_LISTEN}\n";
print STDERR "pproxy_reverse:   $ENV{PPROXY_REVERSE}\n";
print STDERR "io_socket_inet6:  $have_inet6\n";
print STDERR "\n";
if (! $have_inet6) {
	print STDERR "PPROXY: To enable IPv6 connections, install the IO::Socket::INET6 perl module.\n\n";
}

if (1) {
	print STDERR "pproxy 1st: $first\t- $mode_1st\n";
	print STDERR "pproxy 2nd: $second\t- $mode_2nd\n";
	print STDERR "pproxy 3rd: $third\t- $mode_3rd\n";
	print STDERR "\n";
}

sub pdie {
	my $msg = shift;
	kill_proxy_pids();
	die "$msg";
}

if ($ENV{PPROXY_REVERSE} ne "") {
	my ($rhost, $rport) = ($ENV{PPROXY_REVERSE}, "");
	if ($rhost =~ /^(.*):(\d+)$/) {
		$rhost = $1;
		$rport = $2;
	}
	$rport = 5900 unless $rport;
	my $emsg = "";
	$listen_handle = IO::Socket::INET->new(
		PeerAddr => $rhost,
		PeerPort => $rport,
		Proto => "tcp"
	);
	$emsg = $!;
	if (! $listen_handle && $have_inet6) {
		eval {$listen_handle = IO::Socket::INET6->new(
			PeerAddr => $rhost,
			PeerPort => $rport,
			Proto => "tcp"
		);};
		$emsg .= " / $!";
	}
	if (! $listen_handle) {
		pdie "pproxy: $emsg -- PPROXY_REVERSE\n";
	}
	print STDERR "PPROXY_REVERSE: connected to $rhost $rport\n";

} elsif ($ENV{PPROXY_LISTEN} ne "") {
	my $listen_sock = "";
	my $maxtry = 12;
	my $sleep = 5;
	my $p2 = "";
	my $emsg = "";
	for (my $i=0; $i < $maxtry; $i++)  {
		my ($if, $p) = ("", $ENV{PPROXY_LISTEN});
		if ($p =~ /^(.*):(\d+)$/) {
			$if = $1;
			$p = $2;
		}
		$p2 = "*:$p";
		if ($if eq "") {
			$if = "localhost";
		}
		print STDERR "pproxy interface: $if\n";

		$emsg = "";
		if (($if eq "INADDR_ANY6" || $if eq "::") && $have_inet6) {
			eval {$listen_sock = IO::Socket::INET6->new(
				Listen    => 2,
				ReuseAddr => 1,
				Domain    => AF_INET6,
				LocalAddr => "::",
				LocalPort => $p,
				Proto     => "tcp"
			);};
			$p2 = ":::$p";
		} elsif ($if =~ /^INADDR_ANY/) {
			$listen_sock = IO::Socket::INET->new(
				Listen    => 2,
				ReuseAddr => 1,
				LocalPort => $p,
				Proto     => "tcp"
			);
		} elsif (($if eq "INADDR_LOOPBACK6" || $if eq "::1") && $have_inet6) {
			$p2 = "::1:$p";
			eval {$listen_sock = IO::Socket::INET6->new(
				Listen    => 2,
				ReuseAddr => 1,
				Domain    => AF_INET6,
				LocalAddr => "::1",
				LocalPort => $p,
				Proto     => "tcp"
			);};
			$p2 = "::1:$p";
		} else {
			$p2 = "$if:$p";
			$listen_sock = IO::Socket::INET->new(
				Listen    => 2,
				ReuseAddr => 1,
				LocalAddr => $if,
				LocalPort => $p,
				Proto     => "tcp"
			);
			$emsg = $!;
			
			if (! $listen_sock && $have_inet6) {
				print STDERR "PPROXY_LISTEN: retry with INET6\n";
				eval {$listen_sock = IO::Socket::INET6->new(
					Listen    => 2,
					ReuseAddr => 1,
					Domain    => AF_INET6,
					LocalAddr => $if,
					LocalPort => $p,
					Proto     => "tcp"
				);};
				$emsg .= " / $!";
			}
		}
		if (! $listen_sock) {
			if ($i < $maxtry - 1) {
				warn "pproxy: $emsg $!\n";
				warn "Could not listen on port $p2, retrying in $sleep seconds... (Ctrl-C to quit)\n";
				sleep $sleep;
			}
		} else {
			last;
		}
	}
	if (! $listen_sock) {
		pdie "pproxy: $emsg -- PPROXY_LISTEN\n";
	}
	print STDERR "pproxy: listening on $p2\n";
	my $ip;
	($listen_handle, $ip) = $listen_sock->accept();
	my $err = $!;
	close $listen_sock;
	if (! $listen_handle) {
		pdie "pproxy: $err\n";
	}

	if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) {
		my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; 
		if ($sml ne "" && $sml ne "0") {
			setpgrp(0, 0);
			if (fork()) {
				close $viewer_sock;
				wait;
				exit 0;
			}
			if (fork()) {
				close $viewer_sock;
				exit 0;
			}
			setpgrp(0, 0);
			$parent = $$;
		}
	}
}

$sock = IO::Socket::INET->new(
	PeerAddr => $proxy_host,
	PeerPort => $proxy_port,
	Proto => "tcp"
);

my $err = "";

if (! $sock && $have_inet6) {
	$err = $!;

	eval {$sock = IO::Socket::INET6->new(
		PeerAddr => $proxy_host,
		PeerPort => $proxy_port,
		Proto => "tcp"
	);};
	$err .= " / $!";
}

if (! $sock) {
	unlink($0) if $ENV{PPROXY_REMOVE};
	pdie "pproxy: $err\n";
}

unlink($0) if $ENV{PPROXY_REMOVE};

if ($ENV{PPROXY_PROXY} =~ /^vencrypt:/ && $ENV{PPROXY_VENCRYPT_REVERSE}) {
	print STDERR "\nPPROXY: vencrypt+reverse: swapping listen socket with connect socket.\n";
	my $tmp_swap = $sock;
	$sock = $listen_handle;
	$listen_handle = $tmp_swap;
}

$cur_proxy = $first;
setmode($mode_1st);

if ($second ne "") {
	connection($second, 1);

	setmode($mode_2nd);
	$cur_proxy = $second;

	if ($third ne "") {
		connection($third, 2);
		setmode($mode_3rd);
		$cur_proxy = $third;
		connection($connect, 3);
	} else {
		connection($connect, 2);
	}
} else {
	connection($connect, 1);
}

sub kill_proxy_pids() {
	if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) {
		return;
	}
	if ($ENV{PPROXY_KILLPID}) {
		foreach my $p (split(/,/, $ENV{PPROXY_KILLPID})) {
			if ($p =~ /^(\+|-)/) {
				$p = $parent + $p;
			}
			print STDERR "kill TERM, $p (PPROXY_KILLPID)\n";
			kill "TERM", $p;
		}
	}
}

sub xfer {
	my($in, $out) = @_;
	$RIN = $WIN = $EIN = "";
	$ROUT = "";
	vec($RIN, fileno($in), 1) = 1;
	vec($WIN, fileno($in), 1) = 1;
	$EIN = $RIN | $WIN;

	while (1) {
		my $nf = 0;
		while (! $nf) {
			$nf = select($ROUT=$RIN, undef, undef, undef);
		}
		my $len = sysread($in, $buf, 8192);
		if (! defined($len)) {
			next if $! =~ /^Interrupted/;
			print STDERR "pproxy[$$]: $!\n";
			last;
		} elsif ($len == 0) {
			print STDERR "pproxy[$$]: Input is EOF.\n";
			last;
		}
		my $offset = 0;
		my $quit = 0;
		while ($len) {
			my $written = syswrite($out, $buf, $len, $offset);
			if (! defined $written) {
				print STDERR "pproxy[$$]: Output is EOF. $!\n";
				$quit = 1;
				last;
			}
			$len -= $written;
			$offset += $written;
		}
		last if $quit;
	}
	close($out);
	close($in);
	print STDERR "pproxy[$$]: finished xfer.\n";
}

sub handler {
	print STDERR "pproxy[$$]: got SIGTERM.\n";
	close $listen_handle if $listen_handle;
	close $sock if $sock;
	exit;
}

sub xfer_both {
	$child = fork;

	if (! defined $child) {
		kill_proxy_pids();
		exit 1;
	}

	$SIG{TERM} = "handler";

	if ($child) {
		if ($listen_handle) {
			print STDERR "pproxy parent[$$]  listen_handle -> socket\n";
			xfer($listen_handle, $sock);
		} else {
			print STDERR "pproxy parent[$$]  STDIN -> socket\n";
			xfer(STDIN, $sock);
		}
		select(undef, undef, undef, 0.25);
		if (kill 0, $child) {
			select(undef, undef, undef, 0.9);
			if (kill 0, $child) {
				print STDERR "pproxy[$$]: kill TERM child $child\n";
				kill "TERM", $child;
			} else {
				print STDERR "pproxy[$$]: child  $child gone.\n";
			}
		}
	} else {
		select(undef, undef, undef, 0.05);
		if ($listen_handle) {
			print STDERR "pproxy child [$$]  socket -> listen_handle\n";
			if ($initial_data ne "") {
				my $len = length $initial_data;
				print STDERR "pproxy child [$$]  sending initial_data, length $len\n\n";
				syswrite($listen_handle, $initial_data, $len);
			} else {
				print STDERR "\n";
			}
			xfer($sock, $listen_handle);
		} else {
			print STDERR "pproxy child [$$]  socket -> STDOUT\n";
			if ($initial_data ne "") {
				my $len = length $initial_data;
				print STDERR "pproxy child [$$]  sending initial_data, length $len\n\n";
				syswrite(STDOUT, $initial_data, $len);
			} else {
				print STDERR "\n";
			}
			xfer($sock, STDOUT);
		}
		select(undef, undef, undef, 0.25);
		if (kill 0, $parent) {
			select(undef, undef, undef, 0.8);
			if (kill 0, $parent) {
				print STDERR "pproxy[$$]: kill TERM parent $parent\n";
				kill "TERM", $parent;
			} else {
				print STDERR "pproxy[$$]: parent $parent gone.\n";
			}
		}
	}

	kill_proxy_pids();
}

xfer_both();

exit;

sub fsleep {
	select(undef, undef, undef, shift);
}

sub url_parse {
	my $hostport = shift;
	my $mode = "http";
	if ($hostport =~ m,^socks4?://(\S*)$,i) {
		$mode = "socks4";
		$hostport = $1;
	} elsif ($hostport =~ m,^socks5://(\S*)$,i) {
		$mode = "socks5";
		$hostport = $1;
	} elsif ($hostport =~ m,^https?://(\S*)$,i) {
		$mode = "http";
		$hostport = $1;
	} elsif ($hostport =~ m,^ipv6://(\S*)$,i) {
		$mode = "ipv6";
		$hostport = $1;
	} elsif ($hostport =~ m,^repeater://(\S*)\+(\S*)$,i) {
		# ultravnc repeater proxy.
		$hostport = $1;
		$mode = "repeater:$2";
		if ($hostport !~ /:\d+$/) {
			$hostport .= ":5900";
		}
	} elsif ($hostport =~ m,^vencrypt://(\S*)$,i) {
		# vencrypt handshake.
		$hostport = $1;
		my $m = "connect";
		if ($hostpost =~ /^(\S+)\+(\S+)$/) {
			$hostport = $1;
			$mode = $2;
		}
		$mode = "vencrypt:$m";
		if ($hostport !~ /:\d+$/) {
			$hostport .= ":5900";
		}
	}
	return ($hostport, $mode);
}

sub setmode {
	my $mode = shift;
	$ENV{PPROXY_REPEATER} = "";
	$ENV{PPROXY_VENCRYPT} = "";
	if ($mode =~ /^socks/) {
		if ($mode =~ /^socks5/) {
			$ENV{PPROXY_SOCKS} = 5;
		} else {
			$ENV{PPROXY_SOCKS} = 1;
		}
	} elsif ($mode =~ /^ipv6/i) {
		$ENV{PPROXY_SOCKS} = 0;
	} elsif ($mode =~ /^repeater:(.*)/) {
		$ENV{PPROXY_REPEATER} = $1;
		$ENV{PPROXY_SOCKS} = "";
	} elsif ($mode =~ /^vencrypt:(.*)/) {
		$ENV{PPROXY_VENCRYPT} = $1;
		$ENV{PPROXY_SOCKS} = "";
	} else {
		$ENV{PPROXY_SOCKS} = "";
	}
}

sub connection {
	my ($CONNECT, $w) = @_;

	my $con = "";
	my $msg = "";

	if ($ENV{PPROXY_SOCKS} eq "5") {
		# SOCKS5
		my ($h, $p) = ($CONNECT, "");
		if ($h =~ /^(.*):(\d+)$/) {
			$h = $1;
			$p = $2;
		}
		$con .= pack("C", 0x05);
		$con .= pack("C", 0x01);
		$con .= pack("C", 0x00);

		$msg = "SOCKS5 via $cur_proxy to $h:$p\n\n";
		print STDERR "proxy_request$w: $msg";

		syswrite($sock, $con, length($con));

		my ($n1, $n2, $n3, $n4, $n5, $n6);
		my ($r1, $r2, $r3, $r4, $r5, $r6);
		my ($s1, $s2, $s3, $s4, $s5, $s6);

		$n1 = sysread($sock, $r1, 1);
		$n2 = sysread($sock, $r2, 1);

		$s1 = unpack("C", $r1);
		$s2 = unpack("C", $r2);
		if ($s1 != 0x05 || $s2 != 0x00) {
			print STDERR "SOCKS5 fail s1=$s1 s2=$s2 n1=$n1 n2=$n2\n";
			close $sock;
			exit(1);
		}

		$con = "";
		$con .= pack("C", 0x05);
		$con .= pack("C", 0x01);
		$con .= pack("C", 0x00);
		$con .= pack("C", 0x03);
		$con .= pack("C", length($h));
		$con .= $h;
		$con .= pack("C", $p >> 8);
		$con .= pack("C", $p & 0xff);

		syswrite($sock, $con, length($con));

		$n1 = sysread($sock, $r1, 1);
		$n2 = sysread($sock, $r2, 1);
		$n3 = sysread($sock, $r3, 1);
		$n4 = sysread($sock, $r4, 1);
		$s1 = unpack("C", $r1);
		$s2 = unpack("C", $r2);
		$s3 = unpack("C", $r3);
		$s4 = unpack("C", $r4);

		if ($s4 == 0x1) {
			sysread($sock, $r5, 4 + 2);
		} elsif ($s4 == 0x3) {
			sysread($sock, $r5, 1);
			$s5 = unpack("C", $r5);
			sysread($sock, $r6, $s5 + 2);
		} elsif ($s4 == 0x4) {
			sysread($sock, $r5, 16 + 2);
		}

		if ($s1 != 0x5 || $s2 != 0x0 || $s3 != 0x0) {
			print STDERR "SOCKS5 failed: s1=$s1 s2=$s2 s3=$s3 s4=$s4 n1=$n1 n2=$n2 n3=$n3 n4=$n4\n";
			close $sock;
			exit(1);
		}

	} elsif ($ENV{PPROXY_SOCKS} eq "1") {
		# SOCKS4 SOCKS4a
		my ($h, $p) = ($CONNECT, "");
		if ($h =~ /^(.*):(\d+)$/) {
			$h = $1;
			$p = $2;
		}
		$con .= pack("C", 0x04);
		$con .= pack("C", 0x01);
		$con .= pack("n", $p);

		my $SOCKS_4a = 0;
		if ($h eq "localhost" || $h eq "127.0.0.1") {
			$con .= pack("C", 127);
			$con .= pack("C", 0);
			$con .= pack("C", 0);
			$con .= pack("C", 1);
		} elsif ($h =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
			$con .= pack("C", $1);
			$con .= pack("C", $2);
			$con .= pack("C", $3);
			$con .= pack("C", $4);
		} else {
			$con .= pack("C", 0);
			$con .= pack("C", 0);
			$con .= pack("C", 0);
			$con .= pack("C", 3);
			$SOCKS_4a = 1;
		}

		$con .= "nobody";
		$con .= pack("C", 0);

		$msg = "SOCKS4 via $cur_proxy to $h:$p\n\n";
		if ($SOCKS_4a) {
			$con .= $h;
			$con .= pack("C", 0);
			$msg =~ s/SOCKS4/SOCKS4a/;
		}
		print STDERR "proxy_request$w: $msg";
		syswrite($sock, $con, length($con));

		my $ok = 1;
		for (my $i = 0; $i < 8; $i++) {
			my $c;
			sysread($sock, $c, 1);
			my $s = unpack("C", $c);
			if ($i == 0) {
				$ok = 0 if $s != 0x0;
			} elsif ($i == 1) {
				$ok = 0 if $s != 0x5a;
			}
		}
		if (! $ok) {
			print STDERR "SOCKS4 failed.\n";
			close $sock;
			exit(1);
		}
	} elsif ($ENV{PPROXY_SOCKS} eq "0") {
		# hack for ipv6 "proxy", nothing to do, assume INET6 call worked.
		;
	} elsif ($ENV{PPROXY_REPEATER} ne "") {
		my $rep = $ENV{PPROXY_REPEATER};
		print STDERR "repeater: $rep\n";
		$rep .= pack("x") x 250;
		syswrite($sock, $rep, 250);

		my $rfb = "";

		my $ok = 1;
		for (my $i = 0; $i < 12; $i++) {
			my $c;
			last if $ENV{PPROXY_GENERIC_REPEATER};
			sysread($sock, $c, 1);
			print STDERR $c;
			$rfb .= $c;
		}
		if ($rfb ne "" && $rfb !~ /^RFB 000\.000/) {
			$initial_data = $rfb;
			$rfb =~ s/\n//g;
			print STDERR "detected non-UltraVNC repeater; forwarding \"$rfb\"\nlength: ", length($initial_data), "\n";
		}
	} elsif ($ENV{PPROXY_VENCRYPT} ne "") {
		my $vencrypt = $ENV{PPROXY_VENCRYPT};
		vencrypt_dialog($vencrypt);

	} else {
		# Web Proxy:
		$con = "CONNECT $CONNECT HTTP/1.1\r\n";
		$con   .= "Host: $CONNECT\r\n";
		$con   .= "Connection: close\r\n\r\n";
		$msg = $con;

		print STDERR "proxy_request$w: via $cur_proxy:\n$msg";
		syswrite($sock, $con, length($con));

		my $rep = "";
		my $n = 0;
		while ($rep !~ /\r\n\r\n/ && $n < 30000) {
			my $c;
			sysread($sock, $c, 1);
			print STDERR $c;
			$rep .= $c;
			$n++;
		}
		if ($rep !~ m,HTTP/.* 200,) {
			print STDERR "HTTP CONNECT failed.\n";
			close $sock;
			exit(1);
		}
	}
}

sub vdie {
	append_handshake("done\n");
	close $sock;
	kill_proxy_pids();
	exit(1);
}

sub anontls_handshake {
	my ($vmode, $db) = @_;

	print STDERR "\nPPROXY: Doing ANONTLS Handshake\n";

	my $psec = pack("C", $rfbSecTypeAnonTls);
	syswrite($sock, $psec, 1);

	append_handshake("done\n");
}

sub vencrypt_handshake {
	
	my ($vmode, $db) = @_;

	print STDERR "\nPPROXY: Doing VeNCrypt Handshake\n";

	my $psec = pack("C", $rfbSecTypeVencrypt);

	if (exists $ENV{SSVNC_TEST_SEC_TYPE}) {
		my $fake = $ENV{SSVNC_TEST_SEC_TYPE};
		print STDERR "PPROXY: sending sec-type: $fake\n";
		$psec = pack("C", $fake);
	}

	syswrite($sock, $psec, 1);

	my $vmajor;
	my $vminor;
	sysread($sock, $vmajor, 1);
	sysread($sock, $vminor, 1);

	vdie if $vmajor eq "" || $vminor eq "";

	$vmajor = unpack("C", $vmajor);
	$vminor = unpack("C", $vminor);
	print STDERR "server vencrypt version $vmajor.$vminor\n" if $db;

	if (exists $ENV{SSVNC_TEST_SEC_TYPE}) {
		print STDERR "PPROXY: continuing on in test mode.\n";
	} else {
		vdie if $vmajor ne 0;
		vdie if $vminor < 2;
	}

	$vmajor = pack("C", 0);
	$vminor = pack("C", 2);
	append_handshake("subversion=0.2\n");

	syswrite($sock, $vmajor, 1);
	syswrite($sock, $vminor, 1);

	my $result;
	sysread($sock, $result, 1);
	print STDERR "result empty\n" if $db && $result eq "";

	vdie if $result eq "";
	$result = unpack("C", $result);
	print STDERR "result=$result\n" if $db;

	vdie if $result ne 0;

	my $nsubtypes;
	sysread($sock, $nsubtypes, 1);

	vdie if $nsubtypes eq "";
	$nsubtypes = unpack("C", $nsubtypes);
	print STDERR "nsubtypes=$nsubtypes\n" if $db;

	my %subtypes;

	for (my $i = 0; $i < $nsubtypes; $i++) {
		my $subtype = ""; 
		sysread($sock, $subtype, 4);
		vdie if length($subtype) != 4;

		# XXX fix 64bit.
		$subtype = unpack("N", $subtype);
		print STDERR "subtype: $subtype\n" if $db;
		$subtypes{$subtype} = 1;
		append_handshake("sst$i=$subtype\n");
	}

	my $subtype = 0;
	if (exists $subtypes{$rfbVencryptX509None})  {
		$subtype = $rfbVencryptX509None;
		print STDERR "selected rfbVencryptX509None\n" if $db;
	} elsif (exists $subtypes{$rfbVencryptX509Vnc})  {
		$subtype = $rfbVencryptX509Vnc;
		print STDERR "selected rfbVencryptX509Vnc\n" if $db;
	} elsif (exists $subtypes{$rfbVencryptX509Plain})  {
		$subtype = $rfbVencryptX509Plain;
		print STDERR "selected rfbVencryptX509Plain\n" if $db;
	} elsif (exists $subtypes{$rfbVencryptTlsNone})  {
		$subtype = $rfbVencryptTlsNone;
		print STDERR "selected rfbVencryptTlsNone\n" if $db;
	} elsif (exists $subtypes{$rfbVencryptTlsVnc})  {
		$subtype = $rfbVencryptTlsVnc;
		print STDERR "selected rfbVencryptTlsVnc\n" if $db;
	} elsif (exists $subtypes{$rfbVencryptTlsPlain})  {
		$subtype = $rfbVencryptTlsPlain;
		print STDERR "selected rfbVencryptTlsPlain\n" if $db;
	}

	if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) {
		my $fake = $ENV{SSVNC_TEST_SEC_SUBTYPE};
		print STDERR "PPROXY: sending sec-subtype: $fake\n";
		$subtype = $fake;
	}

	append_handshake("subtype=$subtype\n");

	my $pst = pack("N", $subtype);
	syswrite($sock, $pst, 4); 

	if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) {
		print STDERR "PPROXY: continuing on in test mode.\n";
	} else {
		vdie if $subtype == 0;
	}

	my $ok;
	sysread($sock, $ok, 1);
	$ok = unpack("C", $ok);
	print STDERR "ok=$ok\n" if $db;

	append_handshake("done\n");

	vdie if $ok == 0;
}

sub vencrypt_dialog {
	my $vmode = shift;
	my $db = 0;

	$db = 1 if exists $ENV{SS_DEBUG};
	$db = 1 if exists $ENV{SSVNC_VENCRYPT_DEBUG};

	append_handshake("mode=$vmode\n");

	my $server_rfb = "";
	#syswrite($sock, $rep, 250);
	for (my $i = 0; $i < 12; $i++) {
		my $c;
		sysread($sock, $c, 1);
		$server_rfb .= $c;
		print STDERR $c;
	}
	print STDERR "server_rfb: $server_rfb\n" if $db;
	append_handshake("server=$server_rfb");

	my $minor = "";
	if ($server_rfb =~ /^RFB 003\.(\d+)/) {
		$minor = $1;
	} else {
		vdie;
	}
	my $viewer_rfb = "RFB 003.008\n";
	if ($minor < 7) {
		vdie;
	} elsif ($minor == 7) {
		$viewer_rfb = "RFB 003.007\n";
	}
	my $nsec;
	my $t1 = gettime();
	my $t0 = gettime();

	syswrite($sock, $viewer_rfb, 12);
	sysread($sock, $nsec, 1);

	$t1 = gettime();
	$t1 = sprintf("%.6f", $t1 - $t0);

	append_handshake("viewer=$viewer_rfb");
	append_handshake("latency=$t1\n");

	vdie if $nsec eq "";

	$nsec = unpack("C", $nsec);

	print STDERR "nsec: $nsec\n" if $db;
	vdie if $nsec eq 0 || $nsec > 100;

	my %sectypes = ();

	for (my $i = 0; $i < $nsec; $i++) {
		my $sec;
		sysread($sock, $sec, 1);
		vdie if $sec eq "";
		$sec = unpack("C", $sec);
		print STDERR "sec: $sec\n" if $db;
		$sectypes{$sec} = 1;
	}
	
	if (exists $sectypes{$rfbSecTypeVencrypt}) {
		print STDERR "found rfbSecTypeVencrypt\n" if $db;
		append_handshake("sectype=$rfbSecTypeVencrypt\n");
		vencrypt_handshake($vmode, $db);
	} elsif (exists $sectypes{$rfbSecTypeAnonTls}) {
		print STDERR "found rfbSecTypeAnonTls\n" if $db;
		append_handshake("sectype=$rfbSecTypeAnonTls\n");
		anontls_handshake($vmode, $db);
	} else {
		print STDERR "No supported sec-type found\n" if $db;
		vdie;
	}
}

sub append_handshake {
	my $str = shift;
	if ($handshake_file) {
		if (open(HSF, ">>$handshake_file")) {
			print HSF $str;
			close HSF;
		}
	}
}

sub do_vencrypt_viewer_bridge {
	my ($listen, $connect) = @_;
	print STDERR "\npproxy: starting vencrypt_viewer_bridge[$$]: $listen \-> $connect\n";
	my $db = 0;
	my $backwards = 0;
	if ($listen < 0) {
		$backwards = 1;
		$listen = -$listen;
	}
	if ($handshake_file eq "") {
		die "pproxy: vencrypt_viewer_bridge[$$]: no SSVNC_PREDIGESTED_HANDSHAKE\n";
	}
	my $listen_sock;
	my $maxtry = 12;
	my $sleep = 5;
	for (my $i=0; $i < $maxtry; $i++)  {
		$listen_sock = IO::Socket::INET->new(
			Listen    => 2,
			ReuseAddr => 1,
			LocalAddr => "127.0.0.1",
			LocalPort => $listen,
			Proto     => "tcp"
		);
		if (! $listen_sock) {
			if ($i < $maxtry - 1) {
				warn "pproxy: vencrypt_viewer_bridge[$$]: $!\n";
				warn "Could not listen on port $listen, retrying in $sleep seconds... (Ctrl-C to quit)\n";
				sleep $sleep;
			}
		} else {
			last;
		}
	}
	if (! $listen_sock) {
		die "pproxy: vencrypt_viewer_bridge[$$]: $!\n";
	}
	print STDERR "pproxy: vencrypt_viewer_bridge[$$]: listening on port $listen\n\n";
	my ($viewer_sock, $ip) = $listen_sock->accept();
	my $err = $!;
	close $listen_sock;
	if (! $viewer_sock) {
		die "pproxy: vencrypt_viewer_bridge[$$]: $err\n";
	}
	if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) {
		my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; 
		if ($sml ne "" && $sml ne "0") {
			setpgrp(0, 0);
			if (fork()) {
				close $viewer_sock;
				wait;
				exit 0;
			}
			if (fork()) {
				close $viewer_sock;
				exit 0;
			}
			setpgrp(0, 0);
			$parent = $$;
		}
	}
	print STDERR "vencrypt_viewer_bridge[$$]: viewer_sock $viewer_sock\n" if $db;

	print STDERR "pproxy: vencrypt_viewer_bridge[$$]: connecting to 127.0.0.1:$connect\n";
	my $server_sock = IO::Socket::INET->new(
		PeerAddr => "127.0.0.1",
		PeerPort => $connect,
		Proto => "tcp"
	);
	print STDERR "vencrypt_viewer_bridge[$$]: server_sock $server_sock\n" if $db;
	if (! $server_sock) {
		my $err = $!;
		die "pproxy: vencrypt_viewer_bridge[$$]: $err\n";
	}

	if ($backwards) {
		print STDERR "vencrypt_viewer_bridge[$$]: reversing roles of viewer and server.\n";
		my $t = $viewer_sock;
		$viewer_sock = $server_sock;
		$server_sock = $t;
	}

	my %hs = ();
	my $dt = 0.2;
	my $slept = 0.0;
	while ($slept < 20.0) {
		select(undef, undef, undef, $dt);
		$slept += $dt;
		if (-f $handshake_file && open(HSF, "<$handshake_file")) {
			my $done = 0;
			%hs = ();
			my $str = "";
			while (<HSF>) {
				print STDERR "vencrypt_viewer_bridge[$$]: $_" if $ENV{VENCRYPT_VIEWER_BRIDGE_DEBUG};
				$str .= "vencrypt_viewer_bridge[$$]: $_";
				chomp;
				if ($_ eq "done") {
					$done = 1;
				} else {
					my ($k, $v) = split(/=/, $_, 2);
					if ($k ne "" && $v ne "") {
						$hs{$k} = $v;
					}
				}
			}
			close HSF;
			if ($done) {
				print STDERR "\n" . $str;
				last;
			}
		}
	}
	if (! exists $hs{server}) {
		$hs{server} = "RFB 003.008";
	}
	if (! exists $hs{sectype}) {
		unlink($handshake_file);
		die "pproxy: vencrypt_viewer_bridge[$$]: no sectype.\n";
	}
	syswrite($viewer_sock, "$hs{server}\n", length($hs{server}) + 1);
	my $viewer_rfb = "";
	for (my $i = 0; $i < 12; $i++) {
		my $c;
		sysread($viewer_sock, $c, 1);
		$viewer_rfb .= $c;
		print STDERR $c;
	}
	my $viewer_major = 3;
	my $viewer_minor = 8;
	if ($viewer_rfb =~ /RFB (\d+)\.(\d+)/) {
		$viewer_major = $1;	
		$viewer_minor = $2;	
	}
	my $u0 = pack("C", 0);
	my $u1 = pack("C", 1);
	my $u2 = pack("C", 2);
	if ($hs{sectype} == $rfbSecTypeAnonTls) {
		unlink($handshake_file);
		print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeAnonTls\n";
		if ($viewer_major > 3 || $viewer_minor >= 7) {
			;	# setup ok, proceed to xfer.
		} else {
			print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n";
			my $n;
			sysread($server_sock, $n, 1);
			$n = unpack("C", $n);
			if ($n == 0) {
				die "pproxy: vencrypt_viewer_bridge[$$]: nsectypes == $n.\n";
			}
			my %types;
			for (my $i = 0; $i < $n; $i++) {
				my $t;
				sysread($server_sock, $t, 1);
				$t = unpack("C", $t);
				$types{$t} = 1;
			}
			my $use = 1;	# None
			if (exists $types{1}) {
				$use = 1;	# None
			} elsif (exists $types{2}) {
				$use = 2;	# VncAuth
			} else {
				die "pproxy: vencrypt_viewer_bridge[$$]: no valid sectypes" . join(",", keys %types) . "\n";
			}
				
			# send 4 bytes sectype to viewer: 
			# (note this should be MSB, network byte order...)
			my $up = pack("C", $use);
			syswrite($viewer_sock, $u0, 1);
			syswrite($viewer_sock, $u0, 1);
			syswrite($viewer_sock, $u0, 1);
			syswrite($viewer_sock, $up, 1);
			# and tell server the one we selected:
			syswrite($server_sock, $up, 1);
			if ($use == 1) {
				# even None has security result, so read it here and discard it.
				my $sr = "";
				sysread($server_sock, $sr, 4);
			}
		}
	} elsif ($hs{sectype} == $rfbSecTypeVencrypt) {
		print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeVencrypt\n";
		if (! exists $hs{subtype}) {
			unlink($handshake_file);
			die "pproxy: vencrypt_viewer_bridge[$$]: no subtype.\n";
		}
		my $fake_type = "None";
		my $plain = 0;
		my $sub_type = $hs{subtype};
		if ($sub_type == $rfbVencryptTlsNone) {
			$fake_type = "None";
		} elsif ($sub_type == $rfbVencryptTlsVnc) {
			$fake_type = "VncAuth";
		} elsif ($sub_type == $rfbVencryptTlsPlain) {
			$fake_type = "None";
			$plain = 1;
		} elsif ($sub_type == $rfbVencryptX509None) {
			$fake_type = "None";
		} elsif ($sub_type == $rfbVencryptX509Vnc) {
			$fake_type = "VncAuth";
		} elsif ($sub_type == $rfbVencryptX509Plain) {
			$fake_type = "None";
			$plain = 1;
		}
		if ($plain) {
			if (!open(W, ">$handshake_file")) {
				unlink($handshake_file);
				die "pproxy: vencrypt_viewer_bridge[$$]: $handshake_file $!\n";
			}
			print W <<"END";

			proc print_out {} {
				global user pass env

				if [info exists env(SSVNC_UP_DEBUG)] {
					toplevel .b
					button .b.b -text "user=\$user pass=\$pass" -command {destroy .b}
					pack .b.b
					update
					tkwait window .b
				}
				
				if [info exists env(SSVNC_UP_FILE)] {
					set fh "" 
					catch {set fh [open \$env(SSVNC_UP_FILE) w]}
					if {\$fh != ""} {
						puts \$fh user=\$user\\npass=\$pass
						flush \$fh
						close \$fh
						return
					}
				}
				puts stdout user=\$user\\npass=\$pass
				flush stdout
			}

			proc center_win {w} {
				update
				set W [winfo screenwidth  \$w]
				set W [expr \$W + 1]
				wm geometry \$w +\$W+0
				update
				set x [expr [winfo screenwidth  \$w]/2 - [winfo width  \$w]/2]
				set y [expr [winfo screenheight \$w]/2 - [winfo height \$w]/2]

				wm geometry \$w +\$x+\$y
				wm deiconify \$w
				update
			}

			wm withdraw .

			global env
			set up {}
			if [info exists env(SSVNC_UNIXPW)] {
				set rm 0
				set up \$env(SSVNC_UNIXPW)
				if [regexp {^rm:} \$up]  {
					set rm 1
					regsub {^rm:} \$up {} up
				}
				if [file exists \$up] {
					set fh ""
					set f \$up
					catch {set fh [open \$up r]}
					if {\$fh != ""} {
						gets \$fh u	
						gets \$fh p	
						close \$fh
						set up "\$u@\$p"
					}
					if {\$rm} {
						catch {file delete \$f}
					}
				}
			} elseif [info exists env(SSVNC_VENCRYPT_USERPASS)] {
				set up \$env(SSVNC_VENCRYPT_USERPASS)
			}
			#puts stderr up=\$up
			if {\$up != ""} {
				if [regexp {@} \$up] {
					global user pass
					set user \$up
					set pass \$up
					regsub {@.*\$}  \$user "" user
					regsub {^[^@]*@} \$pass "" pass
					print_out
					exit
				}
			}

			wm title . {VeNCrypt Viewer Bridge User/Pass}

			set user {}
			set pass {}

			label .l -text {SSVNC VeNCrypt Viewer Bridge}

			frame .f0
			frame .f0.fL
			label .f0.fL.la -text {Username: }
			label .f0.fL.lb -text {Password: }

			pack .f0.fL.la .f0.fL.lb -side top

			frame .f0.fR
			entry .f0.fR.ea -width 24 -textvariable user
			entry .f0.fR.eb -width 24 -textvariable pass -show *

			pack .f0.fR.ea .f0.fR.eb -side top -fill x

			pack .f0.fL -side left
			pack .f0.fR -side right -expand 1 -fill x

			button .no -text Cancel -command {destroy .}
			button .ok -text Done   -command {print_out; destroy .}

			center_win .
			pack .l .f0 .no .ok -side top -fill x
			update
			wm deiconify .

			bind .f0.fR.ea <Return> {focus .f0.fR.eb}
			bind .f0.fR.eb <Return> {print_out; destroy .}
			focus .f0.fR.ea

			wm resizable . 1 0
			wm minsize . [winfo reqwidth .] [winfo reqheight .]
END
			close W;

			#system("cat $handshake_file");
			my $w = "wish";
			if ($ENV{WISH}) {
				$w = $ENV{WISH};	
			}
			print STDERR "pproxy: vencrypt_viewer_bridge[$$]: prompt  VencryptPlain user and passwd.\n";
			my $res = "";
			if (`uname` =~ /Darwin/) {
				my $mtmp = `mktemp /tmp/hsup.XXXXXX`;
				chomp $mtmp;
				system("env SSVNC_UP_FILE=$mtmp $w $handshake_file");
				$res = `cat $mtmp`;
				unlink $mtmp;
			} else {
				$res = `$w $handshake_file`;
			}
			my $user = "";
			my $pass = "";
			if ($res =~ /user=(\S*)/) {
				$user = $1;
			}
			if ($res =~ /pass=(\S*)/) {
				$pass = $1;
			}
			print STDERR "pproxy: vencrypt_viewer_bridge[$$]: sending VencryptPlain user and passwd.\n";
			my $ulen = pack("C", length($user));
			my $plen = pack("C", length($pass));
			# (note this should be MSB, network byte order...)
			syswrite($server_sock, $u0, 1);
			syswrite($server_sock, $u0, 1);
			syswrite($server_sock, $u0, 1);
			syswrite($server_sock, $ulen, 1);
			syswrite($server_sock, $u0, 1);
			syswrite($server_sock, $u0, 1);
			syswrite($server_sock, $u0, 1);
			syswrite($server_sock, $plen, 1);
			syswrite($server_sock, $user, length($user));
			syswrite($server_sock, $pass, length($pass));
		}
		unlink($handshake_file);

		my $ft = 0;
		if ($fake_type eq "None") {
			$ft = 1;
		} elsif ($fake_type eq "VncAuth") {
			$ft = 2;
		} else {
			die "pproxy: vencrypt_viewer_bridge[$$]: unknown fake type: $fake_type\n";
		}
		my $fp = pack("C", $ft);
		if ($viewer_major > 3 || $viewer_minor >= 7) {
			syswrite($viewer_sock, $u1, 1);
			syswrite($viewer_sock, $fp, 1);
			my $cr;
			sysread($viewer_sock, $cr, 1);
			$cr = unpack("C", $cr);
			if ($cr != $ft) {
				die "pproxy: vencrypt_viewer_bridge[$$]: client selected wrong type: $cr / $ft\n";
			}
		} else {
			print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n";
			# send 4 bytes sect type to viewer: 
			# (note this should be MSB, network byte order...)
			syswrite($viewer_sock, $u0, 1);
			syswrite($viewer_sock, $u0, 1);
			syswrite($viewer_sock, $u0, 1);
			syswrite($viewer_sock, $fp, 1);
			if ($ft == 1) {
				# even None has security result, so read it here and discard it.
				my $sr = "";
				sysread($server_sock, $sr, 4);
			}
		}
	}

	$listen_handle = $viewer_sock;
	$sock = $server_sock;

	xfer_both();
}
'
	# '
	# xpg_echo will expand \n \r, etc.
	# try to unset and then test for it.
	if type shopt > /dev/null 2>&1; then
		shopt -u xpg_echo >/dev/null 2>&1
	fi
	v='print STDOUT "abc\n";'
	echo "$v" > $tf
	chmod 700 $tf

	lc=`wc -l $tf | awk '{print $1}'`
	if [ "X$lc" = "X1" ]; then
		echo "$cod" > $tf
	else
		printf "%s" "$cod" > $tf
		echo "" >> $tf
	fi
	# prime perl
	perl -e 'use IO::Socket::INET; select(undef, undef, undef, 0.01)' >/dev/null 2>&1
}

# make_tcert is no longer invoked via the ssvnc gui (Listen mode).
# make_tcert is for testing only now via -mycert BUILTIN
make_tcert() {
	tcert="/tmp/ss_vnc_viewer_tcert${RANDOM}.$$"
	tcert=`mytmp "$tcert"`
	cat > $tcert <<END
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAvkfXxb0wcxgrjV2ziFikjII+ze8iKcTBt47L0GM/c21efelN
+zZpJUUXLu4zz8Ryq8Q+sQgfNy7uTOpN9bUUaOk1TnD7gaDQnQWiNHmqbW2kL+DS
OKngJVPo9dETAS8hf7+D1e1DBZxjTc1a4RQqWJixwpYj99ixWzu8VC2m/xXsjvOs
jp4+DLBB490nbkwvstmhmiWm1CmI5O5xOkgioVNQqHvQMdVKOSz9PpbjvZiRX1Uo
qoMrk+2NOqwP90TB35yPASXb9zXKpO7DLhkube+yYGf+yk46aD707L07Eb7cosFP
S84vNZ9gX7rQ0UOwm5rYA/oZTBskgaqhtIzkLwIDAQABAoIBAD4ot/sXt5kRn0Ca
CIkU9AQWlC+v28grR2EQW9JiaZrqcoDNUzUqbCTJsi4ZkIFh2lf0TsqELbZYNW6Y
6AjJM7al4E0UqYSKJTv2WCuuRxdiRs2BMwthqyBmjeanev7bB6V0ybt7u3Y8xU/o
MrTuYnr4vrEjXPKdLirwk7AoDbKsRXHSIiHEIBOq1+dUQ32t36ukdnnza4wKDLZc
PKHiCdCk/wOGhuDlxD6RspqUAlRnJ8/aEhrgWxadFXw1hRhRsf/v1shtB0T3DmTe
Jchjwyiw9mryb9JZAcKxW+fUc4EVvj6VdQGqYInQJY5Yxm5JAlVQUJicuuJEvn6A
rj5osQECgYEA552CaHpUiFlB4HGkjaH00kL+f0+gRF4PANCPk6X3UPDVYzKnzmuu
yDvIdEETGFWBwoztUrOOKqVvPEQ+kBa2+DWWYaERZLtg2cI5byfDJxQ3ldzilS3J
1S3WgCojqcsG/hlxoQJ1dZFanUy/QhUZ0B+wlC+Zp1Q8AyuGQvhHp68CgYEA0lBI
eqq2GGCdJuNHMPFbi8Q0BnX55LW5C1hWjhuYiEkb3hOaIJuJrqvayBlhcQa2cGqp
uP34e9UCfoeLgmoCQ0b4KpL2NGov/mL4i8bMgog4hcoYuIi3qxN18vVR14VKEh4U
RLk0igAYPU+IK2QByaQlBo9OSaKkcfm7U1/pK4ECgYAxr6VpGk0GDvfF2Tsusv6d
GIgV8ZP09qSLTTJvvxvF/lQYeqZq7sjI5aJD5i3de4JhpO/IXQJzfZfWOuGc8XKA
3qYK/Y2IqXXGYRcHFGWV/Y1LFd55mCADHlk0l1WdOBOg8P5iRu/Br9PbiLpCx9oI
vrOXpnp03eod1/luZmqguwKBgQCWFRSj9Q7ddpSvG6HCG3ro0qsNsUMTI1tZ7UBX
SPogx4tLf1GN03D9ZUZLZVFUByZKMtPLX/Hi7K9K/A9ikaPrvsl6GEX6QYzeTGJx
3Pw0amFrmDzr8ySewNR6/PXahxPEuhJcuI31rPufRRI3ZLah3rFNbRbBFX+klkJH
zTnoAQKBgDbUK/aQFGduSy7WUT7LlM3UlGxJ2sA90TQh4JRQwzur0ACN5GdYZkqM
YBts4sBJVwwJoxD9OpbvKu3uKCt41BSj0/KyoBzjT44S2io2tj1syujtlVUsyyBy
/ca0A7WBB8lD1D7QMIhYUm2O9kYtSCLlUTHt5leqGaRG38DqlX36
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDzDCCArQCCQDSzxzxqhyqLzANBgkqhkiG9w0BAQQFADCBpzELMAkGA1UEBhMC
VVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcTBkJvc3RvbjETMBEG
A1UEChMKTXkgQ29tcGFueTEcMBoGA1UECxMTUHJvZHVjdCBEZXZlbG9wbWVudDEZ
MBcGA1UEAxMQd3d3Lm5vd2hlcmUubm9uZTEhMB8GCSqGSIb3DQEJARYSYWRtaW5A
bm93aGVyZS5ub25lMB4XDTA3MDMyMzE4MDc0NVoXDTI2MDUyMjE4MDc0NVowgacx
CzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMQ8wDQYDVQQHEwZC
b3N0b24xEzARBgNVBAoTCk15IENvbXBhbnkxHDAaBgNVBAsTE1Byb2R1Y3QgRGV2
ZWxvcG1lbnQxGTAXBgNVBAMTEHd3dy5ub3doZXJlLm5vbmUxITAfBgkqhkiG9w0B
CQEWEmFkbWluQG5vd2hlcmUubm9uZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAL5H18W9MHMYK41ds4hYpIyCPs3vIinEwbeOy9BjP3NtXn3pTfs2aSVF
Fy7uM8/EcqvEPrEIHzcu7kzqTfW1FGjpNU5w+4Gg0J0FojR5qm1tpC/g0jip4CVT
6PXREwEvIX+/g9XtQwWcY03NWuEUKliYscKWI/fYsVs7vFQtpv8V7I7zrI6ePgyw
QePdJ25ML7LZoZolptQpiOTucTpIIqFTUKh70DHVSjks/T6W472YkV9VKKqDK5Pt
jTqsD/dEwd+cjwEl2/c1yqTuwy4ZLm3vsmBn/spOOmg+9Oy9OxG+3KLBT0vOLzWf
YF+60NFDsJua2AP6GUwbJIGqobSM5C8CAwEAATANBgkqhkiG9w0BAQQFAAOCAQEA
vGomHEp6TVU83X2EBUgnbOhzKJ9u3fOI/Uf5L7p//Vxqow7OR1cguzh/YEzmXOIL
ilMVnzX9nj/bvcLAuqEP7MR1A8f4+E807p/L/Sf49BiCcwQq5I966sGKYXjkve+T
2GTBNwMSq+5kLSf6QY8VZI+qnrAudEQMeJByQhTZZ0dH8Njeq8EGl9KUio+VWaiW
CQK6xJuAvAHqa06OjLmwu1fYD4GLGSrOIiRVkSXV8qLIUmzxdJaIRznkFWsrCEKR
wAH966SAOvd2s6yOHMvyDRIL7WHxfESB6rDHsdIW/yny1fBePjv473KrxyXtbz7I
dMw1yW09l+eEo4A7GzwOdw==
-----END CERTIFICATE-----
END
	chmod 600 $tcert
	echo "$tcert"
}

Kecho() {
	NO_KECHO=1
	if [ "X$USER" = "Xrunge" -a "X$NO_KECHO" = "X" ]; then
		echo "dbg: $*"
	fi
}

NHAFL_warning() {
	echo "" 
	echo "** Warning: For the proxy: $proxy" 
	echo "** Warning: the ssh(1) option: $ssh_NHAFL" 
	echo "** Warning: will be used to avoid frequent 'ssh key has changed for localhost'"
	echo "** Warning: dialogs and connection failures (for example, ssh will exit asking"
	echo "** Warning: you to manually remove a key from ~/.ssh/known_hosts.)"
	echo "** Warning: "
	echo "** Warning: This decreases security: a Man-In-The-Middle attack is possible."
	echo "** Warning: For chained ssh connections the first ssh leg is secure but the"
	echo "** Warning: 2nd ssh leg is vulnerable.  For an ssh connection going through"
	echo "** Warning: a HTTP or SOCKS proxy the ssh connection is vulnerable."
	echo "** Warning: "
	echo "** Warning: You can set the SSVNC_SSH_LOCALHOST_AUTH=1 env. var. to disable" 
	echo "** Warning: using the NoHostAuthenticationForLocalhost=yes ssh option." 
	echo "** Warning: "
	echo "** Warning: A better solution is to configure (in the SSVNC GUI) the setting:"
	echo "** Warning: 'Options -> Advanced -> Private SSH KnownHosts file' (or set" 
	echo "** Warning: SSVNC_KNOWN_HOSTS_FILE directly) to a per-connection known hosts" 
	echo "** Warning: file.  That file holds the 'localhost' cert for this specific" 
	echo "** Warning: connection.  This yields a both secure and convenient solution." 
	echo "" 
}

space_expand() {
	str=`echo "$1" | sed -e 's/%SPACE/ /g' -e 's/%TAB/\t/g'`
	echo "$str"
}

# handle ssh case:
#
if [ "X$use_ssh" = "X1" ]; then
	#
	# USING SSH
	#
	ssh_port="22"
	ssh_host="$host"
	vnc_host="$localhost"
	ssh_UKHF=""
	localhost_extra=""
	# let user override ssh via $SSH
	ssh=${SSH:-"ssh -x"}

	sshword=`echo "$ssh" | awk '{print $1}'`
	if [ "X$sshword" != "X" ]; then
		if [ -x "$sshword" ]; then
			:
		elif type "$sshword" > /dev/null 2>&1; then
			:
		else
			echo ""
			echo "*********************************************************"
			echo "** Problem finding the SSH command '$sshword': **"
			echo ""
			type "$sshword"
			echo ""
			echo "** Perhaps you need to install the SSH client package. **"
			echo "*********************************************************"
			echo ""
			sleep 5
		fi
	fi

	ssh_NHAFL="-o NoHostAuthenticationForLocalhost=yes"
	if [ "X$SSVNC_SSH_LOCALHOST_AUTH" = "X1" ]; then
		ssh_NHAFL=""
	fi
	if [ "X$SSVNC_KNOWN_HOSTS_FILE" != "X" ]; then
		ssh_NHAFL=""
	
		ssh_UKHF="-o UserKnownHostsFile=$SSVNC_KNOWN_HOSTS_FILE"
		ssh_args="$ssh_args $ssh_UKHF"
		if [ ! -f "$SSVNC_KNOWN_HOSTS_FILE" ]; then
			touch "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1
		fi
		chmod 600 "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1
	fi
	did_ssh_NHAFL=""

	if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then
		SSVNC_LIM_ACCEPT_PRELOAD="$SSVNC_BASEDIR/$SSVNC_UNAME/$SSVNC_LIM_ACCEPT_PRELOAD"
	fi
	if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then
		echo ""
		echo "SSVNC_LIM_ACCEPT_PRELOAD=$SSVNC_LIM_ACCEPT_PRELOAD"
	fi

	if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" -a -f "$SSVNC_LIM_ACCEPT_PRELOAD" ]; then
		plvar=LD_PRELOAD
		if uname | grep Darwin >/dev/null; then
			plvar="DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES"
		fi
		ssh="env $plvar=$SSVNC_LIM_ACCEPT_PRELOAD $ssh"
	else
		SSVNC_LIM_ACCEPT_PRELOAD=""
	fi

	ssh_vencrypt_proxy=""
	# We handle vencrypt for SSH+SSL mode.
	if echo "$proxy" | grep 'vencrypt://' > /dev/null; then
		proxynew=""
		for part in `echo "$proxy" | tr ',' ' '`
		do
			if echo "$part" | egrep -i '^vencrypt://' > /dev/null; then
				ssh_vencrypt_proxy=$part
			else
				if [ "X$proxynew" = "X" ]; then
					proxynew="$part"
				else
					proxynew="$proxynew,$part"
				fi
			fi
		done
		proxy=$proxynew
	fi 
	Kecho ssh_vencrypt_proxy=$ssh_vencrypt_proxy

	# note that user must supply http:// for web proxy in SSH and SSH+SSL.
	# No xxxx:// implies ssh server+port.
	#
	if echo "$proxy" | egrep '(http|https|socks|socks4|socks5)://' > /dev/null; then
		# Handle Web or SOCKS proxy(ies) for the initial connect.
		Kecho host=$host
		Kecho port=$port
		pproxy=""
		sproxy1=""
		sproxy_rest=""
		for part in `echo "$proxy" | tr ',' ' '`
		do
			Kecho proxy_part=$part
			if [ "X$part" = "X" ]; then
				continue
			elif echo "$part" | egrep -i '^(http|https|socks|socks4|socks5)://' > /dev/null; then
				pproxy="$pproxy,$part"
			else
				if [ "X$sproxy1" = "X" ]; then
					sproxy1="$part"
				else
					sproxy_rest="$sproxy_rest,$part"
				fi
			fi
		done
		pproxy=`echo "$pproxy" | sed -e 's/^,,*//' -e 's/,,*/,/g'`
		sproxy_rest=`echo "$sproxy_rest" | sed -e 's/^,,*//' -e 's/,,*/,/g'`

		Kecho pproxy=$pproxy
		Kecho sproxy1=$sproxy1
		Kecho sproxy_rest=$sproxy_rest

		sproxy1_host=""
		sproxy1_port=""
		sproxy1_user=""

		if [ "X$sproxy1" != "X" ]; then
			sproxy1_host=`echo "$sproxy1" | awk -F: '{print $1}'`
			sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'`
			sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'`
			if [ "X$sproxy1_host" = "X" ]; then
				sproxy1_host=$sproxy1_user
				sproxy1_user=""
			else
				sproxy1_user="${sproxy1_user}@"
			fi
			sproxy1_port=`echo "$sproxy1" | awk -F: '{print $2}'`
			if [ "X$sproxy1_port" = "X" ]; then
				sproxy1_port="22"
			fi
		else
			sproxy1_host=`echo "$host" | awk -F: '{print $1}'`
			sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'`
			sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'`
			if [ "X$sproxy1_host" = "X" ]; then
				sproxy1_host=$sproxy1_user
				sproxy1_user=""
			else
				sproxy1_user="${sproxy1_user}@"
			fi
			sproxy1_port=`echo "$host" | awk -F: '{print $2}'`
			if [ "X$sproxy1_port" = "X" ]; then
				sproxy1_port="22"
			fi
		fi

		Kecho sproxy1_host=$sproxy1_host
		Kecho sproxy1_port=$sproxy1_port
		Kecho sproxy1_user=$sproxy1_user

		ptmp="/tmp/ss_vncviewer_ssh${RANDOM}.$$.pl"
		ptmp=`mytmp "$ptmp"`
		PPROXY_REMOVE=1; export PPROXY_REMOVE
		proxy=$pproxy
		port_save=$port
		host_save=$host
		if [ "X$sproxy1_host" != "X" ]; then
			host=$sproxy1_host
		fi
		if [ "X$sproxy1_port" != "X" ]; then
			port=$sproxy1_port
		fi
		host=`echo "$host" | sed -e 's/^.*@//'`
		port=`echo "$port" | sed -e 's/^.*://'`
		pcode "$ptmp"
		port=$port_save
		host=$host_save

		nd=`findfree 6600`
		PPROXY_LISTEN=$nd; export PPROXY_LISTEN
		# XXX no reverse forever PPROXY_LOOP_THYSELF ...
		$ptmp &
		sleep 1
		if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then
			NHAFL_warning
			ssh_args="$ssh_args $ssh_NHAFL"
			did_ssh_NHAFL=1
		fi
		sleep 1
		if [ "X$sproxy1" = "X" ]; then
			u=""
			if echo "$host" | grep '@' > /dev/null; then
				u=`echo "$host" | sed -e 's/@.*$/@/'`
			fi
			
			proxy="${u}$localhost:$nd"
		else
			proxy="${sproxy1_user}$localhost:$nd"
		fi
		localhost_extra=".2"
		if [ "X$sproxy_rest" != "X" ]; then
			proxy="$proxy,$sproxy_rest"
		fi
		Kecho proxy=$proxy
	fi

	if echo "$proxy" | grep "," > /dev/null; then

		proxy1=`echo "$proxy" | awk -F, '{print $1}'`
		proxy2=`echo "$proxy" | awk -F, '{print $2}'`

		# user1@gw1.com:port1,user2@ws2:port2
		ssh_host1=`echo "$proxy1" | awk -F: '{print $1}'`
		ssh_port1=`echo "$proxy1" | awk -F: '{print $2}'`
		if [ "X$ssh_port1" != "X" ]; then
			ssh_port1="-p $ssh_port1"
		fi
		ssh_host2=`echo "$proxy2" | awk -F: '{print $1}'`
		ssh_user2=`echo "$ssh_host2" | awk -F@ '{print $1}'`
		ssh_host2=`echo "$ssh_host2" | awk -F@ '{print $2}'`
		if [ "X$ssh_host2" = "X" ]; then
			ssh_host2=$ssh_user2
			ssh_user2=""
		else
			ssh_user2="${ssh_user2}@"
		fi
		ssh_port2=`echo "$proxy2" | awk -F: '{print $2}'`
		if [ "X$ssh_port2" = "X" ]; then
			ssh_port2="22"
		fi
		proxport=`findfree 3500`
		if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then
			NHAFL_warning
			did_ssh_NHAFL=1
			sleep 1
		fi
		echo
		echo "Running 1st ssh proxy:"
		ukhf=""
		if [ "X$ssh_UKHF" != "X" ]; then 
			ukhf="$ssh_UKHF$localhost_extra"
		fi
		if echo "$ssh_host1" | grep '%' > /dev/null; then
			uath=`space_expand "$ssh_host1"`
		else
			uath="$ssh_host1"
		fi
		echo "$ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 \"$uath\" \"sleep 30\""
		echo ""
		      $ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 "$uath" "sleep 30"
		ssh_args="$ssh_args $ssh_NHAFL"
		sleep 1
		stty sane
		proxy="${ssh_user2}$localhost:$proxport"
	fi

	if [ "X$proxy" != "X" ]; then
		ssh_port=`echo "$proxy" | awk -F: '{print $2}'`
		if [ "X$ssh_port" = "X" ]; then
			ssh_port="22"
		fi
		ssh_host=`echo "$proxy" | awk -F: '{print $1}'`
		vnc_host="$host"
	fi

	echo ""
	echo "Running ssh:"
	sz=`echo "$ssh_cmd" | wc -c`
	if [ "$sz" -gt 300 ]; then
		info="..."
	else
		info="$ssh_cmd"
	fi

	C=""
	if [ "X$SS_VNCVIEWER_USE_C" != "X" ]; then
		C="-C"
	fi

	getport=""
	teeport=""
	if echo "$ssh_cmd" | egrep "(PORT=|P=) " > /dev/null; then
		getport=1
		if echo "$ssh_cmd" | egrep "P= " > /dev/null; then
			teeport=1
		fi

		PORT=""
		ssh_cmd=`echo "$ssh_cmd" | sed -e 's/PORT=[ 	]*//' -e 's/P=//'`
		SSVNC_NO_ENC_WARN=1
		if [ "X$use_sshssl" = "X" ]; then
			direct_connect=1
		fi
	fi
	if [ "X$getport" != "X" ]; then
		ssh_redir="-D ${use}"
	elif [ "X$reverse" = "X" ]; then
		ssh_redir="-L ${use}:${vnc_host}:${port}"
	else
		ssh_redir="-R ${port}:${vnc_host}:${use}"
	fi
	pmark=`sh -c 'echo $$'`

	# the -t option actually speeds up typing response via VNC!!
	if [ "X$ssh_port" = "X22" ]; then
		ssh_port=""
	else
		ssh_port="-p $ssh_port"
	fi

	if echo "$ssh_host" | grep '%' > /dev/null; then
		uath=`space_expand "$ssh_host"`
	else
		uath="$ssh_host"
	fi
	if [ "X$SS_VNCVIEWER_SSH_ONLY" != "X" ]; then
		echo "$ssh -x $ssh_port $targ $C $ssh_args \"$uath\" \"$info\""
		echo ""
		$ssh -x $ssh_port $targ $C $ssh_args "$uath" "$ssh_cmd"
		exit $?

	elif [ "X$SS_VNCVIEWER_NO_F" != "X" ]; then
		echo "$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\""
		echo ""
		$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd"
		rc=$?

	elif [ "X$getport" != "X" ]; then
		tport=/tmp/ss_vncviewer_tport${RANDOM}.$$
		tport=`mytmp "$tport"`
		tport2=/tmp/ss_vncviewer_tport2${RANDOM}.$$
		tport2=`mytmp "$tport2"`

		if [ "X$rsh" != "X1" ]; then
			if echo "$ssh_cmd" | grep "sudo " > /dev/null; then
				echo ""
				echo "Initial ssh with 'sudo id' to prime sudo so hopefully the next one"
				echo "will require no password..."
				echo ""
				targ="-t"
				$ssh -x $ssh_port $targ $ssh_args "$uath" "sudo id; tty"
				echo ""
			fi
			echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\""
			echo ""
			$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" > $tport 2> $tport2
			if [ "X$teeport" = "X1" ]; then
				tail -f $tport  1>&2 &
				tail_pid=$!
				tail -f $tport2 1>&2 &
				tail_pid2=$!
			fi
			rc=$?
		else
			rsh_setup
			echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\""
			echo ""
			rsh $ul "$ssh_host" "$ssh_cmd" > $tport &
			sleep 1
			rc=0
		fi

		if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
			echo "sleep $SSVNC_EXTRA_SLEEP"
			sleep $SSVNC_EXTRA_SLEEP
		fi

		stty sane
		i=0
		if type perl > /dev/null 2>&1; then
			imax=50
			sleepit="perl -e 'select(undef, undef, undef, 0.20)'"
		else
			imax=10
			sleepit="sleep 1"
		fi
		while [ $i -lt $imax ]; do
			#echo $sleepit
			eval $sleepit
			PORT=`grep "^PORT=" $tport | tr '\r' ' ' | head -n 1 | sed -e 's/PORT=//' -e 's/\r//g' -e 's/ *$//'`
			if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then
				break
			fi
			vnss=`sed -e 's/\r//g' $tport $tport2 | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1 | awk '{print $NF}'`
			if [ "X$vnss" != "X" ]; then
				PORT=`echo "$vnss" | awk -F: '{print $2}'`
				if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then
					if [ $PORT -lt 100 ]; then
						PORT=`expr $PORT + 5900`
					fi
				fi
				if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then
					vnss=`sed -e 's/\r//g' $tport | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1`
					echo "vncserver string: $vnss" 1>&2
					break
				fi
			fi
			i=`expr $i + 1`
		done

		echo "found: PORT='$PORT'" 1>&2
		lh6=""
		if [ "X$SSVNC_PORT_IPV6" != "X" ]; then
			lh6=1
		elif egrep 'Info: listening on IPv6 only|Info: listening only on IPv6' $tport > /dev/null; then
			lh6=1
		fi
		if [ "X$lh6" = "X1" ]; then
			echo "set SOCKS5 localhost to ::1" 1>&2
		fi
		rm -f $tport $tport2
		if [ "X$rsh" = "X1" ]; then
			rsh_viewer "$@"
			exit $?
		fi
		PPROXY_SOCKS=5
		if [ "X$SSVNC_SOCKS5" != "X" ]; then
			PPROXY_SOCKS=5
		elif [ "X$SSVNC_SOCKS4" != "X" ]; then
			PPROXY_SOCKS=1
		fi
		export PPROXY_SOCKS
		if [ "X$lh6" = "X" ]; then
			host="$localhost"
		else
			host="::1"
		fi
		port="$PORT"
		proxy="$localhost:$use"

	else
		if [ "X$rsh" != "X1" ]; then
			echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\""
			echo ""
			$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd"
			rc=$?
		else
			rsh_setup
			echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\""
			echo ""
			rsh $ul "$ssh_host" "$ssh_cmd" &
			sleep 1
			PORT=$port
			rsh_viewer "$@"
			exit $?
		fi
	fi

	if [ "$rc" != "0" ]; then
		echo ""
		echo "ssh to \"$uath\" failed."
		exit 1
	fi
	stty sane

	c=0
	pssh=""
	while [ $c -lt 40 ]
	do
		p=`expr $pmark + $c`
		pout=`ps -p "$p" 2>/dev/null | grep -v '^[ 	]*PID' | sed -e 's/-L.*$//' -e 's/-x .*$//'`
		if echo "$pout" | grep "ssh" > /dev/null; then
			if echo "$pout" | egrep -i 'ssh.*(-add|-agent|-ask|-keygen|-argv0|vnc)' >/dev/null; then
				:
			elif echo "$pout" | egrep -i 'scp|sshd' >/dev/null; then
				:
			else
				pssh=$p
				break
			fi
		fi
		c=`expr $c + 1`
	done
	if [ "X$getport" != "X" ]; then
		:
	elif [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ] ; then
		sleep 2
	elif [ "X$ssh_cmd" = "Xsleep $ssh_sleep" ] ; then
		#echo T sleep 1
		sleep 1
	elif echo "$ssh_cmd" | grep '^sleep ' >/dev/null; then
		#echo T sleep 2
		sleep 2
	else
		# let any command get started a bit.
		#echo T sleep 5
		sleep 5
	fi
	echo ""
	#reset
	stty sane
	if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
		echo "sleep $SSVNC_EXTRA_SLEEP"
		sleep $SSVNC_EXTRA_SLEEP
	fi
	echo "ssh_pid='$pssh'"; echo
	if [ "X$use_sshssl" = "X" -a "X$getport" = "X" ]; then
		echo "Running viewer:"

		trap "final" 0 2 15
		if [ "X$reverse" = "X" ]; then
			echo "$VNCVIEWERCMD" "$@" $localhost:$N
			echo ""
			$VNCVIEWERCMD "$@" $localhost:$N
			if [ $? != 0 ]; then
				echo "vncviewer command failed: $?"
				if [ "X$secondtry" = "X1" ]; then
					sleep 2
					$VNCVIEWERCMD "$@" $localhost:$N
				fi
			fi
		else
			echo ""
			echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode."
			echo ""
			N2=$N
			if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then
				N2=`echo "$N2" | sed -e 's/://g'`
				if [ $N2 -le 200 ]; then
					N2=`expr $N2 + 5500`
				fi
			fi
			echo "$VNCVIEWERCMD" "$@" -listen $N2
			echo ""
			$VNCVIEWERCMD "$@" -listen $N2
		fi

		exit $?
	else
		use2=`findfree 5960`
		host0=$host
		port0=$port
		host=$localhost
		port=$use
		use=$use2
		N=`expr $use - 5900`
		if [ "X$getport" != "X" ]; then
			host="$host0"
			port="$port0"
		else
			proxy=""
		fi
		if [ "X$ssh_vencrypt_proxy" != "X" ]; then
			ssh_vencrypt_proxy="vencrypt://$host:$port"
			if [ "X$proxy" = "X" ]; then
				proxy=$ssh_vencrypt_proxy
			else
				proxy="$proxy,$ssh_vencrypt_proxy"
			fi
			Kecho "proxy_now=$proxy"
			unset PPROXY_LISTEN
		fi
	fi
fi

if [ "X$stunnel_set_here" = "X1" -a "X$showcert" = "X" ]; then
	if type $STUNNEL > /dev/null 2>&1; then
		:
	else
		echo ""
		echo "***************************************************************"
		echo "** Problem finding the Stunnel command '$STUNNEL': **"
		echo ""
		type $STUNNEL
		echo ""
		echo "** Perhaps you need to install the stunnel/stunnel4 package. **"
		echo "***************************************************************"
		echo ""
		sleep 5
	fi
fi

# create the stunnel config file:
if [ "X$verify" != "X" ]; then
	if [ -d $verify ]; then
		verify="CApath = $verify"
	else
		verify="CAfile = $verify"
	fi
	verify="$verify
verify = 2"
fi
if [ "X$SSVNC_STUNNEL_VERIFY3" != "X" ]; then
	verify=`echo "$verify" | sed -e 's/verify = 2/verify = 3/'`
fi
if [ "X$mycert" != "X" ]; then
	cert="cert = $mycert"
fi
if [ "X$crl" != "X" ]; then
	if [ -d $crl ]; then
		crl="CRLpath = $crl"
	else
		crl="CRLfile = $crl"
	fi
fi

if [ "X$showcert" = "X1" ]; then
	if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then
		:
	elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then
		:
	elif [ "X$ipv6" = "X1" -a "X$proxy" = "X" ]; then
		proxy="ipv6://$host:$port"
	fi
fi

if [ "X$direct_connect" != "X" -a "X$STUNNEL_LISTEN" != "X" ]; then
	proxy=reverse_direct
fi

ptmp=""
if [ "X$proxy" != "X" ]; then
	ptmp="/tmp/ss_vncviewer${RANDOM}.$$.pl"
	ptmp=`mytmp "$ptmp"`
	PPROXY_REMOVE=1; export PPROXY_REMOVE
	pcode "$ptmp"
	if [ "X$showcert" != "X1" -a "X$direct_connect" = "X" ]; then
		if uname | egrep 'Darwin|SunOS' >/dev/null; then
			vout=`echo "$proxy" | grep -i vencrypt`
			if [ "X$vout" != "X" -a "X$reverse" = "X1" ]; then
				# need to exec for reverse vencrypt
				connect="exec = $ptmp"
			else
				# on mac and solaris we need to listen on socket instead of stdio:
				nd=`findfree 6700`
				PPROXY_LISTEN=$nd
				export PPROXY_LISTEN
				if [ "X$reverse" = "X" ]; then
					$ptmp &
				fi
				sleep 2
				host="$localhost"
				port="$nd"
				connect="connect = $localhost:$nd"
			fi
		else
			# otherwise on unix we can exec it:
			connect="exec = $ptmp"
		fi
	else
		connect="exec = $ptmp"
	fi
else
	connect="connect = $host:$port"
fi

# handle showcert case:
#
if [ "X$showcert" = "X1" ]; then
	if [ "X$proxy" != "X" ]; then
		PPROXY_LISTEN=$use
		export PPROXY_LISTEN
		if [ "X$SS_DEBUG" != "X" ]; then
			$ptmp &
		else
			$ptmp 2>/dev/null &
		fi
		sleep 1
		more_sleep=1
		if uname | grep Linux > /dev/null; then
			if netstat -ant | grep LISTEN | grep "127.0.0.1:$use" > /dev/null; then
				more_sleep=""
			fi
		elif uname | grep SunOS > /dev/null; then
			if netstat -an -f inet -P tcp | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then
				more_sleep=""
			fi
		elif uname | egrep -i 'bsd|darwin' > /dev/null; then
			if netstat -ant -f inet | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then
				more_sleep=""
			fi
		fi
		if [ "X$more_sleep" = "X1" ]; then
			sleep 1
		fi
		host="$localhost"
		port="$use"
	fi
	cipher_args=""
	if [ "X$ciphers" != "X" ]; then
		cipher_args=`echo "$ciphers" | sed -e 's/ciphers=/-cipher /'`
	fi
	if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then
		:
	elif type openssl > /dev/null 2>&1; then
		:
	else
		echo ""
		echo "********************************************************"
		echo "** Problem finding the OpenSSL command 'openssl': **" 
		echo ""
		type openssl 2>&1
		echo ""
		echo "** Perhaps you need to install the 'openssl' package. **"
		echo "********************************************************"
		echo ""
	fi
	#echo "openssl s_client $cipher_args -connect $host:$port" 
	if [ "X$reverse" = "X" ]; then
		if type host > /dev/null 2>/dev/null; then
			host $host >/dev/null 2>&1
			host $host >/dev/null 2>&1
		fi
		timeout=15
		if [ "X$SSVNC_FETCH_TIMEOUT" != "X" ]; then
			timeout=$SSVNC_FETCH_TIMEOUT
		fi
		if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then
			if type pkill >/dev/null 2>&1; then
				(sleep $timeout; if kill -0 $$; then pkill -TERM -f "ultravnc_dsm_helper.*$host.*$port"; fi) >/dev/null 2>&1 &
			fi
			ultravnc_dsm_helper showcert $host:$port 2>&1
		else
			if type pkill >/dev/null 2>&1; then
				(sleep $timeout; if kill -0 $$; then pkill -TERM -f "openssl.*s_client.*$host.*$port"; fi) >/dev/null 2>&1 &
			fi
			openssl s_client $cipher_args -prexit -connect $host:$port 2>&1 < /dev/null
		fi
		rc=$?
	else
		tcert=""
		if [ "X$mycert" = "X" ]; then
			tcert=`make_tcert`
			cert_args="-cert $tcert -CAfile $tcert"
		else
			cert_args="-cert $mycert -CAfile $mycert"
		fi
		tmp_out=/tmp/showcert_out${RANDOM}.$$
		tmp_out=`mytmp "$tmp_out"`
		tmp_err=/tmp/showcert_err${RANDOM}.$$
		tmp_err=`mytmp "$tmp_err"`

		#echo "openssl s_server $cipher_args $cert_args -accept $port -verify 2 > $tmp_out 2> $tmp_err" 1>&2

		# assume we have perl:
		check_perl perl

		perl -e "
			\$p = open(O, \"|openssl s_server $cipher_args $cert_args -accept $port -verify 2 1>$tmp_out 2> $tmp_err\");
			exit 1 unless \$p;
			while (1) {
				sleep 1;
				if (!open(F, \"<$tmp_out\")) {
					kill \$p;
					exit 1;
				}
				while (<F>) {
					if (/RFB 00/) {
						fsleep(0.25);
						print O \"RFB 000.000\\n\";
						fsleep(1.00);
						kill \$p;
						fsleep(0.25);
						exit 0;
					}
				}
				close F;
			}
			sub fsleep {
				select(undef, undef, undef, shift);
			}
		";

		echo ""
		cat $tmp_out
		echo ""
		echo "----2----"
		cat $tmp_err
		if grep BEGIN.CERTIFICATE $tmp_out >/dev/null; then
			rc=0
		else
			rc=1
		fi

		rm -f $tmp_out $tmp_err
	fi
	if [ "X$SSVNC_PREDIGESTED_HANDSHAKE" != "X" ]; then
		rm -f $SSVNC_PREDIGESTED_HANDSHAKE
	fi
	if [ "X$SSVNC_SHOWCERT_EXIT_0" = "X1" ]; then
		exit 0
	else
		exit $rc
	fi
fi

# handle direct connect case:
#
if [ "X$direct_connect" != "X" ]; then
	if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
		SSVNC_NO_ENC_WARN=1
		echo ""
		echo "Using UltraVNC DSM Plugin key for encryption:"
		echo ""
		ustr=`echo "$SSVNC_ULTRA_DSM" | sed -e 's/pw=[^ ]*/pw=******/g'`
		echo "  $ustr PORT HOST:PORT"
		echo ""
	elif [ "X$getport" = "X" ]; then
		echo ""
		echo "Running viewer for direct connection:"
		if echo X"$@" | grep chatonly > /dev/null; then
			:
		else
			echo ""
			echo "** WARNING: THERE WILL BE NO SSL OR SSH ENCRYPTION **"
			echo ""
		fi
	fi
	x=""
	if [ "X$SSVNC_NO_ENC_WARN" != "X" ]; then
		if [ "X$getport" = "X" ]; then
			sleep 1
		fi
	elif type printf > /dev/null 2>&1; then
		printf  "Are you sure you want to continue? [y]/n "
		read x
	else
		echo -n "Are you sure you want to continue? [y]/n "
		read x
	fi
	if [ "X$x" = "Xn" ]; then
		exit 1
	fi
	echo ""
	if [ "X$ptmp" != "X" ]; then
		if [ "X$reverse" = "X" ]; then
			PPROXY_LISTEN=$use
			export PPROXY_LISTEN
		else
			if [ "X$proxy" = "Xreverse_direct" ]; then
				PPROXY_LISTEN="$STUNNEL_LISTEN:`expr 5500 + $disp`"
				PPROXY_DEST="$localhost:$use"
				PPROXY_PROXY="ipv6://$localhost:$use"	# not always ipv6..
				export PPROXY_LISTEN PPROXY_DEST PPROXY_PROXY
				pps=1
			else
				PPROXY_REVERSE="$localhost:$use"
				export PPROXY_LISTEN
				pps=3
			fi
			if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then
				PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself.${RANDOM}.$$"`
				export PPROXY_LOOP_THYSELF
				pps=2
			fi
			if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
				pps=`expr $pps + $SSVNC_EXTRA_SLEEP`
			fi
			PPROXY_SLEEP=$pps; export PPROXY_SLEEP;
			PPROXY_KILLPID=+1; export PPROXY_KILLPID;
		fi

		$ptmp &

		if [ "X$reverse" = "X" ]; then
			#sleep 2
			#echo T sleep 1
			sleep 1
		fi
		host="$localhost"
		disp="$N"
		port=`expr $disp + 5900`
	fi
	if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
		echo "T sleep $SSVNC_EXTRA_SLEEP"
		sleep $SSVNC_EXTRA_SLEEP
	fi
	if [ "X$reverse" = "X" ]; then
		hostdisp="$host:$disp"
		if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
			if [ "X$SSVNC_USE_OURS" = "X1" ]; then
				hostdisp="exec=$SSVNC_ULTRA_DSM 0 $host:$port"
			else
				pf=`findfree 5970`
				cmd="$SSVNC_ULTRA_DSM -$pf $host:$port"
				pf=`expr $pf - 5900`
				hostdisp="$localhost:$pf"
				ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'`
				echo "Running:"
				echo
				echo "$ustr &"
				echo
				$cmd &
				dsm_pid=$!
				sleep 2
			fi
		fi
		hostdisp2=`echo "$hostdisp" | sed -e 's/pw=[^ ]*/pw=******/g'`
		echo "$VNCVIEWERCMD" "$@" "$hostdisp2"
		trap "final" 0 2 15
		echo ""
		$VNCVIEWERCMD "$@" "$hostdisp"
		if [ $? != 0 ]; then
			echo "vncviewer command failed: $?"
			if [ "X$secondtry" = "X1" ]; then
				sleep 2
				$VNCVIEWERCMD "$@" "$hostdisp"
			fi
		fi
	else
		echo ""
		echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode."
		echo ""
		trap "final" 0 2 15
		if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then
			if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then
				echo "NOTE: The ultravnc_dsm_helper only runs once.  So after the first LISTEN"
				echo "      ends you must restart the Listening mode.  You may also need to"
				echo "      Press Ctrl-C to stop the viewer and restart for another connection."
				echo ""
			fi
			#SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE
			VNCVIEWER_LISTEN_LOCALHOST=1
			export VNCVIEWER_LISTEN_LOCALHOST
			dport=`expr 5500 + $disp`
			cmd="$SSVNC_ULTRA_DSM $dport $localhost:$use"
			ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'`
			echo "Running:"
			echo
			echo "$ustr &"
			echo
			if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then
				$cmd &
				dsm_pid=$!
			else
				while [ 1 ]; do $cmd; sleep 1; done &
				dsm_pid=$!
			fi
			sleep 2
			disp=$use
			if [ $disp -ge 5500 ]; then
				disp=`expr $disp - 5500`
			fi
		fi
		disp2=$disp
		if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then
			disp2=`echo "$disp2" | sed -e 's/://g'`
			if [ $disp2 -le 200 ]; then
				disp2=`expr $disp2 + 5500`
			fi
		fi
		echo "$VNCVIEWERCMD" "$@" -listen $disp2
		echo ""
		$VNCVIEWERCMD "$@" -listen $disp2
		if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then
			rm -f $PPROXY_LOOP_THYSELF
		fi
	fi
	exit $?
fi

tmp_cfg=/tmp/ss_vncviewer${RANDOM}.$$
tmp_cfg=`mytmp "$tmp_cfg"`

stunnel_exec=""
if [ "X$SSVNC_USE_OURS" != "X1" ]; then
	:
elif echo $STUNNEL_EXTRA_SVC_OPTS | grep '#stunnel-exec' > /dev/null; then
	stunnel_exec="#"
fi

if [ "X$reverse" = "X" ]; then

	if echo "$proxy" | grep "^repeater://" > /dev/null; then
		if [ "X$cert" = "XBUILTIN" ]; then
			ttcert=`make_tcert`
			cert="cert = $ttcert"
		fi
		# Note for listen mode, an empty cert will cause stunnel to fail.
		# The ssvnc gui will have already taken care of this.
	fi

	cat > "$tmp_cfg" <<END
foreground = yes
pid =
client = yes
debug = $stunnel_debug
$ciphers
$STUNNEL_EXTRA_OPTS
$STUNNEL_EXTRA_OPTS_USER
$cert
$crl
$verify

${stunnel_exec}[vnc_stunnel]
${stunnel_exec}accept = $localhost:$use
$connect
$STUNNEL_EXTRA_SVC_OPTS
$STUNNEL_EXTRA_SVC_OPTS_USER

END

else
	# REVERSE case:

	stunnel_exec=""	# doesn't work for listening.

	p2=`expr 5500 + $N`
	connect="connect = $localhost:$p2"
	if [ "X$cert" = "XBUILTIN" ]; then
		ttcert=`make_tcert`
		cert="cert = $ttcert"
	fi
	# Note for listen mode, an empty cert will cause stunnel to fail.
	# The ssvnc gui will have already taken care of this.


	hloc=""
	if [ "X$use_ssh" = "X1" ]; then
		hloc="$localhost:"
	elif [ "X$STUNNEL_LISTEN" != "X" ]; then
		hloc="$STUNNEL_LISTEN:"
	fi
	if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then
		hloc="$localhost:"
		pv=`findfree 5570`
		proxy="vencrypt:$pv:$port"
		port=$pv
		if [ "X$anondh_set" = "X1" ]; then
			# not needed for ANONDH in this mode
			#ciphers="ciphers = ADH:@STRENGTH"
			:
		fi
	fi
	cat > "$tmp_cfg" <<END
foreground = yes
pid =
client = no
debug = $stunnel_debug
$ciphers
$STUNNEL_EXTRA_OPTS
$STUNNEL_EXTRA_OPTS_USER
$cert
$crl
$verify

[vnc_stunnel]
accept = $hloc$port
$connect
$STUNNEL_EXTRA_SVC_OPTS
$STUNNEL_EXTRA_SVC_OPTS_USER

END

fi

echo ""
echo "Using this stunnel configuration:"
echo ""
cat "$tmp_cfg" | uniq
echo ""
if egrep -i '^[ 	]*(CApath|CAfile) =' "$tmp_cfg" > /dev/null ; then
	:
else
	echo "** WARNING: THE STUNNEL CONFIG HAS NO SERVER CERTIFICATE SPECIFIED       **"
	echo "** WARNING: (the CApath or CAfile stunnel option) THE VNC SERVER WILL    **"
	echo "** WARNING: NOT BE AUTHENTICATED. A MAN-IN-THE-MIDDLE ATTACK IS POSSIBLE **"
	echo ""
fi
sleep 1

if [ "X$stunnel_exec" = "X" ]; then
	echo ""
	echo "Running stunnel:"
	echo "$STUNNEL $tmp_cfg"
	st=`echo "$STUNNEL" | awk '{print $1}'`
	$st -help > /dev/null 2>&1
	$STUNNEL "$tmp_cfg" < /dev/tty > /dev/tty &
	stunnel_pid=$!
	echo ""

	# pause here to let the user supply a possible passphrase for the
	# mycert key:
	if [ "X$mycert" != "X" ]; then
		nsl=10
		dsl=0
		if [ ! -f $mycert ]; then
			dsl=0
		elif grep -i 'Proc-Type.*ENCRYPTED' "$mycert" > /dev/null 2>/dev/null; then
			dsl=1
		fi
		if [ "X$dsl" = "X1" ]; then
			echo ""
			echo "(** pausing $nsl secs for possible certificate passphrase dialog **)"
			echo ""
			sleep $nsl
			echo "(** done pausing for passphrase **)"
			echo ""
		fi
	fi
	#echo T sleep 1
	sleep 1
	rm -f "$tmp_cfg"
fi


echo ""
if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then
	echo "sleep $SSVNC_EXTRA_SLEEP"
	sleep $SSVNC_EXTRA_SLEEP
fi

if [ "X$reverse" = "X" ]; then
	if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then
		port1=`expr 5900 + $N`		# stunnel port
		port2=`findfree 5970`		# bridge port (viewer connects to it.)
		N=`expr $port2 - 5900`
		env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="$port2,$port1" $ptmp &
		sleep 1
	fi
	echo "Running viewer:"
	vnc_hp=$localhost:$N
	if [ "X$stunnel_exec" != "X" ]; then
		vnc_hp="exec=$STUNNEL $tmp_cfg"
	fi
	echo "$VNCVIEWERCMD" "$@" "$vnc_hp"
	trap "final" 0 2 15
	echo ""
	$VNCVIEWERCMD "$@" "$vnc_hp"
	if [ $? != 0 ]; then
		echo "vncviewer command failed: $?"
		if [ "X$secondtry" = "X1" ]; then
			sleep 2
			$VNCVIEWERCMD "$@" "$vnc_hp"
		fi
	fi
else
	echo "Running viewer:"
	echo ""
	echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode."
	echo ""
	trap "final" 0 2 15
	N2=$N
	N2_trim=`echo "$N2" | sed -e 's/://g'`
	if [ $N2_trim -le 200 ]; then
		N2_trim=`expr $N2_trim + 5500`
	fi
	if [ "X$proxy" != "X" ]; then
		if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then
			pstunnel=`echo "$proxy" | awk -F: '{print $2}'`
			plisten=`echo "$proxy" | awk -F: '{print $3}'`
			IF=INADDR_ANY
			if [ "X$STUNNEL_LISTEN" != "X" ]; then
				IF=$STUNNEL_LISTEN
			fi
			PPROXY_VENCRYPT_REVERSE=1; export PPROXY_VENCRYPT_REVERSE
			PPROXY_LISTEN="$IF:$plisten"; export PPROXY_LISTEN
			PPROXY_PROXY="vencrypt://$localhost:$pstunnel"; export PPROXY_PROXY
			PPROXY_DEST="$localhost:$pstunnel"; export PPROXY_DEST
			STUNNEL_ONCE=1; export STUNNEL_ONCE
			STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS
			if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then
				port1=`expr 5500 + $N2`
				port2=`findfree 5580`
				N2=`expr $port2 - 5500`
				N2_trim=`echo "$N2" | sed -e 's/://g'`
				if [ $N2_trim -le 200 ]; then
					N2_trim=`expr $N2_trim + 5500`
				fi
				if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then
					PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself1.${RANDOM}.$$"`
					export PPROXY_LOOP_THYSELF
					PPROXY_LOOP_THYSELF0=$PPROXY_LOOP_THYSELF
				fi
				env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="-$port1,$port2" $ptmp &
				sleep 1
			fi
		else
			PPROXY_REVERSE="$localhost:$port"; export PPROXY_REVERSE
			PPROXY_SLEEP=1; export PPROXY_SLEEP;
		fi
		PPROXY_KILLPID=+1; export PPROXY_KILLPID;
		if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then
			PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself2.${RANDOM}.$$"`
			export PPROXY_LOOP_THYSELF
		fi
		$ptmp &
		# Important to have no extra pids generated between here and VNCVIEWERCMD
	fi
	if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then
		N2=$N2_trim
	fi
	echo "$VNCVIEWERCMD" "$@" -listen $N2
	echo ""
	$VNCVIEWERCMD "$@" -listen $N2

	if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then
		rm -f $PPROXY_LOOP_THYSELF
	fi
	if [ "X$PPROXY_LOOP_THYSELF0" != "X" ]; then
		rm -f $PPROXY_LOOP_THYSELF0
	fi
fi

sleep 1
