Blame scripts/services/http

Packit Bot ea69bd
##########################################################################
Packit Bot ea69bd
# $Id$
Packit Bot ea69bd
##########################################################################
Packit Bot ea69bd
Packit Bot ea69bd
#####################################################
Packit Bot ea69bd
# Copyright (c) 2008 Michael Romeo <michaelromeo@mromeo.com>
Packit Bot ea69bd
# Covered under the included MIT/X-Consortium License:
Packit Bot ea69bd
#    http://www.opensource.org/licenses/mit-license.php
Packit Bot ea69bd
# All modifications and contributions by other persons to
Packit Bot ea69bd
# this script are assumed to have been donated to the
Packit Bot ea69bd
# Logwatch project and thus assume the above copyright
Packit Bot ea69bd
# and licensing terms.  If you want to make contributions
Packit Bot ea69bd
# under your own copyright or a different license this
Packit Bot ea69bd
# must be explicitly stated in the contribution an the
Packit Bot ea69bd
# Logwatch project reserves the right to not accept such
Packit Bot ea69bd
# contributions.  If you have made significant
Packit Bot ea69bd
# contributions to this script and want to claim
Packit Bot ea69bd
# copyright please contact logwatch-devel@lists.sourceforge.net.
Packit Bot ea69bd
########################################################
Packit Bot ea69bd
Packit Bot ea69bd
#use diagnostics;
Packit Bot ea69bd
use strict;
Packit Bot ea69bd
use Logwatch ':sort';
Packit Bot ea69bd
# use re "debug";
Packit Bot ea69bd
#
Packit Bot ea69bd
#  parse httpd access_log
Packit Bot ea69bd
#
Packit Bot ea69bd
#  Get the detail level and
Packit Bot ea69bd
#  Build tables of the log format to parse it and determine whats what
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
Packit Bot ea69bd
my $ignoreURLs = $ENV{'http_ignore_urls'};
Packit Bot ea69bd
my $ignoreIPs = $ENV{'http_ignore_ips'};
Packit Bot ea69bd
my $ignoreEval = $ENV{'http_ignore_eval'};
Packit Bot ea69bd
my $ignore_error_hacks = $ENV{'http_ignore_error_hacks'} || 0;
Packit Bot ea69bd
my $user_display = $ENV{'http_user_display'};
Packit Bot ea69bd
my $logformat = "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"|%h %l %u %t \"%r\" %>s %b|%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b";
Packit Bot ea69bd
Packit Bot ea69bd
if (defined $ENV{'logformat'}) {
Packit Bot ea69bd
   $logformat = $ENV{'logformat'};
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
my @log_fields = ();
Packit Bot ea69bd
my @log_format = ();
Packit Bot ea69bd
if ((defined $ENV{'http_fields'}) and (defined $ENV{'http_format'})) {
Packit Bot ea69bd
   @log_fields = split(" ", $ENV{'http_fields'});
Packit Bot ea69bd
   @log_format = split(" ", $ENV{'http_format'});
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
# Initialization etc.
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
my $byte_summary = 0;
Packit Bot ea69bd
my $failed_requests = 0;
Packit Bot ea69bd
my %field = ();
Packit Bot ea69bd
my %hacks =();
Packit Bot ea69bd
my %hack_success =();
Packit Bot ea69bd
my %needs_exam =();
Packit Bot ea69bd
my %users_logged =();
Packit Bot ea69bd
my %ban_ip =();
Packit Bot ea69bd
my %robots =();
Packit Bot ea69bd
my $pattern = "";
Packit Bot ea69bd
my $flag = 0;
Packit Bot ea69bd
my $isahack = 0;
Packit Bot ea69bd
my $a5xx_resp = 0;
Packit Bot ea69bd
my $a4xx_resp = 0;
Packit Bot ea69bd
my $a3xx_resp = 0;
Packit Bot ea69bd
my $a2xx_resp = 0;
Packit Bot ea69bd
my $a1xx_resp = 0;
Packit Bot ea69bd
my $image_count = 0;
Packit Bot ea69bd
my $image_bytes = 0;
Packit Bot ea69bd
my $docs_count = 0;
Packit Bot ea69bd
my $docs_bytes = 0;
Packit Bot ea69bd
my $archive_count = 0;
Packit Bot ea69bd
my $archive_bytes = 0;
Packit Bot ea69bd
my $sound_count = 0;
Packit Bot ea69bd
my $sound_bytes = 0;
Packit Bot ea69bd
my $movie_count = 0;
Packit Bot ea69bd
my $movie_bytes = 0;
Packit Bot ea69bd
my $winexec_count = 0;
Packit Bot ea69bd
my $winexec_bytes = 0;
Packit Bot ea69bd
my $content_count = 0;
Packit Bot ea69bd
my $content_bytes = 0;
Packit Bot ea69bd
my $redirect_count = 0;
Packit Bot ea69bd
my $redirect_bytes = 0;
Packit Bot ea69bd
my $other_count = 0;
Packit Bot ea69bd
my $other_bytes = 0;
Packit Bot ea69bd
my $total_hack_count = 0;
Packit Bot ea69bd
my $wpad_count =     0;
Packit Bot ea69bd
my $wpad_bytes =     0;
Packit Bot ea69bd
my $src_count =      0;
Packit Bot ea69bd
my $src_bytes =      0;
Packit Bot ea69bd
my $logs_count =     0;
Packit Bot ea69bd
my $logs_bytes =     0;
Packit Bot ea69bd
my $images_count =   0;
Packit Bot ea69bd
my $images_bytes =   0;
Packit Bot ea69bd
my $fonts_count =    0;
Packit Bot ea69bd
my $fonts_bytes =    0;
Packit Bot ea69bd
my $config_count =   0;
Packit Bot ea69bd
my $config_bytes =   0;
Packit Bot ea69bd
my $xpcomext_count = 0;
Packit Bot ea69bd
my $xpcomext_bytes = 0;
Packit Bot ea69bd
my $mozext_count =   0;
Packit Bot ea69bd
my $mozext_bytes =   0;
Packit Bot ea69bd
my $proxy_count =    0;
Packit Bot ea69bd
my $proxy_bytes =    0;
Packit Bot ea69bd
my %proxy_host =     ();
Packit Bot ea69bd
my $host =           "";
Packit Bot ea69bd
my $notparsed =      "";
Packit Bot ea69bd
my $notparsed_count =0;
Packit Bot ea69bd
Packit Bot ea69bd
######################
Packit Bot ea69bd
# file type comparisons are case-insensitive
Packit Bot ea69bd
my $image_types =    '(\.bmp|\.cdr|\.emz|\.gif|\.ico|\.jpe?g|\.png|\.svg|\.sxd|\.tiff?|\.wbmp|\.webp|\.wmf|\.wmz|\.xdm)';
Packit Bot ea69bd
my $content_types =  '(';
Packit Bot ea69bd
   $content_types =  $content_types.'\/server-status|\/server-info';
Packit Bot ea69bd
   $content_types =  $content_types.'|\.htm|\.html|\.jhtml|\.phtml|\.shtml|\/\.?';
Packit Bot ea69bd
   $content_types =  $content_types.'|\.html\.[a-z]{2,3}(_[A-Z]{2,3})?';
Packit Bot ea69bd
   $content_types =  $content_types.'|\.inc|\.php|\.php3|\.asmx|\.asp|\.pl|\.wml';
Packit Bot ea69bd
   $content_types =  $content_types.'|^\/mailman\/.*';
Packit Bot ea69bd
   $content_types =  $content_types.'|\/sqwebmail.*';
Packit Bot ea69bd
   $content_types =  $content_types.'|^\/announce|^\/scrape'; # BitTorrent tracker mod_bt
Packit Bot ea69bd
   $content_types =  $content_types.'|\.torrent';
Packit Bot ea69bd
   $content_types =  $content_types.'|\.css|\.js|\.cgi';
Packit Bot ea69bd
   $content_types =  $content_types.'|\.fla|\.swf|\.rdf';
Packit Bot ea69bd
   $content_types =  $content_types.'|\.class|\.jsp|\.jar|\.java';
Packit Bot ea69bd
   $content_types =  $content_types.'|COPYRIGHT|README|FAQ|INSTALL|\.txt)';
Packit Bot ea69bd
my $docs_types =     '(\.asc|\.bib|\.djvu|\.docx?|\.dot|\.dtd|\.dvi|\.gnumeric|\.mcd|\.mso|\.pdf|\.pps|\.pptx?|\.ps|\.rtf|\.sxi|\.tex|\.text|\.tm|\.xlsx?|\.xml)';
Packit Bot ea69bd
my $archive_types =  '(\.7z|\.ace|\.bz2|\.cab|\.deb|\.dsc|\.ed2k|\.gz|\.hqx|\.md5|\.rar|\.rpm|\.sig|\.sign|\.tar|\.tbz2|\.tgz|\.vl2|\.z|\.zip|\.hdr)';
Packit Bot ea69bd
my $sound_types =    '(\.aac|\.au|\.aud|\.m4a|\.mid|\.mp3|\.oga|\.pls|\.ram|\.raw|\.rm|\.wav|\.wma|\.xsm)';
Packit Bot ea69bd
my $movie_types =    '(\.asf|\.ass|\.avi|\.idx|\.flv|\.m2?ts|\.mkv|\.mp4|\.mpe?g|\.mov|\.ogg|\.ogv|\.qt|\.psb|\.srt|\.ssa|\.smi|\.sub|\.webm|\.wmv)';
Packit Bot ea69bd
my $winexec_types =  '(\.bat|\.com|\.exe|\.dll)';
Packit Bot ea69bd
my $wpad_files =     '(wpad\.dat|wspad\.dat|proxy\.pac)';
Packit Bot ea69bd
my $program_src =    '(';
Packit Bot ea69bd
   $program_src =    $program_src.'\.bas|\.cs?|\.cpp|\.diff|\.f|\.h|\.init|\.m|\.mo|\.pas|\.patch|\.po|\.pot|\.py|\.sh|\.spec';
Packit Bot ea69bd
   $program_src =    $program_src.'|Makefile|Makefile_c|Makefile_f77)';
Packit Bot ea69bd
my $images_types =   '(\.bin|\.cue|\.img|\.iso|\.run)';
Packit Bot ea69bd
my $logs_types =     '(\.log|_log|-log|\.logs|\.out|\.wyniki)';
Packit Bot ea69bd
my $fonts_types =    '(\.aft|\.otf|\.ttf|\.woff)';
Packit Bot ea69bd
my $config_types =   '(\.cfg|\.conf|\.config|\.ini|\.properties)';
Packit Bot ea69bd
my $xpcomext_types = '(\.xpt)';
Packit Bot ea69bd
my $mozext_types =   '(\.xul)';
Packit Bot ea69bd
Packit Bot ea69bd
# HTTP Status codes from HTTP/Status.pm, to avoid loading package
Packit Bot ea69bd
# that may or may not exist.  We only need those >=400, but all
Packit Bot ea69bd
# are included for potential future use.
Packit Bot ea69bd
my %StatusCode = (
Packit Bot ea69bd
    100 => 'Continue',
Packit Bot ea69bd
    101 => 'Switching Protocols',
Packit Bot ea69bd
    102 => 'Processing',                      # WebDAV
Packit Bot ea69bd
    200 => 'OK',
Packit Bot ea69bd
    201 => 'Created',
Packit Bot ea69bd
    202 => 'Accepted',
Packit Bot ea69bd
    203 => 'Non-Authoritative Information',
Packit Bot ea69bd
    204 => 'No Content',
Packit Bot ea69bd
    205 => 'Reset Content',
Packit Bot ea69bd
    206 => 'Partial Content',
Packit Bot ea69bd
    207 => 'Multi-Status',                    # WebDAV
Packit Bot ea69bd
    300 => 'Multiple Choices',
Packit Bot ea69bd
    301 => 'Moved Permanently',
Packit Bot ea69bd
    302 => 'Found',
Packit Bot ea69bd
    303 => 'See Other',
Packit Bot ea69bd
    304 => 'Not Modified',
Packit Bot ea69bd
    305 => 'Use Proxy',
Packit Bot ea69bd
    307 => 'Temporary Redirect',
Packit Bot ea69bd
    400 => 'Bad Request',
Packit Bot ea69bd
    401 => 'Unauthorized',
Packit Bot ea69bd
    402 => 'Payment Required',
Packit Bot ea69bd
    403 => 'Forbidden',
Packit Bot ea69bd
    404 => 'Not Found',
Packit Bot ea69bd
    405 => 'Method Not Allowed',
Packit Bot ea69bd
    406 => 'Not Acceptable',
Packit Bot ea69bd
    407 => 'Proxy Authentication Required',
Packit Bot ea69bd
    408 => 'Request Timeout',
Packit Bot ea69bd
    409 => 'Conflict',
Packit Bot ea69bd
    410 => 'Gone',
Packit Bot ea69bd
    411 => 'Length Required',
Packit Bot ea69bd
    412 => 'Precondition Failed',
Packit Bot ea69bd
    413 => 'Request Entity Too Large',
Packit Bot ea69bd
    414 => 'Request-URI Too Large',
Packit Bot ea69bd
    415 => 'Unsupported Media Type',
Packit Bot ea69bd
    416 => 'Request Range Not Satisfiable',
Packit Bot ea69bd
    417 => 'Expectation Failed',
Packit Bot ea69bd
    422 => 'Unprocessable Entity',            # WebDAV
Packit Bot ea69bd
    423 => 'Locked',                          # WebDAV
Packit Bot ea69bd
    424 => 'Failed Dependency',               # WebDAV
Packit Bot ea69bd
    500 => 'Internal Server Error',
Packit Bot ea69bd
    501 => 'Not Implemented',
Packit Bot ea69bd
    502 => 'Bad Gateway',
Packit Bot ea69bd
    503 => 'Service Unavailable',
Packit Bot ea69bd
    504 => 'Gateway Timeout',
Packit Bot ea69bd
    505 => 'HTTP Version Not Supported',
Packit Bot ea69bd
    507 => 'Insufficient Storage',            # WebDAV
Packit Bot ea69bd
);
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#   what to look for as an attack  USE LOWER CASE!!!!!!
Packit Bot ea69bd
#
Packit Bot ea69bd
my @exploits = (
Packit Bot ea69bd
   '^null$',
Packit Bot ea69bd
   '/\.\./\.\./\.\./',
Packit Bot ea69bd
   '\.\./\.\./config\.sys',
Packit Bot ea69bd
   '/\.\./\.\./\.\./autoexec\.bat',
Packit Bot ea69bd
   '/\.\./\.\./windows/user\.dat',
Packit Bot ea69bd
   '\\\x02\\\xb1',
Packit Bot ea69bd
   '\\\x04\\\x01',
Packit Bot ea69bd
   '\\\x05\\\x01',
Packit Bot ea69bd
   '\\\x90\\\x02\\\xb1\\\x02\\\xb1',
Packit Bot ea69bd
   '\\\x90\\\x90\\\x90\\\x90',
Packit Bot ea69bd
   '\\\xff\\\xff\\\xff\\\xff',
Packit Bot ea69bd
   '\\\xe1\\\xcd\\\x80',
Packit Bot ea69bd
   '\\\xff\xe0\\\xe8\\\xf8\\\xff\\\xff\\\xff-m',
Packit Bot ea69bd
   '\\\xc7f\\\x0c',
Packit Bot ea69bd
   '\\\x84o\\\x01',
Packit Bot ea69bd
   '\\\x81',
Packit Bot ea69bd
   '\\\xff\\\xe0\\\xe8',
Packit Bot ea69bd
   '\/c\+dir',
Packit Bot ea69bd
   '\/c\+dir\+c',
Packit Bot ea69bd
   '\.htpasswd',
Packit Bot ea69bd
   'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
Packit Bot ea69bd
   'author\.exe',
Packit Bot ea69bd
   'boot\.ini',
Packit Bot ea69bd
   'cmd\.exe',
Packit Bot ea69bd
   'c%20dir%20c',
Packit Bot ea69bd
   'default\.ida',
Packit Bot ea69bd
   'fp30reg\.dll',
Packit Bot ea69bd
   'httpodbc\.dll',
Packit Bot ea69bd
   'nsiislog\.dll',
Packit Bot ea69bd
   'passwd$',
Packit Bot ea69bd
   'root\.exe',
Packit Bot ea69bd
   'shtml\.exe',
Packit Bot ea69bd
   'win\.ini',
Packit Bot ea69bd
   'xxxxxxxxxxxxxxxxxxxxxx',
Packit Bot ea69bd
);
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  Define some useful RE paterns
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
my %re_pattern = (
Packit Bot ea69bd
   space => '(.*)',
Packit Bot ea69bd
   brace => '\[(.*)\]',
Packit Bot ea69bd
   quote => '\"(.*)\"');
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  Build the regex to parse the line
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
for (my $i = 0; $i < @log_format; $i++) {
Packit Bot ea69bd
   $pattern = $pattern.$re_pattern{$log_format[$i]}.'\\s';
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
# this is easier than coding last element logic in the loop
Packit Bot ea69bd
chop($pattern);
Packit Bot ea69bd
chop($pattern);
Packit Bot ea69bd
Packit Bot ea69bd
# The following are used to build up pattern matching strings for
Packit Bot ea69bd
# the log format used in the access_log files.
Packit Bot ea69bd
my @parse_string = ();
Packit Bot ea69bd
my @parse_field = ();
Packit Bot ea69bd
my $parse_index = 0;
Packit Bot ea69bd
my $parse_subindex = 0;
Packit Bot ea69bd
$parse_string[$parse_index] = "";
Packit Bot ea69bd
$parse_field[$parse_index] = ();
Packit Bot ea69bd
if ($pattern) {
Packit Bot ea69bd
   # accommodate usage of HTTP_FIELDS and HTTP_FORMAT
Packit Bot ea69bd
   $parse_string[0] = $pattern;
Packit Bot ea69bd
   $parse_field[0] = [@log_fields];
Packit Bot ea69bd
   $parse_index++;
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
$parse_string[$parse_index] = "";
Packit Bot ea69bd
$parse_field[$parse_index] = ();
Packit Bot ea69bd
my $end_loop = 1;
Packit Bot ea69bd
$logformat =~ s/%[\d,!]*/%/g;
Packit Bot ea69bd
while ($end_loop) {
Packit Bot ea69bd
Packit Bot ea69bd
   if ($logformat =~ /\G%h/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(\\S*?)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "client_ip";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%l/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(\\S*?)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "ident";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%u/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(\\S*?)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "userid";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%t/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(\\[.*\\])";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "timestamp";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%r/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(.*)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "request";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%>?s/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(\\d{3})";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "http_rc";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%b/gc) {
Packit Bot ea69bd
      # "transfered" is misspelled, but not corrected because this string
Packit Bot ea69bd
      # comes from the configuration file, and would create a compatibility
Packit Bot ea69bd
      # issue
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "bytes_transfered";
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(-|\\d*)";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%V/gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(\\S*?)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "server_name";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%I/gc) {
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "bytes_in";
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(-|\\d*)";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%O/gc) {
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "bytes_out";
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(-|\\d*)";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%\{Referer}i/gci) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(.*)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "referrer";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%\{User-Agent}i/gci) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(.*)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "agent";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G%({.*?})?./gc) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= "(.*?)";
Packit Bot ea69bd
      $parse_field[$parse_index][$parse_subindex++] = "not_used";
Packit Bot ea69bd
   } elsif ($logformat =~ /\G\|/gc) {
Packit Bot ea69bd
      $parse_index++;
Packit Bot ea69bd
      $parse_subindex = 0;
Packit Bot ea69bd
      $parse_string[$parse_index] = "";
Packit Bot ea69bd
      $parse_field[$parse_index] = ();
Packit Bot ea69bd
   # perl 5.6 does not detect end of string properly in next elsif block,
Packit Bot ea69bd
   # so we test it explicitly here
Packit Bot ea69bd
   } elsif ($logformat =~ /\G$/gc) {
Packit Bot ea69bd
      $end_loop = 0;
Packit Bot ea69bd
   } elsif ((my $filler) = ($logformat =~ /\G([^%\|]*)/gc)) {
Packit Bot ea69bd
      $parse_string[$parse_index] .= $filler;
Packit Bot ea69bd
   # perl 5.6 loses track of match position, so we force it.  Perl 5.8
Packit Bot ea69bd
   # and later does it correctly, so it was fixed in 5.7 development.
Packit Bot ea69bd
      if ($] < 5.007) {pos($logformat) += length($filler);}
Packit Bot ea69bd
   } else {
Packit Bot ea69bd
      $end_loop = 0;
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
Packit Bot ea69bd
#################   print "RE pattern     = $pattern \n";
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  Process log file on stdin
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
while (my $line = <STDIN>) {
Packit Bot ea69bd
   chomp($line);
Packit Bot ea69bd
Packit Bot ea69bd
   ##################  print "Line = $line \n";
Packit Bot ea69bd
Packit Bot ea69bd
   #
Packit Bot ea69bd
   # parse the line per the input spec
Packit Bot ea69bd
   #
Packit Bot ea69bd
   my @parsed_line;
Packit Bot ea69bd
   for $parse_index (0..$#parse_string) {
Packit Bot ea69bd
      if (@parsed_line = $line =~ /$parse_string[$parse_index]/) {
Packit Bot ea69bd
         @log_fields = @{$parse_field[$parse_index]};
Packit Bot ea69bd
         last;
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
   if (not @parsed_line) {
Packit Bot ea69bd
      $notparsed_count++;
Packit Bot ea69bd
      if ($notparsed_count <= 10) {
Packit Bot ea69bd
         $notparsed = $notparsed . "   " . $line . "\n";
Packit Bot ea69bd
      }
Packit Bot ea69bd
      next;
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
   # hash the results so we can identify the fields
Packit Bot ea69bd
   #
Packit Bot ea69bd
   for my $i (0..$#log_fields) {
Packit Bot ea69bd
      #		print "$i $log_fields[$i] $parsed_line[$i] \n";
Packit Bot ea69bd
      $field{$log_fields[$i]} = $parsed_line[$i];
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
   ##
Packit Bot ea69bd
   ## Do the default stuff
Packit Bot ea69bd
   ##
Packit Bot ea69bd
Packit Bot ea69bd
   #
Packit Bot ea69bd
   # Break up the request into method, url and protocol
Packit Bot ea69bd
   #
Packit Bot ea69bd
Packit Bot ea69bd
   ($field{method},$field{url},$field{protocol}) = split(/ +/,$field{"request"});
Packit Bot ea69bd
   if (! $field{url}) {
Packit Bot ea69bd
      $field{url}='null';
Packit Bot ea69bd
   }
Packit Bot ea69bd
   $field{lc_url} = lc $field{url};
Packit Bot ea69bd
Packit Bot ea69bd
   #
Packit Bot ea69bd
   # Bytes sent Summary
Packit Bot ea69bd
   # Apache uses "-" to represent 0 bytes transferred
Packit Bot ea69bd
   #
Packit Bot ea69bd
Packit Bot ea69bd
   if ($field{bytes_transfered} eq "-") {$field{bytes_transfered} = 0};
Packit Bot ea69bd
   $byte_summary += $field{bytes_transfered};
Packit Bot ea69bd
Packit Bot ea69bd
   #
Packit Bot ea69bd
   #  loop to check for typical exploit attempts
Packit Bot ea69bd
   #
Packit Bot ea69bd
Packit Bot ea69bd
   if (!$ignore_error_hacks) {
Packit Bot ea69bd
      for (my $i = 0; $i < @exploits; $i++) {
Packit Bot ea69bd
         # print "$i $exploits[$i] $field{lc_url} \n";
Packit Bot ea69bd
         if ( ($field{lc_url} =~ /$exploits[$i]/i) &&
Packit Bot ea69bd
              !((defined $ignoreURLs) && ($field{url} =~ /$ignoreURLs/i)) &&
Packit Bot ea69bd
              !((defined $ignoreIPs) && ($field{client_ip} =~ /$ignoreIPs/)) ) {
Packit Bot ea69bd
            $hacks{$field{client_ip}}{$exploits[$i]}++;
Packit Bot ea69bd
            $total_hack_count += 1;
Packit Bot ea69bd
            $ban_ip{$field{client_ip}} = " ";
Packit Bot ea69bd
            if ($field{http_rc} < 300) {
Packit Bot ea69bd
               $hack_success{$field{url}} = $field{http_rc};
Packit Bot ea69bd
            }
Packit Bot ea69bd
         }
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
   #
Packit Bot ea69bd
   #  Count types and bytes
Packit Bot ea69bd
   #
Packit Bot ea69bd
   #	this is only printed if detail > 4 but it also looks
Packit Bot ea69bd
   #	for 'strange' stuff so it needs to run always
Packit Bot ea69bd
   #
Packit Bot ea69bd
Packit Bot ea69bd
   ($field{base_url},$field{url_parms}) = split(/\?/,$field{"lc_url"});
Packit Bot ea69bd
Packit Bot ea69bd
   if ($field{base_url} =~ /$image_types$/oi) {
Packit Bot ea69bd
      $image_count += 1;
Packit Bot ea69bd
      $image_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$docs_types$/oi) {
Packit Bot ea69bd
      $docs_count += 1;
Packit Bot ea69bd
      $docs_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$archive_types$/oi) {
Packit Bot ea69bd
      $archive_count += 1;
Packit Bot ea69bd
      $archive_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$sound_types$/oi) {
Packit Bot ea69bd
      $sound_count += 1;
Packit Bot ea69bd
      $sound_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$movie_types$/oi) {
Packit Bot ea69bd
      $movie_count += 1;
Packit Bot ea69bd
      $movie_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$winexec_types$/oi) {
Packit Bot ea69bd
      $winexec_count += 1;
Packit Bot ea69bd
      $winexec_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$content_types$/oi) {
Packit Bot ea69bd
      $content_count += 1;
Packit Bot ea69bd
      $content_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$wpad_files$/oi) {
Packit Bot ea69bd
      $wpad_count += 1;
Packit Bot ea69bd
      $wpad_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$program_src$/oi) {
Packit Bot ea69bd
      $src_count += 1;
Packit Bot ea69bd
      $src_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$images_types$/oi) {
Packit Bot ea69bd
      $images_count += 1;
Packit Bot ea69bd
      $images_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$logs_types$/oi) {
Packit Bot ea69bd
      $logs_count += 1;
Packit Bot ea69bd
      $logs_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$fonts_types$/oi) {
Packit Bot ea69bd
      $fonts_count += 1;
Packit Bot ea69bd
      $fonts_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$config_types$/oi) {
Packit Bot ea69bd
      $config_count += 1;
Packit Bot ea69bd
      $config_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$xpcomext_types$/oi) {
Packit Bot ea69bd
      $xpcomext_count += 1;
Packit Bot ea69bd
      $xpcomext_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{base_url} =~ /$mozext_types$/oi) {
Packit Bot ea69bd
      $mozext_count += 1;
Packit Bot ea69bd
      $mozext_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{http_rc} =~ /3\d\d/) {
Packit Bot ea69bd
      $redirect_count += 1;
Packit Bot ea69bd
      $redirect_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   } elsif ($field{method} =~ /CONNECT/) {
Packit Bot ea69bd
      $proxy_count += 1;
Packit Bot ea69bd
      $proxy_bytes += $field{bytes_transfered};
Packit Bot ea69bd
      $proxy_host{"$field{client_ip} -> $field{base_url}"}++;
Packit Bot ea69bd
   } else {
Packit Bot ea69bd
      $other_count += 1;
Packit Bot ea69bd
      $other_bytes += $field{bytes_transfered};
Packit Bot ea69bd
   }
Packit Bot ea69bd
   if ( ($field{http_rc} >= 400) && !shouldIgnore("needs_exam") ) {
Packit Bot ea69bd
      my $fmt_url = $field{url};
Packit Bot ea69bd
      if (length($field{url}) > 60) {
Packit Bot ea69bd
         $fmt_url = substr($field{url},0,42) . " ... " .
Packit Bot ea69bd
                    substr($field{url},-15,15);
Packit Bot ea69bd
      }
Packit Bot ea69bd
      $needs_exam{$field{http_rc}}{$fmt_url}++;
Packit Bot ea69bd
   }
Packit Bot ea69bd
   if (defined $field{userid} && $field{userid} ne "-" &&
Packit Bot ea69bd
         (eval $user_display) && !shouldIgnore("users_logged") ) {
Packit Bot ea69bd
       $users_logged{$field{userid}}{$field{client_ip}}++;
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
   ##
Packit Bot ea69bd
   ## Do the > 4 stuff
Packit Bot ea69bd
   ##
Packit Bot ea69bd
   #
Packit Bot ea69bd
   #  Response Summary
Packit Bot ea69bd
   #
Packit Bot ea69bd
Packit Bot ea69bd
   if ($field{http_rc} > 499 ) {
Packit Bot ea69bd
      $a5xx_resp += 1;
Packit Bot ea69bd
   } elsif ($field{http_rc} > 399 ) {
Packit Bot ea69bd
      $a4xx_resp += 1;
Packit Bot ea69bd
   } elsif($field{http_rc} > 299 ) {
Packit Bot ea69bd
      $a3xx_resp += 1;
Packit Bot ea69bd
   } elsif($field{http_rc} > 199 ) {
Packit Bot ea69bd
      $a2xx_resp += 1;
Packit Bot ea69bd
   } else {
Packit Bot ea69bd
      $a1xx_resp += 1;
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
   #
Packit Bot ea69bd
   #  Count the robots who actually ask for the robots.txt file
Packit Bot ea69bd
   #
Packit Bot ea69bd
Packit Bot ea69bd
   if ($field{lc_url} =~ /^\/robots.txt$/) {
Packit Bot ea69bd
      if (defined $field{agent}) {
Packit Bot ea69bd
         $robots{$field{agent}} +=1;
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
Packit Bot ea69bd
} ## End of while loop
Packit Bot ea69bd
Packit Bot ea69bd
#############################################
Packit Bot ea69bd
##   output the results
Packit Bot ea69bd
##
Packit Bot ea69bd
Packit Bot ea69bd
if ($detail >4) {
Packit Bot ea69bd
   printf "%.2f MB transferred " , $byte_summary/(1024*1024);
Packit Bot ea69bd
   print  "in ";
Packit Bot ea69bd
   print my $resp_total = ($a1xx_resp + $a2xx_resp + $a3xx_resp + $a4xx_resp + $a5xx_resp);
Packit Bot ea69bd
   print " responses ";
Packit Bot ea69bd
   print " (1xx $a1xx_resp, 2xx $a2xx_resp, 3xx $a3xx_resp,";
Packit Bot ea69bd
   print " 4xx $a4xx_resp, 5xx $a5xx_resp) \n";
Packit Bot ea69bd
   my $lr = length($resp_total);
Packit Bot ea69bd
   if ($image_count > 0)      { printf "   %*d Images (%.2f MB),\n" , $lr, $image_count, $image_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($docs_count > 0)       { printf "   %*d Documents (%.2f MB),\n" , $lr, $docs_count, $docs_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($archive_count > 0)    { printf "   %*d Archives (%.2f MB),\n" , $lr, $archive_count, $archive_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($sound_count > 0)      { printf "   %*d Sound files (%.2f MB),\n" , $lr, $sound_count, $sound_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($movie_count > 0)      { printf "   %*d Movies files (%.2f MB),\n" , $lr, $movie_count, $movie_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($winexec_count > 0)    { printf "   %*d Windows executable files (%.2f MB),\n" , $lr, $winexec_count, $winexec_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($content_count > 0)    { printf "   %*d Content pages (%.2f MB),\n" , $lr, $content_count, $content_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($redirect_count > 0)   { printf "   %*d Redirects (%.2f MB),\n" , $lr, $redirect_count, $redirect_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($wpad_count > 0)       { printf "   %*d Proxy Configuration Files (%.2f MB),\n" , $lr, $wpad_count, $wpad_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($src_count > 0)        { printf "   %*d Program source files (%.2f MB),\n" , $lr, $src_count, $src_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($images_count > 0)     { printf "   %*d CD Images (%.2f MB),\n" , $lr, $images_count, $images_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($logs_count > 0)       { printf "   %*d Various Logs (%.2f MB),\n" , $lr, $logs_count, $logs_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($fonts_count > 0)      { printf "   %*d Fonts (%.2f MB),\n" , $lr, $fonts_count, $fonts_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($config_count > 0)     { printf "   %*d Configs (%.2f MB),\n" , $lr, $config_count, $config_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($xpcomext_count > 0)   { printf "   %*d XPCOM Type Libraries (%.2f MB),\n" , $lr, $xpcomext_count, $xpcomext_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($mozext_count > 0)     { printf "   %*d Mozilla extensions (%.2f MB),\n" , $lr, $mozext_count, $mozext_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($proxy_count > 0)      { printf "   %*d mod_proxy requests (%.2f MB),\n" , $lr, $proxy_count, $proxy_bytes/(1024*1024); }
Packit Bot ea69bd
   if ($other_count > 0)      { printf "   %*d Other (%.2f MB) \n" , $lr, $other_count, $other_bytes/(1024*1024); }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  List attempted exploits
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
if (($detail >4) and $total_hack_count) {
Packit Bot ea69bd
   print "\nAttempts to use known hacks by ".(keys %hacks).
Packit Bot ea69bd
         " hosts were logged $total_hack_count time(s) from:\n";
Packit Bot ea69bd
   my $order = TotalCountOrder(%hacks);
Packit Bot ea69bd
   foreach my $i (sort $order keys %hacks) {
Packit Bot ea69bd
      my $hacks_per_ip = 0;
Packit Bot ea69bd
      foreach my $j ( keys %{$hacks{$i}} ) {
Packit Bot ea69bd
         $hacks_per_ip += $hacks{$i}{$j};
Packit Bot ea69bd
      }
Packit Bot ea69bd
      print "   $i: $hacks_per_ip Time(s)\n";
Packit Bot ea69bd
      if ($detail > 9) {
Packit Bot ea69bd
         foreach my $j ( keys %{$hacks{$i}} ) {
Packit Bot ea69bd
            print "      $j $hacks{$i}{$j} Time(s) \n";
Packit Bot ea69bd
         }
Packit Bot ea69bd
      } else {
Packit Bot ea69bd
         print "\n";
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
if (keys %proxy_host) {
Packit Bot ea69bd
   print "\nConnection attempts using mod_proxy:\n";
Packit Bot ea69bd
   foreach $host (sort {$a cmp $b} keys %proxy_host) {
Packit Bot ea69bd
      print "   $host: $proxy_host{$host} Time(s)\n";
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
#
Packit Bot ea69bd
#  List (wannabe) blackhat sites
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
$flag = 1;
Packit Bot ea69bd
foreach my $i (sort keys %ban_ip) {
Packit Bot ea69bd
   if ($flag) {
Packit Bot ea69bd
      print "\nA total of ".scalar(keys %ban_ip)." sites probed the server \n";
Packit Bot ea69bd
      $flag = 0;
Packit Bot ea69bd
   }
Packit Bot ea69bd
   #if ($detail > 4) {
Packit Bot ea69bd
      print "   $i\n";
Packit Bot ea69bd
   #}
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  List possible successful probes
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
$flag = 1;
Packit Bot ea69bd
if (keys %hack_success) {
Packit Bot ea69bd
   print "\nA total of " . scalar(keys %hack_success) . " possible successful probes were detected (the following URLs\n";
Packit Bot ea69bd
   print "contain strings that match one or more of a listing of strings that\n";
Packit Bot ea69bd
   print "indicate a possible exploit):\n\n";
Packit Bot ea69bd
Packit Bot ea69bd
   foreach my $i (keys %hack_success) {
Packit Bot ea69bd
      print "   $i HTTP Response $hack_success{$i} \n";
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  List error response codes
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
if (keys %needs_exam) {
Packit Bot ea69bd
   print "\nRequests with error response codes\n";
Packit Bot ea69bd
   # my $count = TotalCountOrder(%needs_exam);
Packit Bot ea69bd
   for my $code (sort keys %needs_exam) {
Packit Bot ea69bd
      if (not defined $StatusCode{$code}) {
Packit Bot ea69bd
         $StatusCode{$code} = "\(undefined\)";
Packit Bot ea69bd
      }
Packit Bot ea69bd
      if( ($ENV{"http_rc_detail_rep_$code"} || $detail) > $detail ) {
Packit Bot ea69bd
      # only display summary for this code
Packit Bot ea69bd
         my $t = 0;
Packit Bot ea69bd
         my $u = 0;
Packit Bot ea69bd
         foreach my $k ( keys %{$needs_exam{$code}}) {
Packit Bot ea69bd
            $u += 1;
Packit Bot ea69bd
            $t += $needs_exam{$code}{$k};
Packit Bot ea69bd
         }
Packit Bot ea69bd
         print "   $code $StatusCode{$code} SUMMARY - $u URLs, total: $t Time(s)\n";
Packit Bot ea69bd
      } else {
Packit Bot ea69bd
         print "   $code $StatusCode{$code}\n";
Packit Bot ea69bd
         for my $url (sort { ($needs_exam{$code}{$b} <=> $needs_exam{$code}{$a}) or ($a cmp $b) } keys %{$needs_exam{$code}}) {
Packit Bot ea69bd
            print "      $url: $needs_exam{$code}{$url} Time(s)\n";
Packit Bot ea69bd
         }
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
if (keys %users_logged) {
Packit Bot ea69bd
   print "\nUsers logged successfully\n";
Packit Bot ea69bd
   for my $user (sort keys %users_logged) {
Packit Bot ea69bd
      my %userips = %{$users_logged{$user}};
Packit Bot ea69bd
      # If one user name logged from many IPs, don't print them all. 5 is arbitrary
Packit Bot ea69bd
      if (scalar(keys %userips) > 5) {
Packit Bot ea69bd
         my $count = 0;
Packit Bot ea69bd
         for my $ip (keys %userips) {
Packit Bot ea69bd
             $count += $userips{$ip};
Packit Bot ea69bd
         }
Packit Bot ea69bd
         print "   $user: $count Time(s) from ".scalar(keys %userips)." addresses\n";
Packit Bot ea69bd
      } else {
Packit Bot ea69bd
         print "   $user\n";
Packit Bot ea69bd
         for my $ip (sort keys %userips) {
Packit Bot ea69bd
            print "      $ip: $userips{$ip} Time(s)\n";
Packit Bot ea69bd
         }
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
#
Packit Bot ea69bd
#  List robots that identified themselves
Packit Bot ea69bd
#
Packit Bot ea69bd
Packit Bot ea69bd
if ($detail > 4) {
Packit Bot ea69bd
   $flag = 1;
Packit Bot ea69bd
   foreach my $i (keys %robots) {
Packit Bot ea69bd
      if ($flag) {
Packit Bot ea69bd
         print "\nA total of ".scalar(keys %robots)." ROBOTS were logged \n";
Packit Bot ea69bd
         $flag = 0;
Packit Bot ea69bd
      }
Packit Bot ea69bd
      if ($detail > 9) {
Packit Bot ea69bd
         print "   $i $robots{$i} Time(s) \n";
Packit Bot ea69bd
      }
Packit Bot ea69bd
   }
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
if ($notparsed) {
Packit Bot ea69bd
   print "\nThis is a listing of log lines that were not parsed correctly.\n";
Packit Bot ea69bd
   print "Perhaps the variable \$LogFormat in file conf/services/http.conf\n";
Packit Bot ea69bd
   print "is not correct?\n\n";
Packit Bot ea69bd
   if ($notparsed_count > 10) {
Packit Bot ea69bd
      print "(Only the first ten are printed; there were a total of $notparsed_count)\n";
Packit Bot ea69bd
   }
Packit Bot ea69bd
   print $notparsed;
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
exit (0);
Packit Bot ea69bd
Packit Bot ea69bd
sub shouldIgnore {
Packit Bot ea69bd
   my($context)=@_;
Packit Bot ea69bd
Packit Bot ea69bd
   if( ((defined $ignoreURLs) && ($field{url} =~ /$ignoreURLs/i)) ||
Packit Bot ea69bd
       ((defined $ignoreIPs) && ($field{client_ip} =~ /$ignoreIPs/)) ) {
Packit Bot ea69bd
      return 1;
Packit Bot ea69bd
   }
Packit Bot ea69bd
   return (eval $ignoreEval);
Packit Bot ea69bd
}
Packit Bot ea69bd
Packit Bot ea69bd
# vi: shiftwidth=3 tabstop=3 syntax=perl et
Packit Bot ea69bd
# Local Variables:
Packit Bot ea69bd
# mode: perl
Packit Bot ea69bd
# perl-indent-level: 3
Packit Bot ea69bd
# indent-tabs-mode: nil
Packit Bot ea69bd
# End: