1#!/usr/bin/env perl 2# 3# Program: Generate LDAP Statistics Reports <ldap-stats.pl> 4# 5# Source code home: http://prefetch.net/code/ldap-stats.pl 6# 7# Author: Matty < matty91 @ gmail dot com > 8# 9# Current Version: 5.2 10# 11# Revision History: 12# 13# Version 5.2 14# Perl::Tidy and Perl::Critic -- Gavin Henry, Suretec Systems Ltd. 15# 16# Version 5.1 17# - Changed the location of the uc() statement -- Quanah Gibson-Mount 18# 19# Version 5.0 20# - Changed reporting structure to be dynamic -- Quanah Gibson-Mount 21# - Fixed a bug with name resolution -- Quanah Gibson-Mount 22# - Added the URL to the script -- Quanah Gibson-Mount 23# 24# Version 4.2 25# - Utilize strict mode -- Peter Schober 26# 27# Version 4.1 28# - Fixed a typo in the length() function -- Peter Schober 29# 30# Version 4.0 31# - Added "-d" option to print all days 32# - Fixed day sort order 33# - Added "-m" option to print all months 34# - Fixed month sort order 35# - Correct spelling. -- Dave Horsfall 36# - Align headings. -- Dave Horsfall 37# - Support ldapi:// connections ("LOCAL-SOCKET"). -- Dave Horsfall 38# - Only do lookup if numeric IP. -- Dave Horsfall 39# 40# Version 3.0 - 3.4 41# - Added ability to resolve IP addresses to hostnames with "-n" option 42# - Adjusted print() routines to limit lines to 80-characters -- Dave Horsfall 43# - Clean up unnecessary (..) in regexes -- Peter Marschall 44# - Split attributes found in searches (controlled by new option -s) -- Peter Marschall 45# - Added report to print which filters are used 46# - Added report to print explicit attributes requested -- Francis Swasey 47# - Fix usage: correct line break, all lines < 80 chars -- Peter Marschall 48# - Replace unnecessary printf() by print -- Peter Marschall 49# - Concatenate arguments into one call to print instead of multiple calls -- Peter Marschall 50# - Adapt underlining of some headers to length of logfile / date -- Peter Marschall 51# - Added additional checks to address missing entries during logfile rotation 52# - Fixed "uninitialized value in hash element" -- Todd Lyons 53# - Added additional comments to code 54# - Added report for operations by time of day 55# - Added report for operations per day 56# - Added report for operations per month 57# - Removed debug statements to speedup logfile processing 58# - Changed printf() format specifiers to match column definitions 59# 60# Version 2.0 - 2.2 61# - Adjusted the Search base comparison to catch "" 62# - Translate "" to RootDSE in the search base results 63# - Only print "Unindexed attribute" if unindexed attributes exist 64# - Normalize the bind DN and search base to avoid duplicates 65# - Fix typo with binddn array 66# - Improved filter for anonymous and authenticated binds -- Peter Marschall 67# - Logfiles are now passed as arguments to ldap-stats.pl 68# (e.g, ldap-stats.pl openldap1 openldap2 openldap3 old* ) -- Peter Marschall 69# - Cleaned up and combined filters for ADDs, MODs, DELs -- Peter Marschall 70# - Added support for CMPs & MODRDNs -- Peter Marschall 71# - Reduced number of regular expressions to one per filter -- Peter Marschall 72# - Removed head and tail program requirements, as dates are read on the fly from the 73# decoded logfile -- Peter Marschall 74# - Support for gzip and bzip2 compressed files -- Peter Marschall 75# - Optimized some expressions -- Peter Marschall 76# - Removed several Perl warnings, and added "-w" to default runtime options -- Peter Marschall 77# - Support for regular expressions in logfile names (e.g., ldap-stats.pl /var/log/openldap* ) -- Peter Marschall 78# - Changed default Perl interpreter to /usr/bin/perl 79# - Changed to OpenLDAP license 80# 81# Version 1.1 - 1.9 82# - Updated the bind, binddn, search, search base, and unindexed search regexs to 83# match a wider array of characters -- added by Peter Marschall 84# - Shortened several regular expressions by replacing "[0-9]" with "\d" -- added by Peter Marschall 85# - Fixed a divide by zero bug when logfiles contain 0 connections -- added by Dave Horsfall 86# - Removed unnecessary file open(s) 87# - Removed end of line ($) character from anonymous BIND regular expressions 88# - Added "-l" option to print lines as they are processed from a logfile 89# - Updated documentation 90# - Updated formatting of search dn report 91# - Updated formatting of search base report 92# - Added an additional report with the number of binds per DN 93# - Updated examples 94# - Added additional debug messages to connection setup 95# - Fixed documentation issues 96# - Added debugging flag (-d) to give detailed information on logfile processing 97# - Added "usage" subroutine to ease option maintenance 98# - Fixed a bug in the BIND calculations -- found and fixed by Quanah Gibson-Mount 99# - Fixed a bug in the MOD calculations -- found and fixed by Quanah Gibson-Mount 100# - Fixed a bug in the SRCH calculations -- found and fixed by Quanah Gibson-Mount 101# - Added a connection associative array to coorelate conn identifiers w/hosts -- Quanah Gibson-Mount 102# - Updated the usage message with information on "-c" option 103# - The "-f" option now accepts multiple logfiles 104# - Changed the headers to include information on all logfiles processed 105# - Added the day the report was run to the report headers 106# 107# Version 1.0 108# Original release 109# 110# Last Updated: 13-11-2006 111# 112# Purpose: 113# Produces numerous reports from OpenLDAP 2.1, 2.2 and 2.3 logfiles. 114# 115# License: 116# 117# Redistribution and use in source and binary forms, with or without 118# modification, are permitted only as authorized by the OpenLDAP 119# Public License. 120# 121# A copy of this license is available in the file LICENSE in the 122# top-level directory of the distribution or, alternatively, at 123# <http://www.OpenLDAP.org/license.html>. 124# 125# Installation: 126# 1. Enable a minimum of 'loglevel 256' in the slapd.conf configuration file. 127# 2. Copy the shell script to a suitable location. 128# 3. Refer to the usage section for options and examples. 129# 130# Usage: 131# Refer to the usage subroutine, 132# 133# Example: 134# Refer to http://prefetch.net/code/ldap-stats.pl.txt to see sample output 135 136use strict; 137use warnings; 138use Getopt::Long; 139use Socket; 140use Carp; 141use 5.006; # As returned by Perl::MinimumVersion 142 143####################### 144### usage subroutine 145### Parameters: None 146####################### 147sub usage { 148 print 149"Usage: ldap-stats.pl [ -s ] [ -c <count> ] [ -l <count> ] [ -h ] <logfile> ...\n" 150 . " -c <count> Number of lines to display for each report [25]\n" 151 . " -d Display all available days in the day of month report\n" 152 . " -h Display a usage help screen\n" 153 . " -l <count> Print status message after processing <count> lines [0]\n" 154 . " -m Display all available months in the month of year report\n" 155 . " -n Resolve IP addresses to hostnames\n" 156 . " -o <ops> -o <ops> ... Operations to print in the reports [ALL]\n" 157 . " Valid operations are: CONNECT, FAILURES, BIND, UNBIND,\n" 158 . " SRCH, CMP, ADD, MOD, MODRDN, DEL\n" 159 . " Predefined reports are: ALL, READ, WRITE\n" 160 . " -s Split attributes found used in searches\n"; 161 return; 162} 163 164### Declare lexical variables 165my ( $logfile, $i, $counter, $help ); 166my ( %unindexed, %search, @operations ); 167 168### Allow the number of entries displayed to be variable 169my $count = 25; 170 171### Figure out if we need to print "Processing X lines" 172my $increment = 0; 173 174## tell whether to split attributes in searches 175my $splitattrs = 0; 176 177# Tell whether to lookup names 178my $resolvename = 0; 179 180# Print all months 181my $printmonths = 0; 182 183# Print all days 184my $printdays = 0; 185 186################################### 187#### Get some options from the user 188################################### 189#getopts("o:l:c:nhsmd", \%options); 190 191GetOptions( 192 'count|c=i' => \$count, 193 'days|d' => \$printdays, 194 'help|h' => \$help, 195 'length|l=i' => \$increment, 196 'months|m' => \$printmonths, 197 'network|n' => \$resolvename, 198 'operations|o=s' => \@operations, 199 'split|s' => \$splitattrs 200); 201 202### print a nice usage message 203if ($help) { 204 usage; 205 exit 1; 206} 207 208### Make sure there is at least one logfile 209if ( !@ARGV ) { 210 usage; 211 exit 1; 212} 213 214############################ 215### Define various variables 216############################ 217my $date = localtime time; 218 219if ( !@operations ) { 220 @operations = ('ALL'); 221} 222 223my %stats = ( 224 TOTAL_CONNECT => 0, 225 TOTAL_BIND => 0, 226 TOTAL_UNBIND => 0, 227 TOTAL_SRCH => 0, 228 TOTAL_DEL => 0, 229 TOTAL_ADD => 0, 230 TOTAL_CMP => 0, 231 TOTAL_MOD => 0, 232 TOTAL_MODRDN => 0, 233 TOTAL_UNINDEXED => 0, 234 TOTAL_AUTHFAILURES => 0, 235); 236 237my %hours; # Hash to store the time of day (e.g., 21st of August) 238my %days; # Hash to store the days of each month (e.g., 21st) 239my %months; # Hash to store the day of the month (e.g., Dec) 240my %hosts; # Hash to store client IP addresses 241my %conns; # Hash to store connection identifiers 242my %binddns; # Hash to store bind DNs 243my %logarray; # Hash to store logfiles 244my %filters; # Hash to store search filters 245my %searchattributes; # Hash to store specific attributes that are requested 246my %operations; # Hash to store operations information 247 248$operations{CONNECT} = { 249 DATA => 0, 250 STRING => ' Connect', 251 SPACING => ' --------', 252 FIELD => '%8s', 253}; 254 255$operations{FAILURES} = { 256 DATA => 0, 257 STRING => ' Failed', 258 SPACING => ' ------', 259 FIELD => '%6s', 260}; 261 262$operations{BIND} = { 263 DATA => 0, 264 STRING => ' Bind', 265 SPACING => ' -------', 266 FIELD => '%7s', 267}; 268 269$operations{UNBIND} = { 270 DATA => 0, 271 STRING => ' Unbind', 272 SPACING => ' -------', 273 FIELD => '%7s', 274}; 275 276$operations{SRCH} = { 277 DATA => 0, 278 STRING => ' Search', 279 SPACING => ' --------', 280 FIELD => '%8s', 281}; 282 283$operations{ADD} = { 284 DATA => 0, 285 STRING => ' Add', 286 SPACING => ' -----', 287 FIELD => '%5s', 288}; 289 290$operations{CMP} = { 291 DATA => 0, 292 STRING => ' Cmp', 293 SPACING => ' -----', 294 FIELD => '%5s', 295}; 296 297$operations{MOD} = { 298 DATA => 0, 299 STRING => ' Mod', 300 SPACING => ' -----', 301 FIELD => '%5s', 302}; 303 304$operations{MODRDN} = { 305 DATA => 0, 306 STRING => ' ModRDN', 307 SPACING => ' ------', 308 FIELD => '%6s', 309}; 310 311$operations{DEL} = { 312 DATA => 0, 313 STRING => ' Del', 314 SPACING => ' ----', 315 FIELD => '%4s', 316}; 317 318################################################### 319### Open the logfile and process all of the entries 320################################################### 321for my $file (@ARGV) { 322 $logfile = $file; 323 my $lines = 0; 324 325 ### find open filter to use 326 my $openfilter = '<' . $logfile . q{}; 327 328 ### decode gzipped / bzip2-compressed files 329 if ( $logfile =~ /\.bz2$/mx ) { 330 $openfilter = q{bzip2 -dc "} . $logfile . q{"|} 331 or carp "Problem decompressing!: $!\n"; 332 } 333 334 if ( $logfile =~ /\.(gz|Z)$/mx ) { 335 $openfilter = q{gzip -dc "} . $logfile . q{"|} 336 or carp "Problem decompressing!: $!\n"; 337 } 338 339 ### If the logfile isn't valid, move on to the next one 340 if ( !open LOGFILE, $openfilter ) { 341 print "ERROR: unable to open '$logfile': $!\n"; 342 next; 343 } 344 345 ### setup the arrray to hold the start/stop times 346 $logarray{$logfile} = { 347 SDATE => q{}, 348 EDATE => q{}, 349 }; 350 351 ### Only print banner if requested 352 if ( $increment > 0 ) { 353 ### Print a banner and initialize the $counter variable 354 print "\nProcessing file \"$logfile\"\n" 355 . q{-} x ( 18 + length ${$logfile} ) . "\n"; 356 $counter = 0; 357 $lines = $increment; 358 } 359 360 while ( my $line = <LOGFILE> ) { 361 362 ### check start and end dates 363 if ( $line =~ /^(\w+\s+\d+\s+\d+:\d+:\d+)/mx ) { 364 if ( !$logarray{$logfile}{SDATE} ) { 365 $logarray{$logfile}{SDATE} = $1; 366 } 367 $logarray{$logfile}{EDATE} = $1; 368 } 369 370 ### Check to see if we have processed $lines lines 371 if ( ( $lines > 0 ) && ( $counter == $lines ) ) { 372 print " Processed $lines lines in \"$logfile\"\n"; 373 $lines += $increment; 374 } 375 376 ### Check for a new connection 377 if ( $line =~ 378/^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+).*conn=(\d+) [ ] fd=\d+ [ ] (?:ACCEPT|connection) [ ] from/mx 379 ) 380 { 381 my $month = $1; 382 my $day = $2; 383 my $hour = $3; 384 my $conn = $6; 385 my $host; 386 387 if ( $line =~ /IP=(\d+\.\d+\.\d+\.\d+):/mx ) { 388 $host = $1; 389 } 390 elsif ( $line =~ /PATH=(\S+)/mx ) { 391 $host = 'LOCAL-SOCKET'; 392 } 393 else { 394 $host = 'UNKNOWN'; 395 } 396 397 ### Create an array to store the list of hosts 398 if ( !( defined $hosts{$host} ) ) { 399 $hosts{$host} = { 400 CONNECT => 1, 401 AUTHFAILURES => 0, 402 BIND => 0, 403 UNBIND => 0, 404 SRCH => 0, 405 ADD => 0, 406 CMP => 0, 407 MOD => 0, 408 MODRDN => 0, 409 DEL => 0, 410 }; 411 } 412 else { 413 ### Entry exists, increment the CONNECT value 414 $hosts{$host}{CONNECT}++; 415 } 416 417 ### Create an array to store the hours 418 if ( !( defined $hours{$hour} ) ) { 419 $hours{$hour} = { 420 CONNECT => 1, 421 AUTHFAILURES => 0, 422 BIND => 0, 423 UNBIND => 0, 424 SRCH => 0, 425 ADD => 0, 426 CMP => 0, 427 MOD => 0, 428 MODRDN => 0, 429 DEL => 0, 430 }; 431 } 432 else { 433 ### Entry exists, increment the CONNECT value 434 $hours{$hour}{CONNECT}++; 435 } 436 437 ### Create an array to store the months 438 if ( !( defined $months{$month} ) ) { 439 $months{$month} = { 440 CONNECT => 1, 441 AUTHFAILURES => 0, 442 BIND => 0, 443 UNBIND => 0, 444 SRCH => 0, 445 ADD => 0, 446 CMP => 0, 447 MOD => 0, 448 MODRDN => 0, 449 DEL => 0, 450 }; 451 } 452 else { 453 ### Entry exists, increment the CONNECT value 454 $months{$month}{CONNECT}++; 455 } 456 457 ### Create an array to store the days 458 if ( !( defined $days{$day} ) ) { 459 $days{$day} = { 460 CONNECT => 1, 461 AUTHFAILURES => 0, 462 BIND => 0, 463 UNBIND => 0, 464 SRCH => 0, 465 ADD => 0, 466 CMP => 0, 467 MOD => 0, 468 MODRDN => 0, 469 DEL => 0, 470 }; 471 } 472 else { 473 ### Entry exists, increment the CONNECT value 474 $days{$day}{CONNECT}++; 475 } 476 477 ### Add the host to the connection table 478 $conns{$conn} = $host; 479 480 ### Increment the total number of connections 481 $stats{TOTAL_CONNECT}++; 482 483 ### Check for anonymous binds 484 } 485 elsif ( $line =~ 486/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] BIND [ ] dn="" [ ] method=128/mx 487 ) 488 { 489 my $month = $1; 490 my $day = $2; 491 my $hour = $3; 492 my $conn = $4; 493 494 ### Increment the counters 495 if ( defined $conns{$conn} 496 && defined $hosts{ $conns{$conn} } ) 497 { 498 $hosts{ $conns{$conn} }{BIND}++; 499 $hours{$hour}{BIND}++; 500 $days{$day}{BIND}++; 501 $months{$month}{BIND}++; 502 $stats{TOTAL_BIND}++; 503 } 504 505 ### Add the binddn to the binddns array 506 $binddns{anonymous}++; 507 508 ### Check for non-anonymous binds 509 } 510 elsif ( $line =~ 511/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] BIND [ ] dn="([^"]+)" [ ] mech=/mx 512 ) 513 { 514 my $month = $1; 515 my $day = $2; 516 my $hour = $3; 517 my $conn = $4; 518 my $binddn = lc $5; 519 520 ### Increment the counters 521 if ( defined $conns{$conn} 522 && defined $hosts{ $conns{$conn} } ) 523 { 524 $hosts{ $conns{$conn} }{BIND}++; 525 $hours{$hour}{BIND}++; 526 $days{$day}{BIND}++; 527 $months{$month}{BIND}++; 528 $stats{TOTAL_BIND}++; 529 } 530 531 ### Add the binddn to the binddns array 532 $binddns{$binddn}++; 533 534 ### Check the search base 535 } 536 elsif ( $line =~ 537/\bconn=\d+ [ ] op=\d+ [ ] SRCH [ ] base="([^"]*?)" [ ] .*filter="([^"]*?)"/mx 538 ) 539 { 540 my $base = lc $1; 541 my $filter = $2; 542 543 ### Stuff the search base into an array 544 if ( defined $base ) { 545 $search{$base}++; 546 } 547 548 if ( defined $filter ) { 549 $filters{$filter}++; 550 } 551 552 ### Check for search attributes 553 } 554 elsif ( $line =~ /\bconn=\d+ [ ] op=\d+ [ ] SRCH [ ] attr=(.+)/mx ) { 555 my $attrs = lc $1; 556 557 if ($splitattrs) { 558 for my $attr ( split q{ }, $attrs ) { 559 $searchattributes{$attr}++; 560 } 561 } 562 else { 563 $searchattributes{$attrs}++; 564 } 565 566 ### Check for SEARCHES 567 } 568 elsif ( $line =~ 569/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] SEARCH [ ] RESULT/mx 570 ) 571 { 572 my $month = $1; 573 my $day = $2; 574 my $hour = $3; 575 my $conn = $4; 576 577 ### Increment the counters 578 if ( defined $conns{$conn} 579 && defined $hosts{ $conns{$conn} } ) 580 { 581 $hosts{ $conns{$conn} }{SRCH}++; 582 $hours{$hour}{SRCH}++; 583 $days{$day}{SRCH}++; 584 $months{$month}{SRCH}++; 585 $stats{TOTAL_SRCH}++; 586 } 587 588 ### Check for unbinds 589 } 590 elsif ( $line =~ 591 /^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] UNBIND/mx 592 ) 593 { 594 my $month = $1; 595 my $day = $2; 596 my $hour = $3; 597 my $conn = $4; 598 599 ### Increment the counters 600 if ( defined $conns{$conn} 601 && defined $hosts{ $conns{$conn} } ) 602 { 603 $hosts{ $conns{$conn} }{UNBIND}++; 604 $hours{$hour}{UNBIND}++; 605 $days{$day}{UNBIND}++; 606 $months{$month}{UNBIND}++; 607 $stats{TOTAL_UNBIND}++; 608 } 609 610 ### Check the result of the last operation 611 ### TODO: Add other err=X values from contrib/ldapc++/src/LDAPResult.h 612 } 613 elsif ( $line =~ 614/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+(?: SEARCH)? [ ] RESULT [ ]/mx 615 ) 616 { 617 my $month = $1; 618 my $day = $2; 619 my $hour = $3; 620 my $conn = $4; 621 622 if ( $line =~ /\berr=49\b/mx ) { 623 ### Increment the counters 624 if ( defined $conns{$conn} 625 && defined $hosts{ $conns{$conn} } ) 626 { 627 $hosts{ $conns{$conn} }{AUTHFAILURES}++; 628 $hours{$hour}{AUTHFAILURES}++; 629 $days{$day}{AUTHFAILURES}++; 630 $months{$month}{AUTHFAILURES}++; 631 $stats{TOTAL_AUTHFAILURES}++; 632 } 633 } 634 635 ### Check for entry changes: add, modify modrdn, delete 636 } 637 elsif ( $line =~ 638/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] (ADD|CMP|MOD|MODRDN|DEL) [ ] dn=/mx 639 ) 640 { 641 my $month = $1; 642 my $day = $2; 643 my $hour = $3; 644 my $conn = $4; 645 my $type = $5; 646 647 ### Increment the counters 648 if ( defined $conns{$conn} 649 && defined $hosts{ $conns{$conn} } ) 650 { 651 $hosts{ $conns{$conn} }{$type}++; 652 $hours{$hour}{$type}++; 653 $days{$day}{$type}++; 654 $months{$month}{$type}++; 655 $stats{ 'TOTAL_' . $type }++; 656 } 657 658 ### Check for unindexed searches 659 } 660 elsif ( $line =~ 661 /: [ ] \(([a-zA-Z0-9\;\-]+)\) [ ] index_param [ ] failed/mx ) 662 { 663 my $attr = $1; 664 665 $unindexed{$attr}++; 666 $stats{TOTAL_UNINDEXED}++; 667 } 668 $counter++; 669 } 670 close LOGFILE; 671} 672 673################################################################### 674### Print a nice header with the logfiles and date ranges processed 675################################################################### 676## Please see file perltidy.ERR 677print "\n\n" 678 . "Report Generated on $date\n" 679 . q{-} x ( 20 + length $date ) . "\n"; 680 681for my $logfile ( sort keys %logarray ) { 682 if ( !-z $logfile ) { 683 printf "Processed \"$logfile\": %s - %s\n", $logarray{$logfile}{SDATE}, 684 $logarray{$logfile}{EDATE}; 685 } 686 else { 687 printf "Processed \"$logfile\": no data\n"; 688 } 689} 690 691####################################### 692### Print an overall report with totals 693####################################### 694 695my $total_operations = 696 $stats{TOTAL_BIND} + $stats{TOTAL_UNBIND} + $stats{TOTAL_SRCH} + 697 $stats{TOTAL_MOD} + $stats{TOTAL_ADD} + $stats{TOTAL_MODRDN} + 698 $stats{TOTAL_DEL}; 699 700print "\n\n" . "Operation totals\n" . "----------------\n"; 701printf "Total operations : %d\n", $total_operations; 702printf "Total connections : %d\n", $stats{TOTAL_CONNECT}; 703printf "Total authentication failures : %d\n", $stats{TOTAL_AUTHFAILURES}; 704printf "Total binds : %d\n", $stats{TOTAL_BIND}; 705printf "Total unbinds : %d\n", $stats{TOTAL_UNBIND}; 706printf "Total searches : %d\n", $stats{TOTAL_SRCH}; 707printf "Total compares : %d\n", $stats{TOTAL_CMP}; 708printf "Total modifications : %d\n", $stats{TOTAL_MOD}; 709printf "Total modrdns : %d\n", $stats{TOTAL_MODRDN}; 710printf "Total additions : %d\n", $stats{TOTAL_ADD}; 711printf "Total deletions : %d\n", $stats{TOTAL_DEL}; 712printf "Unindexed attribute requests : %d\n", $stats{TOTAL_UNINDEXED}; 713printf "Operations per connection : %.2f\n", 714 $stats{TOTAL_CONNECT} ? $total_operations / $stats{TOTAL_CONNECT} : 0; 715 716################################################### 717### Process the host information and print a report 718################################################### 719for my $selected (@operations) { 720 $selected = uc $selected; 721 722 my $ops_ref = { 723 CONNECT => sub { $operations{CONNECT}{DATA} = 1 }, 724 FAILURES => sub { $operations{FAILURES}{DATA} = 1 }, 725 BIND => sub { $operations{BIND}{DATA} = 1 }, 726 UNBIND => sub { $operations{UNBIND}{DATA} = 1 }, 727 SRCH => sub { $operations{SRCH}{DATA} = 1 }, 728 CMP => sub { $operations{CMP}{DATA} = 1 }, 729 ADD => sub { $operations{ADD}{DATA} = 1 }, 730 MOD => sub { $operations{MOD}{DATA} = 1 }, 731 MODRDN => sub { $operations{MODRDN}{DATA} = 1 }, 732 DEL => sub { $operations{DEL}{DATA} = 1 }, 733 ALL => sub { 734 $operations{CONNECT}{DATA} = 1; 735 $operations{FAILURES}{DATA} = 1; 736 $operations{BIND}{DATA} = 1; 737 $operations{UNBIND}{DATA} = 1; 738 $operations{SRCH}{DATA} = 1; 739 $operations{CMP}{DATA} = 1; 740 $operations{ADD}{DATA} = 1; 741 $operations{MOD}{DATA} = 1; 742 $operations{MODRDN}{DATA} = 1; 743 $operations{DEL}{DATA} = 1; 744 }, 745 READ => sub { 746 $operations{CONNECT}{DATA} = 1; 747 $operations{BIND}{DATA} = 1; 748 $operations{UNBIND}{DATA} = 1; 749 $operations{SRCH}{DATA} = 1; 750 $operations{CMP}{DATA} = 1; 751 }, 752 WRITE => sub { 753 $operations{CONNECT}{DATA} = 1; 754 $operations{BIND}{DATA} = 1; 755 $operations{UNBIND}{DATA} = 1; 756 $operations{ADD}{DATA} = 1; 757 $operations{MOD}{DATA} = 1; 758 $operations{MODRDN}{DATA} = 1; 759 $operations{DEL}{DATA} = 1; 760 }, 761 }; 762 if ( $ops_ref->{$selected} ) { $ops_ref->{$selected}->() } 763 else { croak "Unknown operation: '$selected';\n" } 764} 765 766print "\n\n"; 767my $printstr = 'Hostname '; 768$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{}; 769$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{}; 770$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{}; 771$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{}; 772$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{}; 773$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{}; 774$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{}; 775$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{}; 776$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{}; 777$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{}; 778$printstr .= "\n"; 779print $printstr; 780$printstr = '---------------'; 781$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{}; 782$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{}; 783$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{}; 784$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{}; 785$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{}; 786$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{}; 787$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{}; 788$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{}; 789$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{}; 790$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{}; 791print "$printstr\n"; 792 793for my $index ( sort keys %hosts ) { 794 795 ### Resolve IP addresses to names if requested 796 my $host = $index; 797 798 ### Convert the IP address to an Internet address, and resolve with gethostbyaddr() 799 if ( $resolvename && ( $index =~ /\d+\.\d+\.\d+\.\d+/mx ) ) { 800 my $ipaddr = inet_aton($index); 801 $host = gethostbyaddr $ipaddr, AF_INET; 802 if ( !defined $host ) { 803 $host = $index; 804 } 805 } 806 printf '%-15.15s', $host; 807 if ( $operations{CONNECT}{DATA} ) { 808 printf " $operations{CONNECT}{FIELD}", 809 $hosts{$index}{CONNECT} ? $hosts{$index}{CONNECT} : 0; 810 } 811 if ( $operations{FAILURES}{DATA} ) { 812 printf " $operations{FAILURES}{FIELD}", 813 $hosts{$index}{AUTHFAILURES} ? $hosts{$index}{AUTHFAILURES} : 0; 814 } 815 if ( $operations{BIND}{DATA} ) { 816 printf " $operations{BIND}{FIELD}", 817 $hosts{$index}{BIND} ? $hosts{$index}{BIND} : 0; 818 } 819 if ( $operations{UNBIND}{DATA} ) { 820 printf " $operations{UNBIND}{FIELD}", 821 $hosts{$index}{UNBIND} ? $hosts{$index}{UNBIND} : 0; 822 } 823 if ( $operations{SRCH}{DATA} ) { 824 printf " $operations{SRCH}{FIELD}", 825 $hosts{$index}{SRCH} ? $hosts{$index}{SRCH} : 0; 826 } 827 if ( $operations{CMP}{DATA} ) { 828 printf " $operations{CMP}{FIELD}", 829 $hosts{$index}{CMP} ? $hosts{$index}{CMP} : 0; 830 } 831 if ( $operations{ADD}{DATA} ) { 832 printf " $operations{ADD}{FIELD}", 833 $hosts{$index}{ADD} ? $hosts{$index}{ADD} : 0; 834 } 835 if ( $operations{MOD}{DATA} ) { 836 printf " $operations{MOD}{FIELD}", 837 $hosts{$index}{MOD} ? $hosts{$index}{MOD} : 0; 838 } 839 if ( $operations{MODRDN}{DATA} ) { 840 printf " $operations{MODRDN}{FIELD}", 841 $hosts{$index}{MODRDN} ? $hosts{$index}{MODRDN} : 0; 842 } 843 if ( $operations{DEL}{DATA} ) { 844 printf " $operations{DEL}{FIELD}", 845 $hosts{$index}{DEL} ? $hosts{$index}{DEL} : 0; 846 } 847 print "\n"; 848} 849 850####################################################### 851### Process the hours information and print a report 852######################################################## 853print "\n\n"; 854$printstr = 'Hour of Day '; 855$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{}; 856$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{}; 857$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{}; 858$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{}; 859$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{}; 860$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{}; 861$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{}; 862$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{}; 863$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{}; 864$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{}; 865$printstr .= "\n"; 866print $printstr; 867$printstr = '-------------'; 868$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{}; 869$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{}; 870$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{}; 871$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{}; 872$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{}; 873$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{}; 874$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{}; 875$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{}; 876$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{}; 877$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{}; 878print "$printstr\n"; 879 880for my $index ( sort keys %hours ) { 881 printf '%-2s:00 - %2s:59', $index, $index; 882 if ( $operations{CONNECT}{DATA} ) { 883 printf " $operations{CONNECT}{FIELD}", 884 $hours{$index}{CONNECT} ? $hours{$index}{CONNECT} : 0; 885 } 886 if ( $operations{FAILURES}{DATA} ) { 887 printf " $operations{FAILURES}{FIELD}", 888 $hours{$index}{AUTHFAILURES} ? $hours{$index}{AUTHFAILURES} : 0; 889 } 890 if ( $operations{BIND}{DATA} ) { 891 printf " $operations{BIND}{FIELD}", 892 $hours{$index}{BIND} ? $hours{$index}{BIND} : 0; 893 } 894 if ( $operations{UNBIND}{DATA} ) { 895 printf " $operations{UNBIND}{FIELD}", 896 $hours{$index}{UNBIND} ? $hours{$index}{UNBIND} : 0; 897 } 898 if ( $operations{SRCH}{DATA} ) { 899 printf " $operations{SRCH}{FIELD}", 900 $hours{$index}{SRCH} ? $hours{$index}{SRCH} : 0; 901 } 902 if ( $operations{CMP}{DATA} ) { 903 printf " $operations{CMP}{FIELD}", 904 $hours{$index}{CMP} ? $hours{$index}{CMP} : 0; 905 } 906 if ( $operations{ADD}{DATA} ) { 907 printf " $operations{ADD}{FIELD}", 908 $hours{$index}{ADD} ? $hours{$index}{ADD} : 0; 909 } 910 if ( $operations{MOD}{DATA} ) { 911 printf " $operations{MOD}{FIELD}", 912 $hours{$index}{MOD} ? $hours{$index}{MOD} : 0; 913 } 914 if ( $operations{MODRDN}{DATA} ) { 915 printf " $operations{MODRDN}{FIELD}", 916 $hours{$index}{MODRDN} ? $hours{$index}{MODRDN} : 0; 917 } 918 if ( $operations{DEL}{DATA} ) { 919 printf " $operations{DEL}{FIELD}", 920 $hours{$index}{DEL} ? $hours{$index}{DEL} : 0; 921 } 922 print "\n"; 923} 924 925####################################################### 926### Process the month information and print a report 927######################################################## 928print "\n\n"; 929$printstr = 'Day of Month '; 930$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{}; 931$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{}; 932$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{}; 933$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{}; 934$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{}; 935$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{}; 936$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{}; 937$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{}; 938$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{}; 939$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{}; 940$printstr .= "\n"; 941print $printstr; 942$printstr = '-------------'; 943$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{}; 944$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{}; 945$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{}; 946$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{}; 947$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{}; 948$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{}; 949$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{}; 950$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{}; 951$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{}; 952$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{}; 953print "$printstr\n"; 954 955for ( 1 .. 31 ) { 956 if ( defined $days{$_} || $printdays ) { 957 printf ' %-11s', $_; 958 if ( $operations{CONNECT}{DATA} ) { 959 printf " $operations{CONNECT}{FIELD}", 960 $days{$_}{CONNECT} ? $days{$_}{CONNECT} : 0; 961 } 962 if ( $operations{FAILURES}{DATA} ) { 963 printf " $operations{FAILURES}{FIELD}", 964 $days{$_}{AUTHFAILURES} ? $days{$_}{AUTHFAILURES} : 0; 965 } 966 if ( $operations{BIND}{DATA} ) { 967 printf " $operations{BIND}{FIELD}", 968 $days{$_}{BIND} ? $days{$_}{BIND} : 0; 969 } 970 if ( $operations{UNBIND}{DATA} ) { 971 printf " $operations{UNBIND}{FIELD}", 972 $days{$_}{UNBIND} ? $days{$_}{UNBIND} : 0; 973 } 974 if ( $operations{SRCH}{DATA} ) { 975 printf " $operations{SRCH}{FIELD}", 976 $days{$_}{SRCH} ? $days{$_}{SRCH} : 0; 977 } 978 if ( $operations{CMP}{DATA} ) { 979 printf " $operations{CMP}{FIELD}", 980 $days{$_}{CMP} ? $days{$_}{CMP} : 0; 981 } 982 if ( $operations{ADD}{DATA} ) { 983 printf " $operations{ADD}{FIELD}", 984 $days{$_}{ADD} ? $days{$_}{ADD} : 0; 985 } 986 if ( $operations{MOD}{DATA} ) { 987 printf " $operations{MOD}{FIELD}", 988 $days{$_}{MOD} ? $days{$_}{MOD} : 0; 989 } 990 if ( $operations{MODRDN}{DATA} ) { 991 printf " $operations{MODRDN}{FIELD}", 992 $days{$_}{MODRDN} ? $days{$_}{MODRDN} : 0; 993 } 994 if ( $operations{DEL}{DATA} ) { 995 printf " $operations{DEL}{FIELD}", 996 $days{$_}{DEL} ? $days{$_}{DEL} : 0; 997 } 998 print "\n"; 999 } 1000} 1001####################################################### 1002### Process the month information and print a report 1003######################################################## 1004print "\n\n"; 1005$printstr = ' Month '; 1006$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{}; 1007$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{}; 1008$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{}; 1009$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{}; 1010$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{}; 1011$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{}; 1012$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{}; 1013$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{}; 1014$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{}; 1015$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{}; 1016$printstr .= "\n"; 1017print $printstr; 1018$printstr = '-------------'; 1019$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{}; 1020$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{}; 1021$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{}; 1022$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{}; 1023$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{}; 1024$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{}; 1025$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{}; 1026$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{}; 1027$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{}; 1028$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{}; 1029print "$printstr\n"; 1030 1031for my $index qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) { 1032 if ( defined $months{$index} || $printmonths ) { 1033 printf ' %-11s', $index; 1034 if ( $operations{CONNECT}{DATA} ) { 1035 printf " $operations{CONNECT}{FIELD}", 1036 $months{$index}{CONNECT} ? $months{$index}{CONNECT} : 0; 1037 } 1038 if ( $operations{FAILURES}{DATA} ) { 1039 printf " $operations{FAILURES}{FIELD}", 1040 $months{$index}{AUTHFAILURES} 1041 ? $months{$index}{AUTHFAILURES} 1042 : 0; 1043 } 1044 if ( $operations{BIND}{DATA} ) { 1045 printf " $operations{BIND}{FIELD}", 1046 $months{$index}{BIND} ? $months{$index}{BIND} : 0; 1047 } 1048 if ( $operations{UNBIND}{DATA} ) { 1049 printf " $operations{UNBIND}{FIELD}", 1050 $months{$index}{UNBIND} ? $months{$index}{UNBIND} : 0; 1051 } 1052 if ( $operations{SRCH}{DATA} ) { 1053 printf " $operations{SRCH}{FIELD}", 1054 $months{$index}{SRCH} ? $months{$index}{SRCH} : 0; 1055 } 1056 if ( $operations{CMP}{DATA} ) { 1057 printf " $operations{CMP}{FIELD}", 1058 $months{$index}{CMP} ? $months{$index}{CMP} : 0; 1059 } 1060 if ( $operations{ADD}{DATA} ) { 1061 printf " $operations{ADD}{FIELD}", 1062 $months{$index}{ADD} ? $months{$index}{ADD} : 0; 1063 } 1064 if ( $operations{MOD}{DATA} ) { 1065 printf " $operations{MOD}{FIELD}", 1066 $months{$index}{MOD} ? $months{$index}{MOD} : 0; 1067 } 1068 if ( $operations{MODRDN}{DATA} ) { 1069 printf " $operations{MODRDN}{FIELD}", 1070 $months{$index}{MODRDN} ? $months{$index}{MODRDN} : 0; 1071 } 1072 if ( $operations{DEL}{DATA} ) { 1073 printf " $operations{DEL}{FIELD}", 1074 $months{$index}{DEL} ? $months{$index}{DEL} : 0; 1075 } 1076 print "\n"; 1077 } 1078} 1079 1080#################################################### 1081### Process the unindexed searches and print a report 1082#################################################### 1083my @sarray; # sort array 1084if ( $stats{TOTAL_UNINDEXED} > 0 ) { 1085 1086 print "\n\n" 1087 . "# Uses Unindexed attribute\n" 1088 . "---------- -----------------------------------------------------------\n"; 1089 1090 @sarray = 1091 reverse sort { $unindexed{$a} <=> $unindexed{$b} } keys %unindexed; 1092 UNINDEXED: 1093 for my $num ( 0 .. $#sarray ) { 1094 if ( $num > $count ) { 1095 last UNINDEXED; 1096 } 1097 printf " %-8d %-60s\n", $unindexed{ $sarray[$num] }, $sarray[$num]; 1098 } 1099} 1100 1101###################################################### 1102### Process the stored search bases and print a report 1103###################################################### 1104print "\n\n" 1105 . "# Searches Search base\n" 1106 . "---------- -----------------------------------------------------------\n"; 1107 1108@sarray = reverse sort { $search{$a} <=> $search{$b} } keys %search; 1109SEARCH: 1110for my $num ( 0 .. $#sarray ) { 1111 if ( $num > $count ) { 1112 last SEARCH; 1113 } 1114 printf " %-8d %-60s\n", $search{ $sarray[$num] }, 1115 $sarray[$num] || 'RootDSE'; 1116} 1117 1118###################################################### 1119### Process the stored search filters 1120###################################################### 1121print "\n\n" 1122 . "# Uses Filter\n" 1123 . "---------- -----------------------------------------------------------\n"; 1124 1125@sarray = reverse sort { $filters{$a} <=> $filters{$b} } keys %filters; 1126FILTER: 1127for my $num ( 0 .. $#sarray ) { 1128 if ( $num > $count ) { 1129 last FILTER; 1130 } 1131 printf " %-8d %-60s\n", $filters{ $sarray[$num] }, $sarray[$num]; 1132} 1133 1134###################################################### 1135### Process the stored attribute array 1136###################################################### 1137print "\n\n" 1138 . "# Uses Attributes explicitly requested in search string\n" 1139 . "---------- -------------------------------------------------\n"; 1140 1141@sarray = 1142 reverse sort { $searchattributes{$a} <=> $searchattributes{$b} } 1143 keys %searchattributes; 1144SEARCHATTR: 1145for my $num ( 0 .. $#sarray ) { 1146 if ( $num > $count ) { 1147 last SEARCHATTR; 1148 } 1149 printf " %-8d %-60s\n", $searchattributes{ $sarray[$num] }, 1150 $sarray[$num]; 1151} 1152 1153###################################################### 1154### Process the stored binddns and print a report 1155###################################################### 1156print "\n\n" 1157 . "# Binds Bind DN\n" 1158 . "---------- --------------------------------------------------------------\n"; 1159 1160@sarray = reverse sort { $binddns{$a} <=> $binddns{$b} } keys %binddns; 1161BINDDN: 1162for my $num ( 0 .. $#sarray ) { 1163 if ( $num > $count ) { 1164 last BINDDN; 1165 } 1166 printf " %-8d %-60s\n", $binddns{ $sarray[$num] }, $sarray[$num]; 1167} 1168 1169print "\n\n"; 1170 1171# EOF 1172