From 71bad9a64b27670cfdc1e83ac18a851288b9edf8 Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Thu, 26 Nov 2009 23:39:53 +0000 Subject: [PATCH] sockspy version 2.4 imported from cvs Signed-off-by: Pat Thoyts --- bin/sockspy.tcl | 1379 ++++++++++++++++++++++++++--------------------- 1 file changed, 751 insertions(+), 628 deletions(-) diff --git a/bin/sockspy.tcl b/bin/sockspy.tcl index 3a97fa0..852c412 100644 --- a/bin/sockspy.tcl +++ b/bin/sockspy.tcl @@ -8,6 +8,7 @@ exec wish $0 ${1+"$@"} # version 1.0 - december 10, 1998 # version 2.0 - January, 2002 by Keith Vetter # KPV Nov 01, 2002 - added proxy mode +# KPV Dec 21, 2002 - added extract window # # spy on conversation between a tcp client and server # @@ -23,220 +24,273 @@ exec wish $0 ${1+"$@"} # 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 +catch {package require uri} ;# Error handled below array set state { - version 2.4 - bbar 1 - ascii 1 - auto 1 - msg "" - fixed {} - fixedbold {} - playback "" - gui 0 - listen "" - title "not connected" - proxy 0 - fname "" + version 2.4 + bbar 1 + ascii 1 + autoscroll 1 + autowrap 1 + capture 1 + msg "" + fixed {} + fixedbold {} + playback "" + gui 0 + listen "" + title "not connected" + proxy 0 + fname "" } +array set colors {client green server cyan meta red meta2 yellow} array set SP {proxyPort 8080 clntPort 8080 servHost "" servPort 80} -#set filters(client) {^(GET |POST |HEAD )} -#set filters(server) {^(HTTP/|Location: )} +set extract(client) {^(GET |POST |HEAD )} +set extract(server) {^(HTTP/|Location: |Content-)} +#set extract(meta) {.} +set extract(meta2) {.} ##+########################################################################## # -# createMain -# -# Creates the display +# createMain -- Creates the display # proc createMain {} { - global state tcl_platform - + global state colors tcl_platform + if {! $state(gui)} return - - set state(fixed) [font create -family courier -size 10] - set state(fixedbold) [font create -family courier -size 10 -weight bold] - if {"$tcl_platform(platform)" == "windows"} { - doFont -1 - } - wm title . "SockSpy" - wm resizable . 1 1 - wm protocol . WM_DELETE_WINDOW Shutdown ;# So we shut down cleanly - - # - # Set up the menus - # - menu .m -tearoff 0 - . 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 - .m.file add command -label "Reconnect" -underline 0 -command GetSetup - .m.file add separator - .m.file add command -label "Exit" -underline 1 -command Shutdown - - menu .m.view -tearoff 0 - .m.view add command -label " Clear" -underline 1 -command clearOutput - .m.view add separator - .m.view add checkbutton -label " ButtonBar" -variable state(bbar) \ - -underline 1 -command ButtonBar - .m.view add separator - .m.view add command -label " + Font" -command [list doFont 1] - .m.view add command -label " - Font" -command [list doFont -1] - .m.view add separator - .m.view add radiobutton -label " Hex" -underline 1 \ - -variable state(ascii) -value 0 -command redraw - .m.view add radiobutton -label " ASCII" -underline 1 \ - -variable state(ascii) -value 1 -command redraw - .m.view add separator - .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 - # - pack [frame .bbar] -side top -fill x - frame .cmd -relief sunken -bd 2 - radiobutton .cmd.hex -text Hex -variable state(ascii) \ - -value 0 -command redraw - radiobutton .cmd.ascii -text ASCII -variable state(ascii) \ - -value 1 -command redraw - checkbutton .cmd.auto -text Autoscroll -variable state(auto) - button .cmd.clear -text Clear -command clearOutput - button .cmd.incr -text "+ Font" -command [list doFont 1] - button .cmd.decr -text "- Font" -command [list doFont -1] - button .cmd.save -text Save -command saveOutput - button .cmd.kill -text Exit -command Shutdown - pack .cmd.kill .cmd.save .cmd.clear .cmd.decr .cmd.incr .cmd.auto \ - .cmd.ascii .cmd.hex -side right -padx 3 -pady 3 - pack .cmd -side top -fill x -pady 5 -in .bbar - - frame .top - 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 -textvariable state(title) - .title config -font "[.title cget -font] bold" - pack .title -in .top -side left -fill both -expand 1 - - label .stat -textvariable state(msg) -relief ridge -anchor w - pack .stat -side bottom -fill x - - # - # Now for the output area of the display - # - 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 \ - -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 \ - -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 + + set state(fixed) [font create -family courier -size 10] + set state(fixedbold) [font create -family courier -size 10 -weight bold] + if {"$tcl_platform(platform)" == "windows"} { + doFont -1 + } + wm title . "SockSpy -- $state(title)" + wm resizable . 1 1 + wm protocol . WM_DELETE_WINDOW Shutdown ;# So we shut down cleanly + + # + # Set up the menus + # + menu .m -tearoff 0 + . 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 + .m.file add command -label "Reconnect" -underline 0 -command GetSetup + .m.file add separator + .m.file add command -label "Exit" -underline 1 -command Shutdown + + menu .m.view -tearoff 0 + .m.view add command -label " Clear" -underline 1 -command clearOutput + .m.view add separator + .m.view add checkbutton -label " ButtonBar" -variable state(bbar) \ + -underline 1 -command ButtonBar + .m.view add checkbutton -label " Extract Window" -variable state(extract) \ + -underline 1 -command ToggleExtract + .m.view add separator + .m.view add command -label " + Font" -command [list doFont 1] + .m.view add command -label " - Font" -command [list doFont -1] + .m.view add separator + .m.view add radiobutton -label " Hex" -underline 1 \ + -variable state(ascii) -value 0 -command redraw + .m.view add radiobutton -label " ASCII" -underline 1 \ + -variable state(ascii) -value 1 -command redraw + .m.view add separator + .m.view add checkbutton -label " Autoscroll" -underline 5 \ + -variable state(autoscroll) + .m.view add checkbutton -label " Autowrap" -underline 5 \ + -variable state(autowrap) -command ToggleWrap + .m.view add checkbutton -label " Capture" -underline 5 \ + -variable state(capture) -command ToggleCapture + + 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 + # + frame .bbar + frame .cmd -relief sunken -bd 2 + radiobutton .cmd.hex -text Hex -variable state(ascii) \ + -value 0 -command redraw + radiobutton .cmd.ascii -text ASCII -variable state(ascii) \ + -value 1 -command redraw + checkbutton .cmd.autos -text Autoscroll -variable state(autoscroll) + checkbutton .cmd.autow -text Autowrap -variable state(autowrap) \ + -command ToggleWrap + checkbutton .cmd.capture -text Capture -variable state(capture) \ + -command ToggleCapture + button .cmd.clear -text Clear -command clearOutput + #button .cmd.incr -text "+ Font" -command [list doFont 1] + #button .cmd.decr -text "- Font" -command [list doFont -1] + button .cmd.save -text Save -command saveOutput + button .cmd.kill -text Exit -command Shutdown + pack .cmd -side top -fill x -pady 5 -in .bbar + pack .cmd.kill .cmd.save .cmd.clear .cmd.autow .cmd.autos .cmd.capture \ + -side right -padx 3 -pady 3 + #label .title -relief ridge -textvariable state(title) + #.title config -font "[.title cget -font] bold" + label .stat -textvariable state(msg) -relief ridge -anchor w + + # + # Now for the output area of the display + # + scrollbar .yscroll -orient vertical -command {.out yview} + scrollbar .xscroll -orient horizontal -command {.out xview} + text .out -width 80 -height 50 -font $state(fixed) -bg white -setgrid 1 \ + -yscrollcommand ".yscroll set" -xscrollcommand ".xscroll set" + foreach t [array names colors] { + .out tag configure $t -background $colors($t) -borderwidth 2 \ + -relief raised -lmargin1 5 -lmargin2 5 + } + .out tag raise sel ;# Selection is most prominent + + grid .bbar - -row 0 -sticky ew + grid .out .yscroll -row 1 -sticky news + grid .xscroll -row 2 -sticky ew + grid .stat - -row 3 -sticky ew + grid rowconfigure . 1 -weight 1 + grid columnconfigure . 0 -weight 1 + + bind .out clearOutput + bind all {console show} + focus .out + wm geometry . +10+10 } ##+########################################################################## # -# doFont +# createExtract -- Creates the extract toplevel window. +# +proc createExtract {} { + global state colors + + if {[winfo exists .extract]} { + wm deiconify .extract + return + } + set top ".extract" + toplevel $top + wm title $top "SockSpy Extract" + wm protocol $top WM_DELETE_WINDOW [list ToggleExtract -1] + if {[regexp {(\+[0-9]+)(\+[0-9]+)$} [wm geom .] => wx wy]} { + wm geom $top "+[expr {$wx+35+[winfo width .]}]+[expr {$wy+15}]" + } + + frame $top.top -bd 2 -relief ridge + label $top.top.c -text "Client Filter" -anchor e + entry $top.top.ce -textvariable extract(client) -bg $colors(client) + label $top.top.s -text "Server Filter" -anchor e + entry $top.top.se -textvariable extract(server) -bg $colors(server) + label $top.top.m -text "Metadata Filter" -anchor e + entry $top.top.me -textvariable extract(meta2) -bg $colors(meta2) + + text $top.out -width 80 -height 20 -font $state(fixed) -bg beige \ + -setgrid 1 -wrap none -yscrollcommand [list $top.yscroll set] \ + -xscrollcommand [list $top.xscroll set] + foreach t [array names colors] { + $top.out tag configure $t -background $colors($t) -borderwidth 2 \ + -relief raised -lmargin1 5 -lmargin2 5 + } + $top.out tag raise sel ;# Selection is most prominent + + scrollbar $top.yscroll -orient vertical -command [list $top.out yview] + scrollbar $top.xscroll -orient horizontal -command [list $top.out xview] + grid $top.top - -row 0 -sticky ew -ipady 10 + grid $top.out $top.yscroll -sticky news + grid $top.xscroll -sticky ew + + grid rowconfigure $top 1 -weight 1 + grid columnconfigure $top 0 -weight 1 + + grid $top.top.c $top.top.ce -row 0 -sticky ew + grid $top.top.s $top.top.se -sticky ew + grid $top.top.m $top.top.me -sticky ew + grid columnconfigure $top.top 1 -weight 1 + grid columnconfigure $top.top 2 -minsize 10 +} +##+########################################################################## # -# Changes the size of the font used for the display text +# doFont -- Changes the size of the font used for the display text # proc doFont {delta} { - global state - set size [font configure $state(fixed) -size] + global state + set size [font configure $state(fixed) -size] - incr size $delta - font configure $state(fixed) -size $size - font configure $state(fixedbold) -size $size + incr size $delta + font configure $state(fixed) -size $size + font configure $state(fixedbold) -size $size } ##+########################################################################## # -# clearOutput -# -# Erases the content of the output window +# clearOutput -- Erases the content of the output window # proc clearOutput {} { - global state - if {$state(gui)} { - .out delete 0.0 end - } - set state(playback) "" + global state + if {$state(gui)} { + .out delete 0.0 end + catch {.extract.out delete 0.0 end} + } + set state(playback) "" } ##+########################################################################## # -# redraw -# -# Redraws the contents of the output window. It does this by replaying -# the input stream. +# redraw -- Redraws the contents of the output window. +# +# It does this by replaying the input stream. # proc redraw {} { - global state + global state - set save $state(auto) ;# Disable autoscrolling - set state(auto) 0 + set save_as $state(autoscroll) ;# Disable autoscrolling + set state(autoscroll) 0 - set p $state(playback) ;# Save what gets displayed - clearOutput ;# Erase current screen - foreach {who data} $p { ;# Replay the input stream - insertData $who $data - } - set state(auto) $save + set p $state(playback) ;# Save what gets displayed + clearOutput ;# Erase current screen + foreach {who data} $p { ;# Replay the input stream + insertData $who $data 1 + } + set state(autoscroll) $save_as } ##+########################################################################## # -# saveOutput +# saveOutput -- Saves the content of the output window. # -# Saves the content of the output window. It uses the playback stream as -# its data source. +# It uses the playback stream as its data source. # proc saveOutput {} { - 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 . -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 - return - } - fconfigure $fd -translation binary - foreach {who data} $state(playback) { - if {$who == "meta" || $who == "meta2"} continue - if {$but == 2 || ($but == 0 && $who == "server") || \ - ($but == 1 && $who == "client")} { - puts $fd $data - } - } - close $fd - bell + 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 . -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 + return + } + fconfigure $fd -translation binary + foreach {who data} $state(playback) { + if {$who == "meta" || $who == "meta2"} continue + if {$but == 2 || ($but == 0 && $who == "server") || \ + ($but == 1 && $who == "client")} { + puts $fd $data + } + } + close $fd + bell } ##+########################################################################## # @@ -247,71 +301,67 @@ proc saveOutput {} { # 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} + if {[winfo exists .what] == 0} { ;# Don't do anything... + after 200 tk_dialogFix ;# ...until window is mapped + } else { + catch {grid configure .what.button0 -pady 10} + } } ##+########################################################################## # -# printable -# -# Replaces all unprintable characters into dots. +# printable -- Replaces all unprintable characters into dots. # proc printable {s {spaces 0}} { - regsub -all {[^\x09\x20-\x7e]} $s "." n - if {$spaces} { - regsub -all { } $n "_" n - } - return $n; + regsub -all {[^\x09\x20-\x7e]} $s "." n + if {$spaces} { + regsub -all { } $n "_" n + } + return $n; } ##+########################################################################## # -# insertData -# -# Inserts data into the output window. WHO tells us if it is from -# the client, server or meta. -# -proc insertData {who data} { - global state - 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) || [regexp {^meta2?$} $who] } { - regsub -all \r $data "" data - foreach line [split $data \n] { - set line [printable $line] - set tag $who - if {$tag == "client" && [regexp -nocase {^get |post } $line]} { - lappend tag client2 - } - if {$state(gui)} { - .out insert end "$line\n" $tag - } else { - puts "$prefix($who)$line" - } - } - } else { ;# Hex output - while {[string length $data]} { - set line [string range $data 0 15] - set data [string range $data [string length $line] end] - binary scan $line H* hex - regsub -all {([0-9a-f][0-9a-f])} $hex {\1 } hex - set line [format "%-48.48s %-16.16s\n" $hex [printable $line 1]] - if {$state(gui)} { - .out insert end $line $who - } else { - puts "$prefix(who)$line" - } - } - } - if {$state(auto) && $state(gui)} { - .out see end - } +# insertData -- Inserts data into the output window. WHO tells us if it is +# from the client, server or meta. +# +proc insertData {who data {force 0}} { + global state + array set prefix {meta = meta2 = client > server <} + + DoExtract $who $data ;# Display any extracted data + if {! $force && ! $state(capture)} return ;# No display w/o capture on + lappend state(playback) $who $data ;# Save for redraw and saving + + if {$state(ascii) || [regexp {^meta2?$} $who] } { + regsub -all \r $data "" data + foreach line [split $data \n] { + set line [printable $line] + set tag $who + if {$tag == "client" && [regexp -nocase {^get |^post } $line]} { + lappend tag client2 + } + if {$state(gui)} { + .out insert end "$line\n" $tag + } else { + puts "$prefix($who)$line" + } + } + } else { ;# Hex output + while {[string length $data]} { + set line [string range $data 0 15] + set data [string range $data [string length $line] end] + binary scan $line H* hex + regsub -all {([0-9a-f][0-9a-f])} $hex {\1 } hex + set line [format "%-48.48s %-16.16s\n" $hex [printable $line 1]] + if {$state(gui)} { + .out insert end $line $who + } else { + puts "$prefix(who)$line" + } + } + } + if {$state(autoscroll) && $state(gui)} { + .out see end + } } ##+########################################################################## # @@ -320,108 +370,114 @@ proc insertData {who data} { # Puts up an informational message both in the output window and # in the status window. # -proc INFO {msg {who meta}} { - global state - set state(msg) $msg - insertData $who $msg +proc INFO {msg {who meta} {display 0}} { + global state + set state(msg) $msg + insertData $who $msg $display } proc ERROR {emsg} { - if {$::state(gui)} { - tk_messageBox -title "SockSpy Error" -message $emsg -icon error - } else { - puts $emsg - } - exit + if {$::state(gui)} { + tk_messageBox -title "SockSpy Error" -message $emsg -icon error + } else { + puts $emsg + } } ##+########################################################################## # -# sockReadable -# -# Called when there is data available on the fromSocket +# sockReadable -- Called when there is data available on the fromSocket # proc sockReadable {fromSock toSock who} { - global state - set data [read $fromSock] - if {[string length $data] == 0} { - close $fromSock - catch { close $toSock } - insertData meta "----- closed connection -----" - 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 - update + global state + set data [read $fromSock] + if {[string length $data] == 0} { + close $fromSock + catch { close $toSock } + insertData meta "----- closed connection -----" + 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 + update } +##+########################################################################## +# +# ProxyConnect +# +# Called from a new socket connection when we have to determing who +# to forward to. +# 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 + 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 + ERROR $msg + insertData meta "----- closed connection -----" + insertData meta "waiting for new connection..." + return + } + 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 +# clntConnect -- Called when we get a new client connection # proc clntConnect {sockClnt ip port} { - global state SP - - set state(sockClnt) $sockClnt - set state(meta) "" - - INFO "connect from [fconfigure $sockClnt -sockname] $port" meta2 - if {$state(proxy) || $SP(servHost) == {} || $SP(servHost) == "none"} { - set sockServ "" - } 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 - fconfigure $sockClnt -blocking 0 -buffering none -translation binary - fileevent $sockClnt readable \ - [list sockReadable $sockClnt $sockServ client] - - ;# Configure connection to the server - if {[string length $sockServ]} { - fconfigure $sockServ -blocking 0 -buffering none -translation binary - fileevent $sockServ readable \ - [list sockReadable $sockServ $sockClnt server] - } + global state SP + + set state(sockClnt) $sockClnt + set state(meta) "" + + INFO "connect from [fconfigure $sockClnt -sockname] $port" meta2 + if {$state(proxy) || $SP(servHost) == {} || $SP(servHost) == "none"} { + set sockServ "" + } else { + set n [catch {set sockServ [socket $SP(servHost) $SP(servPort)]} reason] + if {$n} { + INFO "cannot connect: $reason" + close $sockClnt + ERROR "cannot connect to $SP(servHost) $SP(servPort): $reason" + insertData meta "----- closed connection -----" + insertData meta "waiting for new connection..." + + } + INFO "connecting to $SP(servHost):$SP(servPort)" meta2 + } + + ;# Configure connection to the client + fconfigure $sockClnt -blocking 0 -buffering none -translation binary + fileevent $sockClnt readable \ + [list sockReadable $sockClnt $sockServ client] + + ;# Configure connection to the server + if {[string length $sockServ]} { + fconfigure $sockServ -blocking 0 -buffering none -translation binary + fileevent $sockServ readable \ + [list sockReadable $sockServ $sockClnt server] + } } ##+########################################################################## # @@ -431,236 +487,275 @@ proc clntConnect {sockClnt ip port} { # it is already open. # proc DoListen {} { - global state SP - - catch {close $state(sockClnt)} ;# Only the last open connection - - ;# Close old listener if it exists - if {$state(listen) != ""} { - set n [catch {close $state(listen)} emsg] - if {$n} { INFO "socket close error: $emsg"} - set state(listen) "" - update ;# Need else socket below fails - } - - # 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 { - 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 + global state SP + + set rval 1 + catch {close $state(sockClnt)} ;# Only the last open connection + + ;# Close old listener if it exists + if {$state(listen) != ""} { + set n [catch {close $state(listen)} emsg] + if {$n} { INFO "socket close error: $emsg"} + set state(listen) "" + update ;# Need else socket below fails + } + + # 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" + set rval 0 + } else { + 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..." + } + wm title . "SockSpy -- $state(title)" + return $rval } ##+########################################################################## # -# GetSetup -# -# Prompts the user for client port, server host and server port +# GetSetup -- Prompts the user for client port, server host and server port # 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 $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 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} - toplevel .dlg - wm title .dlg "Sockspy Setup" - wm geom .dlg +176+176 - #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." - - #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 } - + global state SP ok + array set save [array get SP] + set ok 0 ;# Assume cancelling + + ;# Put in some default values + 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 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 + } + + destroy .dlg + toplevel .dlg + wm title .dlg "Sockspy Setup" + wm geom .dlg +176+176 + #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." + + #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 - tkwait window .dlg - wm deiconify . - if {$ok} { - DoListen - } else { - array set SP [array get save] - } + 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} { + DoListen + } else { + 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) - } + 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 + 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 } ##+########################################################################## # -# Prompt -# -# Non-gui way to get input from the user. +# Prompt -- Non-gui way to get input from the user. # proc Prompt {prompt {default ""}} { - if {$default != ""} { - puts -nonewline "$prompt ($default): " - } else { - puts -nonewline "$prompt: " - } - flush stdout - set n [gets stdin line] - - if {$n == 0 && $default != ""} { - set line $default - } - return $line + if {$default != ""} { + puts -nonewline "$prompt ($default): " + } else { + puts -nonewline "$prompt: " + } + flush stdout + set n [gets stdin line] + + if {$n == 0 && $default != ""} { + set line $default + } + return $line } ##+########################################################################## # -# Shutdown -# -# Closes the listen port before exiting +# Shutdown -- Closes the listen port before exiting # proc Shutdown {} { - global state + global state - catch {close $state(listen)} - exit + catch {close $state(listen)} + exit } ##+########################################################################## # -# ButtonBar -# -# Toggles the visibility of the button bar +# ButtonBar -- Toggles the visibility of the button bar # proc ButtonBar {} { - global state - - if {$state(bbar)} { ;# Need to add button bar - pack .cmd -side top -fill x -pady 5 -in .bbar - } else { - pack forget .cmd - .bbar config -height 1 ;# Need this to give remove gap - } + global state + + if {$state(bbar)} { ;# Need to add button bar + pack .cmd -side top -fill x -pady 5 -in .bbar + } else { + pack forget .cmd + .bbar config -height 1 ;# Need this to give remove gap + } } +##+########################################################################## +# +# ToggleExtract -- Toggles the visibility of the extract window +# +proc ToggleExtract {{how 0}} { + global state + + if {$how == -1} { ;# Hard kill + destroy .extract + set state(extract) 0 + return + } + if {$state(extract)} { + createExtract + } else { + catch {wm withdraw .extract} + } +} +##+########################################################################## +# +# ToggleWrap -- turns on or off wrap in the text window +# +proc ToggleWrap {} { + global state + array set x {0 none 1 char} + .out configure -wrap $x($state(autowrap)) +} +##+########################################################################## +# +# ToggleCapture -- puts up a help message +# +proc ToggleCapture {} { + global state + if {$state(capture)} { + INFO "Data capture display enabled" meta + .out config -bg white + } else { + INFO "Data capture display disabled" meta 1 + .out config -bg grey88 + } +} +##+########################################################################## +# +# Help -- a simple help system +# proc Help {} { - catch {destroy .help} + 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} + -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 @@ -673,102 +768,124 @@ proc Help {} { .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 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 - + 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 "The extract window lets you extract out specific parts of the " + append m "data stream. As data arrives from the client, server or as " + append m "metadata it gets matched against the appropriate regular " + append m "expression filter. If it matches, then the data gets displayed " + append m "in the extract window. (Malformed regular expression are " + append m "silently ignored.)\n\n" + .help.t insert end "Extract Window\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 } - +##+########################################################################## +# +# About -- simple about box +# 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 . + 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\n" + append m "A program to eavesdrop on a tcp client server conversation." + tk_messageBox -icon info -title "About SockSpy" -message $m -parent . } +##+########################################################################## +# +# DoExtract -- Displays any data matching the RE in the extract window +# +proc DoExtract {who data} { + global state extract -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"] + if {! $state(gui)} return + if {! [info exists extract($who)]} return + if {! [winfo exists .extract]} return + + regsub -all \r $data "" data + foreach line [split $data \n] { + if {$extract($who) == ""} continue + catch { + if {[regexp -nocase $extract($who) $line]} { + .extract.out insert end "$line\n" $who + } + } + } + if {$state(autoscroll)} { + .extract.out see end + } } ################################################################ ################################################################ @@ -776,34 +893,40 @@ proc DoFilter {who data} { set state(gui) [info exists tk_version] if {[catch {package present uri}]} { - ERROR "ERROR: SockSpy requires the uri package from tcllib." + ERROR "ERROR: SockSpy requires the uri package from tcllib." + exit 0 } - + if {$state(gui)} { wm withdraw . } createMain +if {[lindex $argv 0] == "-go"} { + set argv [list 8080 localhost 80] + set argc 3 +} + if {[lindex $argv 0] == "-proxy"} { - set state(proxy) 1 - if {$argc == 2} { - set SP(proxyPort) [lindex $argv 1] - DoListen - } else { - GetSetup - } + 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 - } + 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 + } } if {! $state(gui)} { - vwait forever ;# tclsh needs this + vwait forever ;# tclsh needs this } else { - wm deiconify . + wm deiconify . } -- 2.23.0