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]
60 if {[string length $y] != $n*2} { error "binary format failed $n $y" }
65 namespace import Random-Bytes::*
67 proc choice-int {min max} {
68 set rv 0x[random-bytes 3]
70 int( double($rv) / double(0x1000000) * double($max+1-$min) )
75 proc choice-prob {cv def} {
76 set prob [config $cv $def]
77 set rv 0x[random-bytes 3]
78 return [expr {$rv < double($prob)*0x1000000}]
81 proc choice-mult {args} {
82 if {!([llength $args] % 2)} { error "choice-mult must have default" }
83 set x 0x[random-bytes 3]
84 set x [expr { double($x) / double(0x1000000) }]
86 set def [lindex $args end]
87 set args [lreplace $args end end]
88 foreach {val p} $args {
89 set cump [expr {$cump + double($p)}]
90 if {$x < $cump} { return $val }
96 upvar #0 getlog_log log
101 proc config {cv def} {
102 upvar #0 config/$cv v
103 if {[info exists v]} { return $v }
108 proc define {enum val name argnames body} {
109 upvar #0 enum/val2name/$enum v2n
110 upvar #0 enum/name2val/$enum n2v
113 proc enum/val/$enum/$val $argnames $body
116 proc depending-on {scope enum_and_var mtu mtuadjust args} {
117 upvar 1 $enum_and_var val
118 set mtu [expr {$mtu + $mtuadjust}]
119 set procname enum/val/$scope-$enum_and_var/[format %d $val]
120 if {[choice-prob $enum_and_var-unstruct 0.1] ||
121 [catch { info body $procname }]} {
122 # half the time random
128 uplevel 1 [list $procname] $mtu $args
133 proc get-for {scope} {
138 proc get {variable kind args} {
139 upvar 1 get/scope scope
140 upvar 1 $variable var
141 set var [eval [list get/$kind $scope $variable] $args]
144 proc get-config/number {val min max} { return $val }
145 proc get-config/v4addr {val} {
146 if {![regexp {^(\d+)\.(\d+)\.(\d+)\.(\d+)$} $val dummy a b c d]} {
147 error "bad v4addr ?$val?"
149 return [format 0x%02x%02x%02x%02x $a $b $c $d]
152 proc get-config {variable def kind args} {
153 # args currently ignored
154 upvar 1 get/scope scope
155 upvar 1 $variable var
156 set val [config $scope-$variable $def]
157 set var [eval [list get-config/$kind $val] $args]
160 proc get-enum-got {s v rv} {
161 upvar #0 enum/val2name/$s-$v v2n
162 if {[info exists v2n($rv)]} {
163 getlog "$v=$v2n($rv)\[$rv]"
170 proc get/enum-rand {s v min max} {
171 set rv [choice-int $min $max]
172 return [get-enum-got $s $v $rv]
175 proc get/enum-def {s v} {
176 upvar #0 enum/val2name/$s-$v v2n
177 set rv [choice-int 1 [array size v2n]]
178 set rv [lindex [array names v2n] [expr {$rv-1}]]
179 return [get-enum-got $s $v $rv]
182 proc get/enum {s v min max prand} {
184 get any choice $prand
186 return [get/enum-rand $s $v $min $max]
188 return [get/enum-def $s $v]
192 proc get/number {s v min max} {
193 set rv [choice-int $min $max]
198 proc get/hex {s v min max} {
199 set rv [choice-int $min $max]
200 getlog [format %s=0x%x $v $rv]
204 proc get/flag {s v defprob} {
205 set rv [choice-prob $s-$v $defprob]
206 if {$rv} { getlog "$v" } else { getlog "!$v" }
210 proc get/choice {s v defprob} {
211 set rv [choice-prob $s-$v $defprob]
212 if {$rv} { getlog "($v)" } else { getlog "(!$v)" }
216 proc get/rand {s v minlen maxlen} {
218 get l number $minlen $maxlen
219 return [random-bytes $l]
222 proc get/ip-timestamp {s v} {
223 set rv [expr {[clock seconds] | 0x80000000}]
224 getlog "$v=[format %x $rv]"
228 proc get/v4addr {s v} {
232 for {set i 0} {$i<4} {incr i} {
233 set b [random-bytes 1]
235 append p $d [format %d 0x$b]
242 proc get/choice-mult {s v args} {
243 set rv [eval choice-mult $args]
248 proc get/string {s v minlen maxlen first rest} {
251 for {set l [choice-int $minlen $maxlen]} {$l>0} {incr l -1} {
252 set cn [choice-int 0 [expr {[string length $now]-1}]]
253 append o [string index $now $cn]
257 return [packet-fromstring $o]
261 proc assemble {outvarname format} {
262 # format should look like those RFC diagrams.
263 # +-+-+ stuff and good formatting is mandatory.
264 # Tabs are forbidden.
266 # Field names are converted to lowercase; internal spaces
267 # are replaced with _. They are then assumed to be
268 # variable names in the caller's scope. The packet is
269 # assembled from those values (which must all be set)
270 # and stored in $varname in the caller's scope.
272 # Variables ?_whatever will be *set* with the location of the
273 # field in the string (in internal format); the corresponding
274 # `whatever' (with the ?_ stripped) will be read when assembling.
276 # Field name `0' means set the field to zero.
278 upvar 1 $outvarname out
281 debug 7 "ASSEMBLY $outvarname\n$format"
282 foreach l [split $format "\n"] {
284 if {[regexp -nocase {^ *\| +\| *$} $l]} {
285 if {![info exists wordbits]} {
286 error "vspace not in data @$lno\n?$l?"
289 } elseif {[regexp -nocase {^ *[|? a-z0-9]+$} $l]} {
290 if {[info exists words]} {
291 error "data without delimline @$lno\n?$l?"
295 } elseif {[regexp {^ *[-+]+ *$} $l]} {
298 while {[regexp {^([-= ]+)\+(.*)$} $l dummy before after]} {
299 set atpos([string length $before]) $wordbits
301 set l "$before=$after"
302 append newlineform "@[string length $before]:$wordbits "
305 append newlineform $wordbits
306 if {[info exists lineform]} {
307 if {"$newlineform" != "$lineform"} {
308 error "format change @$lno\n?$l?\n$wordformat != $oldwordformat"
310 if {![info exists words] || $words<0} {
311 error "consecutive delimlines @$lno\n?$l?"
313 append out [string repeat 00 [expr {$words*$wordbits/8}]]
315 while {[regexp -nocase \
316 {^([ =]*)\|( *[? a-z0-9]*[a-z0-9] *)\|(.*)$} \
317 $l dummy before midpart after]} {
318 debug 7 "RWORKG ?$l?"
319 set varname [string tolower [string trim $midpart]]
320 set varname [string map {{ } _} $varname]
321 set p1 [string length $before]
323 [string length $before] +
324 [string length $midpart] + 1
326 if {![info exists atpos($p1)] ||
327 ![info exists atpos($p2)]} {
328 error "vdelims not found @$lno $varname $p1 $p2 -- $lineform ?$cue?"
331 [string length $out]*4
336 $atpos($p2) - $atpos($p1) + ($words-1)*$wordbits
338 set location [list $bit1 $bitlen $outvarname-$varname]
339 if {[regexp {^\?_(.*)} $varname dummy realvarname]} {
340 debug 7 "LOCATING $varname $location"
341 upvar 1 $varname locvarname
342 set locvarname $location
343 set varname $realvarname
345 if {"$varname" == "0"} {
348 set value [uplevel 1 [list set $varname]]
350 assembly-overwrite out $location $value
352 append l [string repeat = [string length $midpart]]
355 debug 7 "REMAIN ?$l?"
357 if {$wordbits % 8 || $wordbits >32} {
358 error "bad wordbits $wordbits @$lno ?$l? $newlineform"
360 set lineform $newlineform
362 catch { unset words }
363 } elseif {[regexp {^ *$} $l]} {
365 error "huh? @$lno ?$l?"
368 debug 7 "ASSEMBLY\n$out\n"
372 proc assembly-overwrite {outvarname location value} {
373 upvar 1 $outvarname out
374 manyset $location bit1 bitlen diag
375 debug 8 "ASSEMBLY $diag == $value == 0x[format %x $value] (@$bit1+$bitlen)"
376 if {$bitlen < 32 && $value >= (1<<$bitlen)} {
377 error "$diag $value >= 2**$bitlen"
379 if {!($bit1 % 8) && !($bitlen % 8)} {
380 set char0no [expr {$bit1/4}]
381 set charlen [expr {$bitlen/4}]
382 set chareno [expr {$char0no + $charlen -1}]
383 set repl [format %0${charlen}x $value]
384 set out [string replace $out $char0no $chareno $repl]
386 while {$bitlen > 0} {
387 set byteno [expr {$bit1 / 8}]
388 set char0no [expr {$byteno*2}]
389 set char1no [expr {$char0no+1}]
390 set bytebit [expr {128>>($bit1 % 8)}]
391 set byte 0x[string range $out $char0no $char1no]
392 debug 7 "< $bit1+$bitlen $byteno $char0no $bytebit $byte $out"
394 ($value & (1<<($bitlen-1)))
396 : ($byte & ~$bytebit)
398 set out [string replace $out $char0no $char1no [format %02x $byte]]
399 debug 8 "> $bit1+$bitlen $byteno $char0no $bytebit 0x[format %02x $byte] $out"
407 proc gen_1_ip {mtu} {
409 upvar #0 ip_proto proto
410 upvar #0 ip_source source
411 upvar #0 ip_dest dest
414 get tos hex 0x00 0xff
415 get id hex 0x0000 0xffff
422 get frag number 0 0x1fff
424 get-config ttl 255 number 0 255
425 get proto enum 1 255 0.05
426 get-config source 127.0.0.1 v4addr
427 get-config dest 127.0.0.1 v4addr
428 # we don't do any IP options
430 set body [depending-on ip proto $mtu [expr {-4*$ihl}]]
431 set total_length [expr {$ihl + [packet-len $body]}]
432 set header_checksum 0
433 set flags [expr {$df*2 + $mf}]
435 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
436 |Version| IHL |TOS | Total Length |
437 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
439 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440 | TTL | Proto | ? Header Checksum |
441 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
443 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
445 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
447 assembly-overwrite ip ${?_header_checksum} [packet-csum-ip $ip]
452 define ip-proto 1 icmp {mtu} {
455 get type enum 0 255 0.2
456 manyset [depending-on icmp type $mtu -4] body code
457 if {![string length $code]} { get code number 0 255 }
460 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
461 | Type | Code | ? Checksum |
462 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
465 assembly-overwrite icmp ${?_checksum} [packet-csum-ip $icmp]
469 proc define-icmp-type-vanilla {num name} {
470 define icmp-type $num $name {mbl} "icmp-vanilla \$mbl [list $name]"
472 proc icmp-vanilla {mbl typename} {
473 get-for icmp-$typename
474 get code enum 0 255 0.4
476 return [list $body $code]
479 define-icmp-type-vanilla 3 unreach
480 define icmp-unreach-code 0 net {} {}
481 define icmp-unreach-code 1 host {} {}
482 define icmp-unreach-code 2 proto {} {}
483 define icmp-unreach-code 3 port {} {}
484 define icmp-unreach-code 4 fragneeded {} {}
485 define icmp-unreach-code 5 sourceroutefail {} {}
487 define-icmp-type-vanilla 11 timeout
488 define icmp-timeout-code 0 intransit {} {}
489 define icmp-timeout-code 1 fragment {} {}
491 define-icmp-type-vanilla 12 parameters
492 define icmp-parameters-code 0 seepointer {} {}
494 define-icmp-type-vanilla 4 sourcequench
495 define icmp-sourcequench-code 0 quench {} {}
497 define icmp-type 5 redirect {mbl} {
498 get-for icmp-redirect
499 get code enum 0 255 0.4
502 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
504 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
506 get data rand 0 [expr {$mbl-4}]
508 return [list $body $code]
511 define icmp-redirect-code 0 net {} {}
512 define icmp-redirect-code 1 host {} {}
513 define icmp-redirect-code 2 net+tos {} {}
514 define icmp-redirect-code 3 host+tos {} {}
516 define icmp-type 8 ping {mbl} { icmp-echo $mbl }
517 define icmp-type 0 pong {mbl} { icmp-echo $mbl }
518 proc icmp-echo {mbl} {
520 get code enum 0 255 0.4
524 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
526 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
528 get data rand 0 [expr {$mbl-8}]
530 return [list $body $code]
532 define icmp-echo-code 0 echo {} {}
534 define icmp-type 13 timestamp {mbl} { icmp-timestamp }
535 define icmp-type 14 timestampreply {mbl} { icmp-timestamp }
536 proc icmp-timestamp {} {
537 get-for icmp-timestamp
538 get code enum 0 255 0.4
541 get originate ip-timestamp
542 get receive ip-timestamp
543 get transmit ip-timestamp
545 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
547 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
549 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
551 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
553 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
555 return [list $body $code]
557 define icmp-timestamp-code 0 timestamp {} {}
559 define icmp-type 15 inforequest {mbl} { icmp-inforeq }
560 define icmp-type 16 inforeply {mbl} { icmp-inforeq }
561 proc icmp-inforeq {} {
563 get code enum 0 255 0.4
567 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
569 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
571 return [list $body $code]
573 define icmp-inforeq-code 0 timestamp {} {}
575 # MAYADD ICMP traceroute RFC1393
576 # MAYADD ICMP router discovery RFC1256
578 proc port_pair_data {scope mtu mtuadjust} {
581 get style choice-mult \
587 if {"$style" != "random"} {
591 if {"$style" != "servers"} {
592 get port enum-rand 0 0xffff
595 switch -exact $style {
596 random { set source_port $rand_port; set dest_port $rand_port }
597 request { set source_port $rand_port; set dest_port $def_port }
598 reply { set source_port $def_port; set dest_port $rand_port }
599 servers { set source_port $def_port; set dest_port $def_port }
601 if {"$style" != "random"} {
603 set data [depending-on $scope port $mtu $mtuadjust $style]
605 get data rand 0 [expr {$mtu + $mtuadjust}]
607 return [list $source_port $dest_port $data]
610 define ip-proto 17 udp {mtu} {
612 get checksum choice-mult \
616 manyset [port_pair_data udp $mtu 8] source_port dest_port data
617 set length [packet-len $data]
620 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
621 | Source Port | Dest Port |
622 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
623 | Length | ? Checksum |
624 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
627 if {"$checksum" != "none"} {
628 set csum [packet-csum-ip $udp]
629 if {!$csum} { set csum 0xffff }
630 if {"$checksum" == "bad"} {
631 get error hex 1 0xffff
632 set csum [expr {$csum ^ $error}]
637 assembly-overwrite udp ${?_checksum} $csum
640 define udp-port 50 remailck {mtu style} {
642 if {"$style" == "request"} {
643 get what choice-mult \
650 get what choice-mult \
657 switch -exact $what {
660 get user string 1 8 \
661 abcdefghijklmnopqrustuvwxyz \
662 abcdefghijklmnopqrustuvwxyz-0123456789_
666 get user rand 0 [expr {$mtu - 4}]
669 get auth enum 0 31 0.5
670 set user [depending-on remailck auth $mtu -4]
673 get auth hex 0 0xffff
678 get mail choice-mult \
684 switch -exact $mail {
698 get modified number 1 600
699 get read number 1 600
701 default { error "mail? $mail" }
704 default { error "what? $what" }
709 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
711 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
717 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
719 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
721 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
723 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
726 default { error "what?? $what" }
731 define remailck-auth 31 passwd {mtu} {
732 get-for remailck-passwd
733 get passwd string 6 8 \
734 0123456789abcdefghijklmnopqrstuvxwyz \
735 0123456789abcdefghijklmnopqrstuvxwyz
741 global getlog_log errorInfo
744 set packet [gen_1_ip 576]
745 puts stdout "[format %06d $count] $getlog_log\n $packet"
747 puts stderr "\nERROR\n$count\n\n$emsg\n\n$errorInfo\n\n"
748 puts stdout "[format %06d $count] error"
752 if {![llength $argv]} {
753 for {set count 1} {$count < 100} {incr count} { emit $count }
754 } elseif {"$argv" == "--infinite"} {
756 while 1 { emit $count; incr count }
758 foreach count $argv { emit $count }