Handle hostnames with upper-case letters
[webmin.git] / bandwidth / index.cgi
1 #!/usr/local/bin/perl
2 # Show a form for selecting a report
3
4 require './bandwidth-lib.pl';
5 &ReadParse();
6 use Time::Local;
7 use Socket;
8
9 # Validate the bandwidth file
10 if (-d $bandwidth_log) {
11         &ui_print_header(undef, $text{'index_title'}, "", "intro",
12                          1, 1);
13         &ui_print_endpage(&text('index_elog', "<tt>$bandwidth_log</tt>",
14                           "../config.cgi?$module_name"));
15         }
16
17 # Validate the bandwidth directory
18 if ($config{'bandwidth_dir'} && !-d $hours_dir) {
19         &ui_print_header(undef, $text{'index_title'}, "", "intro",
20                          1, 1);
21         &ui_print_endpage(&text('index_edir', "<tt>$hours_dir</tt>",
22                           "../config.cgi?$module_name"));
23         }
24
25 # Make sure the net and cron modules work
26 foreach $m (split(/\s+/, $module_info{'depends'})) {
27         next if ($m =~ /^[0-9\.]+$/);
28         if (!&foreign_installed($m)) {
29                 &ui_print_header(undef, $text{'index_title'}, "", "intro",
30                                  1, 1);
31                 %minfo = &get_module_info($m);
32                 &ui_print_endpage(&text('index_emod', $minfo{'desc'}));
33                 }
34         }
35
36 # Make sure one of the syslog modules works
37 if (!$syslog_module) {
38         &ui_print_header(undef, $text{'index_title'}, "", "intro",
39                          1, 1);
40         &ui_print_endpage(&text('index_esyslog'));
41         }
42
43 # Check the firewall system
44 if (!$config{'firewall_system'}) {
45         # Try automatic detection
46         $sys = &detect_firewall_system();
47         if (!$sys) {
48                 &ui_print_header(undef, $text{'index_title'}, "", "intro",
49                                  1, 1);
50                 &ui_print_endpage($text{'index_efiresys'});
51                 }
52         $config{'firewall_system'} = $sys;
53         &save_module_config();
54         }
55 else {
56         # Is the selected one installed?
57         if (!&check_firewall_system($config{'firewall_system'})) {
58                 &ui_print_header(undef, $text{'index_title'}, "", "intro",
59                                  1, 1);
60                 &ui_print_endpage(&text('index_efiresys2',
61                                   $text{'system_'.$config{'firewall_system'}},
62                                   "../config.cgi?$module_name"));
63                 }
64         }
65 &ui_print_header(undef, $text{'index_title'}, "", "intro",
66                  1, 1, 0, undef, undef, undef,
67                  &text('index_firesys',
68                        $text{'system_'.$config{'firewall_system'}},
69                        $text{'syslog_'.$syslog_module}));
70
71 # Make sure the needed firewall rules and syslog entry are in place
72 $missingrule = !&check_rules();
73 if ($syslog_module eq "syslog") {
74         # Normal syslog
75         $conf = &syslog::get_config();
76         $sysconf = &find_sysconf($conf);
77         }
78 else {
79         # Syslog-ng
80         $conf = &syslog_ng::get_config();
81         ($ngdest, $ngfilter, $nglog) = &find_sysconf_ng($conf);
82         $sysconf = $ngdest && $ngfilter && $nglog;
83         }
84
85 if (($missingrule || !$sysconf) && $access{'setup'}) {
86         # Something is missing .. offer to set up
87         print "$text{'index_setupdesc'}\n";
88         if ($missingrule && !$sysconf) {
89                 print $text{'index_missing3'};
90                 }
91         elsif ($missingrule) {
92                 print $text{'index_missing2'};
93                 }
94         elsif (!$sysconf) {
95                 print $text{'index_missing1'};
96                 }
97         print "<p>\n";
98         print "$text{'index_setupdesc2'}<p>\n";
99         if ($iptableserr) {
100                 print $iptableserr,"<p>\n";
101                 }
102         print &ui_form_start("setup.cgi");
103         print "<b>$text{'index_iface'}</b>\n";
104         foreach $i (&net::active_interfaces(), &net::boot_interfaces()) {
105                 push(@ifaces, $i->{'fullname'}) if ($i->{'virtual'} eq '' &&
106                                                     $i->{'fullname'});
107                 }
108         print &ui_select("iface", $config{'iface'} || $ifaces[0],
109                          [ (map { [ $_, $_ ] } &unique(@ifaces)),
110                            [ '', $text{'index_other'} ] ],
111                          1, 0, $config{'iface'} ? 1 : 0)." ".
112               &ui_textbox("other", undef, 10);
113         print &ui_submit($text{'index_setup'});
114         print &ui_form_end();
115         print "<p>\n";
116         }
117 elsif ($missingrule || !$sysconf) {
118         print "$text{'index_setupdesc'}\n";
119         print "$text{'index_setupcannot'}<p>\n";
120         }
121
122 # See if any hours have been summarized yet
123 @hours = &list_hours();
124 if (@hours) {
125         # Show reporting form
126         print &ui_form_start("index.cgi");
127         print "<table>\n";
128
129         print "<tr> <td><b>$text{'index_by'}</b></td>\n";
130         print "<td>",&ui_select("by", $in{'by'},
131                 [ [ 'hour', $text{'index_hour'} ],
132                   [ 'day', $text{'index_day'} ],
133                   [ 'host', $text{'index_host'} ],
134                   [ 'proto', $text{'index_proto'} ],
135                   [ 'iport', $text{'index_iport'} ],
136                   [ 'oport', $text{'index_oport'} ],
137                   [ 'port', $text{'index_port'} ] ]),"</td>\n";
138
139         print "<td><b>$text{'index_for'}</b></td>\n";
140         print "<td>",&ui_select("for", $in{'for'},
141                 [ [ '', $text{'index_all'} ],
142                   [ 'host', $text{'index_forhost'} ],
143                   [ 'proto', $text{'index_forproto'} ],
144                   [ 'iport', $text{'index_foriport'} ],
145                   [ 'oport', $text{'index_foroport'} ] ]),"\n";
146         print &ui_textbox("what", $in{'for'} ? $in{'what'} : "", 20),
147               "</td> </tr>\n";
148
149         print "<tr> <td><b>$text{'index_from'}</b></td>\n";
150         print "<td colspan=4>",
151                 &date_input($in{'from_day'}, $in{'from_month'},
152                             $in{'from_year'}, "from"),
153                 &hourmin_input($in{'from_hour'}, "00", "from"),"</td> </tr>\n";
154
155         print "<tr> <td><b>$text{'index_to'}</b></td>\n";
156         print "<td colspan=4>",
157                 &date_input($in{'to_day'}, $in{'to_month'},
158                             $in{'to_year'}, "to"),
159                 &hourmin_input($in{'to_hour'}, "00", "to"),"</td> </tr>\n";
160
161         if (!%in) {
162                 # Enable by default
163                 $in{'low'} = 1;
164                 }
165         print "<tr> <td></td> <td colspan=4>\n";
166         print &ui_checkbox("low", 1, $text{'index_low'}, $in{'low'});
167         print &ui_checkbox("resolv", 1, $text{'index_resolv'}, $in{'resolv'});
168         print "</td> </tr>\n";
169
170         print "<tr> <td colspan=4>",
171                 &ui_submit($text{'index_search'}),"</td> </td>\n";
172
173         print "</table>\n";
174         print &ui_form_end();
175         }
176 elsif (!$missingrule && $sysconf) {
177         print "<b>$text{'index_none'}</b><p>\n";
178         }
179
180 # Find and show any results
181 if ($in{'by'}) {
182         # Work out the time range, if any
183         &error_setup($text{'index_err'});
184         $fhour = &parse_hour("from");
185         $thour = &parse_hour("to");
186
187         # First find traffic that matches the 'for' part
188         if ($in{'for'} eq 'host') {
189                 if ($in{'what'} =~ /^(\d+)\.(\d+)\.(\d+)\.0$/) {
190                         %forhost = map { ("$1.$2.$3.$_", 1) } (0 .. 255);
191                         }
192                 else {
193                         $forhost = &to_ipaddress($in{'what'});
194                         $forhost || &error($text{'index_ehost'});
195                         $forhost{$forhost}++;
196                         }
197                 }
198         elsif ($in{'for'} eq 'proto') {
199                 $forproto = uc($in{'what'});
200                 $forproto || &error($text{'index_eproto'});
201                 }
202         elsif ($in{'for'} eq 'iport' || $in{'for'} eq 'oport') {
203                 if ($in{'what'} =~ /^\d+$/) {
204                         $forportmin = $forportmax = $in{'what'};
205                         }
206                 elsif ($in{'what'} =~ /^(\d+)\-(\d+)$/) {
207                         $forportmin = $1;
208                         $forportmax = $2;
209                         }
210                 else {
211                         $forportmin = getservbyname($in{'what'}, 'tcp');
212                         $forportmin ||= getservbyname($in{'what'}, 'udp');
213                         $forportmin || &error($text{'index_eport'});
214                         $forportmax = $forportmin;
215                         }
216                 }
217         foreach $h (@hours) {
218                 next if ($fhour && $h < $fhour);
219                 next if ($thour && $h > $thour);
220                 $hour = &get_hour($h);
221
222                 # Work out start time for this day
223                 @tm = localtime($h*60*60);
224                 $thisday = timelocal(0, 0, 0, $tm[3], $tm[4], $tm[5])/(60*60);
225
226                 # Scan all traffic for the hour
227                 foreach $k (keys %$hour) {
228                         # Skip this count if not relevant
229                         ($host, $proto, $iport, $oport) = split(/_/, $k);
230                         next if (!$proto);
231                         next if (%forhost && !$forhost{$host});
232                         next if ($forproto && $proto ne $forproto);
233                         next if ($in{'for'} eq 'iport' &&
234                                  ($iport < $forportmin || $iport >$forportmax));
235                         next if ($in{'for'} eq 'oport' &&
236                                  ($oport < $forportmin || $oport >$forportmax));
237
238                         # Skip this count if classifying by port and there
239                         # isn't one
240                         next if ($in{'by'} eq 'iport' && !$iport ||
241                                  $in{'by'} eq 'oport' && !$oport ||
242                                  $in{'by'} eq 'port' && !$iport && !$oport);
243
244                         # Work out a nice service name
245                         local ($nsname, $nsoname, $nsiname);
246                         local $relport;
247                         if ($in{'by'} eq 'iport') {
248                                 $nsname = $nsiname = getservbyport($iport, lc($proto));
249                                 }
250                         elsif ($in{'by'} eq 'oport') {
251                                 $nsname = $nsoname = getservbyport($oport, lc($proto));
252                                 }
253                         elsif ($in{'by'} eq 'port') {
254                                 $nsoname = getservbyport($oport, lc($proto));
255                                 $nsiname = getservbyport($iport, lc($proto));
256                                 $nsname = $nsoname || $nsiname;
257                                 }
258
259                         # Resolv the hostname
260                         local $resolved;
261                         if ($in{'resolv'} && $in{'by'} eq 'host') {
262                                 $resolved = &to_hostname($host);
263                                 }
264
265                         # Skip traffic to high ports, if requested
266                         next if ($in{'low'} && $in{'by'} eq 'iport' &&
267                                  $iport >= 1024 && !$nsname);
268                         next if ($in{'low'} && $in{'by'} eq 'oport' &&
269                                  $oport >= 1024 && !$nsname);
270
271                         # Update the relevant category
272                         ($in, $out) = split(/ /, $hour->{$k});
273                         if ($in{'by'} eq 'hour') {
274                                 $count{$h} += $in+$out;
275                                 $icount{$h} += $in;
276                                 $ocount{$h} += $out;
277                                 }
278                         elsif ($in{'by'} eq 'day') {
279                                 $count{$thisday} += $in+$out;
280                                 $icount{$thisday} += $in;
281                                 $ocount{$thisday} += $out;
282                                 }
283                         elsif ($in{'by'} eq 'host') {
284                                 $count{$resolved || $host} += $in+$out;
285                                 $icount{$resolved || $host} += $in;
286                                 $ocount{$resolved || $host} += $out;
287                                 }
288                         elsif ($in{'by'} eq 'proto') {
289                                 $count{$proto} += $in+$out;
290                                 $icount{$proto} += $in;
291                                 $ocount{$proto} += $out;
292                                 }
293                         elsif ($in{'by'} eq 'iport') {
294                                 $count{$nsname || "$proto $iport"} += $in+$out;
295                                 $icount{$nsname || "$proto $iport"} += $in;
296                                 $ocount{$nsname || "$proto $iport"} += $out;
297                                 }
298                         elsif ($in{'by'} eq 'oport') {
299                                 $count{$nsname || "$proto $oport"} += $in+$out;
300                                 $icount{$nsname || "$proto $oport"} += $in;
301                                 $ocount{$nsname || "$proto $oport"} += $out;
302                                 }
303                         elsif ($in{'by'} eq 'port') {
304                                 if (!$in{'low'} || $oport < 1024 || $nsoname) {
305                                         $count{$nsoname || "$proto $oport"} += $in;
306                                         $icount{$nsoname || "$proto $oport"} += $in;
307                                         }
308                                 if (!$in{'low'} || $iport < 1024 || $nsiname) {
309                                         $count{$nsiname || "$proto $iport"} += $out;
310                                         $ocount{$nsiname || "$proto $iport"} += $out;
311                                         }
312                                 }
313                         }
314                 }
315
316         # Find max and size
317         $max = 0;
318         foreach $k (keys %count) {
319                 if ($count{$k} > $max) {
320                         $max = $count{$k};
321                         }
322                 }
323         $width = 500;
324
325         # Fill in missing hours or days
326         if ($in{'by'} eq 'hour' || $in{'by'} eq 'day') {
327                 @order = sort { $b <=> $a } keys %count;
328                 $inc = $in{'by'} eq 'hour' ? 1 : 24;
329                 $plus = $in{'by'} eq 'hour' ? 0 : 1;
330                 for($i=$order[0]; $i>=$order[$#order]; $i-=$inc) {
331                         $count{$i} = 0 if (!$count{$i} &&
332                                            !$count{$i+$plus} && !$count{$i-$plus});
333                         }
334                 }
335
336         # Show graph
337         if ($in{'by'} eq 'hour' || $in{'by'} eq 'day') {
338                 @order = sort { $b <=> $a } keys %count;
339                 }
340         else {
341                 @order = sort { $count{$b} <=> $count{$a} } keys %count;
342                 }
343         if ($in{'by'} ne 'hour' && $in{'by'} ne 'day') {
344                 @order = grep { $count{$_} } @order;
345                 }
346         if (@order) {
347                 print "<table width=100% cellpadding=0 cellspacing=0>\n";
348                 print "<tr>\n";
349                 print "<td><b>",$text{'index_h'.$in{'by'}},"</b></td>\n";
350                 print "<td colspan=2><b>$text{'index_usage'}</b></td>\n";
351                 print "</tr>\n";
352                 $total = 0;
353                 foreach $k (@order) {
354                         print "<tr>\n";
355                         if ($in{'by'} eq 'hour') {
356                                 print "<td>",&make_date($k*60*60),"</td>\n";
357                                 }
358                         elsif ($in{'by'} eq 'day') {
359                                 $date = &make_date_day($k*60*60);
360                                 print "<td>$date</td>\n";
361                                 }
362                         else {
363                                 print "<td>$k</td>\n";
364                                 }
365                         print "<td>";
366                         printf "<img src=images/red.gif width=%d height=10>",
367                                 $max ? int($width * $icount{$k}/$max)+1 : 1;
368                         printf "<img src=images/blue.gif width=%d height=10>",
369                                 $max ? int($width * $ocount{$k}/$max)+1 : 1;
370                         print "</td>";
371                         print "<td>",&nice_size($count{$k}),"</td>\n";
372                         $total += $count{$k};
373                         print "</tr>\n";
374                         }
375                 print "<tr> <td colspan=2></td> <td><b>",
376                 &nice_size($total),"</td> </tr>\n";
377                 print "</table>\n";
378                 }
379         else {
380                 print "<b>$text{'index_nomatch'}</b><p>\n";
381                 }
382 }
383
384 if (!$missingrule && $sysconf) {
385         print &ui_hr();
386         print &ui_buttons_start();
387
388         # Show button to rotate now
389         print &ui_buttons_row("rotate.cgi", $text{'index_rotate'},
390                               $text{'index_rotatedesc'});
391
392         if ($access{'setup'}) {
393                 # Show button to turn off reporting
394                 print &ui_buttons_row("turnoff.cgi", $text{'index_turnoff'},
395                                       $text{'index_turnoffdesc'});
396                 }
397
398         print &ui_buttons_end();
399         }
400
401 &ui_print_footer("/", $text{'index'});
402
403 sub parse_hour
404 {
405 local ($pfx) = @_;
406 local ($day, $month, $year) =
407         ($in{$pfx."_day"}, $in{$pfx."_month"}-1, $in{$pfx."_year"}-1900);
408 local $hour = $in{$pfx."_hour"};
409 return undef if (!$day);
410 if ($hour eq "") {
411         $hour = $pfx eq "from" ? 0 : 23;
412         }
413
414 eval { $tm = timelocal(0, 0, $hour, $day, $month, $year) };
415 if (!$tm || $@) {
416         &error($text{'index_e'.$pfx});
417         }
418 return int($tm/(60*60));
419 }
420
421 # make_date_day(seconds)
422 # Converts a Unix date/time in seconds to a human-readable form
423 sub make_date_day
424 {
425 local(@tm);
426 @tm = localtime($_[0]);
427 return sprintf "%d/%s/%d",
428                 $tm[3], $text{"smonth_".($tm[4]+1)},
429                 $tm[5]+1900;
430 }
431
432