6 proc debug {level str} {
8 if {$level < $debug_level} { puts stderr "debug\[$level] $str" }
11 proc manyset {list args} {
12 foreach val $list var $args {
19 proc start_gen {use_gen_counter} {
20 global gen_counter rand_counter getlog_log rand_buf
21 random-bytes-init $use_gen_counter
25 proc packet-len {p} { expr {[string length $p]/2} }
27 proc packet-csum-ip {packet} {
30 while {[regexp {^([0-9a-f][0-9a-f])(.*)$} $packet dummy this packet]} {
33 return [expr {$cs & 0xffff}]
36 proc packet-fromstring {s} {
41 namespace eval Random-Bytes {
42 namespace export random-bytes random-bytes-init
44 proc random-bytes-init {seed} {
47 catch { set h $fh; unset fh; close $h }
49 set fh [open |[list openssl bf-ofb < /dev/zero -e -k " $seed"] r]
50 fconfigure $fh -translation binary
52 proc random-bytes {n} {
55 if {[string length $x] != $n} {
56 set h $fh; unset fh; close $h
57 error "openssl bf-ofb exited unexpectedly"
59 set y [packet-fromstring $x]
64 namespace import Random-Bytes::*
66 proc choice-int {min max} {
67 set rv 0x[random-bytes 3]
69 int( double($rv) / double(0x1000000) * double($max+1-$min) )
74 proc choice-prob {cv def} {
75 set prob [config $cv $def]
76 set rv 0x[random-bytes 3]
77 return [expr {$rv < double($prob)*0x1000000}]
80 proc choice-mult {args} {
81 if {!([llength $args] % 2)} { error "choice-mult must have default" }
82 set x 0x[random-bytes 3]
83 set x [expr { double($x) / double(0x1000000) }]
85 set def [lindex $args end]
86 set args [lreplace $args end end]
87 foreach {val p} $args {
88 set cump [expr {$cump + double($p)}]
89 if {$x < $cump} { return $val }
95 upvar #0 getlog_log log
100 proc config {cv def} {
101 upvar #0 config/$cv v
102 if {[info exists v]} { return $v }
107 proc define {enum val name argnames body} {
108 upvar #0 enum/val2name/$enum v2n
109 upvar #0 enum/name2val/$enum n2v
112 proc enum/val/$enum/$val $argnames $body
115 proc depending-on {scope enum_and_var mtu mtuadjust args} {
116 upvar 1 $enum_and_var val
117 set mtu [expr {$mtu + $mtuadjust}]
118 set procname enum/val/$scope-$enum_and_var/[format %d $val]
119 if {[choice-prob $enum_and_var-unstruct 0.1] ||
120 [catch { info body $procname }]} {
121 # half the time random
127 uplevel 1 [list $procname] $mtu $args
132 proc get-for {scope} {
137 proc get {variable kind args} {
138 upvar 1 get/scope scope
139 upvar 1 $variable var
140 set var [eval [list get/$kind $scope $variable] $args]
143 proc get-config/number {val min max} { return $val }
144 proc get-config/v4addr {val} {
145 if {![regexp {^(\d+)\.(\d+)\.(\d+)\.(\d+)$} $val dummy a b c d]} {
146 error "bad v4addr ?$val?"
148 return [format 0x%02x%02x%02x%02x $a $b $c $d]
151 proc get-config {variable def kind args} {
152 # args currently ignored
153 upvar 1 get/scope scope
154 upvar 1 $variable var
155 set val [config $scope-$variable $def]
156 set var [eval [list get-config/$kind $val] $args]
159 proc get-enum-got {s v rv} {
160 upvar #0 enum/val2name/$s-$v v2n
161 if {[info exists v2n($rv)]} {
162 getlog "$v=$v2n($rv)\[$rv]"
169 proc get/enum-rand {s v min max} {
170 set rv [choice-int $min $max]
171 return [get-enum-got $s $v $rv]
174 proc get/enum-def {s v} {
175 upvar #0 enum/val2name/$s-$v v2n
176 set rv [choice-int 1 [array size v2n]]
177 set rv [lindex [array names v2n] [expr {$rv-1}]]
178 return [get-enum-got $s $v $rv]
181 proc get/enum {s v min max prand} {
183 get any choice $prand
185 return [get/enum-rand $s $v $min $max]
187 return [get/enum-def $s $v]
191 proc get/number {s v min max} {
192 set rv [choice-int $min $max]
197 proc get/hex {s v min max} {
198 set rv [choice-int $min $max]
199 getlog [format %s=0x%x $v $rv]
203 proc get/flag {s v defprob} {
204 set rv [choice-prob $s-$v $defprob]
205 if {$rv} { getlog "$v" } else { getlog "!$v" }
209 proc get/choice {s v defprob} {
210 set rv [choice-prob $s-$v $defprob]
211 if {$rv} { getlog "($v)" } else { getlog "(!$v)" }
215 proc get/rand {s v minlen maxlen} {
217 get l number $minlen $maxlen
218 return [random-bytes $l]
221 proc get/ip-timestamp {s v} {
222 set rv [expr {[clock seconds] | 0x80000000}]
223 getlog "$v=[format %x $rv]"
227 proc get/v4addr {s v} {
231 for {set i 0} {$i<4} {incr i} {
232 set b [random-bytes 1]
234 append p $d [format %d 0x$b]
241 proc get/choice-mult {s v args} {
242 set rv [eval choice-mult $args]
247 proc get/string {s v minlen maxlen first rest} {
250 for {set l [choice-int $minlen $maxlen]} {$l>0} {incr l -1} {
251 set cn [choice-int 0 [expr {[string length $now]-1}]]
252 append o [string index $now $cn]
256 return [packet-fromstring $o]
260 namespace eval Assembler {
261 namespace export assemble assembly-overwrite
263 proc assemble {outvarname format} {
264 # format should look like those RFC diagrams. +-+-+ stuff and
265 # good formatting is mandatory. You can have a single data
266 # item at the end ending in ..., which means append that data
267 # item. Tabs are forbidden.
269 # Field names are converted to lowercase; internal spaces
270 # are replaced with _. They are then assumed to be
271 # variable names in the caller's scope. The packet is
272 # assembled from those values (which must all be set)
273 # and stored in $varname in the caller's scope.
275 # Variables ?_whatever will be *set* with the location of the
276 # field in the string (in internal format); the corresponding
277 # `whatever' (with the ?_ stripped) will be read when assembling.
279 # Field name `0' means set the field to zero.
282 upvar 1 $outvarname out
283 if {[catch { set parsed $cache($format) }]} {
284 set parsed [parse $format]
285 set cache($format) $parsed
288 manyset $parsed outbytes lout
289 set out [string repeat 00 $outbytes]
290 foreach {location varname locvarname} $lout {
291 if {"$varname" == "0"} {
294 set value [uplevel 1 [list set $varname]]
296 if {[string length $locvarname]} {
297 upvar 1 $locvarname lv
300 assembly-overwrite out $location $value
304 proc parse {format} {
308 debug 7 "ASSEMBLY $format"
309 foreach l [split $format "\n"] {
311 if {[regexp -nocase {^ *\| +\| *$} $l]} {
312 if {![info exists wordbits]} {
313 error "vspace not in data @$lno\n?$l?"
316 } elseif {[regexp -nocase {^ *[|? a-z0-9.]+$} $l]} {
317 if {[info exists words]} {
318 error "data without delimline @$lno\n?$l?"
322 } elseif {[regexp {^ *[-+]+ *$} $l]} {
325 while {[regexp {^([-= ]+)\+(.*)$} $l dummy before after]} {
326 set atpos([string length $before]) $wordbits
328 set l "$before=$after"
329 append newlineform "@[string length $before]:$wordbits "
332 append newlineform $wordbits
333 if {[info exists lineform]} {
334 if {"$newlineform" != "$lineform"} {
335 error "format change @$lno\n?$l?\n$wordformat != $oldwordformat"
337 if {![info exists words] || $words<0} {
338 error "consecutive delimlines @$lno\n?$l?"
340 incr outbytes [expr {$words*$wordbits/8}]
342 while {[regexp -nocase \
343 {^([ =]*)\|( *[? a-z0-9]*[a-z0-9] *\.* *)\|(.*)$} \
344 $l dummy before midpart after]} {
345 debug 7 "RWORKG ?$l?"
347 error "two things at end @$lno\n?$l?"
349 set varname [string tolower [string trim $midpart]]
350 if {[regexp {(.*[a-z0-9])\s*\.\.\.$} $varname \
351 dummy realvarname]} {
352 set varname $realvarname
355 set varname [string map {{ } _} $varname]
356 set p1 [string length $before]
358 [string length $before] +
359 [string length $midpart] + 1
361 if {![info exists atpos($p1)] ||
362 ![info exists atpos($p2)]} {
363 error "vdelims not found @$lno $varname $p1 $p2 -- $lineform ?$cue?"
371 $atpos($p2) - $atpos($p1) + ($words-1)*$wordbits
375 error "atend not at byte @$lno\n?$l?"
377 set outbytes [expr {$bit1/8}]
378 set location [list $bit1 0 $varname]
380 set location [list $bit1 $bitlen $varname]
382 if {[regexp {^\?_(.*)} $varname dummy realvarname]} {
383 debug 7 "LOCATING $varname $location"
384 set locvarname $varname
385 set varname $realvarname
389 lappend lout $location $varname $locvarname
391 append l [string repeat = [string length $midpart]]
394 debug 7 "REMAIN ?$l?"
395 if {![regexp {^[ =]*\|? *$} $l]} {
396 error "unclear @$lno\n?$l?"
399 if {$wordbits % 8 || $wordbits >32} {
400 error "bad wordbits $wordbits @$lno ?$l? $newlineform"
402 set lineform $newlineform
404 catch { unset words }
405 } elseif {[regexp {^ *$} $l]} {
407 error "huh? @$lno ?$l?"
410 debug 7 "ASSEMBLY\n$outbytes\n$lout\n"
411 return [list $outbytes $lout]
414 proc assembly-overwrite {outvarname location value} {
415 upvar 1 $outvarname out
416 manyset $location bit1 bitlen diag
417 if {$bitlen > 0 && $bitlen != 32 && $value >= (1<<$bitlen)} {
418 error "$diag $value >= 2**$bitlen"
420 if {!($bit1 % 8) && !($bitlen % 8)} {
421 set char0no [expr {$bit1/4}]
422 set charlen [expr {$bitlen/4}]
423 set chareno [expr {$char0no + $charlen -1}]
425 debug 8 "ASSEMBLY $diag == $value == 0x[format %x $value] (@$bit1+$bitlen)"
426 set repl [format %0${charlen}x $value]
427 set out [string replace $out $char0no $chareno $repl]
429 debug 8 "ASSEMBLY-ADD $diag (@$bit1)"
430 # bitlen==0 => append
431 set out [string range $out 0 $chareno]
435 while {$bitlen > 0} {
436 set byteno [expr {$bit1 / 8}]
437 set char0no [expr {$byteno*2}]
438 set char1no [expr {$char0no+1}]
439 set bytebit [expr {128>>($bit1 % 8)}]
440 set byte 0x[string range $out $char0no $char1no]
441 debug 7 "< $bit1+$bitlen $byteno $char0no $bytebit $byte $out"
443 ($value & (1<<($bitlen-1)))
445 : ($byte & ~$bytebit)
447 set out [string replace $out $char0no $char1no \
449 debug 8 "> $bit1+$bitlen $byteno $char0no $bytebit 0x[format %02x $byte] $out"
456 namespace import Assembler::*
458 proc gen_1_ip {mtu} {
460 upvar #0 ip_proto proto
461 upvar #0 ip_source source
462 upvar #0 ip_dest dest
465 get tos hex 0x00 0xff
466 get id hex 0x0000 0xffff
473 get frag number 0 0x1fff
475 get-config ttl 255 number 0 255
476 get proto enum 1 255 0.05
477 get-config source 127.0.0.1 v4addr
478 get-config dest 127.0.0.1 v4addr
479 # we don't do any IP options
481 set body [depending-on ip proto $mtu [expr {-4*$ihl}]]
482 set total_length [expr {$ihl + [packet-len $body]}]
483 set header_checksum 0
484 set flags [expr {$df*2 + $mf}]
486 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
487 |Version| IHL |TOS | Total Length |
488 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
490 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
491 | TTL | Proto | ? Header Checksum |
492 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
494 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
496 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
498 assembly-overwrite ip ${?_header_checksum} [packet-csum-ip $ip]
503 define ip-proto 1 icmp {mtu} {
506 get type enum 0 255 0.2
507 manyset [depending-on icmp type $mtu -4] body code
508 if {![string length $code]} { get code number 0 255 }
511 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
512 | Type | Code | ? Checksum |
513 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
515 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
517 assembly-overwrite icmp ${?_checksum} [packet-csum-ip $icmp]
521 proc define-icmp-type-vanilla {num name} {
522 define icmp-type $num $name {mbl} "icmp-vanilla \$mbl [list $name]"
524 proc icmp-vanilla {mbl typename} {
525 get-for icmp-$typename
526 get code enum 0 255 0.4
528 return [list $body $code]
531 define-icmp-type-vanilla 3 unreach
532 define icmp-unreach-code 0 net {} {}
533 define icmp-unreach-code 1 host {} {}
534 define icmp-unreach-code 2 proto {} {}
535 define icmp-unreach-code 3 port {} {}
536 define icmp-unreach-code 4 fragneeded {} {}
537 define icmp-unreach-code 5 sourceroutefail {} {}
539 define-icmp-type-vanilla 11 timeout
540 define icmp-timeout-code 0 intransit {} {}
541 define icmp-timeout-code 1 fragment {} {}
543 define-icmp-type-vanilla 12 parameters
544 define icmp-parameters-code 0 seepointer {} {}
546 define-icmp-type-vanilla 4 sourcequench
547 define icmp-sourcequench-code 0 quench {} {}
549 define icmp-type 5 redirect {mbl} {
550 get-for icmp-redirect
551 get code enum 0 255 0.4
553 get data rand 0 [expr {$mbl-4}]
555 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
557 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
559 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
561 return [list $body $code]
564 define icmp-redirect-code 0 net {} {}
565 define icmp-redirect-code 1 host {} {}
566 define icmp-redirect-code 2 net+tos {} {}
567 define icmp-redirect-code 3 host+tos {} {}
569 define icmp-type 8 ping {mbl} { icmp-echo $mbl }
570 define icmp-type 0 pong {mbl} { icmp-echo $mbl }
571 proc icmp-echo {mbl} {
573 get code enum 0 255 0.4
576 get data rand 0 [expr {$mbl-8}]
578 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
580 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
582 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
584 return [list $body $code]
586 define icmp-echo-code 0 echo {} {}
588 define icmp-type 13 timestamp {mbl} { icmp-timestamp }
589 define icmp-type 14 timestampreply {mbl} { icmp-timestamp }
590 proc icmp-timestamp {} {
591 get-for icmp-timestamp
592 get code enum 0 255 0.4
595 get originate ip-timestamp
596 get receive ip-timestamp
597 get transmit ip-timestamp
599 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
601 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
603 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
605 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
607 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609 return [list $body $code]
611 define icmp-timestamp-code 0 timestamp {} {}
613 define icmp-type 15 inforequest {mbl} { icmp-inforeq }
614 define icmp-type 16 inforeply {mbl} { icmp-inforeq }
615 proc icmp-inforeq {} {
617 get code enum 0 255 0.4
621 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
623 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
625 return [list $body $code]
627 define icmp-inforeq-code 0 timestamp {} {}
629 # MAYADD ICMP traceroute RFC1393
630 # MAYADD ICMP router discovery RFC1256
632 proc port-pair {scope} {
635 get style choice-mult \
641 if {"$style" != "random"} {
647 if {"$style" != "servers"} {
648 get port enum-rand 0 0xffff
651 switch -exact $style {
652 random { set source_port $rand_port; set dest_port $rand_port }
653 request { set source_port $rand_port; set dest_port $def_port }
654 reply { set source_port $def_port; set dest_port $rand_port }
655 servers { set source_port $def_port; set dest_port $def_port }
657 return [list $source_port $dest_port $def_port $style]
660 define ip-proto 17 udp {mtu} {
663 get checksum choice-mult \
667 manyset [port-pair udp] source_port dest_port def_port style
669 if {"$style" != "random"} {
671 set data [depending-on udp port $mtu -8 $style]
673 get data rand 0 [expr {$mtu-8}]
676 set length [packet-len $data]
679 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
680 | Source Port | Dest Port |
681 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
682 | Length | ? Checksum |
683 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
685 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
687 if {"$checksum" != "none"} {
688 set csum [packet-csum-ip $udp]
689 if {!$csum} { set csum 0xffff }
690 if {"$checksum" == "bad"} {
691 get error hex 1 0xffff
692 set csum [expr {$csum ^ $error}]
697 assembly-overwrite udp ${?_checksum} $csum
700 define udp-port 50 remailck {mtu style} {
702 if {"$style" == "request"} {
703 get what choice-mult \
710 get what choice-mult \
717 switch -exact $what {
720 get user string 1 8 \
721 abcdefghijklmnopqrustuvwxyz \
722 abcdefghijklmnopqrustuvwxyz-0123456789_
726 get user rand 0 [expr {$mtu - 4}]
729 get auth enum 0 31 0.5
730 set user [depending-on remailck auth $mtu -4]
733 get auth hex 0 0xffff
738 get mail choice-mult \
744 switch -exact $mail {
758 get modified number 1 600
759 get read number 1 600
761 default { error "mail? $mail" }
764 default { error "what? $what" }
769 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
771 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
773 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
778 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
780 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
782 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
784 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
787 default { error "what?? $what" }
792 define remailck-auth 31 passwd {mtu} {
793 get-for remailck-passwd
794 get passwd string 6 8 \
795 0123456789abcdefghijklmnopqrstuvxwyz \
796 0123456789abcdefghijklmnopqrstuvxwyz
800 # define ip-proto 6 tcp {mtu} {
803 # manyset [port-pair tcp] source_port dest_port style
804 # get event choice-many \
815 # switch -exact $event {
816 # connect { set s 1; set a 0 }
819 # reset { set a 0; set r 1 }
827 # default { error "event? $event" }
829 # get seq hex 0 0xffffffff
830 # get ack hex 0 0xffffffff
831 # if {[choice-prob tcp-smallwindow 0.7]} {
832 # get window number 0 1
834 # get window hex 0 0xffff
836 # set urg hex 0 0xffff
841 # switch -exact [choice-mult tcp-opts junk 0.3 some 0.6 none] {
844 # get options rand 1 60
847 # while {[choice-prob tcp-opts-more 0.5]} {
848 # get opt enum 0 255 0.5
855 # set data [depending-on tcp opt 10 0]
858 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
859 # | Opt | ? Option Len |
860 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
862 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
864 # aseembly-overwrite option ${?_option_len} \
865 # [packet-len $option]
867 # append options $option
874 # [expr {[packet-len $od] + 2}]
876 # append options [format %02x%02s $opt \
877 # [expr {[packet-len $od]+2}]]
878 # append options $optdata
896 global getlog_log errorInfo
899 set packet [gen_1_ip 576]
900 puts stdout "[format %06d $count] $getlog_log\n $packet"
902 puts stderr "\nERROR\n$count\n\n$emsg\n\n$errorInfo\n\n"
903 puts stdout "[format %06d $count] error"
907 if {![llength $argv]} {
908 for {set count 1} {$count < 100} {incr count} { emit $count }
909 } elseif {"$argv" == "--infinite"} {
911 while 1 { emit $count; incr count }
913 foreach count $argv { emit $count }