Tcl Library Source Code

Changes On Branch odie
Login

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

Changes In Branch odie Excluding Merge-Ins

This is equivalent to a diff from 4522045077 to f819e0bcac

2016-11-21
20:32
Added a new command to the coroutine::util namespace: gets_safety. It is intended for http servers which have to read in a line from a socket, but where attackers may attempt to pull off a stack overflow by sending a stream of data with no newline. Modified the http package to utilize the new gets_safety command. Closed-Leaf check-in: f819e0bcac user: hypnotoad tags: odie
2016-11-12
04:53
Fix syntax error in fumagic/rtcore.tcl. check-in: b5a9ce2f78 user: pooryorick tags: trunk
2016-11-09
15:30
Pulling changes from trunk check-in: bd97f4c914 user: hypnotoad tags: odie
2016-11-08
10:00
fumagic/cfront {deal with comments on a mimetype line} check-in: 4522045077 user: pooryorick tags: trunk
09:21
fumagic/cfront.tcl {fix error in filtypes.tcl generation for "name" test types} fumagic/filetypes.tcl {workaround for bad syntax in Magdir/dos} fumagic/rtcore.tcl {fix error message in [use] command} fumagic/fumagic.testsupport {fix Pe test} check-in: 30da0c3db0 user: pooryorick tags: trunk

Changes to modules/coroutine/coroutine.tcl.

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
	# builtin gets command with the bogus arguments gives us the
	# necessary error with the proper message.
	tailcall ::chan gets {*}$args
    }

    # Loop until we have a complete line. Yield to the event loop
    # where necessary. During 

    while {1} {
        set blocking [::chan configure $chan -blocking]
        ::chan configure $chan -blocking 0

	try {
	    set result [::chan gets $chan line]
	} on error {result opts} {
            ::chan configure $chan -blocking $blocking
            return -code $result -options $opts







|

<







173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
	# builtin gets command with the bogus arguments gives us the
	# necessary error with the proper message.
	tailcall ::chan gets {*}$args
    }

    # Loop until we have a complete line. Yield to the event loop
    # where necessary. During 
    set blocking [::chan configure $chan -blocking]
    while {1} {

        ::chan configure $chan -blocking 0

	try {
	    set result [::chan gets $chan line]
	} on error {result opts} {
            ::chan configure $chan -blocking $blocking
            return -code $result -options $opts
200
201
202
203
204
205
206





































207
208
209
210
211
212
213
                return $result
            } else {
                return $line
            }
        }
    }
}






































# - -- --- ----- -------- -------------

proc ::coroutine::util::read {args} {
    # Process arguments.
    # Acceptable syntax:
    # * read ?-nonewline ? CHAN







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
                return $result
            } else {
                return $line
            }
        }
    }
}


proc ::coroutine::util::gets_safety {chan limit varname} {
    # Process arguments.
    # Acceptable syntax:
    # * gets CHAN ?VARNAME?

    # Loop until we have a complete line. Yield to the event loop
    # where necessary. During 
    set blocking [::chan configure $chan -blocking]
    upvar 1 $varname line
    try {
	while {1} {
	    ::chan configure $chan -blocking 0
	    if {[::chan pending input $chan]>= $limit} {
		error {Too many notes, Mozart. Too many notes}
	    }
	    try {
		set result [::chan gets $chan line]
	    } on error {result opts} {
		return -code $result -options $opts
	    }
    
	    if {[::chan blocked $chan]} {
		::chan event $chan readable [list [info coroutine]]
		yield
		::chan event $chan readable {}
	    } else {
		return $result
	    }
	}
    } finally {
        ::chan configure $chan -blocking $blocking
    }
}



# - -- --- ----- -------- -------------

proc ::coroutine::util::read {args} {
    # Process arguments.
    # Acceptable syntax:
    # * read ?-nonewline ? CHAN

Changes to modules/coroutine/tcllib_coroutine.man.

74
75
76
77
78
79
80







81
82
83
84
85
86
87
returned.

[call [cmd {coroutine::util gets}] [arg chan] [opt [arg varname]]]

This command reads a line from the channel [arg chan] and returns it
either as its result, or, if a [arg varname] was specified, writes it
to the named variable and returns the number of characters read.








[call [cmd {coroutine::util global}] [arg varname]...]

This command imports the named global variables of the coroutine into
the current scope. From the technical point of view these variables
reside in level [const #1] of the Tcl stack. I.e. these are not the
regular global variable in to the global namespace, and each coroutine







>
>
>
>
>
>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
returned.

[call [cmd {coroutine::util gets}] [arg chan] [opt [arg varname]]]

This command reads a line from the channel [arg chan] and returns it
either as its result, or, if a [arg varname] was specified, writes it
to the named variable and returns the number of characters read.

[call [cmd {coroutine::util gets_safety}] [arg chan] [arg limit] [arg varname]]

This command reads a line from the channel [arg chan] up to size [arg limit]
and stores the result in [arg varname]. Of [arg limit] is reached before the
set first newline, an error is thrown. The command returns the number of
characters read.

[call [cmd {coroutine::util global}] [arg varname]...]

This command imports the named global variables of the coroutine into
the current scope. From the technical point of view these variables
reside in level [const #1] of the Tcl stack. I.e. these are not the
regular global variable in to the global namespace, and each coroutine

Changes to modules/httpd/httpd.tcl.

464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479






480
481

482
483
484

485
486
487
488
489
490
491
492
493
  }
  
  method connect {sock ip port} {
    ###
    # If an IP address is blocked
    # send a "go to hell" message
    ###
    if {[my validation Blocked_IP $sock $ip]} {
      catch {close $sock}
      return
    }
    
    chan configure $sock \
      -blocking 0 \
      -translation {auto crlf} \
      -buffering line






    chan event $sock readable [namespace code [list my Connect $sock $ip]]
  }

  method Connect {sock ip} {
    chan even $sock readable {}
    my counter url_hit

    try {
      set readCount [gets $sock line]
      dict set query REMOTE_ADDR     $ip
      dict set query REQUEST_METHOD  [lindex $line 0]
      set uriinfo [::uri::split [lindex $line 1]]
      dict set query REQUEST_URI     [lindex $line 1]
      dict set query REQUEST_PATH    [dict get $uriinfo path]
      dict set query REQUEST_VERSION [lindex [split [lindex $line end] /] end]
      if {[dict get $uriinfo host] eq {}} {







|



|




>
>
>
>
>
>
|

>
|
<

>

|







464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489

490
491
492
493
494
495
496
497
498
499
500
  }
  
  method connect {sock ip port} {
    ###
    # If an IP address is blocked
    # send a "go to hell" message
    ###
    if {[my Validate_Connection $sock $ip]} {
      catch {close $sock}
      return
    }
    set uuid [::tool::uuid_short] 
    chan configure $sock \
      -blocking 0 \
      -translation {auto crlf} \
      -buffering line
    
    set coro [coroutine [namespace current]::CORO$uuid ::apply [list {uuid sock ip} {
      yield [info coroutine]
      tailcall my Connect $uuid $sock $ip
    } [namespace current]] $uuid $sock $ip]

    chan event $sock readable $coro
  }
  
  method Connect {uuid sock ip} {

    my counter url_hit
    set line {}
    try {
      set readCount [::coroutine::util::gets_safety $sock 4096 line]
      dict set query REMOTE_ADDR     $ip
      dict set query REQUEST_METHOD  [lindex $line 0]
      set uriinfo [::uri::split [lindex $line 1]]
      dict set query REQUEST_URI     [lindex $line 1]
      dict set query REQUEST_PATH    [dict get $uriinfo path]
      dict set query REQUEST_VERSION [lindex [split [lindex $line end] /] end]
      if {[dict get $uriinfo host] eq {}} {
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549

550
551
552
553
554
555
556
      set reply [my dispatch $query]
      if {[llength $reply]} {
        if {[dict exists $reply class]} {
          set class [dict get $reply class]          
        } else {
          set class [my cget reply_class]
        }  
        set pageobj [$class create [namespace current]::reply::[::tool::uuid_short] [self]]
        if {[dict exists $reply mixin]} {
          oo::objdefine $pageobj mixin [dict get $reply mixin]
        }
        $pageobj dispatch $sock $reply
        my log HttpAccess $line
      } else {
        try {
          my log HttpMissing $line
          chan puts $sock "HTTP/1.0 404 NOT FOUND"
          dict with query {}
          set body [subst [my template notfound]]
          chan puts $sock "Content-length: [string length $body]"
          chan puts $sock
          chan puts $sock $body
        } on error {err errdat} {
          puts stderr "FAILED ON 404: $err"
        } finally {
          catch {chan close $sock}

        }
      }
    } on error {err errdat} {
      try {
        puts stderr [dict print $errdat]
        chan puts $sock "HTTP/1.0 505 INTERNAL ERROR"
        dict with query {}
        set body [subst [my template internal_error]]
        chan puts $sock "Content-length: [string length $body]"
        chan puts $sock
        chan puts $sock $body
        my log HttpError $line
      } on error {err errdat} {
        puts stderr "FAILED ON 505: $::errorInfo"
      } finally {
        catch {chan close $sock}

      }
    }
  }

  method counter which {
    my variable counters
    incr counters($which)







|


















>
















>







515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
      set reply [my dispatch $query]
      if {[llength $reply]} {
        if {[dict exists $reply class]} {
          set class [dict get $reply class]          
        } else {
          set class [my cget reply_class]
        }  
        set pageobj [$class create [namespace current]::reply$uuid [self]]
        if {[dict exists $reply mixin]} {
          oo::objdefine $pageobj mixin [dict get $reply mixin]
        }
        $pageobj dispatch $sock $reply
        my log HttpAccess $line
      } else {
        try {
          my log HttpMissing $line
          chan puts $sock "HTTP/1.0 404 NOT FOUND"
          dict with query {}
          set body [subst [my template notfound]]
          chan puts $sock "Content-length: [string length $body]"
          chan puts $sock
          chan puts $sock $body
        } on error {err errdat} {
          puts stderr "FAILED ON 404: $err"
        } finally {
          catch {chan close $sock}
          catch {destroy $pageobj}
        }
      }
    } on error {err errdat} {
      try {
        puts stderr [dict print $errdat]
        chan puts $sock "HTTP/1.0 505 INTERNAL ERROR"
        dict with query {}
        set body [subst [my template internal_error]]
        chan puts $sock "Content-length: [string length $body]"
        chan puts $sock
        chan puts $sock $body
        my log HttpError $line
      } on error {err errdat} {
        puts stderr "FAILED ON 505: $::errorInfo"
      } finally {
        catch {chan close $sock}
        catch {destroy $pageobj}
      }
    }
  }

  method counter which {
    my variable counters
    incr counters($which)
672
673
674
675
676
677
678
679
680
681
682
683
684
  }
  
  ###
  # Return true if this IP address is blocked
  # The socket will be closed immediately after returning
  # This handler is welcome to send a polite error message
  ###
  method validation::Blocked_IP {sock ip} {
    return 0
  }
}

package provide httpd 4.0.1







|





681
682
683
684
685
686
687
688
689
690
691
692
693
  }
  
  ###
  # Return true if this IP address is blocked
  # The socket will be closed immediately after returning
  # This handler is welcome to send a polite error message
  ###
  method Validate_Connection {sock ip} {
    return 0
  }
}

package provide httpd 4.0.1