sockspy version 2.3b imported from cvs
authorPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 26 Nov 2009 23:37:39 +0000 (23:37 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 26 Nov 2009 23:37:39 +0000 (23:37 +0000)
Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
bin/sockspy.tcl

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