File Coverage

blib/lib/HTTP/Server/Encrypt.pm
Criterion Covered Total %
statement 197 284 69.3
branch 46 138 33.3
condition 25 77 32.4
subroutine 27 27 100.0
pod 1 5 20.0
total 296 531 55.7


line stmt bran cond sub pod time code
1             package HTTP::Server::Encrypt;
2 7     7   589097 use 5.008008;
  7         22  
  7         217  
3 7     7   35 use strict;
  7         8  
  7         188  
4 7     7   29 use warnings;
  7         12  
  7         169  
5 7     7   29 use Carp qw(croak);
  7         8  
  7         468  
6 7     7   3339 use HTTP::Server::Daemon qw(become_daemon server_perfork_dynamic peer_info get_msg send_msg);
  7         15  
  7         589  
7 7     7   1040 use HTTP::Status qw(status_message);
  7         4423  
  7         1125  
8 7     7   940 use HTTP::Date qw(time2str);
  7         4857  
  7         414  
9 7     7   6262 use MIME::Base64 qw(encode_base64);
  7         5560  
  7         407  
10 7     7   49 use File::Basename qw(dirname basename);
  7         8  
  7         319  
11 7     7   5737 use Sys::Sendfile qw(sendfile);
  7         4027  
  7         431  
12 7     7   5694 use Log::Lite qw(log logpath);
  7         7895  
  7         459  
13 7     7   6874 use Crypt::CBC;
  7         42860  
  7         274  
14 7     7   66 use Digest::MD5 qw(md5_hex);
  7         15  
  7         413  
15 7     7   48 use Data::Dump qw(ddx);
  7         8  
  7         309  
16 7     7   6129 use Sys::Hostname;
  7         9907  
  7         468  
17 7     7   6592 use LWP::MediaTypes qw(guess_media_type);
  7         106279  
  7         814  
18 7     7   64 use vars qw(@ISA @EXPORT_OK $right_auth $username $script_base_dir $peer_port $peer_ip $script %data $body %header $file %_GET %_POST %_HEAD %res $send_bytes $static_expires_secs $blowfish $blowfish_key $blowfish_encrypt $blowfish_decrypt $_POST %ip_allow %ip_deny $log_dir $port $colonel_version);
  7         20  
  7         6723  
19              
20             require Exporter;
21             @ISA = qw(Exporter);
22             @EXPORT_OK = qw(http_server_start);
23              
24             our $VERSION = '0.12';
25              
26             sub http_server_start
27             {
28 5     5 1 11710 my $ref_http_conf = shift;
29 5         2095 my %http_conf = %$ref_http_conf;
30 5   50     280 our $port = $http_conf{'port'} || 80;
31 5   50     310 my $protocol = $http_conf{'protocol'} || 'http';
32 5   50     155 my $min_spare = $http_conf{'min_spare'} || 10;
33 5   50     155 my $max_spare = $http_conf{'max_spare'} || 20;
34 5   50     120 our $script_base_dir = $http_conf{'docroot'} || 'htdoc';
35 5   50     555 our $static_expires_secs = $http_conf{'cache_expires_secs'} || 3600;
36 5         15 our $username = $http_conf{'username'};
37 5         40 my $passwd = $http_conf{'passwd'};
38 5         50 my $blowfish_key = $http_conf{'blowfish_key'};
39 5         80 our $blowfish_encrypt = $http_conf{'blowfish_encrypt'};
40 5         10 our $blowfish_decrypt = $http_conf{'blowfish_decrypt'};
41 5 50       130 our %ip_allow = %{$http_conf{'ip_allow'}} if $http_conf{'ip_allow'};
  0         0  
42 5 50       70 our %ip_deny = %{$http_conf{'ip_deny'}} if $http_conf{'ip_deny'};
  0         0  
43 5 50       520 our $log_dir = $http_conf{'log_dir'} if $http_conf{'log_dir'};
44 5 50 33     245 $log_dir = '' if defined $log_dir and $log_dir eq 'no';
45 5 50 33     130 logpath($log_dir) if defined $log_dir and $log_dir;
46 5         40 our $colonel_version = 'Colonel/0.9';
47              
48 5 50       15 if ($blowfish_key)
49             {
50 0         0 our $blowfish = Crypt::CBC->new(
51             -key => $blowfish_key ,
52             -cipher => 'Blowfish',
53             );
54             }
55              
56 5 50 33     155 if ($username or $passwd)
57             {
58 5 50       45 our $right_auth = $username if $username;
59 5         50 $right_auth.= ":";
60 5 50       70 $right_auth.= $passwd if $passwd;
61 5         105 $right_auth = encode_base64($right_auth);
62 5         2685 chomp $right_auth;
63             }
64              
65 5         105 my ($package, $invoker) = caller;
66 5         2855 chdir( dirname($invoker) );
67              
68 5         1650 my $pidfile = become_daemon($invoker);
69 4     1   412 $SIG{TERM} = sub { unlink $pidfile; kill HUP => $$; };
  1         3157  
  1         61  
70              
71 4 50       136 if (lc($protocol) eq 'perl')
72             {
73 0         0 server_perfork_dynamic(\&do_child_perl, $port, $min_spare, $max_spare);
74             }
75             else
76             {
77 4         104 server_perfork_dynamic(\&do_child_http, $port, $min_spare, $max_spare);
78             }
79 1         168 return $pidfile;
80             }
81              
82             sub do_child_http
83             {
84 7     7   43 no warnings 'uninitialized';
  7         20  
  7         7718  
85 4     4 0 29 my $sock = shift;
86 4         75 local ($peer_port, $peer_ip) = peer_info($sock);
87 4 0       42 if (%ip_allow) {return unless $ip_allow{$peer_ip};}
  0 50       0  
88 4 0       32 if (%ip_deny) {return if $ip_deny{$peer_ip};}
  0 50       0  
89 4         25 my $status = 100;
90 4         9 my $send_bytes;
91             my $method;
92 0         0 my $request_uri;
93 0         0 my $protocol;
94 0         0 my %header;
95 4         309 local %ENV;
96 4 50       97 $ENV{'AUTH_TYPE'} = "Basic" if $username;
97 4         27 $ENV{'GATEWAY_INTERFACE'} = "CGI/1.1";
98 4         130 $ENV{'HTTP'} = '';
99 4         27 $ENV{'REMOTE_ADDR'} = $peer_ip;
100 4         19 $ENV{'REMOTE_HOST'} = $peer_ip;
101 4         30 $ENV{'REMOTE_IDENT'} = "";
102 4 50       57 $ENV{'REMOTE_USER'} = $username if $username;
103 4         83 $ENV{'SERVER_NAME'} = hostname;
104 4         100 $ENV{'SERVER_PORT'} = $port;
105 4         31 $ENV{'SERVER_PROTOCOL'} = "HTTP/1.0";
106 4         41 $ENV{'SERVER_SOFTWARE'} = $colonel_version;
107              
108 4         33 my $chunk = http_readline($sock);
109 4 50 33     91 if (!$chunk or length($chunk) > 16*1024)
110             {
111 0         0 $status = 414;
112 0         0 goto HTTP_RESP;
113             }
114              
115 4         139 ($method, $request_uri, $protocol) = $chunk =~ m/^(\w+)\s+(\S+)(?:\s+(\S+))?\r?$/;
116 4 50 33     88 if ($method !~ /^(?:GET|POST)$/ or $request_uri =~ /\.\./)
117             {
118 0         0 $status = 405;
119 0         0 goto HTTP_RESP;
120             }
121 4         43 $ENV{'REQUEST_METHOD'} = $method;
122              
123 4         26 my ($script, $query_string ) = $request_uri =~ /([^?]*)(?:\?(.*))?/s;
124 4         53 $ENV{'PATH_INFO'} = $script;
125 4         17 $ENV{'PATH_TRANSLATED'} = $script;
126 4         25 $ENV{'QUERY_STRING'} = $query_string;
127 4         26 $ENV{'SCRIPT_NAME'} = $script;
128              
129 4         22 local %_GET;
130 4 50       52 if($query_string)
131             {
132 0         0 my @query = split /\&/, $query_string;
133 0         0 foreach (@query)
134             {
135 0         0 my ($k, $v) = $_ =~ /(.*)\=(.*)/;
136 0 0 0     0 next unless defined $k and $k and defined $v and $v;
      0        
      0        
137 0 0       0 if (!$_GET{$k})
    0          
    0          
138             {
139 0         0 $_GET{$k} = $v;
140             }
141             elsif (!ref($_GET{$k}))
142             {
143 0         0 my $tmp_value = $_GET{$k};
144 0         0 undef $_GET{$k};
145 0         0 push @{$_GET{$k}} , $tmp_value;
  0         0  
146 0         0 push @{$_GET{$k}} , $v;
  0         0  
147             }
148             elsif (ref($_GET{$k}) eq 'ARRAY')
149             {
150 0         0 push @{$_GET{$k}} , $v;
  0         0  
151             }
152             else
153             {
154 0         0 next;
155             }
156             }
157             }
158 4         59 local %_HEAD = http_get_header($sock);
159 4         36 $ENV{'CONTENT_LENGTH'} = $_HEAD{'Content-Length'};
160 4         20 $ENV{'CONTENT_TYPE'} = $_HEAD{'Content-Type'};
161              
162 4 50       126 if( -d "$script_base_dir$script" )
163             {
164 0 0       0 if (substr($script, -1) ne '/')
165             {
166 0         0 $status = 301;
167 0         0 $header{'Location'} = "http://" . $_HEAD{'Host'} . "$script/$query_string";
168 0         0 goto HTTP_RESP;
169             }
170              
171 0 0       0 if (-e "$script_base_dir$script/index.html")
    0          
    0          
172             {
173 0 0       0 $script .= '/' if substr($script,-1) ne '/';
174 0         0 $script .= 'index.html';
175             }
176             elsif (-e "$script_base_dir$script/index.htm")
177             {
178 0 0       0 $script .= '/' if substr($script,-1) ne '/';
179 0         0 $script .= 'index.htm';
180             }
181             elsif (-e "$script_base_dir$script/index.pl")
182             {
183 0 0       0 $script .= '/' if substr($script,-1) ne '/';
184 0         0 $script .= 'index.pl';
185             }
186             }
187 4         10 my $script_file = "$script_base_dir$script";
188              
189 4 50       40 if ($right_auth)
190             {
191 4 100       40 my ($client_auth) = $_HEAD{'Authorization'} =~ /Basic\s*([\w\+\=]+)/ if $_HEAD{'Authorization'};
192 4 100 66     43 unless (defined $client_auth and $client_auth eq $right_auth)
193             {
194 2         4 $status = 401;
195 2         24 goto HTTP_RESP;
196             }
197             }
198              
199 2         19 local %_POST;
200 2         7 local $_POST;
201 2 50       10 if ($method eq 'POST')
202             {
203 7     7   43 use bytes;
  7         15  
  7         160  
204 0         0 my $post_data = '';
205 0 0       0 if(defined $_HEAD{'Content-Length'})
206             {
207 0         0 read($sock, $post_data, $_HEAD{'Content-Length'});
208             }
209             else
210             {
211 0         0 my $i = 0;
212 0         0 while( substr($post_data, -2) ne "\015\012" )
213             {
214 0         0 read($sock, my $buf, 1);
215 0         0 $post_data .= $buf;
216 0         0 $i++;
217 0 0       0 if ($i > 4096)
218             {
219 0         0 $status = 411;
220 0         0 goto HTTP_RESP;
221             }
222             }
223             }
224 0 0       0 last unless $post_data;
225              
226 0 0       0 $post_data = $blowfish->decrypt($post_data) if $blowfish_decrypt;
227 0         0 $_POST = $post_data;
228 0         0 my @post_query = split /\&/, $post_data;
229 0         0 foreach (@post_query)
230             {
231 0         0 my ($k_post, $v_post) = $_ =~ /(.*)\=(.*)/;
232 0 0 0     0 next unless defined $k_post and $k_post and defined $v_post and $v_post;
      0        
      0        
233 0 0       0 if (!$_POST{$k_post})
    0          
    0          
234             {
235 0         0 $_POST{$k_post} = $v_post;
236             }
237             elsif (!ref($_POST{$k_post}))
238             {
239 0         0 my $tmp_value = $_POST{$k_post};
240 0         0 undef $_POST{$k_post};
241 0         0 push @{$_POST{$k_post}} , $tmp_value;
  0         0  
242 0         0 push @{$_POST{$k_post}} , $v_post;
  0         0  
243             }
244             elsif (ref($_POST{$k_post}) eq 'ARRAY')
245             {
246 0         0 push @{$_POST{$k_post}} , $v_post;
  0         0  
247             }
248             else
249             {
250 0         0 next;
251             }
252             }
253             }
254              
255 2         5 my $boolen_sendfile;
256 2         33 my $body = '';
257 2 100 66     86 if (-e $script_file and -r $script_file and -s $script_file)
      66        
258             {
259             eval
260 1         2 {
261 1         5 $status = 200;
262 1 50       7 if ( substr( $script_file, -3) eq '.pl' )
263             {
264 7     7   2988 no warnings;
  7         16  
  7         13466  
265 0         0 close STDOUT;
266 0 0       0 open STDOUT,">",\$body or die "couldn`t open memory file: $!";
267 0 0       0 unless (my $return = do $script_file)
268             {
269 0 0       0 die "couldn`t parse $script_file: $@" if $@;
270 0 0       0 die "couldn`t do $script_file: $!" unless defined $return ;
271 0 0       0 die "couldn`t run $script_file" unless $return;
272             }
273             }
274             else
275             {
276 1 50       75 open my $fh,"<",$script_file or die "couldn`t open file";
277 1         5 binmode $fh;
278              
279 1         21 my($ct,$ce) = guess_media_type($script_file);
280 1 50 33     294 if(!$blowfish_encrypt and $^O eq 'linux')
281             {
282 1         23 syswrite $sock, "HTTP/1.0 $status " . status_message($status) . "\015\012";
283 1 50       74 syswrite $sock, "Content-Type: $ct\015\012" if $ct;
284 1 50       4 syswrite $sock, "Content-Encoding: $ce\015\012" if $ce;
285 1         28 syswrite $sock, "Cache-Control: max-age=$static_expires_secs\015\012";
286 1         25 syswrite $sock, "\015\012";
287 1         52 $send_bytes = sendfile($sock, $fh);
288 1         2 $boolen_sendfile = 1;
289 1         34 goto HTTP_RESP;
290             }
291             else
292             {
293 0         0 $body = do {local $/; <$fh>};
  0         0  
  0         0  
294 0 0       0 $header{'Content-Type'} = $ct if $ct;
295 0 0       0 $header{'Content-Encoding'} = $ce if $ce;
296             }
297 0         0 close $fh;
298             }
299              
300 0 0       0 if($blowfish_encrypt)
301             {
302 0         0 $body = $blowfish->encrypt($body);
303 0         0 $header{'Content-Type'} = "application/octet-stream";
304             }
305             };
306 0 0       0 if($@)
307             {
308 0         0 $status = 500;
309 0         0 $body = $@;
310 0         0 goto HTTP_RESP;
311             }
312             }
313             else
314             {
315 1         2 $status = 404;
316 1         41 goto HTTP_RESP;
317             }
318              
319 4 100       106 HTTP_RESP: $send_bytes = http_response($sock, $status, $body, %header) unless $boolen_sendfile;
320 4 50       15 log('http_access', $peer_ip, $status, $method, $request_uri, $send_bytes, status_message($status), $@) if $log_dir;
321 4         118 return $send_bytes;
322             }
323              
324             sub http_get_header
325             {
326 4     4 0 8 my $sock = shift;
327 4         8 my @header;
328 4         20 while ( my $line = http_readline($sock) )
329             {
330 22 100       81 last if ( $line =~ /^\s*$/ );
331 18         95 my ($k, $v) = $line =~ /^([\w\-]+)\s*:\s*(.*)/;
332 18         62 $v =~ s/[\015\012]//g;
333 18         54 push @header, $k => $v;
334             }
335 4         92 return @header;
336             }
337              
338             sub http_readline
339             {
340 26     26 0 38 my $sock = shift;
341 26         23 my $line;
342 26         57684 while ( read( $sock, my $buf, 1 ) )
343             {
344 597 100       966 last if $buf eq "\012";
345 571         1305 $line .= $buf;
346             }
347 26         78 return $line;
348             }
349              
350             sub http_response
351             {
352 3     3 0 5 my $sock = shift;
353 3   50     12 my $status = shift || 200;
354 3         11 my $body = shift;
355 3         8 my %header = @_;
356              
357 3         74 my $status_msg = status_message($status);
358 3 50 33     151 if (!$body and $status != 200 and $status != 301 and $status != 302 )
      33        
      33        
359             {
360 3         18 $body = "$status $status_msg

Colonel ERROR: $status $status_msg


";
361             }
362 3 50       86 $header{'Date'} = time2str(time) unless defined $header{'Date'};
363 3 50       128 $header{'Server'} = $colonel_version unless defined $header{'Server'};
364 3 50       26 $header{'Content-Type'} = 'text/html' unless defined $header{'Content-Type'} ;
365 7     7   45 use bytes;
  7         9  
  7         35  
366 3 50 33     51 $header{'Content-Length'} = length($body) if defined $body and $body and !defined $header{'Content-Length'} ;
      33        
367 3 100       14 $header{'WWW-Authenticate'} = 'Basic realm="Colonel Authentication System"' if $status == 401 ;
368              
369 3         18 my $head = "HTTP/1.0 $status $status_msg\015\012";
370 3         10 foreach (keys %header)
371             {
372 14         43 $head .= "$_: " . $header{$_} . "\015\012";
373             }
374              
375 3         8 my $output = $head . "\015\012";
376 3 50 33     37 $output .= $body if defined $body and $body;
377 3         61 print $sock $output;
378 3         15 return length($output);
379             }
380              
381             1;
382             __END__