#!/usr/bin/wish

proc check_callback {} {
	global debug
	if {$debug} {
		puts stderr "."
	}
	check_closed
	after 1000 check_callback 
}

proc getout {} {
	global client_fh server_fh

	set delay 50
	catch {flush $client_fh}
	after $delay
	catch {close $client_fh}
	set client_fh ""
	after $delay
	catch {flush $server_fh}
	after $delay
	catch {close $server_fh}
	set server_fh ""
	after $delay

	global bmesg_cnt
	if [info exists bmesg_cnt] {
		catch {tkwait window .bmesg$bmesg_cnt}
	}
	destroy .
	exit
}

proc check_closed {} {
	global got_connection debug
	global client_fh server_fh

	if {! $got_connection} {
		return
	}
	if {$client_fh != ""} {
		set ef ""
		catch {set ef [eof $client_fh]}
		if {$ef == 1} {
			if {$debug} {
				puts stderr "client_fh EOF"
			}
			getout
		}
	}
	if {$server_fh != ""} {
		set ef ""
		catch {set ef [eof $server_fh]}
		if {$ef == 1} {
			if {$debug} {
				puts stderr "server_fh EOF"
			}
			getout
		}
	}
}

proc xfer_in_to_out {} {
	global client_fh server_fh debug do_bridge
	if {$client_fh != "" && ![eof $client_fh]} {
		set ef ""
		catch {set ef [eof $client_fh]}
		if {$ef == 0} {
			set str ""
			catch {set str [read $client_fh 4096]}
			if {$debug} {
				#puts stderr "xfer_in_to_out: $str"
				puts stderr "xfer_in_to_out: [string length $str]"
			}
			if {$server_fh != "" && $str != ""} {
				catch {puts -nonewline $server_fh $str}
				catch {flush $server_fh}
			}
		}
	}
	check_closed
}

proc xfer_out_to_in {} {
	global client_fh server_fh debug do_bridge
	if {$server_fh != ""} {
		set ef ""
		catch {set ef [eof $server_fh]}
		if {$ef == 0} {
			set str ""
			catch {set str [read $server_fh 4096]}
			if {$debug} {
				#puts stderr "xfer_out_to_in: $str"
				puts stderr "xfer_out_to_in: [string length $str]"
			}
			if {$client_fh != "" && $str != ""} {
				catch {puts -nonewline $client_fh $str}
				catch {flush $client_fh}
			}
		}
	}
	check_closed
}

proc bmesg {msg} {
	global env
	if {! [info exists env(BMESG)]} {
		return
	}
	if {$env(BMESG) == 0} {
		return
	}

	global bmesg_cnt
	if {! [info exists bmesg_cnt]} {
		set bmesg_cnt 0
	}
	incr bmesg_cnt
	set w .bmesg$bmesg_cnt
	catch {destroy $w}
	toplevel $w
	label $w.l -width 70 -text "$msg"
	pack $w.l
	update
	if {$env(BMESG) > 1} {
		for {set i 0} {$i < $env(BMESG)} {incr i} {
			after 1000
			update
		}
	}
}

proc do_connect_http {sock hostport which} {
	global debug cur_proxy
	set con ""
	append con "CONNECT $hostport HTTP/1.1\r\n"
	append con "Host: $hostport\r\n"
	append con "Connection: close\r\n\r\n"

	puts stderr "pxy=$which CONNECT $hostport HTTP/1.1 via $cur_proxy"
	bmesg "H: $which CONNECT $hostport HTTP/1.1 $cur_proxy";

	puts -nonewline $sock $con
	flush $sock

	set r ""
	set cnt 0
	while {1} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after 20
		}
		append r $c
		if {[regexp "\r\n\r\n" $r] || [regexp "a--no--\n\n" $r]} {
			break
		}
		if {$cnt > 30000} {
			break
		}
	}
	if {! [regexp {HTTP/.* 200} $r]} {
		puts stderr "did not find HTTP 200 #1"
		destroy .
		exit 1
	}
}

proc do_connect_socks4 {sock hostport which} {
	global debug cur_proxy

	set host ""
	set port ""
	if [regexp {^(.*):([0-9][0-9]*)$} $hostport mvar host port] {
		;
	} else {
		puts stderr "could not parse host:port $hostport"
		destroy .
		exit 1
	}

	set i1 ""
	set i2 ""
	set i3 ""
	set i4 ""

	set socks4a 0

	if {$host == "localhost" || $host == "127.0.0.1"} {
		set i1 127
		set i2 0
		set i3 0
		set i4 1
		
	} elseif [regexp {^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$} $host] {
		set n [split $host "."]
		set i1 [lindex $n 0]
		set i2 [lindex $n 1]
		set i3 [lindex $n 2]
		set i4 [lindex $n 3]
	} else {
		set i1 0
		set i2 0
		set i3 0
		set i4 3
		
		set socks4a 1
	}

	if {$socks4a} {
		puts stderr "pxy=$which socks4a connection to $host:$port via $cur_proxy"
	} else {
		puts stderr "pxy=$which socks4  connection to $host:$port via $cur_proxy"
	}

	set p1 [binary format ccScccc 4 1 $port $i1 $i2 $i3 $i4]
	set p2 "nobody"
	set p3 [binary format c 0]

	puts -nonewline $sock $p1
	puts -nonewline $sock $p2
	puts -nonewline $sock $p3
	if {$socks4a} {
		puts -nonewline $sock $host
		puts -nonewline $sock $p3
	}
	flush $sock

	set r ""; set s ""; set i 0; set cnt 0
	set ok 1
	while {$cnt < 30000 && $i < 8} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after 20
			continue
		}
		
		binary scan $c c s
		if {$i == 0 && $s != 0} {
			puts stderr "socks4: $i - $s"
			set ok 0
		}
		if {$i == 1 && $s != 90} {
			puts stderr "socks4: $i - $s"
			set ok 0
		}
		set r "$r,$s"
		incr i
	}
	if {! $ok} {
		puts stderr "socks4 failure: $r"
		destroy .
		exit 1
	}
}

proc do_connect_socks5 {sock hostport which} {
	global debug cur_proxy

	set host ""
	set port ""
	if [regexp {^(.*):([0-9][0-9]*)$} $hostport mvar host port] {
		;
	} else {
		puts stderr "could not parse host:port $hostport"
		destroy .
		exit 1
	}

	set p1 [binary format ccc 5 1 0]
	puts -nonewline $sock $p1
	flush $sock

	set r ""; set s ""; set i 0; set cnt 0
	set ok 1
	while {$cnt < 30000 && $i < 2} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after 20
			continue
		}
		
		binary scan $c c s
		if {$i == 0 && $s != 5} {
			puts stderr "$i - $s"
			set ok 0
		}
		if {$i == 1 && $s != 0} {
			puts stderr "$i - $s"
			set ok 0
		}
		set r "$r,$s"
		incr i
	}
	if {! $ok} {
		puts stderr "socks5 failure: $r"
		destroy .
		exit 1
	}

	set len [string length $host]
	set p1 [binary format ccccc 5 1 0 3 $len]
	set p2 $host

	set n1 [expr int($port/256)]
	set n2 [expr "$port - $n1 * 256"]
	set p3 [binary format cc $n1 $n2]

	puts stderr "pxy=$which socks5  connection to $host:$port via $cur_proxy"

	puts -nonewline $sock $p1
	puts -nonewline $sock $p2
	puts -nonewline $sock $p3
	flush $sock

	set i1 ""; set i2 ""; set i3 ""; set i4 ""
	set r ""; set s ""; set i 0; set cnt 0
	set ok 1
	while {$cnt < 30000 && $i < 4} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after 20
			continue
		}
		
		binary scan $c c s
		if {$i == 0} {
			set i1 $s
		} elseif {$i == 1} {
			set i2 $s
		} elseif {$i == 2} {
			set i3 $s
		} elseif {$i == 3} {
			set i4 $s
		}
		incr i
	}
	set r "i1=$i1,i2=$i2,i3=$i3,i4=$i4"

	if {$i4 == 1} {
		set n 6
	} elseif {$i4 == 3} {
		set c ""
		for {set i 0} {$i < 1000} {incr i} {
			set c [read $sock 1]
			if {$c == ""} {
				check_closed
				after 20
				continue
			}
			break;
		}
		if {$c == ""} {
			puts stderr "socks5 failure c: $r"
			destroy .
			exit 1
		}
		binary scan $c c s
		set n [expr $s + 2] 
	} elseif {$i4 == 4} {
		set n 18 
	} else {
		puts stderr "socks5 failure x: $r"
		destroy .
		exit 1
	}
	#puts "n=$n --- $r"

	set i 0; set cnt 0
	while {$cnt < 30000 && $i < $n} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after 20
			continue
		}
		incr i
	}
	if {$i1 != 5 || $i2 != 0 || $i3 != 0} {
		puts stderr "socks failure $r"
		destroy .
		exit 1
	}
}

proc do_connect_repeater {sock hostport which repeater} {
	global debug cur_proxy

	# 250 is UltraVNC buffer size.
	set con [binary format a250 $repeater]

	puts stderr "pxy=$which REPEATER $repeater via $cur_proxy"
	bmesg "R: $which CONNECT $hostport | $repeater $cur_proxy";

	puts -nonewline $sock $con
	flush $sock

	set r ""
	set cnt 0
	while {1} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after 20
		}
		append r $c
		if {[string length $r] >= 12} {
			puts stderr "do_connect_repeater: $r"
			break
		}
		if {$cnt > 30000} {
			break
		}
	}
}

proc vread {n sock} {
	set str ""
	set max 3000
	set dt 10
	set i 0
	set cnt 0
	while {$cnt < $max && $i < $n} {
		incr cnt
		set c [read $sock 1]
		if {$c == ""} {
			check_closed
			after $dt
			continue
		}
		incr i
		append str $c
	}
	if {$i != $n} {
		puts stderr "vread failure $n  $i"
		destroy .; exit 1
	}
	return $str
}

proc append_handshake {str} {
	global env
	if [info exists env(SSVNC_PREDIGESTED_HANDSHAKE)] {
		set file $env(SSVNC_PREDIGESTED_HANDSHAKE)
		set fh ""
		catch {set fh [open $file a]}
		if {$fh != ""} {
			puts $fh $str
			catch {close $fh}
		}
	}
}

proc vencrypt_bridge_connection {fh host port} {
	puts stderr "vencrypt_bridge_connection: got connection $fh $host $port"
	bmesg       "vencrypt_bridge_connection: got connection $fh $host $port"
	global viewer_sock
	set viewer_sock $fh
}

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
}


proc get_user_pass {} {
	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	
				catch {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)
	}
	if {$up != ""} {
		return $up
	}

	toplevel .t
	wm title .t {VeNCrypt Viewer Bridge User/Pass}

	global user pass
	set user ""
	set pass ""
	label .t.l -text {SSVNC VeNCrypt Viewer Bridge}

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

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

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

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

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

	button .t.no -text Cancel -command {set user ""; set pass ""; destroy .t}
	button .t.ok -text Done   -command {destroy .t}

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

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

	wm resizable .t 1 0
	wm minsize .t [winfo reqwidth .t] [winfo reqheight .t]

	tkwait window .t
	if {$user == "" || $pass == ""} {
		return ""
	} else {
		return "$user@$pass"
	}
}

proc do_vencrypt_viewer_bridge {listen connect} {
	global env

	#set env(BMESG) 1

	vencrypt_constants

	set backwards 0

	if {! [info exists env(SSVNC_PREDIGESTED_HANDSHAKE)]} {
		puts stderr "no SSVNC_PREDIGESTED_HANDSHAKE filename in environment."	
		destroy .; exit 1
	}
	set handshake $env(SSVNC_PREDIGESTED_HANDSHAKE)
	bmesg $handshake

	if {$listen < 0} {
		set backwards 1
		set listen [expr -$listen]
	}

	# listen on $listen	
	global viewer_sock
	set viewer_sock ""
	set lsock ""
	set rc [catch {set lsock [socket -myaddr 127.0.0.1 -server vencrypt_bridge_connection $listen]}]
	if {$rc != 0} {
		puts stderr "error listening on 127.0.0.1:$listen"	
		destroy .; exit 1
	}
	bmesg "listen on $listen OK"

	# accept
	vwait viewer_sock
	catch {close $lsock}
	fconfigure $viewer_sock -translation binary -blocking 0

	global got_connection
	set got_connection 1

	# connect to $connect
	set server_sock ""
	set rc [catch {set server_sock [socket 127.0.0.1 $connect]}]
	if {$rc != 0} {
		puts stderr "error connecting to 127.0.0.1:$connect"	
		destroy .; exit 1
	}
	bmesg "made connection to $connect"
	fconfigure $server_sock -translation binary -blocking 0

	if {$backwards} {
		puts stderr "reversing roles of viewer and server"
		set t $viewer_sock
		set viewer_sock $server_sock
		set server_sock $t
	}

	# wait for SSVNC_PREDIGESTED_HANDSHAKE "done", put in hash.
	set dt 200
	set slept 0
	set maxwait 20000
	set hs(mode) init 
	while {$slept < $maxwait} {
		after $dt
		set slept [expr $slept + $dt]
		set done 0
		set fh ""
		catch {set fh [open $handshake r]}
		set str ""
		if {$fh != ""} {
			array unset hs 
			while {[gets $fh line] > -1} {
				set line [string trim $line]
				set str "$str$line\n";
				if {$line == "done"} {
					set done 1
				} elseif [regexp {=} $line] {
					set s [split $line "="]
					set key [lindex $s 0]
					set val [lindex $s 1]
					set hs($key) $val
				}
			}
			catch {close $fh}
		}
		if {$done} {
			puts stderr $str
			bmesg "$str"
			break
		}
	}

	catch [file delete $handshake]

	if {! [info exists hs(sectype)]} {
		puts stderr "no hs(sectype) found"	
		destroy .; exit 1
	}

	# read viewer RFB
	if {! [info exists hs(server)]} {
		set hs(server) "RFB 003.008"
	}
	puts -nonewline $viewer_sock "$hs(server)\n"
	flush $viewer_sock
	puts stderr "sent $hs(server) to viewer sock."

	set viewer_rfb [vread 12 $viewer_sock]
	puts stderr "read viewer_rfb $viewer_rfb"

	set viewer_major 3 
	set viewer_minor 8 
	if [regexp {^RFB 003\.0*([0-9][0-9]*)} $viewer_rfb m v] {
		set viewer_minor $v
	}

	if {$hs(sectype) == $rfbSecTypeAnonTls} {
		puts stderr "handling rfbSecTypeAnonTls"
		if {$viewer_major > 3 || $viewer_minor >= 7} {
			puts stderr "viewer >= 3.7, nothing to set up."
		} else {
			puts stderr "viewer <= 3.3, faking things up."
			set t [vread 1 $server_sock]
			binary scan $t c nsectypes
			puts stderr "nsectypes=$nsectypes"
			for {set i 0} {$i < $nsectypes} {incr i} {
				set t [vread 1 $server_sock]
				binary scan $t c st
				puts stderr "   $i: $st"
				set types($st) $i
			}
			set use 1
			if [info exists types(1)] {
				set use 1
			} elseif [info exists types(2)] {
				set use 2
			} else {
				puts stderr "no valid sectypes"	
				destroy .; exit 1
			}
			# this should be MSB:
			vsend_uchar $viewer_sock 0
			vsend_uchar $viewer_sock 0
			vsend_uchar $viewer_sock 0
			vsend_uchar $viewer_sock $use

			vsend_uchar $server_sock $use
			if {$use == 1} {
				set t [vread 4 $server_sock]
			}
		}
	} elseif {$hs(sectype) == $rfbSecTypeVencrypt} {
		puts stderr "handling rfbSecTypeVencrypt"
		if {! [info exists hs(subtype)]} {
			puts stderr "no subtype"	
			destroy .; exit 1
		}
		set fake_type "None"
		set plain 0

		set sub_type $hs(subtype)


		if {$sub_type == $rfbVencryptTlsNone} {
			set fake_type "None"
		} elseif {$sub_type == $rfbVencryptTlsVnc} {
			set fake_type "VncAuth"
		} elseif {$sub_type == $rfbVencryptTlsPlain} {
			set fake_type "None"
			set plain 1
		} elseif {$sub_type == $rfbVencryptX509None} {
			set fake_type "None"
		} elseif {$sub_type == $rfbVencryptX509Vnc} {
			set fake_type "VncAuth"
		} elseif {$sub_type == $rfbVencryptX509Plain} {
			set fake_type "None"
			set plain 1
		}

		if {$plain} {
			set up [get_user_pass]
			if [regexp {@} $up] {
				set user $up
				set pass $up
				regsub {@.*$}  $user "" user
				regsub {^[^@]*@} $pass "" pass
				vsend_uchar $server_sock 0
				vsend_uchar $server_sock 0
				vsend_uchar $server_sock 0
				vsend_uchar $server_sock [string length $user]
				vsend_uchar $server_sock 0
				vsend_uchar $server_sock 0
				vsend_uchar $server_sock 0
				vsend_uchar $server_sock [string length $pass]
				puts stderr "sending VencryptPlain user and pass."
				puts -nonewline $server_sock $user
				puts -nonewline $server_sock $pass
				flush $server_sock
			}
		}
		set ft 0
		if {$fake_type == "None"} {
			set ft 1
		} elseif {$fake_type == "VncAuth"} {
			set ft 2
		} else {
			puts stderr "no valid fake_type"	
			destroy .; exit 1
		}

		if {$viewer_major > 3 || $viewer_minor >= 7} {
			vsend_uchar $viewer_sock 1
			vsend_uchar $viewer_sock $ft
			set t [vread 1 $viewer_sock]
			binary scan $t c cr
			if {$cr != $ft} {
				puts stderr "client selected wront type $cr $ft"	
				destroy .; exit 1
			}
		} else {
			puts stderr "viewer <= 3.3, faking things up."
			# this should be MSB:
			vsend_uchar $viewer_sock 0
			vsend_uchar $viewer_sock 0
			vsend_uchar $viewer_sock 0
			vsend_uchar $viewer_sock $ft

			if {$ft == 1} {
				set t [vread 4 $server_sock]
			}
		}
	}

	global client_fh server_fh
	set client_fh $viewer_sock
	set server_fh $server_sock

	fileevent $client_fh readable xfer_in_to_out
	fileevent $server_fh readable xfer_out_to_in
}

proc vsend_uchar {sock n} {
	set s [binary format c $n]
	puts -nonewline $sock $s
	flush $sock
}

proc vencrypt_constants {} {
	uplevel {
		set rfbSecTypeAnonTls  18
		set rfbSecTypeVencrypt 19

		set rfbVencryptPlain        256
		set rfbVencryptTlsNone      257
		set rfbVencryptTlsVnc       258
		set rfbVencryptTlsPlain     259
		set rfbVencryptX509None     260
		set rfbVencryptX509Vnc      261
		set rfbVencryptX509Plain    262
	}
}

proc do_vencrypt {sock which} {

	vencrypt_constants

	set t [vread 1 $sock]
	binary scan $t c vs_major
	set t [vread 1 $sock]
	binary scan $t c vs_minor

	if {$vs_minor == "" || $vs_major == "" || $vs_major != 0 || $vs_minor < 2} {
		puts stderr "vencrypt failure bad vs version major=$major minor=$minor"
		destroy .; exit 1
	}
	puts stderr "server vencrypt version $vs_major.$vs_minor"
	bmesg "server vencrypt version $vs_major.$vs_minor"

	append_handshake "subversion=0.2"
	vsend_uchar $sock 0
	vsend_uchar $sock 2

	set t [vread 1 $sock]
	binary scan $t c result
	if {$result != 0} {
		puts stderr "vencrypt failed result: $result"
		bmesg "vencrypt failed result: $result"
		destroy .; exit 1
	}

	set t [vread 1 $sock]
	binary scan $t c nsubtypes
	puts stderr "nsubtypes: $nsubtypes"
	bmesg "nsubtypes: $nsubtypes"

	for {set i 0} {$i < $nsubtypes} {incr i} {
		set t [vread 4 $sock]
		binary scan $t I stype
		puts stderr "subtypes: $i: $stype"
		append_handshake "sst$i=$stype"
		set subtypes($stype) $i
	}

	set subtype 0
	if [info exists subtypes($rfbVencryptX509None)] {
		set subtype $rfbVencryptX509None
		puts stderr "selected rfbVencryptX509None"
	} elseif [info exists subtypes($rfbVencryptX509Vnc)] {
		set subtype $rfbVencryptX509Vnc
		puts stderr "selected rfbVencryptX509Vnc"
	} elseif [info exists subtypes($rfbVencryptX509Plain)] {
		set subtype $rfbVencryptX509Plain
		puts stderr "selected rfbVencryptX509Plain"
	} elseif [info exists subtypes($rfbVencryptTlsNone)] {
		set subtype $rfbVencryptTlsNone
		puts stderr "selected rfbVencryptTlsNone"
	} elseif [info exists subtypes($rfbVencryptTlsVnc)] {
		set subtype $rfbVencryptTlsVnc
		puts stderr "selected rfbVencryptTlsVnc"
	} elseif [info exists subtypes($rfbVencryptTlsPlain)] {
		set subtype $rfbVencryptTlsPlain
		puts stderr "selected rfbVencryptTlsPlain"
	}
	append_handshake "subtype=$subtype"
	set st [binary format I $subtype]
	puts -nonewline $sock $st
	flush $sock
	
	if {$subtype == 0} {
		puts stderr "vencrypt could not find an acceptable subtype: $subtype"
		destroy .; exit 1
	}

	set t [vread 1 $sock]
	binary scan $t c result
	puts stderr "result=$result"

	append_handshake "done"

	if {$result == 0} {
		puts stderr "vencrypt failure result: $result"
		destroy .; exit 1
	}

}

proc do_connect_vencrypt {sock hostport which} {
	global debug cur_proxy

	vencrypt_constants

	puts stderr "pxy=$which vencrypt $hostport via $cur_proxy"
	bmesg "V: $which vencrypt $hostport via $cur_proxy"

	append_handshake "mode=connect"

	set srfb [vread 12 $sock]
	puts stderr "srfb: $srfb"
	bmesg "srfb: $srfb"
	set srfb [string trim $srfb]
	append_handshake "server=$srfb"

	set minor ""
	if [regexp {^RFB 00[456]\.} $srfb] {
		set minor 8
	} elseif [regexp {^RFB 003\.0*([0-9][0-9]*)} $srfb mvar minor] {
		;
	}
	if {$minor == "" || $minor < 7} {
		puts stderr "vencrypt failure bad minor=$minor"
		destroy .; exit 1
	}

	set vrfb "RFB 003.008\n"
	if {$minor == 7} {
		set vrfb "RFB 003.007\n"
	}
	puts -nonewline $sock $vrfb
	flush $sock

	set vrfb [string trim $vrfb] 
	append_handshake "viewer=$vrfb"
	append_handshake "latency=0.10"

	set str [vread 1 $sock]
	binary scan $str c nsec
	puts stderr "nsec: $nsec"
	bmesg "nsec: $nsec"
	for {set i 0} {$i < $nsec} {incr i} {
		set str [vread 1 $sock]
		binary scan $str c sec
		puts stderr "sec: $sec"
		bmesg "sec: $sec"
		set sectypes($i) $sec
	}
	for {set i 0} {$i < $nsec} {incr i} {
		if {$sectypes($i) == $rfbSecTypeVencrypt} {
			append_handshake "sectype=$rfbSecTypeVencrypt"
			vsend_uchar $sock $rfbSecTypeVencrypt
			after 500
			bmesg "do_vencrypt $sock $which"
			do_vencrypt $sock $which
			return
		}
	}
	for {set i 0} {$i < $nsec} {incr i} {
		if {$sectypes($i) == $rfbSecTypeAnonTls} {
			append_handshake "sectype=$rfbSecTypeAnonTls"
			vsend_uchar $sock $rfbSecTypeAnonTls
			bmesg "rfbSecTypeAnonTls"
			after 500
			append_handshake "done"
			return
		}
	}
}

proc do_connect {sock type hostport which} {
	if {$type == "http"} 	{
		do_connect_http $sock $hostport $which
	} elseif {$type == "socks"} {
		do_connect_socks4 $sock $hostport $which
	} elseif {$type == "socks5"} {
		do_connect_socks5 $sock $hostport $which
	} elseif [regexp -nocase {^repeater:} $type] {
		regsub -nocase {^repeater:} $type "" repeater
		do_connect_repeater $sock $hostport $which $repeater
	} elseif {$type == "vencrypt"} {
		do_connect_vencrypt $sock $hostport $which
	}
}

proc handle_connection {fh host port} {
	global proxy1_host proxy1_port proxy1_type
	global proxy2_host proxy2_port proxy2_type
	global proxy3_host proxy3_port proxy3_type
	global proxy1 proxy2 proxy3 dest
	global debug cur_proxy
	global got_connection

	if {$got_connection} {
		catch {close $fh}
		return
	}
	set got_connection 1

	if {$debug} {
		puts stderr "connection from: $host $port"	
		puts stderr "socket $proxy1_host $proxy1_port"
	}

	set rc [catch {set sock [socket $proxy1_host $proxy1_port]}]
	if {$rc != 0} {
		puts stderr "error connecting"	
		catch {close $sock}
		destroy .
		exit
	}

	if {$debug} {
		puts stderr "got sock: $sock"	
	}
	
	global client_fh server_fh
	set client_fh $fh
	set server_fh $sock

	fconfigure $fh   -translation binary -blocking 0
	fconfigure $sock -translation binary -blocking 0

	set cur_proxy $proxy1
	if {$proxy2 != ""} {
		do_connect $sock $proxy1_type "$proxy2_host:$proxy2_port" 1

		set cur_proxy $proxy2
		if {$proxy3 != ""} {
			do_connect $sock $proxy2_type "$proxy3_host:$proxy3_port" 2

			set cur_proxy $proxy3
			do_connect $sock $proxy3_type $dest 3

		} else {
			do_connect $sock $proxy2_type $dest 2
		}
	} else {
		do_connect $sock $proxy1_type $dest 1
	}

	fileevent $fh   readable xfer_in_to_out
	fileevent $sock readable xfer_out_to_in
}

proc proxy_type {proxy} {
	if [regexp -nocase {^socks://} $proxy] {
		return "socks"
	} elseif [regexp -nocase {^socks4://} $proxy] {
		return "socks"
	} elseif [regexp -nocase {^socks4a://} $proxy] {
		return "socks"
	} elseif [regexp -nocase {^socks5://} $proxy] {
		return "socks5"
	} elseif [regexp -nocase {^http://} $proxy] {
		return "http"
	} elseif [regexp -nocase {^https://} $proxy] {
		return "http"
	} elseif [regexp -nocase {^repeater://.*\+(.*)$} $proxy mat idstr] {
		return "repeater:$idstr"
	} elseif [regexp -nocase {^vencrypt://} $proxy] {
		return "vencrypt"
	} else {
		return "http"
	}
}

proc proxy_hostport {proxy} {
	regsub -nocase {^[a-z][a-z0-9]*://} $proxy "" hp
	regsub {\+.*$} $hp "" hp
	if {! [regexp {:[0-9]} $hp] && [regexp {^repeater:} $proxy]} {
		set hp "$hp:5900"
	}
	return $hp
}

proc setb {} {
	wm withdraw .
	catch {destroy .b}
	button .b -text "CONNECT_BR" -command {destroy .}
	pack .b
	after 1000 check_callback 
}

proc connect_br_sleep {} {
	global env
	if [info exists env(CONNECT_BR_SLEEP)] {
		if [regexp {^[0-9][0-9]*$} $env(CONNECT_BR_SLEEP)] {
			setb
			for {set i 0} {$i < $env(CONNECT_BR_SLEEP)} {incr i} {
				bmesg "$i sleep"	
				after 1000
			}
		}
	}
}

global env

set got_connection 0
set proxy1 ""
set proxy2 ""
set proxy3 ""
set client_fh ""
set server_fh ""
set do_bridge 0
set debug 0

if [info exists env(CONNECT_BR_DEBUG)] {
	set debug 1
}

if [info exists env(SSVNC_VENCRYPT_VIEWER_BRIDGE)] {
	set s [split $env(SSVNC_VENCRYPT_VIEWER_BRIDGE) ","]
	set listen  [lindex $s 0]
	set connect [lindex $s 1]

	setb

	do_vencrypt_viewer_bridge $listen $connect
	set do_bridge 1
}

if {$do_bridge} {
	;
} else {
	if {$debug && 0} {
		if {! [info exists env(SSVNC_DEST)]} {
			set env(SSVNC_DEST) "haystack:2037"
		}
		if {! [info exists env(SSVNC_PROXY)]} {
			set env(SSVNC_PROXY) "haystack:2037"
		}
		if {! [info exists env(SSVNC_LISTEN)]} {
			set env(SSVNC_LISTEN) "6789"
		}
	} else {
		if {! [info exists env(SSVNC_DEST)]} {
			destroy .; exit;
		}
		if {! [info exists env(SSVNC_PROXY)]} {
			destroy .; exit;
		}
		if {! [info exists env(SSVNC_LISTEN)] && ! [info exists env(SSVNC_REVERSE)]} {
			destroy .; exit;
		}
	}

	#set env(BMESG) 1

	set dest $env(SSVNC_DEST)

	if [regexp {,} $env(SSVNC_PROXY)] {
		set s [split $env(SSVNC_PROXY) ","]
		set proxy1 [lindex $s 0]
		set proxy2 [lindex $s 1]
		set proxy3 [lindex $s 2]
	} else {
		set proxy1 $env(SSVNC_PROXY)
	}

	set proxy1_type [proxy_type     $proxy1]
	set proxy1_hp   [proxy_hostport $proxy1]

	set proxy1_host ""
	set proxy1_port ""
	if [regexp {^(.*):([0-9][0-9]*)$} $proxy1_hp mvar proxy1_host proxy1_port] {
		;
	} else {
		puts stderr "could not parse hp1 host:port $proxy1_hp"
		destroy .
		exit 1
	}

	set proxy2_type ""
	set proxy2_host ""
	set proxy2_port ""

	if {$proxy2 != ""} {
		set proxy2_type [proxy_type     $proxy2]
		set proxy2_hp   [proxy_hostport $proxy2]

		set proxy2_host ""
		set proxy2_port ""
		if [regexp {^(.*):([0-9][0-9]*)$} $proxy2_hp mvar proxy2_host proxy2_port] {
			;
		} else {
			puts stderr "could not parse hp2 host:port $proxy2_hp"
			destroy .
			exit 1
		}
	}

	set proxy3_type ""
	set proxy3_host ""
	set proxy3_port ""

	if {$proxy3 != ""} {
		set proxy3_type [proxy_type     $proxy3]
		set proxy3_hp   [proxy_hostport $proxy3]

		set proxy3_host ""
		set proxy3_port ""
		if [regexp {^(.*):([0-9][0-9]*)$} $proxy3_hp mvar proxy3_host proxy3_port] {
			;
		} else {
			puts stderr "could not parse hp3 host:port $proxy3_hp"
			destroy .
			exit 1
		}
	}

	bmesg "1: '$proxy1_host' '$proxy1_port' '$proxy1_type'";
	bmesg "2: '$proxy2_host' '$proxy2_port' '$proxy2_type'";
	bmesg "3: '$proxy3_host' '$proxy3_port' '$proxy3_type'";

	if [info exists env(SSVNC_REVERSE)] {
		set rhost ""
		set rport ""
		if [regexp {^(.*):([0-9][0-9]*)$} $env(SSVNC_REVERSE) mvar rhost rport] {
			;
		} else {
			puts stderr "could not parse SSVNC_REVERSE host:port $env(SSVNC_REVERSE)"
			destroy .
			exit 1
		}
		setb
		set rc [catch {set lsock [socket $rhost $rport]}]
		if {$rc != 0} {
			puts stderr "error reversing"	
			bmesg "1 error reversing"	
			after 2000
			set rc [catch {set lsock [socket $rhost $rport]}]
		}
		if {$rc != 0} {
			puts stderr "error reversing"	
			bmesg "2 error reversing"	
			after 2000
			set rc [catch {set lsock [socket $rhost $rport]}]
		}
		if {$rc != 0} {
			puts stderr "error reversing"	
			bmesg "3 error reversing"	
			destroy .; exit 1
		}
		puts stderr "SSVNC_REVERSE to $rhost $rport OK";
		bmesg "SSVNC_REVERSE to $rhost $rport OK";
		connect_br_sleep
		handle_connection $lsock $rhost $rport
	} else {
		set lport $env(SSVNC_LISTEN)
		connect_br_sleep
		set rc [catch {set lsock [socket -myaddr 127.0.0.1 -server handle_connection $lport]}]
		if {$rc != 0} {
			puts stderr "error listening"	
			destroy .; exit 1
		}
		puts stderr "SSVNC_LISTEN on $lport OK";
		setb
	}
}