我正在使用Visual Studio 2013 Community Edition编写命令行C ++程序。它通过LDAP连接到Active Directory服务器,并检索几个属性中的唯一值列表(例如:办公室位置,部门)。
程序编译得很好,但是当我运行它时会遇到内存访问问题:
Unhandled exception at 0x74EDC6B1 (Wldap32.dll) in LdapSearchResultTest1.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
这是我第一次将C ++与外部库一起使用,所以我不确定如何进行调试(通常我会为Android编写Java)。我花了大部分时间来寻找SO并根据类似问题的答案尝试想法,但我仍然无法弄明白。
确切的问题在于此函数调用中的最后一个参数:
// Do the search
int searchReturnCode = ldap_search_s(
ldapSession,
&searchBase[0],
LDAP_SCOPE_SUBTREE,
filter,
pAttributes,
0,
&pSearchResults); // Error is here
我的代码基于MSDN网站上的一个示例,我在我的代码之后复制了该代码。以下是SSCCE来说明问题:
#include<iostream>
#include<Windows.h>
#include<Winldap.h>
#include<WinBer.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
// Function headers
vector<string> get_unique_departments(LDAP*, char*, string);
vector<string> get_unique_office_locations(LDAP*, char*, string);
vector<string> get_unique_values_by_property(LDAP*, char*, string, string);
void print_list(string, vector<string>);
// Main function
int main(int argc, char* argv[]) {
/**
* Take AD connection arguments from Windows command line:
* - Server address
* - Server port
* - Username
* - Password
* - Search base(s) (space separated if there are multiple)
*
* Example call from Windows command line:
* > program.exe ad-test.example.com 389 joe@ad-test.example.com L3tM31n "OU=Development Team,DC=ad-test,DC=example,DC=com" "OU=Management Team,DC=ad-test,DC=example,DC=com"
*/
string serverAddress = argv[1];
int serverPort = atoi(argv[2]);
string username = argv[3];
string password = argv[4];
vector<string> searchBases;
for (int i = 0; i < argc; i++) {
searchBases.push_back(argv[i]);
}
// If debug build, print received parameters
#ifdef _DEBUG
cout << "Server address: " << serverAddress << endl
<< "Server port: " << serverPort << endl
<< "Username: " << username << endl
<< "Password: " << password << endl;
for (size_t i = 5; i < searchBases.size(); i++) {
cout << "Search base: " << searchBases.at(i) << endl;
}
cout << endl;
#endif
// Initiate LDAP connection to Active Directory
int ldapVersion = LDAP_VERSION3;
LDAP* ldapSession = ldap_init(&serverAddress[0], serverPort);
ldap_set_option(ldapSession, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
ULONG ldapConnection = ldap_connect(ldapSession, nullptr);
// Bind user
int ldapBindResult = ldap_simple_bind_s(ldapSession, &username[0], &password[0]);
if (ldapBindResult != LDAP_SUCCESS) {
ldap_unbind(ldapSession);
#ifdef _DEBUG
cout << "Unable to connect to LDAP directory" << endl << endl;
#endif
}
else {
#ifdef _DEBUG
cout << "Connected to LDAP!" << endl << endl;
#endif
}
// The LDAP object filter
char* filter = "(&(objectCategory=person)(objectClass=user))";
// Get lists of departments and offices
vector<string> departments, offices;
for (int i = 0; i < searchBases.size(); i++) {
vector<string> tempDepts = get_unique_departments(ldapSession, filter, searchBases.at(i));
vector<string> tempOffices = get_unique_office_locations(ldapSession, filter, searchBases.at(i));
for (int j = 0; j < tempDepts.size(); j++) {
departments.push_back(tempDepts.at(j));
}
for (int j = 0; j < tempOffices.size(); j++) {
offices.push_back(tempOffices.at(j));
}
}
// Print the lists
print_list("Departments", departments);
print_list("Offices", offices);
// Return
return 0;
}
// Retrieve a list of unique departments
vector<string> get_unique_departments(LDAP* session, char* filter, string searchBase) {
return get_unique_values_by_property(session, filter, searchBase, "department");
}
// Retrieve a list of unique office locations
vector<string> get_unique_office_locations(LDAP* session, char* filter, string searchBase) {
return get_unique_values_by_property(session, filter, searchBase, "office");
}
// Get a list of an attribute's unique values
vector<string> get_unique_values_by_property(LDAP* ldapSession, char* filter, string searchBase, string property) {
// Initialize some variables
vector<string> results;
char* pAttributes[1];
pAttributes[0] = &property[0];
LDAPMessage* pSearchResults = NULL;
int numResults = 0;
// Do the search
int searchReturnCode = ldap_search_s(
ldapSession,
&searchBase[0],
LDAP_SCOPE_SUBTREE,
filter,
pAttributes,
0,
&pSearchResults); // Error is here
// Process results
if (searchReturnCode == LDAP_SUCCESS) {
// Initialize some variables
LDAPMessage* pEntry = NULL;
char* pEntryDN = NULL;
char* sMsg;
BerElement* pBer = NULL;
char* pAttribute = NULL;
char** ppValue = NULL;
ULONG iValue = 0;
// Count the results
numResults = ldap_count_entries(ldapSession, pSearchResults);
// Loop over results
for (ULONG i = 0; i < numResults; i++) {
// Get the first/next entry
if (!i) {
pEntry = ldap_first_entry(ldapSession, pSearchResults);
}
else {
pEntry = ldap_next_entry(ldapSession, pEntry);
}
// Fail if unable to get entry
if (pEntry == NULL) {
results.clear();
return results;
}
// Loop over the attributes
pAttribute = ldap_first_attribute(ldapSession, pEntry, &pBer);
while (pAttribute != NULL) {
// Get and handle the values
ppValue = ldap_get_values(ldapSession, pEntry, pAttribute);
if (ppValue != NULL) {
iValue = ldap_count_values(ppValue);
if (find(results.begin(), results.end(), *ppValue) == results.end()) {
results.push_back(*ppValue);
}
// Memory management
ldap_value_free(ppValue);
ppValue = NULL;
ldap_memfree(pAttribute);
// Get the next attribute
pAttribute = ldap_next_attribute(ldapSession, pEntry, pBer);
}
}
// Memory management
if (pBer != NULL) {
ber_free(pBer, 0);
}
pBer = NULL;
}
}
// Free search result memory
if (pSearchResults != NULL) {
ldap_msgfree(pSearchResults);
}
// Return
return results;
}
// Print a vector-based list w/ header
void print_list(string header, vector<string> items) {
if (items.size() > 0) {
cout << header << ":" << endl;
for (int i = 0; i < items.size(); i++) {
cout << items.at(i) << endl;
}
cout << endl;
}
}
来自https://msdn.microsoft.com/en-us/library/aa367016%28v=vs.85%29.aspx
//----------------------------------------------
// Performing an LDAP Synchronous Search.
//
// Be aware that you must set the command prompt screen buffer
// height to 350 and the width to 90.
//-------------------------------------------------------------
#include <windows.h>
#include <winldap.h>
#include <winber.h>
#include <rpc.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 session. 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, // Credential structure
LDAP_AUTH_NEGOTIATE); // Auth mode
if(lRtn == LDAP_SUCCESS)
{
printf("ldap_bind_s succeeded \n");
secIdent.Password = NULL; // Remove password pointer
pPassword = NULL; // Remove password pointer
}
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 attributes
0, // Get both attributes 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
// requested 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 first 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 are 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, // Session 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;
}
答案 0 :(得分:2)
阅读ldap_search_s的文档,我看到了:
base [in]
指向以null结尾的字符串的指针,该字符串包含开始搜索的条目的可分辨名称。
但是,&searchBase[0]
只会为您提供指向存储std::string
使用的指针 - 不要求此为空终止。您应该使用searchBase.c_str()
代替,因为这可以保证为您提供空终止的c字符串。
答案 1 :(得分:1)
EAGER
数组必须以空值终止。
代码应如下所示:
pAttributes