package provide sshtunnels::sshtunnel 1.0
namespace eval sshtunnels {}
package require Itcl
itcl::class sshtunnels::sshtunnel {
# ssh parameters
public variable hostname localhost
public variable username ""
public variable port ""
public variable password ""
public variable keyfile ""
public variable sshcommand {plink -ssh}
public variable tunnels {}
# timer events for connecting and pinging
private variable timerConnect ""
private variable timerPing ""
# pipe to ssh command
private variable pipe ""
private variable pingcount 0
constructor {args} {
eval configure $args
}
destructor {
after cancel $timerConnect
after cancel $timerPing
catch {close $pipe}
}
# mechanisms for logging information - mostly for debugging
private method dolog {mode string}
# connect to SSH server
public method connect {}
# disconnect from SSH server
public method disconnect {}
# connect later - if connecting right now fails
private method _connectLater {}
public method doping {}
# handle pong
private method dopong {}
# ping each 110 seconds
private method _pingLater {}
}
itcl::body sshtunnels::sshtunnel::connect {} {
after cancel $timerConnect
after cancel $timerPing
set command $sshcommand
if {$username != ""} {
lappend command "-l" $username
}
if {$port != ""} {
lappend command "-P" $port
}
if {$password != ""} {
lappend command "-pw" $password
}
if {$keyfile != ""} {
lappend command "-i" $keyfile
}
lappend command $hostname
foreach tunnel $tunnels {
foreach {type localip localport remoteip remoteport} $tunnel break
switch -- $type {
local {
if {$localip == ""} {
lappend command "-L" "${localport}:${remoteip}:${remoteport}"
} else {
lappend command "-L" "${localip}:${localport}:${remoteip}:${remoteport}"
}
}
remote {
if {$localip == ""} {
lappend command "-R" "${localport}:${remoteip}:${remoteport}"
} else {
lappend command "-R" "${localip}:${localport}:${remoteip}:${remoteport}"
}
}
}
}
lappend command "cat"
dolog debug "Command to run: $command"
catch {close $pipe}
if {[catch {
set pipe [open "|$command" r+]
fconfigure $pipe -translation binary -blocking 0 -buffering line
fileevent $pipe readable [itcl::code $this dopong]
after 500
puts $pipe "y"
flush $pipe
} error]} {
set pipe ""
dolog error $error
_connectLater
} else {
set pingcount 0
_pingLater
after cancel $timerConnect
}
}
itcl::body sshtunnels::sshtunnel::disconnect {} {
after cancel $timerPing
after cancel $timerConnect
catch {close $pipe}
set pipe ""
}
itcl::body sshtunnels::sshtunnel::_connectLater {} {
after cancel $timerConnect
set timerConnect [after 10000 [itcl::code $this connect]]
}
Usage: plink [options] [user@]host [command]
("host" can also be a PuTTY saved session name)
Options:
-V print version information and exit
-pgpfp print PGP key fingerprints and exit
-v show verbose messages
-load sessname Load settings from saved session
-ssh -telnet -rlogin -raw
force use of a particular protocol
-P port connect to specified port
-l user connect with specified username
-batch disable all interactive prompts
The following options only apply to SSH connections:
-pw passw login with specified password
-D [listen-IP:]listen-port
Dynamic SOCKS-based port forwarding
-L [listen-IP:]listen-port:host:port
Forward local port to remote address
-R [listen-IP:]listen-port:host:port
Forward remote port to local address
-X -x enable / disable X11 forwarding
-A -a enable / disable agent forwarding
-t -T enable / disable pty allocation
-1 -2 force use of particular protocol version
-4 -6 force use of IPv4 or IPv6
-C enable compression
-i key private key file for authentication
-m file read remote command(s) from file
-s remote command is an SSH subsystem (SSH-2 only)
-N don't start a shell/command (SSH-2 only)
itcl::body sshtunnels::sshtunnel::doping {} {
if {[catch {
puts $pipe "DOPING"
flush $pipe
}]} {
dolog error "Ping failed"
after cancel $timerPing
after cancel $timerConnect
catch {close $pipe}
set pipe ""
_connectLater
} else {
incr pingcount
dolog debug "Ping succeded - ping count is $pingcount"
}
}
itcl::body sshtunnels::sshtunnel::dopong {} {
catch {set eof [eof $pipe]}
if {!$eof} {
set eof [catch {gets $pipe line}]
}
if {!$eof} {
dolog debug "Received line: $line"
} else {
dolog error "Received EOF - reconnecting in 10 seconds"
}
if {$eof} {
dolog error "Pong failed"
after cancel $timerPing
catch {close $pipe}
set pipe ""
_connectLater
} elseif {$line == "DOPING"} {
incr pingcount -1
if {$pingcount < 0} {
set pingcount 0
}
} else {
dolog error "Received unknown line text: $line"
}
}
itcl::body sshtunnels::sshtunnel::_pingLater {} {
after cancel $timerPing
set timerPing [after 110000 [itcl::code $this doping]]
}
package ifneeded sshtunnels 1.0 [list source [file join $dir sshtunnels.tcl]]