use strict; use warnings; package App::DubiousHTTP::Tests::Clen; use App::DubiousHTTP::Tests::Common; SETUP( 'clen', "playing with content-length", <<'DESC', These tests look at the behavior if the content-length mismatches the content, e.g. content is short or longer then specified length or contradicting content-lenth headers are given. DESC # ------------------------ Tests ----------------------------------- [ 'VALID: single or no content-length' ], [ MUSTBE_VALID, 'close,clen,content' => 'single content-length with connection close'], #[ VALID, 'keep-alive,clen,content' => 'single content-length with keep-alive'], [ MUSTBE_VALID, 'close,content' => 'no content-length with connection close'], [ UNCOMMON_INVALID, 'close,clen,content,junk' => 'single content-length, content followed by junk, then connection close'], [ UNCOMMON_INVALID, 'close,clen,clen,content,junk' => 'correct content-length twice, content followed by junk, then connection close'], [ UNCOMMON_VALID, 'close,000clen,content' => 'lots of 0 before clen' ], [ UNCOMMON_VALID, 'close,000clen,content,junk' => 'lots of 0 before clen, body content+junk' ], [ 'INVALID: content-length does not match content' ], [ INVALID, 'close,clen200,content' => 'content-length double real content, close after real content' ], [ INVALID, 'close,clen50,content' => 'content-length half real content, close after real content' ], [ 'INVALID: multiple conflicting content-length' ], [ INVALID, 'close,clen50,clen,content' => 'content-length half and full' ], [ INVALID, 'close,clen,clen50,content' => 'content-length full and half' ], [ INVALID, 'close,clen200,clen,content,junk' => 'content-length double and full, content followed by junk and close' ], [ INVALID, 'close,clen,clen200,content,junk' => 'content-length full and double, content followed by junk and close' ], [ INVALID, 'close,clen-folding100,clen200,content,junk' => 'content-length full (folded) and double' ], [ INVALID, 'close,xte,clen50,clen,content' => 'content-length half and full, invalid Transfer-Encoding' ], [ INVALID, 'close,xte,clen,clen50,content' => 'content-length full and half, invalid Transfer-Encoding' ], [ INVALID, 'close,xte,clen200,clen,content,junk' => 'content-length double and full, invalid Transfer-Encoding' ], [ INVALID, 'close,xte,clen,clen200,content,junk' => 'content-length full and double, invalid Transfer-Encoding' ], [ INVALID, 'close,xte,clen-folding100,clen200,content,junk' => 'content-length full (folded) and double, invalid Transfer-Encoding' ], [ 'INVALID: multiple content-length, but one empty or invalid' ], [ INVALID, 'close,clen,clen-empty,content' => 'content-length full and empty' ], [ INVALID, 'close,clen,clen-empty,content,junk' => 'content-length full and empty, content followed by junk and close' ], [ INVALID, 'close,clen-empty,clen,content' => 'content-length empty and full' ], [ INVALID, 'close,clen-empty,clen,content,junk' => 'content-length empty and full, content followed by junk and close' ], [ INVALID, 'close,clen,clen-invalid,content' => 'content-length full and invalid' ], [ INVALID, 'close,clen,clen-invalid,content,junk' => 'content-length full and invalid, content followed by junk and close' ], [ INVALID, 'close,clen-invalid,clen,content' => 'content-length invalid and full' ], [ INVALID, 'close,clen-invalid,clen,content,junk' => 'content-length invalid and full, content followed by junk and close' ], [ 'INVALID: content-length header containing two numbers' ], [ INVALID, 'close,clen50-folding100,content' => 'content-length half but full after line folding, close after real content' ], [ INVALID, 'close,clen50-100,content' => 'content-length half and full on same line, close after real content' ], [ INVALID, 'close,clen50-(100),content' => 'content-length half and full on same line, but full as MIME comment, close after real content' ], [ INVALID, 'close,clen100-folding50,content' => 'content-length full but half after line folding, close after real content' ], [ INVALID, 'close,clen100-50,content' => 'content-length full and half on same line, close after real content' ], [ INVALID, 'close,clen(100)-50,content' => 'content-length full and half on same line, but full as MIME comment, close after real content' ], [ INVALID, 'close,clen100-folding200,content,junk' => 'content-length full but double after line folding, close after real content+junk' ], [ INVALID, 'close,clen100-(200),content,junk' => 'content-length full and double on same line, but double as MIME comment, close after real content+junk' ], [ INVALID, 'close,clen200-folding100,content,junk' => 'content-length double but full after line folding, close after real content+junk' ], [ INVALID, 'close,clen(200)-100,content,junk' => 'content-length double and full on same line, but double as MIME comment, close after real content+junk' ], [ 'INVALID: invalid characters around content-length value' ], [ INVALID, 'close,\073(clen),content,junk' => '"Content-length: ;len", body content+junk' ], [ INVALID, 'close,(clen)\073,content,junk' => '"Content-length: len;", body content+junk' ], [ INVALID, 'close,\054(clen),content,junk' => '"Content-length: ,len", body content+junk' ], [ INVALID, 'close,(clen)\054,content,junk' => '"Content-length: len,", body content+junk' ], [ INVALID, 'close,(clen)\054(clen),content,junk' => '"Content-length: len,len", body content+junk' ], [ INVALID, 'close,\042(clen)\042,content,junk' => "'Content-length: \"len\"', body content+junk" ], [ INVALID, 'close,(clen)A,content,junk' => '"Content-length: lenA", body content+junk' ], [ INVALID, 'close,A(clen),content,junk' => '"Content-length: Alen", body content+junk' ], [ INVALID, 'close,(clen)\040A,content,junk' => '"Content-length: len A", body content+junk' ], [ INVALID, 'close,A\040(clen),content,junk' => '"Content-length: A len", body content+junk' ], [ INVALID, 'close,\240(clen),content,junk' => '"Content-length: \240len", body content+junk' ], [ INVALID, 'close,(clen)\240,content,junk' => '"Content-length: len\240", body content+junk' ], [ INVALID, 'close,(clen).0,content,junk' => '"Content-length: len.0", body content+junk' ], [ INVALID, 'close,(clen).9,content,junk' => '"Content-length: len.9", body content+junk' ], [ INVALID, 'close,clenx0,content' => 'Content-length value with \0 inside, body content' ], [ INVALID, 'close,clenx0,content,junk' => 'Content-length value with \0 inside, body content+junk' ], [ 'INVALID: bad content-length value (hex, overflow, huge...)' ], [ INVALID, 'close,hexlen,content' => '"Content-length: 0xhexlen", body content' ], [ INVALID, 'close,hexlen,content,junk' => '"Content-length: 0xhexlen", body content+junk' ], [ INVALID, 'close,overflow32,content' => 'overflowing uint32, body content' ], [ INVALID, 'close,overflow32,content,junk' => 'overflowing uint32, body content+junk' ], #[ INVALID, 'close,overflow64,content' => 'overflowing uint64, body content' ], #[ INVALID, 'close,overflow64,content,junk' => 'overflowing uint64, body content+junk' ], [ INVALID, 'close,huge,content' => 'huge >64bit fake clen' ], [ INVALID, 'close,big,content' => 'big 1GB fake clen' ], ); sub make_response { my ($self,$page,$spec) = @_; return make_index_page() if $page eq ''; my ($hdr,$data) = content($page,$self->ID."-".$spec) or die "unknown page $page"; my $version = '1.1'; my $body; my $te; for (split(',',$spec)) { if ( ! $_ || $_ eq 'close' ) { $hdr .= "Connection: close\r\n"; } elsif ( $_ eq '000clen' ) { $hdr .= "Content-length: ".( "0" x 1000 ).length($data)."\r\n"; } elsif ( $_ eq 'overflow32' ) { $hdr .= sprintf("Content-length: %.0lf\r\n",2.0**32+length($data)); } elsif ( $_ eq 'huge' ) { $hdr .= "Content-length: 9999999999999999999999\r\n"; } elsif ( $_ eq 'big' ) { $hdr .= "Content-length: 1073741824\r\n"; } elsif ( $_ eq 'clenx0' ) { my $len = length($data); $len =~s{(\d)\z}{\0$1}; $hdr .= "Content-length: $len\r\n"; } elsif ( $_ eq 'clen-empty' ) { $hdr .= "Content-length: \r\n"; } elsif ( $_ eq 'clen-invalid' ) { $hdr .= "Content-length: xxx\r\n"; } elsif ( $_ eq 'hexlen' ) { $hdr .= sprintf("Content-length: 0x%x\r\n",length($data)); } elsif ( s{^clen(\(?)(\d*)(\)?)}{} ) { $hdr .= "Content-length: "; $hdr .= $1; $hdr .= int((($2||100)/100)*length($data)) if $2 || $_ eq ''; $hdr .= $3; while (s{^-(folding)?(\(?)(\d+)(\)?)}{}) { $hdr .= "\r\n" if $1; $hdr .= $2; $hdr .= " ".int(($3/100)*length($data)); $hdr .= $4; } $hdr .= "\r\n"; } elsif ( m{\(clen\)}) { s{\\([0-7]{3})}{ chr(oct($1)) }esg; s{\(clen\)}{ length($data) }esg; $hdr .= "Content-length: $_\r\n"; $body = $data; } elsif ( $_ eq 'content' ) { $body = $data; } elsif ( $_ eq 'junk' ) { # fake PKZIP magic for confusion my $junk = "PK\003\004" x int(length($data)/4+1); $body .= substr($junk,0,length($data)); } elsif ( $_ eq 'xte' ) { $hdr .= "Transfer-Encoding: lalala\r\n"; } else { die $_ } } $hdr = "HTTP/$version 200 ok\r\n$hdr"; return "$hdr\r\n$body"; } 1;