【问题标题】:NodeJS getaddrinfo ENOTFOUND uncaughtNodeJS getaddrinfo ENOTFOUND 未捕获
【发布时间】:2020-10-09 16:11:10
【问题描述】:

在我的由 PM2 运行的 NodeJS 服务器中,我使用 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 似乎是这个特定模块中的这个特定错误的问题。

有什么想法吗?

【问题讨论】:

    标签: node.js authentication ldap ldapauth


    【解决方案1】:

    不幸的是,这似乎是底层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));
      }
    });
    

    【讨论】:

    • 感谢您对错误和替代方案的确认,这个解释很清楚。第二个似乎合乎逻辑,但仍然想知道它是否会起作用,因为相同的未处理错误可能来自同一个脚本(虽然不确定)。第一个更加优雅和高效,乍一看它应该可以工作,但也需要测试。当我有时间的时候,我会测试两者并回复。
    • @jaume 不,dns.lookup() 调用应该处理它,在我的示例中,承诺拒绝应该是可捕获的,而 ldap 库中的 ENOTFOUND 则不是。我对其进行了测试,它在主机名错误的情况下按预期工作。
    • 最后我尝试了第二个,因为我对摆弄模块本身没有信心。哟是对的,它就像一个魅力。谢谢。
    • 但我做了一些修改以区分凭据错误和通信中断的错误,我将在答案中发布。
    【解决方案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();
    
        }
    
      });
    
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-24
      • 2016-04-20
      • 1970-01-01
      • 1970-01-01
      • 2016-07-03
      • 2014-06-09
      相关资源
      最近更新 更多