# 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
#
# 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 <Control-l> clearOutput
- bind all <Alt-c> {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 <Control-l> clearOutput
+ bind all <Alt-c> {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
}
##+##########################################################################
#
# 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
+ }
}
##+##########################################################################
#
# 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]
+ }
}
##+##########################################################################
#
# 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 <Return> [bind all <Key-Tab>]
- bind .dlg.proxy <Return> [bind all <Key-Tab>]
- bind .dlg.fe1 <Return> [bind all <Key-Tab>]
- bind .dlg.fe2 <Return> [bind all <Key-Tab>]
- bind .dlg.fe3 <Return> [list .dlg.ok invoke]
- bind .dlg.pe1 <Return> [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 <Return> [bind all <Key-Tab>]
+ bind .dlg.proxy <Return> [bind all <Key-Tab>]
+ bind .dlg.fe1 <Return> [bind all <Key-Tab>]
+ bind .dlg.fe2 <Return> [bind all <Key-Tab>]
+ bind .dlg.fe3 <Return> [list .dlg.ok invoke]
+ bind .dlg.pe1 <Return> [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
.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 <listen-port> <server-host> <server-port>\n or\n"
- append m "$ sockspy -proxy <proxy-port>\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 <listen-port> <server-host> <server-port>\n or\n"
- append m "$ tclsh sockspy -proxy <proxy-port>\n\n"
- .help.t insert end $m fixed
-
- set m "<listen-port>: the tcp port on which to listen. Clients should "
- append m "connect to this port.\n"
- append m "<server-host>: the host where the real server runs.\n"
- append m "<server-port>: the tcp port on which the real server listens.\n"
- append m "<proxy-port>: 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 <listen-port> <server-host> <server-port>\n or\n"
+ append m "$ sockspy -proxy <proxy-port>\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 <listen-port> <server-host> <server-port>\n or\n"
+ append m "$ tclsh sockspy -proxy <proxy-port>\n\n"
+ .help.t insert end $m fixed
+
+ set m "<listen-port>: the tcp port on which to listen. Clients should "
+ append m "connect to this port.\n"
+ append m "<server-host>: the host where the real server runs.\n"
+ append m "<server-port>: the tcp port on which the real server listens.\n"
+ append m "<proxy-port>: 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
+ }
}
################################################################
################################################################
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 .
}