diff --git a/Extension/src/id.ts b/Extension/src/id.ts new file mode 100644 index 0000000000..d407567738 --- /dev/null +++ b/Extension/src/id.ts @@ -0,0 +1,68 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { execChildProcess } from './common'; +import { isWindows } from './constants'; +import { logLanguageServerEvent } from './telemetry'; + +/** + * Hash the MAC addresses on the machine (Windows-only) and log telemetry. + */ +export async function logMachineIdMappings(): Promise { + if (!isWindows) { + return; + } + + const macAddresses = await getMacAddresses(); + + // The first MAC address is the one Visual Studio uses + const primary = await getMachineId(macAddresses.shift()); + if (primary) { + logLanguageServerEvent('machineIdMap', {primary}); + } + + // VS Code uses os.networkInterfaces() which has different sorting and availability, + // but all MAC addresses are returned by getmac.exe. The ID VS Code uses may change + // based on changes to the network configuration. Log the extras so we can assess + // how frequently this impacts the machine id. + for (const macAddress of macAddresses) { + const additional = await getMachineId(macAddress); + if (additional) { + logLanguageServerEvent('machineIdMap', {additional}); + } + } +} + +/** + * Parse the output of getmac.exe to get the list of MAC addresses for the PC. + */ +async function getMacAddresses(): Promise { + try { + const output = await execChildProcess('getmac'); + const regex = /(?:[a-z0-9]{2}[:\-]){5}[a-z0-9]{2}/gmi; + return output.match(regex) ?? []; + } catch (err) { + return []; + } +} + +/** + * Code below is adapted from: + * - vscode\src\vs\base\node\id.ts + */ + +async function getMachineId(macAddress?: string): Promise { + if (!macAddress) { + return undefined; + } + + try { + const crypto = await import('crypto'); + const normalized = macAddress.toUpperCase().replace(/:/g, '-'); + return crypto.createHash('sha256').update(normalized, 'utf8').digest('hex'); + } catch (err) { + return undefined; + } +} diff --git a/Extension/src/main.ts b/Extension/src/main.ts index 832ca853b0..c02ee313cc 100644 --- a/Extension/src/main.ts +++ b/Extension/src/main.ts @@ -22,6 +22,7 @@ import { PersistentState } from './LanguageServer/persistentState'; import { CppSettings } from './LanguageServer/settings'; import { logAndReturn, returns } from './Utility/Async/returns'; import { CppTools1 } from './cppTools1'; +import { logMachineIdMappings } from './id'; import { disposeOutputChannels, log } from './logger'; import { PlatformInformation } from './platform'; @@ -214,6 +215,7 @@ function sendTelemetry(info: PlatformInformation): void { break; } Telemetry.logDebuggerEvent("acquisition", telemetryProperties); + logMachineIdMappings().catch(logAndReturn.undefined); } async function checkVsixCompatibility(): Promise {