NodeJS getaddrinfo ENOTFOUND 未捕获

NodeJS getaddrinfo ENOTFOUND uncaught

在我的 NodeJS 服务器中,运行 通过 PM2,我使用 npm 模块 "ldap-authentication" 通过 LAPD 服务对我的用户进行身份验证。

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 重新启动了代码)。我需要处理可能断开的连接并避免服务器崩溃。

看起来这不是从异步函数捕获错误的问题,因为捕获了该函数的其他类型的错误。参加 this link 似乎是这个特定模块中的这个特定错误的问题。

有什么想法吗?

不幸的是,这似乎是底层 ldapjs 库中的错误。 ENOTFOUND 需要在 Socket.on('error') 中处理,但是直到调用 Socket.connect() 之后才会设置,因此在发生 DNS 错误时它不可用。

你可以在这里看到代码https://github.com/ldapjs/node-ldapjs/blob/master/lib/client/client.js#L827

if (server && 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 && 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));
  }
});

稍作修改以区分错误凭据错误和调用函数中的通信中断情况。

如果错误则抛出拒绝,如果凭据错误则在没有参数的情况下解决。

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();

    }

  });

});