#!/usr/bin/perl
#
# This script will search an LDAP server for objects that match the -filter
# option, starting at the DN given by the -basedn option. Each DN found must
# contain the attribute given by the -attribute option and the attribute's
# value must match the value given by the -value option.  Servers are given on
# the command line. At least one server must be specified.

# This script use the Net::LDAPapi, which uses some LDAP libraries like those
# from UMich, Netscape, or ISODE.
#
# Copyright (C) 1998, David Eckelkamp <davide@tradewave.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# $Id: ldap.monitor,v 1.2 1998/06/09 15:26:13 trockij Exp $
#

use Net::LDAPapi;
use Getopt::Long;

# Here are the default values for the things you can specify via options
$LDAPPort = 389;
$BaseDN   = "o=Your Org, c=US";
$Filter   = "cn=Directory Admin";
$Attribute = "objectClass";
$Value    = "YourValue";
$verbose = 0;

%OptVars = ("port"   => \$LDAPPort,
	    "basedn" => \$BaseDN,
	    "filter" => \$Filter,
	    "attribute" => \$Attribute,
	    "value"  => \$Value,
	    "verbose" => \$verbose);

if (!GetOptions(\%OptVars,
		"port=i", "basedn=s", "filter=s",
		"attribute=s", "value=s", "verbose")) {
    print "Problems with Options, sorry.\n";
    exit 1;
}

# There has to be at least one argument left, the ldap server to query.
if ($#ARGV < 0) {
    print "$0: Insufficient arguments. There must be at least 1 server to query\n";
    exit 1;
}

# Loop through all the server given on the command line.
$ErrCnt = 0;
foreach $LDAPHost (@ARGV) {

    # Open the connection to the server and do a simple, anonymous bind
    if (($ldap = ldap_open($LDAPHost, $LDAPPort)) == 0) {
	push(@FailedHosts, "$LDAPHost:$LDAPPort");
	push(@errs, "ldap_init Failed: host=$LDAPHost:$LDAPPort: $!");
	$ErrCnt++;
	next;
    }
    if ((ldap_simple_bind_s($ldap, "", "")) != LDAP_SUCCESS) {
	$ErrCnt++;
	push(@FailedHosts, "$LDAPHost:$LDAPPort");
	#ldap_perror($ldap, "ldap bind failed: host=$LDAPHost:$LDAPPort\n");
	push(@errs, "ldap bind failed: host=$LDAPHost:$LDAPPort");
	next;
    }

   # Do the "get"
    @attrs=();
    $msgid = ldap_search($ldap,$BaseDN,LDAP_SCOPE_SUBTREE,$Filter,\@attrs,0);
    if ($msgid < 0) {
	my($errnd, $extramsg, $err);
	$err = ldap_get_lderrno($ldap, $errdn, $extramsg);
	push(@errs, "$LDAPHost " . &ldap_err2string($err));
	ldap_unbind($ldap);
	push(@FailedHosts, "$LDAPHost:$LDAPPort");
	$ErrCnt++;
	next;
    }

    $nentries = 0;
    $timeout = 0;
    while (($rc = ldap_result($ldap, $msgid, 0, $timeout, $result)) ==
	   LDAP_RES_SEARCH_ENTRY) {
	$nentries++;
	for ($ent = ldap_first_entry($ldap, $result); $ent != 0; 
	     $ent = ldap_next_entry($ldap, $ent)) {
	    if (($dn = ldap_get_dn($ld, $ent)) eq "") {
		ldap_perror($ld, "ldap_get_dn");
	    }
	    for ($attr = ldap_first_attribute($ldap, $ent, $ber);
		 $attr ne "";
		 $attr = ldap_next_attribute($ldap, $ent, $ber)) {
		@vals = ldap_get_values_len($ldap, $ent, $attr);
		$record{$dn}->{$attr} = [@vals];
	    }
	}
	ldap_msgfree($result);
    }
    if ($rc == LDAP_RES_SEARCH_RESULT) {
	$err = ldap_result2error($ldap, $result, 0);
	if ($err != LDAP_SUCCESS) {
	    $texterr = ldap_err2string($err);
	    push(@errs,"Error for $LDAPHost: $texterr");
	    push(@FailedHosts, "$LDAPHost:$LDAPPort");
	    $ErrCnt++;
	    next;
	}
    }

    ldap_unbind($ldap);

    # Analyze results.
    $errors = 0;
    # Step 1 is to loop through all DNs returned from the search.
    print "Looking for $Attribute=$Value\n" if $verbose;
    foreach $dn (sort keys %record) {
	print "checking object $dn\n" if $verbose;
	# Loop through the attributes for this DN
	$attrFound = 0;
	$goodVal = 0;
	foreach $attr (keys %{$record{$dn}}) {
	    print "  checking attr=$attr\n" if $verbose;
	    next unless ($attr eq $Attribute);
	    $attrFound++;
	    print "  found correct attribute\n" if $verbose;
	    # Each value could be/is an array so search the array
	    foreach $val (@{$record{$dn}{$attr}}) {
		print "    checking val = $val\n" if $verbose;
		next unless ($val eq $Value);
		$goodVal++;
		print "    found correct value\n" if $verbose;
		last;
	    }
	    last if ($goodVal);
	}
	if (!$attrFound || !$goodVal) {
	    print "For object $dn:\n";
	}
	if (!$attrFound) {
	    $ErrCnt++;
	    push(@errs,"Could not find Attribute \"$Attribute\" for DN=$dn");
	    push(@FailedHosts, "$LDAPHost:$LDAPPort");
	} elsif (!$goodVal) {
	    $ErrCnt++;
	    push(@errs, "Value \"$Value\" not found for Attribute \"$Attribute\"");
	    push(@FailedHosts, "$LDAPHost:$LDAPPort");
	}
    }
}
if ($ErrCnt > 0) {
    print "@FailedHosts\n";
    print join("\n", @errs), "\n";
}
exit $ErrCnt;
