NodeJS getaddrinfo ENOTFOUND uncaught

#node.js #authentication #ldap #ldapauth

#node.js #аутентификация #ldap #ldapauth

Вопрос:

На моем сервере NodeJS, управляемом PM2, я аутентифицирую своих пользователей с помощью службы LAPD, используя модуль npm «ldap-аутентификация».

 const { authenticate } = require('ldap-authentication');
...
try {
   const auth = await authenticate(options);
   return auth;
} catch(err){
   return err;
}
  

Он отлично работает, когда учетные данные в порядке.

Когда учетные данные неверны, функция выдает правильную ошибку, которую можно перехватить и легко обработать.

Проблема возникает, когда нет соединения и возникает ошибка «getaddrinfo ENOTFOUND»,

 Error: getaddrinfo ENOTFOUND xxx.xxx.corp
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)
  

поскольку код выходит из строя без обнаружения какой-либо ошибки, вызывающей недопустимую проблему (несмотря на то, что PM2 перезапускает код). Мне нужно обработать возможное разорванное соединение и избежать сбоя сервера.

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

Есть идеи?

Ответ №1:

К сожалению, это, похоже, ошибка в базовой ldapjs библиотеке. ENOTFOUND Необходимо обработать, Socket.on('error') однако это не настроено до тех пор, пока после вызова Socket.connect() so оно не будет доступно при возникновении ошибки DNS.

Вы можете увидеть код здесь https://github.com/ldapjs/node-ldapjs/blob/master/lib/client/client.js#L827

 if (server amp;amp; server.secure) {
  socket = tls.connect(port, host, self.tlsOptions)
  socket.once('secureConnect', onConnect)
} else {
  socket = net.connect(port, host)
  socket.once('connect', onConnect)
}
socket.once('error', onResult)
  

Для обработки ошибки DNS она должна выглядеть примерно следующим образом

 // reference to the socket
let socket;
if (server amp;amp; server.secure) {
  // create TLS socket and connection handler
  socket = new tls.TLSSocket(self.tlsOptions);
  socket.once('secureConnect', onConnect)
} else {
  // create net socket and connection handler
  socket = new net.Socket();
  socket.once('connect', onConnect)
}
// set up error handler - including DNS 
socket.once('error', onResult)
// connect the socket after we have an error handler
socket.connect(port, host)
  

Я не тестировал, но возможно, что добавление обработчика ошибок в модуль, который вы используете здесь, также будет работать —
https://github.com/shaozi/ldap-authentication/blob/master/index.js#L8

 var client = ldap.createClient(ldapOpts)
client.on('error', (err) = { /* set up before connect */ });
  

Помимо отправки PR для исправления базовой библиотеки, моя лучшая идея — использовать оболочку для выполнения поиска DNS перед попыткой подключения — https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback

 const dns = require('dns');

const auth = await new Promise((resolve, reject) => { 
  dns.lookup('xxx.xxx.corp', (err, address, family) => {
    // console.log('address: %j family: IPv%s', address, family));
    if (err) {
      return reject(err);
    }
    return resolve(authenticate(options));
  }
});
  

Комментарии:

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

2. @jaume нет, dns.lookup() вызов должен обработать это, и в моем примере отклонение обещания должно быть уловимым, тогда как ENOTFOUND из библиотеки ldap — нет. Я протестировал его, и он работает, как и ожидалось, с неправильным именем хоста.

3. наконец, я попробовал второй, так как не был уверен в том, как настроить сам модуль. Вы были правы, и это работает как шарм. Спасибо.

4. Тем не менее, я внес небольшие изменения, чтобы различать ошибку из-за неверных учетных данных и случай прерывания связи, я опубликую в ответах.

Ответ №2:

Небольшие изменения, чтобы различать ошибку из-за неверных учетных данных и случай прерывания связи в вызывающей функции.

Если выдается ошибка отклонения, если неверные учетные данные разрешаются без параметра.

 return new Promise((resolve, reject) => {

  dns.lookup('xxx.xxx.corp', async (err, address, family) => {

    if (err) {

      return reject(err);

    };

    try {

      const x = await(authenticate(options));

      return resolve(x);

    } catch(err){

      return resolve();

    }

  });

});