'rqbuf'
,
'req'
,
'info'
,
'btype'
,
);
sub
RTYPES {
return
(
IMP_PREPASS,
IMP_PASS,
IMP_DENY,
IMP_LOG,
);
}
sub
new_analyzer {
my
(
$class
,
%args
) =
@_
;
my
$self
=
$class
->SUPER::new_analyzer(
%args
);
$self
->run_callback(
[ IMP_PREPASS,0,IMP_MAXOFFSET ],
[ IMP_PASS,1,IMP_MAXOFFSET ],
);
return
$self
;
}
sub
request_hdr {
my
(
$self
,
$hdr
) =
@_
;
my
$req
=
$self
->{req} = HTTP::Request->parse(
$hdr
) or
do
{
$self
->run_callback(
[ IMP_DENY,0,
"failed to parse request header"
]);
return
;
};
$self
->{rqbuf} =
''
;
$self
->{info} =
undef
;
$self
->{btype} =
undef
;
if
(
my
@qp
=
$req
->uri->query_form ) {
my
@param
;
for
(
my
$i
=0;
$i
<
@qp
;
$i
+=2 ) {
push
@param
,[
$qp
[
$i
],
$qp
[
$i
+1] ];
}
$self
->{info}{
'header.query_string'
} = \
@param
}
my
$ct
=
$req
->content_type;
if
(
$ct
&&
$req
->method eq
'POST'
and
$ct
eq
'application/x-www-form-urlencoded'
||
$ct
eq
'multipart/form-data'
){
$self
->{btype} =
$ct
;
}
else
{
my
$len
=
$req
->content_length // 0;
$self
->_log_formdata()
if
$self
->{info};
$self
->{rqbuf} =
''
;
$self
->run_callback( [ IMP_PASS,0,
$self
->offset(0) +
$len
]);
}
}
sub
request_body {
my
(
$self
,
$data
,
$offset
) =
@_
;
$offset
and
die
"gaps should not happen"
;
if
((
$data
//
''
) eq
''
) {
if
( !
$self
->{btype} ) {
}
elsif
(
$self
->{btype} eq
'application/x-www-form-urlencoded'
) {
my
@param
;
for
(
split
( /\&/,
$self
->{rqbuf}) ) {
my
(
$k
,
$v
) =
split
(
'='
,
$_
,2);
for
(
$k
,
$v
) {
defined
(
$_
) or
next
;
s{\+}{ }g;
s{%([\da-fA-F]{2})}{
chr
(
hex
($1)) }esg;
}
push
@param
,[
$k
,
$v
];
}
$self
->{info}{
'body.urlencoded'
} = \
@param
;
}
elsif
(
$self
->{btype} eq
'multipart/form-data'
) {
my
(
undef
,
$boundary
) =
$self
->{req}->header(
'content-type'
)
=~m{;\s
*boundary
=(\
"?)([^"
;,]+)\1}i;
if
( !
$boundary
) {
$self
->run_callback([
IMP_DENY,0,
"missing boundary for multipart/form-data"
]);
}
my
@param
;
for
my
$part
(
split
(
m{^--\Q
$boundary
\E(?:--)?\r?\n}m,
$self
->{rqbuf} )) {
$part
=~m{\A(.*?(\r?\n))\2(.*)}s or
next
;
my
(
$hdr
,
$v
) = ($1,$3);
my
(
$cd
) =
$hdr
=~m{^Content-Disposition:[ \t]*(.*(?:\r?\n[ \t].*)*)}mi
or
do
{
debug(
"no content-disposition in multipart header: $hdr"
);
next
;
};
$cd
=~s{\r?\n}{}g;
my
$name
=
$cd
=~m{;\s
*name
=(?:\"([^\"]+)\"|([^\s\";]+))} && ($1||$2);
$name
or
do
{
debug(
"no name in content-disposition in multipart header: $hdr"
);
next
;
};
my
$fname
=
$cd
=~m{;\s
*filename
=(?:\"([^\"]+)\"|([^\s\";]+))} && ($1||$2);
$v
=~s{\r?\n\Z}{};
$v
=
"UPLOAD:$fname ("
.
length
(
$v
).
" bytes)"
if
$fname
;
push
@param
, [
$name
,
$v
];
}
$self
->{info}{
'body.multipart'
} = \
@param
;
}
else
{
die
"unhandled POST content-type $self->{btype}"
}
$self
->_log_formdata();
}
elsif
(
$self
->{btype} ) {
$self
->{rqbuf} .=
$data
;
}
}
sub
response_hdr {}
sub
response_body {}
sub
any_data {}
sub
chunk_header {}
sub
chunk_trailer {}
sub
_log_formdata {
my
$self
=
shift
;
my
$info
=
$self
->{info} or
return
;
my
$text
;
if
(
eval
{
require
YAML } ) {
$text
= YAML::Dump(
$info
)
$text
= YAML::Tiny::Dump(
$info
)
$text
= Data::Dumper->new([
$info
])->Terse(1)->Dump;
}
else
{
die
"WTF, not even Data::Dumper is installed?"
;
}
$self
->run_callback([ IMP_LOG,0,0,0,IMP_LOG_INFO,
$text
]);
$self
->{info} =
undef
;
}