From 5b5acc6801a5166af5d83f7e4575e68f529c3bc6 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 21 Sep 2023 13:49:28 +0200 Subject: [PATCH] fix: resolve SRV hostname before passing it to mongodb-cloud-info VSCODE-442 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now, we are passing the first hostname from the connection string to mongodb-cloud-info, regardless of whether the connection string is a `mongodb://` connection string or a `mongodb+srv://` one. In the latter case, telemetry gathering fails, because mongodb-cloud-info expects the name of an actual host with associated A or AAAA records (so that it can look up the host’s IP address), not a SRV one. Using resolve-mongodb-srv first matches what Compass does and solves this issue. --- package-lock.json | 1 + package.json | 1 + src/telemetry/connectionTelemetry.ts | 32 +++++++++++++++++++++++----- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e83f4404..a73bacbc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "react-dom": "^17.0.2", "react-redux": "^8.1.1", "redux": "^4.2.1", + "resolve-mongodb-srv": "^1.1.2", "ts-log": "^2.2.5", "uuid": "^8.3.2", "vscode-languageclient": "^8.1.0", diff --git a/package.json b/package.json index d8ae9f115..3cd91c8c0 100644 --- a/package.json +++ b/package.json @@ -1005,6 +1005,7 @@ "react-dom": "^17.0.2", "react-redux": "^8.1.1", "redux": "^4.2.1", + "resolve-mongodb-srv": "^1.1.2", "ts-log": "^2.2.5", "uuid": "^8.3.2", "vscode-languageclient": "^8.1.0", diff --git a/src/telemetry/connectionTelemetry.ts b/src/telemetry/connectionTelemetry.ts index dd1d439ca..cd055dee8 100644 --- a/src/telemetry/connectionTelemetry.ts +++ b/src/telemetry/connectionTelemetry.ts @@ -1,9 +1,11 @@ import type { DataService } from 'mongodb-data-service'; import { getCloudInfo } from 'mongodb-cloud-info'; import mongoDBBuildInfo from 'mongodb-build-info'; +import resolveMongodbSrv from 'resolve-mongodb-srv'; import { ConnectionTypes } from '../connectionController'; import { createLogger } from '../logging'; +import ConnectionString from 'mongodb-connection-string-url'; const log = createLogger('connection telemetry helper'); // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -34,14 +36,36 @@ type CloudInfo = { publicCloudName?: string | null; }; +async function getHostnameForConnection( + connectionStringData: ConnectionString +): Promise { + if (connectionStringData.isSRV) { + const uri = await resolveMongodbSrv(connectionStringData.toString()).catch( + () => null + ); + if (!uri) { + return null; + } + connectionStringData = new ConnectionString(uri, { + looseValidation: true, + }); + } + + const [hostname] = (connectionStringData.hosts[0] ?? '').split(':'); + return hostname; +} + async function getCloudInfoFromDataService( - firstServerHostname: string + dataService: DataService ): Promise { + const hostname = await getHostnameForConnection( + dataService.getConnectionString() + ); const cloudInfo: { isAws?: boolean; isAzure?: boolean; isGcp?: boolean; - } = await getCloudInfo(firstServerHostname); + } = await getCloudInfo(hostname); if (cloudInfo.isAws) { return { @@ -82,15 +106,13 @@ export async function getConnectionTelemetryProperties( try { const connectionString = dataService.getConnectionString(); - const firstHost = connectionString.hosts[0] || ''; - const [hostname] = firstHost.split(':'); const authMechanism = connectionString.searchParams.get('authMechanism'); const username = connectionString.username ? 'DEFAULT' : 'NONE'; const authStrategy = authMechanism ?? username; const [instance, cloudInfo] = await Promise.all([ dataService.instance(), - getCloudInfoFromDataService(hostname), + getCloudInfoFromDataService(dataService), ]); preparedProperties = {