#!/usr/bin/env tclsh

package prefer latest
package require tls

set dir			[file join [file dirname [info script]] ../tests/certs]
set OPTS(-cafile)	[file join $dir ca.pem]
set OPTS(-cert)		[file join $dir server.pem]
set OPTS(-key)		[file join $dir server.key]

set OPTS(-port)	2468
set OPTS(-debug) 1
set OPTS(-require) 1

foreach {key val} $argv {
    if {![info exists OPTS($key)]} {
	puts stderr "Usage: $argv0 ?options?\
		\n\t-debug    boolean  Debugging on or off ($OPTS(-debug))\
		\n\t-cafile   file     Cert. Auth. File ($OPTS(-cafile))\
		\n\t-cert     file     Server Cert ($OPTS(-cert))\
		\n\t-key      file     Server Key ($OPTS(-key))\
		\n\t-require  boolean  Require Certificate ($OPTS(-require))\
		\n\t-port     num      Port to listen on ($OPTS(-port))"
	exit
    }
    set OPTS($key) $val
}

# Catch  any background errors.
proc bgerror {msg} { puts stderr "BGERROR: $msg" }

# debugging helper code
proc shortstr {str} {
    return "[string replace $str 10 end ...] [string length $str]b"
}
proc dputs {msg} { if {$::OPTS(-debug)} { puts stderr $msg ; flush stderr } }

# As a response we just echo the data sent to us.
#
proc respond {chan} {
    if {[catch {read $chan} data]} {
	#dputs "EOF $chan ([shortstr $data)"
	catch {close $chan}
	return
    }
    #if {$data ne ""} { dputs "got $chan ([shortstr $data])" }
    if {[eof $chan]} {
	# client gone or finished
	dputs "EOF $chan"
	close $chan		;#  release the port
	return
    }
    puts -nonewline $chan $data
    flush $chan
    #dputs "sent $chan ([shortstr $data])"
}

# Once connection is established, we need to ensure handshake.
#
proc handshake {s cmd} {
    if {[eof $s]} {
	dputs "handshake eof $s"
	close $s
    } elseif {[catch {tls::handshake $s} result]} {
	# Some errors are normal.  Specifically, I (hobbs) believe that
	# TLS throws EAGAINs when it may not need to (or is inappropriate).
	dputs "handshake error $s: $result"
    } elseif {$result == 1} {
	# Handshake complete
	dputs "handshake complete $s"
	fileevent $s readable [list $cmd $s]
    }
}

# Callback proc to accept a connection from a client.
#
proc accept { chan ip port } {
    dputs "[info level 0] [fconfigure $chan]"
    fconfigure $chan -blocking 0
    fileevent $chan readable [list handshake $chan respond]
}

tls::init -cafile $OPTS(-cafile) -certfile $OPTS(-cert) -keyfile $OPTS(-key)
set chan [tls::socket -server accept -require $OPTS(-require) $OPTS(-port)]

puts "Server waiting connection on $chan ($OPTS(-port))"
puts [fconfigure $chan]

vwait __forever__
