4 package require profiler
10 proc debug {level str} {
12 if {$level < $debug_level} { puts stderr "debug\[$level] $str" }
15 proc manyset {list args} {
16 foreach val $list var $args {
23 proc start_gen {use_gen_counter} {
24 global gen_counter rand_counter getlog_log rand_buf
25 random-bytes-init $use_gen_counter
29 proc packet-len {p} { expr {[string length $p]/2} }
31 proc packet-csum-ip {packet} {
34 while {[regexp {^([0-9a-f][0-9a-f])(.*)$} $packet dummy this packet]} {
37 return [expr {$cs & 0xffff}]
40 proc packet-fromstring {s} {
45 namespace eval Random-Bytes {
46 namespace export random-bytes random-bytes-init
48 proc random-bytes-init {seed} {
51 catch { set h $fh; unset fh; close $h }
53 set fh [open |[list openssl bf-ofb < /dev/zero -e -k " $seed"] r]
54 fconfigure $fh -translation binary
56 proc random-bytes {n} {
59 if {[string length $x] != $n} {
60 set h $fh; unset fh; close $h
61 error "openssl bf-ofb exited unexpectedly"
63 set y [packet-fromstring $x]
68 namespace import Random-Bytes::*
70 proc choice-int {min max} {
71 set rv 0x[random-bytes 3]
73 int( double($rv) / double(0x1000000) * double($max+1-$min) )
78 proc choice-prob {cv def} {
79 set prob [config $cv $def]
80 set rv 0x[random-bytes 3]
81 return [expr {$rv < double($prob)*0x1000000}]
84 proc choice-mult {args} {
85 if {!([llength $args] % 2)} { error "choice-mult must have default" }
86 set x 0x[random-bytes 3]
87 set x [expr { double($x) / double(0x1000000) }]
89 set def [lindex $args end]
90 set args [lreplace $args end end]
91 foreach {val p} $args {
92 set cump [expr {$cump + double($p)}]
93 if {$x < $cump} { return $val }
99 upvar #0 getlog_log log
101 debug 2 "getlog $msg"
104 proc config {cv def} {
105 upvar #0 config/$cv v
106 if {[info exists v]} { return $v }
111 proc define {enum val name argnames body} {
112 upvar #0 enum/val2name/$enum v2n
113 upvar #0 enum/name2val/$enum n2v
116 proc enum/val/$enum/$val $argnames $body
119 proc depending-on {scope enum_and_var mtu mtuadjust args} {
120 upvar 1 $enum_and_var val
121 set mtu [expr {$mtu + $mtuadjust}]
122 set procname enum/val/$scope-$enum_and_var/[format %d $val]
123 if {[choice-prob $enum_and_var-unstruct 0.1] ||
124 [catch { info body $procname }]} {
125 # half the time random
131 uplevel 1 [list $procname] $mtu $args
136 proc get-for {scope} {
141 proc get {variable kind args} {
142 upvar 1 get/scope scope
143 upvar 1 $variable var
144 set var [eval [list get/$kind $scope $variable] $args]
147 proc get-config/number {val min max} { return $val }
148 proc get-config/v4addr {val} {
149 if {![regexp {^(\d+)\.(\d+)\.(\d+)\.(\d+)$} $val dummy a b c d]} {
150 error "bad v4addr ?$val?"
152 return [format 0x%02x%02x%02x%02x $a $b $c $d]
155 proc get-config {variable def kind args} {
156 # args currently ignored
157 upvar 1 get/scope scope
158 upvar 1 $variable var
159 set val [config $scope-$variable $def]
160 set var [eval [list get-config/$kind $val] $args]
163 proc get-enum-got {s v rv} {
164 upvar #0 enum/val2name/$s-$v v2n
165 if {[info exists v2n($rv)]} {
166 getlog "$v=$v2n($rv)\[$rv]"
173 proc get/enum-rand {s v min max} {
174 set rv [choice-int $min $max]
175 return [get-enum-got $s $v $rv]
178 proc get/enum-def {s v} {
179 upvar #0 enum/val2name/$s-$v v2n
180 set rv [choice-int 1 [array size v2n]]
181 set rv [lindex [array names v2n] [expr {$rv-1}]]
182 return [get-enum-got $s $v $rv]
185 proc get/enum {s v min max prand} {
187 get any choice $prand
189 return [get/enum-rand $s $v $min $max]
191 return [get/enum-def $s $v]
195 proc get/number {s v min max} {
196 set rv [choice-int $min $max]
201 proc get/hex {s v min max} {
202 set rv [choice-int $min $max]
203 getlog [format %s=0x%x $v $rv]
207 proc get/flag {s v defprob} {
208 set rv [choice-prob $s-$v $defprob]
209 if {$rv} { getlog "$v" } else { getlog "!$v" }
213 proc get/choice {s v defprob} {
214 set rv [choice-prob $s-$v $defprob]
215 if {$rv} { getlog "($v)" } else { getlog "(!$v)" }
219 proc get/rand {s v minlen maxlen} {
221 get l number $minlen $maxlen
222 return [random-bytes $l]
225 proc get/ip-timestamp {s v} {
226 set rv [expr {[clock seconds] | 0x80000000}]
227 getlog "$v=[format %x $rv]"
231 proc get/v4addr {s v} {
235 for {set i 0} {$i<4} {incr i} {
236 set b [random-bytes 1]
238 append p $d [format %d 0x$b]
245 proc get/choice-mult {s v args} {
246 set rv [eval choice-mult $args]
251 proc get/string {s v minlen maxlen first rest} {
254 for {set l [choice-int $minlen $maxlen]} {$l>0} {incr l -1} {
255 set cn [choice-int 0 [expr {[string length $now]-1}]]
256 append o [string index $now $cn]
260 return [packet-fromstring $o]
264 namespace eval Assembler {
265 namespace export assemble assembly-overwrite
267 proc assemble {outvarname format} {
268 # format should look like those RFC diagrams.
269 # +-+-+ stuff and good formatting is mandatory.
270 # Tabs are forbidden.
272 # Field names are converted to lowercase; internal spaces
273 # are replaced with _. They are then assumed to be
274 # variable names in the caller's scope. The packet is
275 # assembled from those values (which must all be set)
276 # and stored in $varname in the caller's scope.
278 # Variables ?_whatever will be *set* with the location of the
279 # field in the string (in internal format); the corresponding
280 # `whatever' (with the ?_ stripped) will be read when assembling.
282 # Field name `0' means set the field to zero.
285 upvar 1 $outvarname out
286 if {[catch { set parsed $cache($format) }]} {
287 set parsed [parse $format]
288 set cache($format) $parsed
291 manyset $parsed outbytes lout
292 set out [string repeat 00 $outbytes]
293 foreach {location varname locvarname} $lout {
294 if {"$varname" == "0"} {
297 set value [uplevel 1 [list set $varname]]
299 if {[string length $locvarname]} {
300 upvar 1 $locvarname lv
303 assembly-overwrite out $location $value
307 proc parse {format} {
310 debug 7 "ASSEMBLY $format"
311 foreach l [split $format "\n"] {
313 if {[regexp -nocase {^ *\| +\| *$} $l]} {
314 if {![info exists wordbits]} {
315 error "vspace not in data @$lno\n?$l?"
318 } elseif {[regexp -nocase {^ *[|? a-z0-9]+$} $l]} {
319 if {[info exists words]} {
320 error "data without delimline @$lno\n?$l?"
324 } elseif {[regexp {^ *[-+]+ *$} $l]} {
327 while {[regexp {^([-= ]+)\+(.*)$} $l dummy before after]} {
328 set atpos([string length $before]) $wordbits
330 set l "$before=$after"
331 append newlineform "@[string length $before]:$wordbits "
334 append newlineform $wordbits
335 if {[info exists lineform]} {
336 if {"$newlineform" != "$lineform"} {
337 error "format change @$lno\n?$l?\n$wordformat != $oldwordformat"
339 if {![info exists words] || $words<0} {
340 error "consecutive delimlines @$lno\n?$l?"
342 incr outbytes [expr {$words*$wordbits/8}]
344 while {[regexp -nocase \
345 {^([ =]*)\|( *[? a-z0-9]*[a-z0-9] *)\|(.*)$} \
346 $l dummy before midpart after]} {
347 debug 7 "RWORKG ?$l?"
348 set varname [string tolower [string trim $midpart]]
349 set varname [string map {{ } _} $varname]
350 set p1 [string length $before]
352 [string length $before] +
353 [string length $midpart] + 1
355 if {![info exists atpos($p1)] ||
356 ![info exists atpos($p2)]} {
357 error "vdelims not found @$lno $varname $p1 $p2 -- $lineform ?$cue?"
365 $atpos($p2) - $atpos($p1) + ($words-1)*$wordbits
367 set location [list $bit1 $bitlen $varname]
368 if {[regexp {^\?_(.*)} $varname dummy realvarname]} {
369 debug 7 "LOCATING $varname $location"
370 set locvarname $varname
371 set varname $realvarname
375 lappend lout $location $varname $locvarname
377 append l [string repeat = [string length $midpart]]
380 debug 7 "REMAIN ?$l?"
382 if {$wordbits % 8 || $wordbits >32} {
383 error "bad wordbits $wordbits @$lno ?$l? $newlineform"
385 set lineform $newlineform
387 catch { unset words }
388 } elseif {[regexp {^ *$} $l]} {
390 error "huh? @$lno ?$l?"
393 debug 7 "ASSEMBLY\n$outbytes\n$lout\n"
394 return [list $outbytes $lout]
397 proc assembly-overwrite {outvarname location value} {
398 upvar 1 $outvarname out
399 manyset $location bit1 bitlen diag
400 debug 8 "ASSEMBLY $diag == $value == 0x[format %x $value] (@$bit1+$bitlen)"
401 if {$bitlen < 32 && $value >= (1<<$bitlen)} {
402 error "$diag $value >= 2**$bitlen"
404 if {!($bit1 % 8) && !($bitlen % 8)} {
405 set char0no [expr {$bit1/4}]
406 set charlen [expr {$bitlen/4}]
407 set chareno [expr {$char0no + $charlen -1}]
408 set repl [format %0${charlen}x $value]
409 set out [string replace $out $char0no $chareno $repl]
411 while {$bitlen > 0} {
412 set byteno [expr {$bit1 / 8}]
413 set char0no [expr {$byteno*2}]
414 set char1no [expr {$char0no+1}]
415 set bytebit [expr {128>>($bit1 % 8)}]
416 set byte 0x[string range $out $char0no $char1no]
417 debug 7 "< $bit1+$bitlen $byteno $char0no $bytebit $byte $out"
419 ($value & (1<<($bitlen-1)))
421 : ($byte & ~$bytebit)
423 set out [string replace $out $char0no $char1no \
425 debug 8 "> $bit1+$bitlen $byteno $char0no $bytebit 0x[format %02x $byte] $out"
432 namespace import Assembler::*
434 proc gen_1_ip {mtu} {
436 upvar #0 ip_proto proto
437 upvar #0 ip_source source
438 upvar #0 ip_dest dest
441 get tos hex 0x00 0xff
442 get id hex 0x0000 0xffff
449 get frag number 0 0x1fff
451 get-config ttl 255 number 0 255
452 get proto enum 1 255 0.05
453 get-config source 127.0.0.1 v4addr
454 get-config dest 127.0.0.1 v4addr
455 # we don't do any IP options
457 set body [depending-on ip proto $mtu [expr {-4*$ihl}]]
458 set total_length [expr {$ihl + [packet-len $body]}]
459 set header_checksum 0
460 set flags [expr {$df*2 + $mf}]
462 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
463 |Version| IHL |TOS | Total Length |
464 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
466 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
467 | TTL | Proto | ? Header Checksum |
468 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
470 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
472 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
474 assembly-overwrite ip ${?_header_checksum} [packet-csum-ip $ip]
479 define ip-proto 1 icmp {mtu} {
482 get type enum 0 255 0.2
483 manyset [depending-on icmp type $mtu -4] body code
484 if {![string length $code]} { get code number 0 255 }
487 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
488 | Type | Code | ? Checksum |
489 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
492 assembly-overwrite icmp ${?_checksum} [packet-csum-ip $icmp]
496 proc define-icmp-type-vanilla {num name} {
497 define icmp-type $num $name {mbl} "icmp-vanilla \$mbl [list $name]"
499 proc icmp-vanilla {mbl typename} {
500 get-for icmp-$typename
501 get code enum 0 255 0.4
503 return [list $body $code]
506 define-icmp-type-vanilla 3 unreach
507 define icmp-unreach-code 0 net {} {}
508 define icmp-unreach-code 1 host {} {}
509 define icmp-unreach-code 2 proto {} {}
510 define icmp-unreach-code 3 port {} {}
511 define icmp-unreach-code 4 fragneeded {} {}
512 define icmp-unreach-code 5 sourceroutefail {} {}
514 define-icmp-type-vanilla 11 timeout
515 define icmp-timeout-code 0 intransit {} {}
516 define icmp-timeout-code 1 fragment {} {}
518 define-icmp-type-vanilla 12 parameters
519 define icmp-parameters-code 0 seepointer {} {}
521 define-icmp-type-vanilla 4 sourcequench
522 define icmp-sourcequench-code 0 quench {} {}
524 define icmp-type 5 redirect {mbl} {
525 get-for icmp-redirect
526 get code enum 0 255 0.4
529 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
531 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
533 get data rand 0 [expr {$mbl-4}]
535 return [list $body $code]
538 define icmp-redirect-code 0 net {} {}
539 define icmp-redirect-code 1 host {} {}
540 define icmp-redirect-code 2 net+tos {} {}
541 define icmp-redirect-code 3 host+tos {} {}
543 define icmp-type 8 ping {mbl} { icmp-echo $mbl }
544 define icmp-type 0 pong {mbl} { icmp-echo $mbl }
545 proc icmp-echo {mbl} {
547 get code enum 0 255 0.4
551 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
553 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
555 get data rand 0 [expr {$mbl-8}]
557 return [list $body $code]
559 define icmp-echo-code 0 echo {} {}
561 define icmp-type 13 timestamp {mbl} { icmp-timestamp }
562 define icmp-type 14 timestampreply {mbl} { icmp-timestamp }
563 proc icmp-timestamp {} {
564 get-for icmp-timestamp
565 get code enum 0 255 0.4
568 get originate ip-timestamp
569 get receive ip-timestamp
570 get transmit ip-timestamp
572 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
574 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
576 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
578 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
580 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
582 return [list $body $code]
584 define icmp-timestamp-code 0 timestamp {} {}
586 define icmp-type 15 inforequest {mbl} { icmp-inforeq }
587 define icmp-type 16 inforeply {mbl} { icmp-inforeq }
588 proc icmp-inforeq {} {
590 get code enum 0 255 0.4
594 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
596 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
598 return [list $body $code]
600 define icmp-inforeq-code 0 timestamp {} {}
602 # MAYADD ICMP traceroute RFC1393
603 # MAYADD ICMP router discovery RFC1256
605 proc port_pair_data {scope mtu mtuadjust} {
608 get style choice-mult \
614 if {"$style" != "random"} {
618 if {"$style" != "servers"} {
619 get port enum-rand 0 0xffff
622 switch -exact $style {
623 random { set source_port $rand_port; set dest_port $rand_port }
624 request { set source_port $rand_port; set dest_port $def_port }
625 reply { set source_port $def_port; set dest_port $rand_port }
626 servers { set source_port $def_port; set dest_port $def_port }
628 if {"$style" != "random"} {
630 set data [depending-on $scope port $mtu $mtuadjust $style]
632 get data rand 0 [expr {$mtu + $mtuadjust}]
634 return [list $source_port $dest_port $data]
637 define ip-proto 17 udp {mtu} {
639 get checksum choice-mult \
643 manyset [port_pair_data udp $mtu 8] source_port dest_port data
644 set length [packet-len $data]
647 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
648 | Source Port | Dest Port |
649 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
650 | Length | ? Checksum |
651 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
654 if {"$checksum" != "none"} {
655 set csum [packet-csum-ip $udp]
656 if {!$csum} { set csum 0xffff }
657 if {"$checksum" == "bad"} {
658 get error hex 1 0xffff
659 set csum [expr {$csum ^ $error}]
664 assembly-overwrite udp ${?_checksum} $csum
667 define udp-port 50 remailck {mtu style} {
669 if {"$style" == "request"} {
670 get what choice-mult \
677 get what choice-mult \
684 switch -exact $what {
687 get user string 1 8 \
688 abcdefghijklmnopqrustuvwxyz \
689 abcdefghijklmnopqrustuvwxyz-0123456789_
693 get user rand 0 [expr {$mtu - 4}]
696 get auth enum 0 31 0.5
697 set user [depending-on remailck auth $mtu -4]
700 get auth hex 0 0xffff
705 get mail choice-mult \
711 switch -exact $mail {
725 get modified number 1 600
726 get read number 1 600
728 default { error "mail? $mail" }
731 default { error "what? $what" }
736 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
738 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
744 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
746 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
748 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
750 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
753 default { error "what?? $what" }
758 define remailck-auth 31 passwd {mtu} {
759 get-for remailck-passwd
760 get passwd string 6 8 \
761 0123456789abcdefghijklmnopqrstuvxwyz \
762 0123456789abcdefghijklmnopqrstuvxwyz
768 global getlog_log errorInfo
771 set packet [gen_1_ip 576]
772 puts stdout "[format %06d $count] $getlog_log\n $packet"
774 puts stderr "\nERROR\n$count\n\n$emsg\n\n$errorInfo\n\n"
775 puts stdout "[format %06d $count] error"
779 if {![llength $argv]} {
780 for {set count 1} {$count < 100} {incr count} { emit $count }
781 } elseif {"$argv" == "--infinite"} {
783 while 1 { emit $count; incr count }
785 foreach count $argv { emit $count }
789 puts [::profiler::dump]
791 puts ---------------------IWJ
793 puts [::profiler::print]
795 puts ---------------------IWJ
797 foreach i [::profiler::sortFunctions totalRuntime] {
799 puts [::profiler::print $f]