Ошибка обработки ldap_initalize в C

Общие сведения
У меня возникли проблемы с обработкой функции ldap_initialize в C.

Прочитав многочисленные документы (большинство из которых представляют собой просто копии справочных страниц), я нашел несколько способов (которые, возможно, вводят в заблуждение или неверны) для обработки ошибок этой функции.

Вот код, с которым у меня возникли проблемы:

//--------------------------------------
// declarations
//--------------------------------------
int status = 0;
int optionHandlingStatus = 0;
char * ldapErrorString = NULL;
char serverAddress[PLS_STRING_ARRAY_SIZE];

//--------------------------------------
// prepare the server's address
//--------------------------------------
sprintf(
    serverAddress,
    "ldap://%s:%s",
    settings->serverAddress,
    settings->serverPort
);
//writeDebugLog("LDAP server address set to [%s].", serverAddress);

//--------------------------------------
// connect to the server
//--------------------------------------
status = ldap_initialize(&ldapServerConnection, serverAddress);
optionHandlingStatus = ldap_get_option(ldapServerConnection, LDAP_OPT_RESULT_CODE, &status);
if (status != LDAP_SUCCESS || errno != LDAP_SUCCESS) { //the specs are strange
    if (status && errno) {
        writeDebugLog("CONNECTION Fail!.");
        writeSyslog("CONNECTION Fail!.");
    }
    ldapErrorString = ldap_err2string(status);
    writeDebugLog("Connection to server failed with [%s].", ldapErrorString);
    writeSyslog(
        "LDAP server initalization error. Check [%s] server's status.",
        serverAddress
    );
    return(NULL);
}  

Проблема
В документах говорится, что ldap_initialize должен:
1. возвращать указатель NULL в случае ошибки соединения (если я использую ldap_init)
2. возвращать статус ошибки который можно получить с помощью ldap_get_option

Однако ни один из этих методов не является надежным. Я провел тщательное тестирование функции, которую я создал для запуска подключения к моему серверу LDAP. Иногда ldap_init возвращает действительный указатель на объект LDAP, ldap_initialize возвращает код ошибки LDAP Success (при использовании ldap_get_option), а ldap_get_option успешно возвращает коды ошибок.

Я обнаружил, что если я проверю переменную errno C, я могу довольно точно получить статус соединения функции ldap_initialize, поскольку она, кажется, устанавливает ошибку при сбое (errno == 2) => "Нет такого файла или каталога".

Когда я указываю случайные неиспользуемые порты в своей программе, это обычно верно. Однако я решил более тщательно протестировать свою программу с помощью программы с именем pamtester, и она не смогла аутентифицировать пользователей, которых я указал. Ошибка, которая была установлена, была «Нет такого файла или каталога» сразу после моего вызова ldap_initialize.

Я принудительно установил C (errno = 0) после ldap_initialize, и мне удалось успешно аутентифицировать пользователей в моей базе данных LDAP.

Вопросы
1. Как правильно обработать ошибку функции ldap_initialize?
2. Должен ли я использовать другую функцию для инициализации подключения LDAP?
3. Если ошибка статус на самом деле обрабатывается флагом C errno, а не ldap_get_options, почему все справочные страницы и примеры говорят об обратном?


person Dodzi Dzakuma    schedule 30.04.2014    source источник
comment
Проверяйте errno только в том случае, если ldap_initilize вернул что-то отличное от LDAP_SUCCESS. Также сделайте это сразу после возвращения ldap_initilize, иначе errno мог быть испорчен. В качестве альтернативы сохраните значение errno после возврата ldap_initilize.   -  person alk    schedule 30.04.2014
comment
Я не знаю, какие справочные страницы вы читали, но раздел «Ошибки» кажется довольно ясным: linux.die.net/man/3/ldap_initialize   -  person ooga    schedule 30.04.2014
comment
Я делал это, однако проблема в том, что иногда ldap_initialize возвращает LDAP_SUCCESS при недопустимом соединении, но errno будет установлено. Обработка ошибок постоянно конфликтовала, оставляя меня в замешательстве, поэтому я написал здесь.   -  person Dodzi Dzakuma    schedule 30.04.2014
comment
Вы не делаете этого, по крайней мере, в коде, который вы нам показываете.   -  person alk    schedule 30.04.2014
comment
errno не имеет смысла, если только функция не вернула значение, указывающее на ошибку. Библиотечные функции не устанавливают для errno значение 0 в случае успеха. Попробуйте установить для errno значение 0 перед вызовом функции, и оно должно быть равно 0 после успешного завершения функции.   -  person ooga    schedule 30.04.2014
comment
В случае возникновения ошибки ldap_open() и ldap_init() вернут NULL, а значение errno должно быть установлено соответствующим образом. ldap_initialize() и ldap_init_fd() напрямую вернут код LDAP, связанный с ошибкой (или LDAP_SUCCESS в случае успеха); errno также должен быть установлен всякий раз, когда это уместно Я прочитал это и следовал указаниям на этом сайте, однако несколько раз я передал недопустимый параметр сервера, где ldap_initialize возвращает LDAP_SUCCESS, но errno установлено значение 2. В этой ситуации errno было правильно, а ldap_initialize — нет.   -  person Dodzi Dzakuma    schedule 30.04.2014
comment
Кроме того, ldap_get_options, похоже, не имеет ничего общего с ошибками.   -  person ooga    schedule 30.04.2014
comment
Согласно странице руководства, если вы посмотрите на LDAP_OPT_RESULT_CODE, это способ проверить статус, установленный в объекте LDAP при использовании ldap_initialize, поскольку ldap_initialize не возвращает указатель NULL при ошибке. Я могу неправильно читать документы, и, возможно, я не полностью их понимаю.   -  person Dodzi Dzakuma    schedule 30.04.2014
comment
ldap_initialize() никогда не возвращает NULL, так как возвращает int, а NULL является значением указателя. Вы, кажется, путаете "ldap_initialize() и "ldap_init().   -  person alk    schedule 30.04.2014


Ответы (1)


Проверяйте errno только в том случае, если ldap_initilize вернул что-то отличное от LDAP_SUCCESS. Также сделайте это сразу после возвращения ldap_initilize, иначе errno мог быть испорчен.

Код должен выглядеть так:

int status = ldap_initialize(&ldapServerConnection, serverAddress);
if (status != LDAP_SUCCESS)
{
  /* int errno_save = errno; */
  perror("ldap_initialize() failed"); /* This call shows the error message related to errno. */
  /* If on a GNU system also the following will do: */
  /* fprintf(stderr, ""ldap_initialize() failed with #%d: '%s'\n", errno_save, strerror(errno_save)); */

  writeDebugLog("CONNECTION Fail!.");
  writeSyslog("CONNECTION Fail!.");

  ldapErrorString = ldap_err2string(status);
  writeDebugLog("Connection to server failed with [%s].", ldapErrorString);
  writeSyslog(
    "LDAP server initalization error. Check [%s] server's status.",
    serverAddress
  );
  return(NULL);
}

Обновление:

Использование функций ldap_init*() не пытается установить соединение, поэтому любые недействительные адресные данные не будут обнаружены как таковые. Чтобы проверить соединение, используйте ldap_open(), но ldsp_init().

Из man ldap_open:

ldap_init() действует так же, как ldap_open(), но не открывает соединение с сервером LDAP. Фактическое открытие соединения произойдет при попытке первой операции.

person alk    schedule 30.04.2014
comment
Я попробую ваше предложение и посмотрю, что произойдет. Пожалуйста, подождите минутку. - person Dodzi Dzakuma; 30.04.2014
comment
Я попробовал ваше предложение, и у меня, похоже, точно такая же проблема. status == LDAP_SUCCESS хотя я намеренно установил несуществующий адрес сервера. ldap://123.45.67.89:9321 - person Dodzi Dzakuma; 30.04.2014
comment
Вам нужно позвонить ldap_open(), чтобы узнать, что этот адрес недействителен. - person alk; 30.04.2014
comment
Собираюсь дать, что я попробую прямо сейчас. Я должен вызывать ldap_initalize(), а затем ldap_open() вот так последовательно? - person Dodzi Dzakuma; 30.04.2014
comment
Похоже, что только ldap_open() является ответом. Я сделаю еще несколько проверок. - person Dodzi Dzakuma; 30.04.2014
comment
На самом деле это была документация, которая привела меня к ldap_initalize(), потому что в ней говорилось, что ldap_open() устарел в пользу ldap_initalize(). Эта информация также есть в документации. - person Dodzi Dzakuma; 30.04.2014