Wapp

Check-in [fb5eafae32]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improved SCGI security: (1) The --scgi option only listens on IP address 127.0.0.1. The new --remote-scgi option must be used if the webserver is on a different machine. (2) The new --fromip option can be used to restrict incoming requests to a particular IP address. (3) In SCGI mode, the new parameter "SERVER_ADDR" contains the IP address of the webserver that originated the SCGI request.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: fb5eafae32c267434df7a9a427ca9b68faf254e8cbae4e13fd40c2295893cbb7
User & Date: drh 2019-04-01 00:53:50
Context
2019-04-01
01:31
Fix error in the first example of the "intro.md" page. check-in: 83e002a08c user: drh tags: trunk
00:53
Improved SCGI security: (1) The --scgi option only listens on IP address 127.0.0.1. The new --remote-scgi option must be used if the webserver is on a different machine. (2) The new --fromip option can be used to restrict incoming requests to a particular IP address. (3) In SCGI mode, the new parameter "SERVER_ADDR" contains the IP address of the webserver that originated the SCGI request. check-in: fb5eafae32 user: drh tags: trunk
2019-03-08
00:08
Add the forgotten helloworld.md documentation file. check-in: 5f79eb875f user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to docs/intro.md.

    59     59   web-browser at that script.
    60     60   
    61     61   Run the hello-world program as SCGI like this:
    62     62   
    63     63   >
    64     64       wapptclsh main.tcl --scgi 9000
    65     65   
    66         -Then configure your web-server to send SCGI requests to TCL port 9000
    67         -for some specific URI, and point your web-browser at that URI.
           66  +Then configure your web-server to send SCGI requests to TCP port 9000
           67  +for some specific URI, and point your web-browser at that URI.  By
           68  +default, the web-server must be on the same machine as the wapp script.
           69  +The --scgi option only accepts SCGI requests from IP address 127.0.0.1.
           70  +If your webserver is running on a different machine, use the --remote-scgi
           71  +option instead, probably with a --fromip option to specify the IP address
           72  +of the machine that is running the webserver.
    68     73   
    69     74   1.2 Using Plain Old Tclsh
    70     75   -------------------------
    71     76   
    72     77   Wapp applications are pure TCL code.  You can run them using an ordinary
    73     78   "tclsh" command if desired, instead of the "wapptclsh" shown above.  We
    74     79   normally use "wapptclsh" for the following reasons:

Changes to docs/params.md.

   244    244        it should invoke the "wapp-allow-xorigin-params" interface to explicitly
   245    245        signal that cross-origin parameters are safe for that page.
   246    246   
   247    247     +  **SELF\_URL**  
   248    248        The URL for the current page, stripped of query parameter. This is
   249    249        useful for filling in the action= attribute of forms.
   250    250   
          251  +  +  **SERVER\_ADDR**  
          252  +     In SCGI mode only, this variable is the address of the webserver from which
          253  +     the SCGI request originates.
          254  +
   251    255     +  **WAPP\_MODE**  
   252    256        This parameter has a value of "cgi", "local", "scgi", or "server" depending
   253    257        on how Wapp was launched.
   254    258   
   255    259   
   256    260   ### 3.1 URL Parsing Example
   257    261   

Changes to docs/quickref.md.

    64     64   |REMOTE\_ADDR|→|IP address of the client|
    65     65   |REMOTE\_PORT|→|TCP port of the client|
    66     66   |REQUEST\_METHOD|→|"GET" or "POST" or "HEAD"|
    67     67   |SAME\_ORIGIN|→|True if this request is from the same origin|
    68     68   |SCRIPT\_FILENAME|→|Full pathname of the Wapp application script|
    69     69   |SCRIPT\_NAME|→|Prefix of PATH\_INFO that identifies the application script|
    70     70   |SELF\_URL|→|URL of this request without PATH\_TAIL|
    71         -|WAPP\_MODE|→|One of "cgi", "scgi", "server", or "local"|
           71  +|SERVER\_ADDR|→|IP address of the webserver sending an SCGI request|
           72  +|WAPP\_MODE|→|One of "cgi", "scgi", "remote-scgi", "server", or "local"|
    72     73   
    73     74   4.0 URL Parsing
    74     75   ---------------
    75     76   
    76     77   Assuming "env.tcl" is the name of the Wapp application script:
    77     78   
    78     79   >

Changes to wapp.tcl.

   363    363   
   364    364   # Start up a listening socket.  Arrange to invoke wappInt-new-connection
   365    365   # for each inbound HTTP connection.
   366    366   #
   367    367   #    port            Listen on this TCP port.  0 means to select a port
   368    368   #                    that is not currently in use
   369    369   #
   370         -#    wappmode        One of "scgi", "server", or "local".
          370  +#    wappmode        One of "scgi", "remote-scgi", "server", or "local".
   371    371   #
   372         -proc wappInt-start-listener {port wappmode} {
   373         -  if {$wappmode=="scgi"} {
          372  +#    fromip          If not {}, then reject all requests from IP addresses
          373  +#                    other than $fromip
          374  +#
          375  +proc wappInt-start-listener {port wappmode fromip} {
          376  +  if {[string match *scgi $wappmode]} {
   374    377       set type SCGI
   375         -    set server [list wappInt-new-connection wappInt-scgi-readable $wappmode]
          378  +    set server [list wappInt-new-connection \
          379  +                wappInt-scgi-readable $wappmode $fromip]
   376    380     } else {
   377    381       set type HTTP
   378         -    set server [list wappInt-new-connection wappInt-http-readable $wappmode]
          382  +    set server [list wappInt-new-connection \
          383  +                wappInt-http-readable $wappmode $fromip]
   379    384     }
   380         -  if {$wappmode=="local"} {
          385  +  if {$wappmode=="local" || $wappmode=="scgi"} {
   381    386       set x [socket -server $server -myaddr 127.0.0.1 $port]
   382    387     } else {
   383    388       set x [socket -server $server $port]
   384    389     }
   385    390     set coninfo [chan configure $x -sockname]
   386    391     set port [lindex $coninfo 2]
   387    392     if {$wappmode=="local"} {
   388    393       wappInt-start-browser http://127.0.0.1:$port/
          394  +  } elseif {$fromip!=""} {
          395  +    puts "Listening for $type requests on TCP port $port from IP $fromip"
   389    396     } else {
   390    397       puts "Listening for $type requests on TCP port $port"
   391    398     }
   392    399   }
   393    400   
   394    401   # Start a web-browser and point it at $URL
   395    402   #
................................................................................
   404    411     }
   405    412   }
   406    413   
   407    414   # This routine is a "socket -server" callback.  The $chan, $ip, and $port
   408    415   # arguments are added by the socket command.
   409    416   #
   410    417   # Arrange to invoke $callback when content is available on the new socket.
   411         -# The $callback will process inbound HTTP or SCGI content.
          418  +# The $callback will process inbound HTTP or SCGI content.  Reject the
          419  +# request if $fromip is not an empty string and does not match $ip.
   412    420   #
   413         -proc wappInt-new-connection {callback wappmode chan ip port} {
          421  +proc wappInt-new-connection {callback wappmode fromip chan ip port} {
   414    422     upvar #0 wappInt-$chan W
          423  +  if {$fromip!="" && ![string match $fromip $ip]} {
          424  +    close $chan
          425  +    return
          426  +  }
   415    427     set W [dict create REMOTE_ADDR $ip REMOTE_PORT $port WAPP_MODE $wappmode \
   416    428            .header {}]
   417    429     fconfigure $chan -blocking 0 -translation binary
   418    430     fileevent $chan readable [list $callback $chan]
   419    431   }
   420    432   
   421    433   # Close an input channel
................................................................................
   803    815     if {![dict exists $W .toread]} {
   804    816       # If the .toread key is not set, that means we are still reading
   805    817       # the header.
   806    818       #
   807    819       # An SGI header is short.  This implementation assumes the entire
   808    820       # header is available all at once.
   809    821       #
          822  +    dict set W .remove_addr [dict get $W REMOTE_ADDR]
   810    823       set req [read $chan 15]
   811    824       set n [string length $req]
   812    825       scan $req %d:%s len hdr
   813    826       incr len [string length "$len:,"]
   814    827       append hdr [read $chan [expr {$len-15}]]
   815    828       foreach {nm val} [split $hdr \000] {
   816    829         if {$nm==","} break
................................................................................
   821    834         set len [dict get $W CONTENT_LENGTH]
   822    835       }
   823    836       if {$len>0} {
   824    837         # Still need to read the query content
   825    838         dict set W .toread $len
   826    839       } else {
   827    840         # There is no query content, so handle the request immediately
          841  +      dict set W SERVER_ADDR [dict get $W .remove_addr]
   828    842         set wapp $W
   829    843         wappInt-handle-request $chan 0
   830    844       }
   831    845     } else {
   832    846       # If .toread is set, that means we are reading the query content.
   833    847       # Continue reading until .toread reaches zero.
   834    848       set got [read $chan [dict get $W .toread]]
   835    849       dict append W CONTENT $got
   836    850       dict set W .toread [expr {[dict get $W .toread]-[string length $got]}]
   837    851       if {[dict get $W .toread]<=0} {
   838    852         # Handle the request as soon as all the query content is received
          853  +      dict set W SERVER_ADDR [dict get $W .remove_addr]
   839    854         set wapp $W
   840    855         wappInt-handle-request $chan 0
   841    856       }
   842    857     }
   843    858   }
   844    859   
   845    860   # Start up the wapp framework.  Parameters are a list passed as the
   846    861   # single argument.
   847    862   #
   848    863   #    -server $PORT         Listen for HTTP requests on this TCP port $PORT
   849    864   #
   850    865   #    -local $PORT          Listen for HTTP requests on 127.0.0.1:$PORT
   851    866   #
   852         -#    -scgi $PORT           Listen for SCGI requests on TCP port $PORT
          867  +#    -scgi $PORT           Listen for SCGI requests on 127.0.0.1:$PORT
          868  +#
          869  +#    -remote-scgi $PORT    Listen for SCGI requests on TCP port $PORT
   853    870   #
   854    871   #    -cgi                  Handle a single CGI request
   855    872   #
   856    873   # With no arguments, the behavior is called "auto".  In "auto" mode,
   857    874   # if the GATEWAY_INTERFACE environment variable indicates CGI, then run
   858    875   # as CGI.  Otherwise, start an HTTP server bound to the loopback address
   859    876   # only, on an arbitrary TCP port, and automatically launch a web browser
   860    877   # on that TCP port.
   861    878   #
   862    879   # Additional options:
   863    880   #
          881  +#    -fromip GLOB         Reject any incoming request where the remote
          882  +#                         IP address does not match the GLOB pattern.  This
          883  +#                         value defaults to '127.0.0.1' for -local and -scgi.
          884  +#
   864    885   #    -nowait              Do not wait in the event loop.  Return immediately
   865    886   #                         after all event handlers are established.
   866    887   #
   867    888   #    -trace               "puts" each request URL as it is handled, for
   868    889   #                         debugging
   869    890   #
   870    891   #    -lint                Run wapp-safety-check on the application instead
................................................................................
   874    895   #
   875    896   #
   876    897   proc wapp-start {arglist} {
   877    898     global env
   878    899     set mode auto
   879    900     set port 0
   880    901     set nowait 0
          902  +  set fromip {}
   881    903     set n [llength $arglist]
   882    904     for {set i 0} {$i<$n} {incr i} {
   883    905       set term [lindex $arglist $i]
   884    906       if {[string match --* $term]} {set term [string range $term 1 end]}
   885    907       switch -glob -- $term {
   886    908         -server {
   887    909           incr i;
   888    910           set mode "server"
   889    911           set port [lindex $arglist $i]
   890    912         }
   891    913         -local {
   892    914           incr i;
   893    915           set mode "local"
          916  +        set fromip 127.0.0.1
   894    917           set port [lindex $arglist $i]
   895    918         }
   896    919         -scgi {
   897    920           incr i;
   898    921           set mode "scgi"
          922  +        set fromip 127.0.0.1
          923  +        set port [lindex $arglist $i]
          924  +      }
          925  +      -remote-scgi {
          926  +        incr i;
          927  +        set mode "remote-scgi"
   899    928           set port [lindex $arglist $i]
   900    929         }
   901    930         -cgi {
   902    931           set mode "cgi"
   903    932         }
          933  +      -fromip {
          934  +        incr i
          935  +        set fromip [lindex $arglist $i]
          936  +      }
   904    937         -nowait {
   905    938           set nowait 1
   906    939         }
   907    940         -trace {
   908    941           proc wappInt-trace {} {
   909    942             set q [wapp-param QUERY_STRING]
   910    943             set uri [wapp-param BASE_URL][wapp-param PATH_INFO]
................................................................................
   928    961           }
   929    962         }
   930    963         default {
   931    964           error "unknown option: $term"
   932    965         }
   933    966       }
   934    967     }
   935         -  if {($mode=="auto"
   936         -       && [info exists env(GATEWAY_INTERFACE)]
   937         -       && [string match CGI/1.* $env(GATEWAY_INTERFACE)])
   938         -    || $mode=="cgi"
   939         -  } {
   940         -    wappInt-handle-cgi-request
   941         -    return
          968  +  if {$mode=="auto"} {
          969  +    if {[info exists env(GATEWAY_INTERFACE)]
          970  +        && [string match CGI/1.* $env(GATEWAY_INTERFACE)]} {
          971  +      set mode cgi
          972  +    } else {
          973  +      set mode local
          974  +    }
   942    975     }
   943         -  if {$mode=="scgi"} {
   944         -    wappInt-start-listener $port scgi
   945         -  } elseif {$mode=="server"} {
   946         -    wappInt-start-listener $port server
          976  +  if {$mode=="cgi"} {
          977  +    wappInt-handle-cgi-request
   947    978     } else {
   948         -    wappInt-start-listener $port local
   949         -  }
   950         -  if {!$nowait} {
   951         -    vwait ::forever
          979  +    wappInt-start-listener $port $mode $fromip
          980  +    if {!$nowait} {
          981  +      vwait ::forever
          982  +    }
   952    983     }
   953    984   }
   954    985   
   955    986   # Call this version 1.0
   956    987   package provide wapp 1.0