From 2e9caaf7dcae30bdbfc828fdbf2673e4af4c2d1d Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Thu, 26 Nov 2009 23:37:39 +0000 Subject: [PATCH] sockspy version 2.3b imported from cvs Signed-off-by: Pat Thoyts --- bin/sockspy.tcl | 502 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 400 insertions(+), 102 deletions(-) diff --git a/bin/sockspy.tcl b/bin/sockspy.tcl index cf99430..feca655 100644 --- a/bin/sockspy.tcl +++ b/bin/sockspy.tcl @@ -1,29 +1,32 @@ #!/bin/sh # restart using wish \ - exec wish8.0 $0 ${1+"$@"} +exec wish $0 ${1+"$@"} # sockspy: copyright tom poindexter 1998 +# sockspy: copyright Keith Vetter 2002 # tpoindex@nyx.net # version 1.0 - december 10, 1998 -# version 2.0 - Keith Vetter keith@ebook.gemstar.com -# reorganized GUI to be 1 pane with different colors -# allow changing socket info -# works both as GUI and command line interface -# +# version 2.0 - January, 2002 by Keith Vetter +# KPV Nov 01, 2002 - added proxy mode +# # spy on conversation between a tcp client and server # # usage: sockspy clientPort serverHost serverPort -# clientPort - port to which clients connect -# serverHost - machine where real server runs -# serverPort - port on which real server listens +# -or- sockspy -proxy proxyPort +# clientPort - port to which clients connect +# serverHost - machine where real server runs +# serverPort - port on which real server listens # # e.g. to snoop on http connections to a web server: -# sockspy 8080 webhost 80 +# sockspy 8080 www.some.com 80 # then client web browser should use a url like: # http://localhost:8080/index.html # (or set your browser's proxy to use 8080 on the sockspy machine) +catch {package require uri} ;# Error handled below + array set state { + version 2.3b bbar 1 ascii 1 auto 1 @@ -33,7 +36,13 @@ array set state { playback "" gui 0 listen "" + title "not connected" + proxy 0 + fname "" } +array set SP {proxyPort 8080 clntPort 8080 servHost "" servPort 80} +#set filters(client) {^(GET |POST |HEAD )} +#set filters(server) {^(HTTP/|Location: )} ##+########################################################################## # # createMain @@ -50,7 +59,7 @@ proc createMain {} { if {"$tcl_platform(platform)" == "windows"} { doFont -1 } - wm title . "sockspy" + wm title . "SockSpy" wm resizable . 1 1 wm protocol . WM_DELETE_WINDOW Shutdown ;# So we shut down cleanly @@ -61,6 +70,7 @@ proc createMain {} { . configure -menu .m .m add cascade -menu .m.file -label "File" -underline 0 .m add cascade -menu .m.view -label "View" -underline 0 + .m add cascade -menu .m.help -label "Help" -underline 0 menu .m.file -tearoff 0 .m.file add command -label "Save" -underline 0 -command saveOutput @@ -85,6 +95,10 @@ proc createMain {} { .m.view add checkbutton -label " Autoscroll" -underline 5 \ -variable state(auto) + menu .m.help -tearoff 0 + .m.help add command -label Help -underline 1 -command Help + .m.help add separator + .m.help add command -label About -command About # # Title and status window # @@ -105,13 +119,12 @@ proc createMain {} { pack .cmd -side top -fill x -pady 5 -in .bbar frame .top - pack .top -side top -fill x -pady 2 -expand 1 + pack .top -side top -fill x -pady 2 -expand 0 #button .clear -text Clear -command clearOutput #pack .clear -in .top -side right -padx 3 - label .title -relief ridge + label .title -relief ridge -textvariable state(title) .title config -font "[.title cget -font] bold" - .title config -text "localhost:$::clntPort <--> $::servHost:$::servPort" pack .title -in .top -side left -fill both -expand 1 label .stat -textvariable state(msg) -relief ridge -anchor w @@ -122,14 +135,21 @@ proc createMain {} { # text .out -width 80 -height 50 -font $state(fixed) \ -yscrollcommand ".scroll set" -bg white -setgrid 1 - .out tag configure server -background cyan -borderwidth 2 -relief raised - .out tag configure client -background green -borderwidth 2 -relief raised + .out tag configure server -background cyan -borderwidth 2 -relief raised \ + -lmargin1 5 -lmargin2 5 + .out tag configure client -background green -borderwidth 2 -relief raised \ + -lmargin1 5 -lmargin2 5 .out tag configure client2 -font $state(fixedbold) - .out tag configure meta -background red -borderwidth 2 -relief raised + .out tag configure meta -background red -borderwidth 2 -relief raised \ + -lmargin1 5 -lmargin2 5 + .out tag configure meta2 -background yellow -borderwidth 2 -relief raised \ + -lmargin1 5 -lmargin2 5 scrollbar .scroll -orient vertical -command {.out yview} pack .scroll -side right -fill y pack .out -side left -fill both -expand 1 - + bind .out clearOutput + bind all {console show} + focus .out wm geometry . +10+10 } ##+########################################################################## @@ -187,17 +207,21 @@ proc redraw {} { # its data source. # proc saveOutput {} { - global state - - set but [tk_dialog .what "save" "save which window?" "" 2 \ - server client both cancel] + global state but + + set but -1 + after 1 {set but [tk_dialog .what "SockSpy Save" "Save which window?" \ + questhead 2 server client both cancel]} + after 1 tk_dialogFIX + vwait but + if {$but == -1 || $but == 3} { return } - set file [tk_getSaveFile -parent .] - if {"$file" == ""} { - return - } + set file [tk_getSaveFile -parent . -initialfile $state(fname)] + if {$file == ""} return + + set state(fname) $file if {[catch {open $file w} fd]} { tk_messageBox -message "file $file cannot be opened" -icon error \ -type ok @@ -205,7 +229,7 @@ proc saveOutput {} { } fconfigure $fd -translation binary foreach {who data} $state(playback) { - if {$who == "meta"} continue + if {$who == "meta" || $who == "meta2"} continue if {$but == 2 || ($but == 0 && $who == "server") || \ ($but == 1 && $who == "client")} { puts $fd $data @@ -216,12 +240,27 @@ proc saveOutput {} { } ##+########################################################################## # +# tk_dialogFIX +# +# tk_dialog is SOOO ugly. This is a bit of a hack to at least put +# some padding around the buttons. This probably will break under +# future versions hence the catch. +# +proc tk_dialogFIX {} { + for {set i 0} {$i < 5} {incr i} { ;# Don't do anything... + if {[winfo exists .what]} break ;# ...until window is mapped + after 200 + } + catch {grid configure .what.button0 -pady 10} +} +##+########################################################################## +# # printable # # Replaces all unprintable characters into dots. # proc printable {s {spaces 0}} { - regsub -all {[^\x20-\x7e]} $s "." n + regsub -all {[^\x09\x20-\x7e]} $s "." n if {$spaces} { regsub -all { } $n "_" n } @@ -236,11 +275,13 @@ proc printable {s {spaces 0}} { # proc insertData {who data} { global state - array set prefix {meta = client > server <} - + array set prefix {meta = meta2 = client > server <} + + set data [DoFilter $who $data] + if {$data == ""} return lappend state(playback) $who $data ;# Save for redraw and saving - if {$state(ascii) || [string equal $who meta]} { + if {$state(ascii) || [regexp {^meta2?$} $who] } { regsub -all \r $data "" data foreach line [split $data \n] { set line [printable $line] @@ -279,10 +320,18 @@ proc insertData {who data} { # Puts up an informational message both in the output window and # in the status window. # -proc INFO {msg} { +proc INFO {msg {who meta}} { global state set state(msg) $msg - insertData meta $msg + insertData $who $msg +} +proc ERROR {emsg} { + if {$::state(gui)} { + tk_messageBox -title "SockSpy Error" -message $emsg -icon error + } else { + puts $emsg + } + exit } ##+########################################################################## # @@ -300,30 +349,66 @@ proc sockReadable {fromSock toSock who} { INFO "waiting for new connection..." return } + if {$toSock == ""} { ;# Not connected yet + ProxyConnect $fromSock $data ;# Do proxy forwarding + } else { + catch { puts -nonewline $toSock $data } ;# Forward if we have a socket + } insertData $who $data - catch { puts -nonewline $toSock $data } ;# Forward if we have a socket - update } +proc ProxyConnect {fromSock data} { + set line1 [lindex [split $data \r] 0] + set bad [regexp -nocase {(http:[^ ]+)} $line1 => uri] + if {$bad == 0} { + INFO "ERROR: cannot extract URI from '$line1'" + close $fromSock + insertData meta "----- closed connection -----" + insertData meta "waiting for new connection..." + } + set state(uri) $uri ;# For debugging + array set URI [::uri::split $uri] + if {$URI(port) == ""} { set URI(port) 80 } + set bad [catch {set sockServ [socket $URI(host) $URI(port)]} reason] + if {$bad} { + set msg "cannot connect to $URI(host):$URI(port) => $reason" + INFO $msg + close $fromSock + insertData meta "----- closed connection -----" + insertData meta "waiting for new connection..." + tk_messageBox -icon error -type ok -message $msg + break + } + INFO "fowarding to $URI(host):$URI(port)" meta2 + fileevent $fromSock readable \ + [list sockReadable $fromSock $sockServ client] + fconfigure $sockServ -blocking 0 -buffering none -translation binary + fileevent $sockServ readable \ + [list sockReadable $sockServ $fromSock server] + puts -nonewline $sockServ $data +} ##+########################################################################## # # clntConnect # # Called when we get a new client connection # -proc clntConnect {servHost servPort sockClnt ip port} { - global state +proc clntConnect {sockClnt ip port} { + global state SP set state(sockClnt) $sockClnt + set state(meta) "" - INFO "connect from [fconfigure $sockClnt -sockname] $port" - if {$servHost == {} || $servHost == "none"} { + INFO "connect from [fconfigure $sockClnt -sockname] $port" meta2 + if {$state(proxy) || $SP(servHost) == {} || $SP(servHost) == "none"} { set sockServ "" - } elseif {[catch {set sockServ [socket $servHost $servPort]} reason]} { - INFO "cannot connect: $reason" - tk_messageBox -icon error -type ok \ - -message "cannot connect to $servHost $servPort: $reason" - exit + } else { + set n [catch {set sockServ [socket $SP(servHost) $SP(servPort)]} reason] + if {$n} { + INFO "cannot connect: $reason" + ERROR "cannot connect to $servHost $servPort: $reason" + } + INFO "connecting to $SP(servHost):$SP(servPort)" meta2 } ;# Configure connection to the client @@ -346,7 +431,7 @@ proc clntConnect {servHost servPort sockClnt ip port} { # it is already open. # proc DoListen {} { - global state clntPort servHost servPort + global state SP catch {close $state(sockClnt)} ;# Only the last open connection @@ -358,16 +443,26 @@ proc DoListen {} { update ;# Need else socket below fails } - set n [catch {set state(listen) \ - [socket -server [list clntConnect $servHost $servPort] $clntPort]} \ - emsg] + # Listen on clntPort or proxyPort for incoming connections + set port $SP(clntPort) + if {$state(proxy)} {set port $SP(proxyPort)} + set n [catch {set state(listen) [socket -server clntConnect $port]} emsg] if {$n} { INFO "socket open error: $emsg" + set state(title) "not connected" + return 0 } else { - INFO "localhost:$clntPort <--> $servHost:$servPort" + if {$state(proxy)} { + set state(title) "proxy localhost:$SP(proxyPort)" + } else { + set state(title) "localhost:$SP(clntPort) <--> " + append state(title) "$SP(servHost):$SP(servPort)" + } + INFO $state(title) INFO "waiting for new connection..." } + return 1 } ##+########################################################################## # @@ -375,66 +470,139 @@ proc DoListen {} { # # Prompts the user for client port, server host and server port # -proc GetSetup {{connect 1}} { - global state clntPort servHost servPort ok - set save [list $clntPort $servHost $servPort] +proc GetSetup {} { + global state SP ok + array set save [array get SP] set ok 0 ;# Assume cancelling ;# Put in some default values - if {![string length $clntPort]} {set clntPort 8080} - if {![string length $servPort]} {set servPort 80} + if {![string length $SP(proxyPort)]} {set SP(proxyPort) 8080} + if {![string length $SP(clntPort)]} {set SP(clntPort) 8080} + if {![string length $SP(servPort)]} {set SP(servPort) 80} if {! $state(gui)} { catch {close $state(listen)} - - set clntPort [Prompt "Client port" $clntPort] - set servHost [Prompt "Server host" $servHost] - set servPort [Prompt "Server port" $servPort] - if {$connect} DoListen + + set d "no" ; if {$state(proxy)} { set d yes } + set p [Prompt "Proxy mode" $d] + if {[regexp -nocase {^y$|^yes$} $p]} { + set state(proxy) 1 + set SP(proxyPort) [Prompt "proxy port" $SP(proxyPort)] + } else { + set state(proxy) 0 + set SP(clntPort) [Prompt "Client port" $SP(clntPort)] + set SP(servHost) [Prompt "Server host" $SP(servHost)] + set SP(servPort) [Prompt "Server port" $SP(servPort)] + } + DoListen return } - catch {destroy .dlg} + catch {destroy .dlg} toplevel .dlg - grab set .dlg wm title .dlg "Sockspy Setup" wm geom .dlg +176+176 - wm transient .dlg . - - frame .dlg.top -bd 2 -relief ridge - pack .dlg.top -side top -pady 1m -padx .5m - pack [frame .dlg.top.t] -side top -pady .5m - pack [frame .dlg.top.b] -side bottom -pady .5m - - foreach {n txt var} {1 "Client Port:" clntPort - 2 "Server Host:" servHost - 3 "Server Port:" servPort} { - set f .dlg.f$n - pack [frame $f] -side top -fill x -expand 1 -in .dlg.top - pack [frame $f.f] -side right -padx .5m - label $f.l -text $txt -anchor e - entry $f.e -textvariable $var - pack $f.e $f.l -side right - bind $f.e [list .dlg.okay invoke] - } + #wm transient .dlg . + + label .dlg.top -bd 2 -relief raised + set msg "You can configure SockSpy to either forward data\n" + append msg "a fixed server and port or to use the HTTP Proxy\n" + append msg "protocol to dynamically determine the server and\n" + append msg "port to forward data to." - button .dlg.okay -text OK -command { - if {[string length $clntPort] && [string length $servHost] && \ - [string length $servPort]} { set ok 1 ; destroy .dlg } - } - - button .dlg.quit -text Cancel -command { destroy .dlg } - pack .dlg.okay .dlg.quit -side left -expand 1 -pady 2 - focus .dlg.f1.e - .dlg.f1.e icursor end + #labelframe .dlg.fforward -text "Fixed Server Forwarding" + #labelframe .dlg.fproxy -text "HTTP Proxy" + frame .dlg.fforward + frame .dlg.fproxy + + label .dlg.msg -text $msg -justify left + radiobutton .dlg.forward -text "Use fixed server forwarding" \ + -variable state(proxy) -value 0 -anchor w -command GetSetup2 + label .dlg.fl1 -text "Client Port:" -anchor e + entry .dlg.fe1 -textvariable SP(clntPort) + + label .dlg.fl2 -text "Server Host:" -anchor e + entry .dlg.fe2 -textvariable SP(servHost) + label .dlg.fl3 -text "Server Port:" -anchor e + entry .dlg.fe3 -textvariable SP(servPort) + + radiobutton .dlg.proxy -text "Use HTTP Proxying" \ + -variable state(proxy) -value 1 -anchor w -command GetSetup2 + label .dlg.pl1 -text "Proxy Port:" -anchor e + entry .dlg.pe1 -textvariable SP(proxyPort) + button .dlg.ok -text OK -width 10 -command [list ValidForm 1] + button .dlg.cancel -text Cancel -width 10 -command [list destroy .dlg] + + grid .dlg.top -row 0 -column 0 -columnspan 3 -sticky ew -padx 10 -pady 10 + grid columnconfigure .dlg 0 -weight 1 + grid x .dlg.ok .dlg.cancel -padx 10 + grid configure .dlg.ok -padx 0 + grid rowconfigure .dlg 2 -minsize 8 + pack .dlg.msg -in .dlg.top -side top -fill x -padx 10 -pady 5 + pack .dlg.fforward .dlg.fproxy -in .dlg.top -side top -fill x \ + -padx 10 -pady 10 + + grid .dlg.proxy - - -in .dlg.fproxy -sticky w + grid x .dlg.pl1 .dlg.pe1 -in .dlg.fproxy -sticky ew + grid columnconfigure .dlg.fproxy 0 -minsize .2i + grid columnconfigure .dlg.fproxy 2 -weight 1 + grid columnconfigure .dlg.fproxy 3 -minsize 10 + grid rowconfigure .dlg.fproxy 2 -minsize 10 + + grid .dlg.forward - - -in .dlg.fforward -sticky w + grid x .dlg.fl1 .dlg.fe1 -in .dlg.fforward -sticky ew + grid x .dlg.fl2 .dlg.fe2 -in .dlg.fforward -sticky ew + grid x .dlg.fl3 .dlg.fe3 -in .dlg.fforward -sticky ew + grid columnconfigure .dlg.fforward 0 -minsize .2i + grid columnconfigure .dlg.fforward 2 -weight 1 + grid columnconfigure .dlg.fforward 3 -minsize 10 + grid rowconfigure .dlg.fforward 4 -minsize 10 + raise .dlg + + bind .dlg.forward [bind all ] + bind .dlg.proxy [bind all ] + bind .dlg.fe1 [bind all ] + bind .dlg.fe2 [bind all ] + bind .dlg.fe3 [list .dlg.ok invoke] + bind .dlg.pe1 [list .dlg.ok invoke] + + GetSetup2 + .dlg.pe1 icursor end + .dlg.fe2 icursor end + if {$state(proxy)} { focus -force .dlg.pe1 } { focus -force .dlg.fe2 } + + raise .dlg tkwait window .dlg + wm deiconify . if {$ok} { - if {$connect} DoListen + DoListen } else { - foreach {clntPort servHost servPort} $save break + array set SP [array get save] + } +} +proc GetSetup2 {} { + global state + array set s {1 normal 0 disabled} + if {! $state(proxy)} { array set s {0 normal 1 disabled} } + + .dlg.pl1 config -state $s(1) + .dlg.pe1 config -state $s(1) + foreach w {1 2 3} { + .dlg.fl$w config -state $s(0) + .dlg.fe$w config -state $s(0) + } +} +proc ValidForm {kill} { + global state SP ok + set ok 0 + if {$state(proxy)} { + if {$SP(proxyPort) != ""} {set ok 1} + } elseif {$SP(clntPort) !="" && $SP(servHost) !="" && $SP(servPort) !=""} { + set ok 1 } + if {$ok && $kill} {destroy .dlg} return $ok } ##+########################################################################## @@ -485,27 +653,157 @@ proc ButtonBar {} { .bbar config -height 1 ;# Need this to give remove gap } } - +proc Help {} { + catch {destroy .help} + toplevel .help + wm title .help "SockSpy Help" + wm geom .help "+[expr {[winfo x .] + 50}]+[expr {[winfo y .] + 50}]" + + text .help.t -relief raised -wrap word -width 70 -height 25 \ + -padx 10 -pady 10 -cursor {} -yscrollcommand {.help.sb set} + scrollbar .help.sb -orient vertical -command {.help.t yview} + button .help.dismiss -text Dismiss -command {destroy .help} + pack .help.dismiss -side bottom -pady 10 + pack .help.sb -side right -fill y + pack .help.t -side top -expand 1 -fill both + + set bold "[font actual [.help.t cget -font]] -weight bold" + set fixed "[font actual [.help.t cget -font]] -family courier" + .help.t tag config title -justify center -foregr red -font "Times 20 bold" + .help.t tag configure title2 -justify center -font "Times 12 bold" + .help.t tag configure header -font $bold + .help.t tag configure n -lmargin1 15 -lmargin2 15 + .help.t tag configure fixed -font $fixed -lmargin1 25 -lmargin2 55 + + .help.t insert end "SockSpy\n" title + .help.t insert end "Authors: Tom Poindexter and Keith Vetter\n\n" title2 + + set m "SockSpy lets you watch the conversation of a tcp client and server. " + append m "SockSpy acts much like a gateway: it waits for a tcp connection, " + append m "then connects to the real server. Data from the client is passed " + append m "onto the server, and data from the server is passed onto the " + append m "client.\n\n" + + append m "Along the way, the data streams are also displayed in text " + append m "widget with data sent from the client displayed in green, data " + append m "from the server in blue and connection metadata in red. The data " + append m "can be displayed as printable ASCII strings, or as a hex dump " + append m "format of both hex and printable characters.\n\n" + .help.t insert end "What is SockSpy?\n" header $m n + + set m "Why might you want to use SockSpy? Debugging tcp client/server " + append m "programs, examining protocols and diagnosing network problems " + append m "are top candidates. Perhaps you just want to figure out how " + append m "somethings work. I've used it to bypass firewalls, to rediscover " + append m "my lost smtp password, to access a news server on a remote " + append m "network, etc.\n\nIt's not a replacement for heavy duty tools " + append m "such as 'tcpdump' and other passive packet sniffers. On the " + append m "other hand, SockSpy doesn't require any special priviledges to " + append m "run (unless of course, you try to listen on a Unix reserved tcp " + append m "port less than 1024.)\n\n" + .help.t insert end "Why Use SockSpy?\n" header $m n + + set m "Just double click on SockSpy to start it. You will be prompted for " + append m "various connection parameters described below.\n\n" + append m "Alternatively, you can specify the connection parameters on the " + append m "command line. This is also how you can run SockSpy in text mode " + append m "without a GUI.\n\n" + append m "To start SockSpy from the command line:\n" + .help.t insert end "How to Use SockSpy\n" header $m n + + set m "$ sockspy \n or\n" + append m "$ sockspy -proxy \n\n" + .help.t insert end $m fixed + + set m "To start SockSpy in text mode without a GUI:\n" + .help.t insert end $m n + set m "$ tclsh sockspy \n or\n" + append m "$ tclsh sockspy -proxy \n\n" + .help.t insert end $m fixed + + set m ": the tcp port on which to listen. Clients should " + append m "connect to this port.\n" + append m ": the host where the real server runs.\n" + append m ": the tcp port on which the real server listens.\n" + append m ": the tcp port on which to listen in proxy-mode.\n\n" + .help.t insert end $m n + + set m "In proxy mode SockSpy works like a simple HTTP proxy server. " + append m "Instead of forwarding to a fixed server and port, it follows the " + append m "HTTP proxy protocol and reads the server information from the " + append m "first line of HTTP request.\n\n" + append m "You can turn on proxy mode by selecting it in the SockSpy Setup " + append m "dialog, or by specifying -proxy on the command line.\n\n" + .help.t insert end "Proxy Mode\n" header $m n + + set m "To spy on HTTP connections to a server, type:\n" + .help.t insert end "Example\n" header $m n + .help.t insert end " sockspy 8080 www.some.com 80\n" fixed + .help.t insert end "and point your browser to\n" n + .help.t insert end " http://localhost:8080/index.html\n\n" fixed + .help.t insert end "Alternatively, you could configure your browser to " n + .help.t insert end "use localhost and port 8000 as its proxy, and then " n + .help.t insert end "type:\n" n + .help.t insert end " sockspy -proxy 8000\n" fixed + .help.t insert end "and user your browser normally.\n" n + + .help.t config -state disabled +} + +proc About {} { + set m "SockSpy version $::state(version)\n" + append m "by Tom Poindexter and Keith Vetter\n" + append m "Copyright 1998-[clock format [clock seconds] -format %Y]\n" + append m "A program to eavesdrop on a tcp client server conversation." + tk_messageBox -icon info -title "About SockSpy" -message $m -parent . +} + +proc DoFilter {who data} { + global filters + + if {! [info exists filters($who)]} { return $data } + set ndata "" + foreach line [split $data \n] { + if {[regexp $filters($who) $line]} { + lappend ndata $line + } + } + return [join $ndata "\n"] +} ################################################################ ################################################################ ################################################################ -# get args and start it up - set state(gui) [info exists tk_version] -set state(listen) "" - -set clntPort [lindex $argv 0] -set servHost [lindex $argv 1] -set servPort [lindex $argv 2] - +if {[catch {package present uri}]} { + ERROR "ERROR: SockSpy requires the uri package from tcllib." +} + +if {$state(gui)} { wm withdraw . } createMain -if {[llength $argv] < 3} { - GetSetup 0 +if {[lindex $argv 0] == "-proxy"} { + set state(proxy) 1 + if {$argc == 2} { + set SP(proxyPort) [lindex $argv 1] + DoListen + } else { + GetSetup + } +} else { + set state(proxy) 0 + if {$argc >= 1} { set SP(clntPort) [lindex $argv 0] } + if {$argc >= 2} { set SP(servHost) [lindex $argv 1] } + if {$argc >= 3} { set SP(servPort) [lindex $argv 2] } + if {$argc >= 3} { + DoListen + } else { + GetSetup + } } -DoListen if {! $state(gui)} { vwait forever ;# tclsh needs this +} else { + wm deiconify . } -- 2.23.0