Repository: axios/axios (js)

Issue:
dns lookup upgraded in 1.6 incorrectly attempts to resolve address in case of an error from the lookup callback
### Describe the bug

TL;DR: if you supply custom DNS `lookup` in 1.6+ and it fails with `ENOTFOUND`, `axios` throws `TypeError: address must be a string` instead

I supply custom DNS `lookup` to precisely control the timeout. I am using https://github.com/szmarczak/cacheable-lookup. It works correctly in axios before 1.6 but fails after because of this change:

![image](https://github.com/axios/axios/assets/498673/50a0e6ba-7fe0-4b03-b5c9-6b05d7b4cfa1)

When the callback returns the `err` as the first argument, the other two can be (and are) `undefined`. Axios 1.6+ attempts to resolve `family` and `address` regardless of `err` and trips over `undefined`.

Take a look:

![image](https://github.com/axios/axios/assets/498673/5ee84a2e-361b-4bd0-a589-85d81fe9208e)


### To Reproduce

Perform axios request against a domain that does not exists with a custom `lookup`. DNS `lookup` fails with `ENOTFOUND` but `axios` then fails with `TypeError: address must be a string`

### Code snippet

```js
// simplified test:


import CacheableLookup from 'cacheable-lookup';
import axios from 'axios';

const dns = new CacheableLookup({ resolver: new Resolver({ timeout: 200 }), maxTtl: 60 * 60, lookup: false });
const controller = new AbortController();

await axios.post('https://no-such-domain-987654.com', {}, {
  signal: controller.signal,
  lookup: dns.lookup.bind(dns),
});
```

### Expected behavior

`axios` should fail with the DNS `lookup` error, not the `TypeError`

### Axios Version

1.6+

### Adapter Version

_No response_

### Browser

_No response_

### Browser Version

_No response_

### Node.js Version

_No response_

### OS

_No response_

### Additional Library Versions

_No response_

### Additional context/Screenshots

_No response_

Pull request:
fix(dns): fixed lookup error handling;
Fixes #6174 

Patch:
diff --git a/lib/adapters/http.js b/lib/adapters/http.js
index 03f0f96..78255d2 100755
--- a/lib/adapters/http.js
+++ b/lib/adapters/http.js
@@ -171,6 +171,10 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
       // hotfix to support opt.all option which is required for node 20.x
       lookup = (hostname, opt, cb) => {
         _lookup(hostname, opt, (err, arg0, arg1) => {
+          if (err) {
+            return cb(err);
+          }
+
           const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)];
 
           opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family);


Test patch:
diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js
index a1c6da4..67e01b1 100644
--- a/test/unit/adapters/http.js
+++ b/test/unit/adapters/http.js
@@ -9,6 +9,7 @@ import util from 'util';
 import assert from 'assert';
 import fs from 'fs';
 import path from 'path';
+import {lookup} from 'dns';
 let server, proxy;
 import AxiosError from '../../../lib/core/AxiosError.js';
 import FormDataLegacy from 'form-data';
@@ -2216,5 +2217,13 @@ describe('supports http with nodejs', function () {
 
       assert.strictEqual(data, payload);
     });
+
+    it('should handle errors', () => {
+      return assert.rejects(async () => {
+        await axios.get('https://no-such-domain-987654.com', {
+          lookup
+        });
+      }, /ENOTFOUND/);
+    });
   });
 });