diff --git a/package-lock.json b/package-lock.json index 92783108a9..a0386cc751 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4606,7 +4606,8 @@ "dev": true, "optional": true, "requires": { - "bindings": "^1.5.0" + "bindings": "^1.5.0", + "nan": "^2.12.1" } }, "glob-parent": { @@ -15854,6 +15855,13 @@ "thenify-all": "^1.0.0" } }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", diff --git a/packages/build/src/compile/signable-compiler.ts b/packages/build/src/compile/signable-compiler.ts index a42dd49ae4..19aa276e53 100644 --- a/packages/build/src/compile/signable-compiler.ts +++ b/packages/build/src/compile/signable-compiler.ts @@ -66,6 +66,10 @@ export class SignableCompiler { path: await findModulePath('service-provider-server', 'mongodb-client-encryption'), requireRegexp: /\bmongocrypt\.node$/ }; + const osDnsAddon = { + path: await findModulePath('service-provider-server', 'os-dns-native'), + requireRegexp: /\bos_dns_native\.node$/ + }; const winCAAddon = process.platform === 'win32' ? { path: await findModulePath('cli-repl', 'win-export-certificate-and-key'), requireRegexp: /\bwin_export_cert\.node$/ @@ -93,7 +97,8 @@ export class SignableCompiler { AWS_SECRET_ACCESS_KEY: process.env.DEVTOOLS_CI_AWS_SECRET }, addons: [ - fleAddon + fleAddon, + osDnsAddon ].concat(winCAAddon ? [ winCAAddon ] : []).concat(macKeychainAddon ? [ diff --git a/packages/connectivity-tests/test/atlas.sh b/packages/connectivity-tests/test/atlas.sh index 064cb7b4ef..da31a3b9f0 100644 --- a/packages/connectivity-tests/test/atlas.sh +++ b/packages/connectivity-tests/test/atlas.sh @@ -129,11 +129,24 @@ function test_data_lake() { check_failed } +function test_srv_without_nodejs_dns() { + printf "test_srv_without_nodejs_dns ... " + + CONNECTION_STRING="mongodb+srv://${ATLAS_USERNAME}:${ATLAS_PASSWORD}@${ATLAS_HOSTNAME}/admin" + + echo "${CONNECTION_STATUS_COMMAND}" | NODE_OPTIONS="-r ${MONGOSH_ROOT_DIR}/testing/disable-dns-srv.js" mongosh "${CONNECTION_STRING}" | + grep -Fq "${CONNECTION_STATUS_CHECK_STRING}" || + FAILED="Can't connect to Atlas using connection string without Node.js SRV/TXT DNS support" + + check_failed +} + test_connection_string test_atlas_in_logs test_credentials_masking test_cli_args test_password_prompt test_data_lake +test_srv_without_nodejs_dns echo "All Atlas tests are passing" diff --git a/packages/service-provider-server/package-lock.json b/packages/service-provider-server/package-lock.json index ff494d0753..0965664d87 100644 --- a/packages/service-provider-server/package-lock.json +++ b/packages/service-provider-server/package-lock.json @@ -234,6 +234,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "optional": true }, + "ipv6-normalize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz", + "integrity": "sha1-GzJYKQ02X6gyOeiZB93kWS52IKg=", + "optional": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -249,6 +255,12 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true + }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -315,6 +327,12 @@ "semver": "^5.4.1" } }, + "node-addon-api": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", + "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==", + "optional": true + }, "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", @@ -354,6 +372,17 @@ "wrappy": "1" } }, + "os-dns-native": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/os-dns-native/-/os-dns-native-1.0.3.tgz", + "integrity": "sha512-mAkA8SmvbaHqAfScwqZ0bg0W88oe2os/gYWRhleU8ZakjGigeEZEv/HgwRx/tFyc6+O8b6SZ2yaoPrZPdrX6Fg==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "ipv6-normalize": "^1.0.1", + "node-addon-api": "^3.1.0" + } + }, "prebuild-install": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.0.1.tgz", @@ -393,6 +422,12 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -428,6 +463,15 @@ } } }, + "resolve-mongodb-srv": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-mongodb-srv/-/resolve-mongodb-srv-1.0.1.tgz", + "integrity": "sha512-ZJCe1E7LMyxDvKtg/sRkVIK/X+tJrPvoP6tHd8oxDYx/xlBybruto4L+G/rQ4u/fdAgrBEk+dk8/yr5Lv6cXXw==", + "optional": true, + "requires": { + "whatwg-url": "^8.5.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -576,6 +620,15 @@ } } }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "optional": true, + "requires": { + "punycode": "^2.1.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -591,6 +644,23 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "optional": true + }, + "whatwg-url": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", + "optional": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, "which-pm-runs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", diff --git a/packages/service-provider-server/package.json b/packages/service-provider-server/package.json index 1a97e14756..9d27c1b7e8 100644 --- a/packages/service-provider-server/package.json +++ b/packages/service-provider-server/package.json @@ -50,6 +50,8 @@ "@types/bl": "^2.1.0" }, "optionalDependencies": { - "mongodb-client-encryption": "^1.2.3" + "mongodb-client-encryption": "^1.2.3", + "os-dns-native": "^1.0.3", + "resolve-mongodb-srv": "^1.0.1" } } diff --git a/packages/service-provider-server/src/cli-service-provider.ts b/packages/service-provider-server/src/cli-service-provider.ts index dea27b1adc..39d19a15f8 100644 --- a/packages/service-provider-server/src/cli-service-provider.ts +++ b/packages/service-provider-server/src/cli-service-provider.ts @@ -156,6 +156,28 @@ async function connectWithFailFast(client: MongoClient): Promise { } } +let resolveDnsHelpers: { + resolve: typeof import('resolve-mongodb-srv'), + osDns: typeof import('os-dns-native') +} | undefined = undefined; + +async function resolveMongodbSrv(uri: string): Promise { + if (uri.startsWith('mongodb+srv://')) { + try { + resolveDnsHelpers ??= { + resolve: require('resolve-mongodb-srv'), + osDns: require('os-dns-native') + }; + } catch { /* ignore */ } + if (resolveDnsHelpers !== undefined) { + return await resolveDnsHelpers.resolve(uri, { + dns: resolveDnsHelpers.osDns.withNodeFallback + }); + } + } + return uri; +} + /** * Connect a MongoClient. If AutoEncryption is requested, first connect without the encryption options and verify that * the connection is to an enterprise cluster. If not, then error, otherwise close the connection and reconnect with the @@ -182,6 +204,7 @@ export async function connectMongoClient(uri: string, clientOptions: MongoClient } await client.close(); } + uri = await resolveMongodbSrv(uri); const client = new MClient(uri, clientOptions); await connectWithFailFast(client); return client; diff --git a/testing/disable-dns-srv.js b/testing/disable-dns-srv.js new file mode 100644 index 0000000000..7f75a0cef1 --- /dev/null +++ b/testing/disable-dns-srv.js @@ -0,0 +1,26 @@ +'use strict'; +const dns = require('dns'); +console.log('!!! Disabling SRV and TXT DNS queries through the Node.js API !!!'); + +const origResolve = dns.resolve; +const origPromiseResolve = dns.promises.resolve; +const err = Object.assign(new Error('SRV and TXT not available'), { code: 'ENODATA' }); + +dns.resolve = (hostname, type, cb) => { + if (type === 'SRV' || type === 'TXT') + return process.nextTick(cb, err); + return origResolve(hostname, type, cb); +}; +dns.resolveSrv = (hostname, cb) => { + return process.nextTick(cb, err); +}; +dns.resolveTxt = (hostname, cb) => { + return process.nextTick(cb, err); +}; +dns.promises.resolve = async(hostname, type) => { + if (type === 'SRV' || type === 'TXT') + throw err; + await origPromiseResolve; +}; +dns.promises.resolveSrv = () => Promise.reject(err); +dns.promises.resolveTxt = () => Promise.reject(err);