Directory Services

Searching a Directory

Search is the most common directory activity. The LDAP API includes a variety of search criteria and result-retrieval methods to find directory data. As with other LDAP operations, you can perform a search synchronously or asynchronously. Extensions to the base LDAP API enable you to add sorting criteria and other extensions to your search request. Three settings in the LDAP structure affect a search:

The ldap_search and ldap_search_s functions are the original (LDAP 2) asynchronous and synchronous search functions. To specify a local timeout for a synchronous search, use ldap_search_st. The extended functions ldap_search_ext and ldap_search_ext_s support LDAP 3 server controls and client controls, and enable you to specify varying size and time limits for each search operation.

For more information, see Synchronous vs. Asynchronous Calls, ldap_search_init_page, and ldap_search_abandon_page.

The following C++ code example shows how to use LDAP functions to initialize, connect to, and perform a synchronous search of an Active Directory, and it outputs the search results to a command prompt window. It lists the first ten, or fewer, users and the names and values of a selected subset of attributes for the user. To retrieve the value of an attribute whose type is a null-terminated character string, use ldap_get_values, but for an attribute which contains binary data you should use ldap_get_values_len. Determining the type for each attribute can become complex, and is not shown in this code example. Due to the complexity of this operation, it is recommended that, when possible, you use the ADSI interface for searching Active Directory, where much of the complexity has been minimized. However, if you must use LDAP in your application, the following code example should help.

The following code example:

//----------------------------------------------
// Performing an LDAP Synchronous Search.
//
// Be aware that you must set the command prompt screen buffer 
// height to be 350 and the width to be 90.
//-------------------------------------------------------------

#include <windows.h>
#include <winldap.h>
#include <winber.h>
#include <rpcdce.h>
#include <stdio.h>

//-----------------------------------------------------------
// This subroutine must have validated credentials (name and
// password) passed to it.
//-----------------------------------------------------------
int MyLDAPSearch(PCHAR pUserName, PCHAR pPassword)
{
	//---------------------------------------------------------
	// Initialize a sesion. LDAP_PORT is the default port, 389.
	//---------------------------------------------------------
	PCHAR hostName = "fabrikam.com";
	LDAP* pLdapConnection = NULL;

	pLdapConnection = ldap_init(hostName, LDAP_PORT);

	if (pLdapConnection == NULL)
	{
		printf("ldap_init failed with 0x%x.\n",LdapGetLastError());
		ldap_unbind(pLdapConnection);
		return -1;
}
	else
		printf("ldap_init succeeded \n");


	//-------------------------------------------------------
	// Set session options.
	//-------------------------------------------------------
	ULONG version = LDAP_VERSION3;
	ULONG numReturns = 10;
	ULONG lRtn = 0;

	// Set the version to 3.0 (default is 2.0).
	lRtn = ldap_set_option(
					pLdapConnection, 	 // Session handle
					LDAP_OPT_PROTOCOL_VERSION, // Option
					(void*) &version); 	 // Option value

	if(lRtn == LDAP_SUCCESS)
		printf("ldap version set to 3.0 \n");
	else
	{
		printf("SetOption Error:%0lX\n", lRtn);
		ldap_unbind(pLdapConnection);
		return -1;
}

	// Set the limit on the number of entries returned to 10.
	lRtn = ldap_set_option(
					pLdapConnection,  // Session handle
					LDAP_OPT_SIZELIMIT, // Option
					(void*) &numReturns);  // Option value

	if(lRtn == LDAP_SUCCESS)
		printf("Max return entries set to 10 \n");
	else
	{
		printf("SetOption Error:%0lX\n", lRtn);
		ldap_unbind(pLdapConnection);
		return -1;
}


	//--------------------------------------------------------
	// Connect to the server.
	//--------------------------------------------------------

	lRtn = ldap_connect(pLdapConnection, NULL);

	if(lRtn == LDAP_SUCCESS)
		printf("ldap_connect succeeded \n");
	else
	{
		printf("ldap_connect failed with 0x%lx.\n",lRtn);
		ldap_unbind(pLdapConnection);
		return -1;
}


	//--------------------------------------------------------
	// Bind with credentials.
	//--------------------------------------------------------
	PCHAR pMyDN = "DC=fabrikam,DC=com";
	SEC_WINNT_AUTH_IDENTITY secIdent;
 
	secIdent.User = (unsigned char*)pUserName;
	secIdent.UserLength = strlen(pUserName);
	secIdent.Password = (unsigned char*)pPassword;
	secIdent.PasswordLength = strlen(pPassword);
	secIdent.Domain = (unsigned char*)hostName;
	secIdent.DomainLength = strlen(hostName);
	secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

	lRtn = ldap_bind_s(
				pLdapConnection, // Session Handle
				pMyDN, 			// Domain DN
				(PCHAR)&secIdent,  // Cedential struct
				LDAP_AUTH_NEGOTIATE); // Auth mode
	if(lRtn == LDAP_SUCCESS)
	{
		printf("ldap_bind_s succeeded \n");
		secIdent.Password = NULL; // Remove password ptr.
		pPassword = NULL; 	 // Remove password ptr.
}
	else
	{
		printf("ldap_bind_s failed with 0x%lx.\n",lRtn);
		ldap_unbind(pLdapConnection);
		return -1;
}
		 
	//----------------------------------------------------------
	// Perform a synchronous search of fabrikam.com for 
	// all user objects that have a "person" category.
	//----------------------------------------------------------
	ULONG errorCode = LDAP_SUCCESS;
	LDAPMessage* pSearchResult;
	PCHAR pMyFilter = "(&(objectCategory=person)(objectClass=user))";
	PCHAR pMyAttributes[6];

	pMyAttributes[0] = "cn";
	pMyAttributes[1] = "company";
	pMyAttributes[2] = "department";
	pMyAttributes[3] = "telephoneNumber";
	pMyAttributes[4] = "memberOf";
	pMyAttributes[5] = NULL;

	errorCode = ldap_search_s(
					pLdapConnection, // Session handle
					pMyDN, 		// DN to start search
					LDAP_SCOPE_SUBTREE, // Scope
					pMyFilter, 	// Filter
					pMyAttributes, // Retrieve list of attrs
					0, 			// Get both attrs and values
					&pSearchResult); // [out] Search results

	if (errorCode != LDAP_SUCCESS)
	{
		printf("ldap_search_s failed with 0x%0lx \n",errorCode);
		ldap_unbind_s(pLdapConnection);
		if(pSearchResult != NULL)
			ldap_msgfree(pSearchResult);
		return -1;
}
	else
		printf("ldap_search succeeded \n");

	//----------------------------------------------------------
	// Get the number of entries returned.
	//----------------------------------------------------------
	ULONG numberOfEntries;

	numberOfEntries = ldap_count_entries(
						pLdapConnection, // Session handle
						pSearchResult);  // Search result

	if(numberOfEntries == NULL)
	{
		printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
		ldap_unbind_s(pLdapConnection);
		if(pSearchResult != NULL)
			ldap_msgfree(pSearchResult);
		return -1;
}
	else
		printf("ldap_count_entries succeeded \n");

	printf("The number of entries is: %d \n", numberOfEntries);


	//----------------------------------------------------------
	// Loop through the search entries, get, and output the
	// rquested list of attributes and values.
	//----------------------------------------------------------
	LDAPMessage* pEntry = NULL;
	PCHAR pEntryDN = NULL;
	ULONG iCnt = 0;
	char* sMsg;
	BerElement* pBer = NULL;
	PCHAR pAttribute = NULL;
	PCHAR* ppValue = NULL;
	ULONG iValue = 0;

	for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
	{
		// Get the first/next entry.
		if( !iCnt )
			pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
		else
			pEntry = ldap_next_entry(pLdapConnection, pEntry);
	
		// Output a status message.
		sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
		if( pEntry == NULL )
		{
			printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
			ldap_unbind_s(pLdapConnection);
			ldap_msgfree(pSearchResult);
			return -1;
	}
		else
			printf("%s succeeded\n",sMsg);
	
		// Output the entry number.
		printf("ENTRY NUMBER %i \n", iCnt);
			
		// Get the firt attribute name.
		pAttribute = ldap_first_attribute(
					pLdapConnection,   // Session handle
					pEntry, 		// Current entry
					&pBer); 		// [out] Current BerElement
	
		// Output the attribute names for the current object
		// and output values.
		while(pAttribute != NULL)
		{
			// Output the attribute name.
			printf("	 ATTR: %s",pAttribute);
		
			// Get the string values.

			ppValue = ldap_get_values(
						pLdapConnection,  // Session Handle
						pEntry, 	 // Current entry
						pAttribute); // Current attribute

			// Print status if no values returned (NULL ptr)
			if(ppValue == NULL)
			{
				printf(": [NO ATTRIBUTE VALUE RETURNED]");
		}

			// Output the attribute values
			else
			{
				iValue = ldap_count_values(ppValue);
				if(!iValue)
				{
					printf(": [BAD VALUE LIST]");
			}
				else
				{
					// Output the first attribute value
					printf(": %s", *ppValue);

					// Output more values if available
					ULONG z;
					for(z=1; z<iValue; z++)
					{
						printf(", %s", ppValue[z]);
				}
			}
		} 

			// Free memory.
			if(ppValue != NULL)  
				ldap_value_free(ppValue);
			ppValue = NULL;
			ldap_memfree(pAttribute);
		
			// Get next attribute name.
			pAttribute = ldap_next_attribute(
							pLdapConnection,   // Sesion Handle
							pEntry, 		// Current entry
							pBer); 		 // Current BerElement
			printf("\n");
	}
	
		if( pBer != NULL )
			ber_free(pBer,0);
		pBer = NULL;
}

	//----------------------------------------------------------
	// Normal cleanup and exit.
	//----------------------------------------------------------
	ldap_unbind(pLdapConnection);
	ldap_msgfree(pSearchResult);
	ldap_value_free(ppValue);
	return 0;

}