Skip to content

Commit 7c92254

Browse files
committed
feat(NODE-5754): support auto select family in kms requests
1 parent 6321b2c commit 7c92254

File tree

9 files changed

+106
-14
lines changed

9 files changed

+106
-14
lines changed

src/client-side-encryption/auto_encrypter.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import * as cryptoCallbacks from './crypto_callbacks';
1515
import { MongoCryptInvalidArgumentError } from './errors';
1616
import { MongocryptdManager } from './mongocryptd_manager';
1717
import { type KMSProviders, refreshKMSCredentials } from './providers';
18-
import { type CSFLEKMSTlsOptions, StateMachine } from './state_machine';
18+
import {
19+
type ClientEncryptionSocketOptions,
20+
type CSFLEKMSTlsOptions,
21+
StateMachine
22+
} from './state_machine';
1923

2024
/** @public */
2125
export interface AutoEncryptionOptions {
@@ -101,6 +105,8 @@ export interface AutoEncryptionOptions {
101105
proxyOptions?: ProxyOptions;
102106
/** The TLS options to use connecting to the KMS provider */
103107
tlsOptions?: CSFLEKMSTlsOptions;
108+
/** Options for KMS socket requests. */
109+
socketOptions?: ClientEncryptionSocketOptions;
104110
}
105111

106112
/**
@@ -150,6 +156,7 @@ export class AutoEncrypter {
150156
_kmsProviders: KMSProviders;
151157
_bypassMongocryptdAndCryptShared: boolean;
152158
_contextCounter: number;
159+
_socketOptions: ClientEncryptionSocketOptions;
153160

154161
_mongocryptdManager?: MongocryptdManager;
155162
_mongocryptdClient?: MongoClient;
@@ -234,6 +241,7 @@ export class AutoEncrypter {
234241
this._proxyOptions = options.proxyOptions || {};
235242
this._tlsOptions = options.tlsOptions || {};
236243
this._kmsProviders = options.kmsProviders || {};
244+
this._socketOptions = options.socketOptions || {};
237245

238246
const mongoCryptOptions: MongoCryptOptions = {
239247
cryptoCallbacks
@@ -379,7 +387,8 @@ export class AutoEncrypter {
379387
promoteValues: false,
380388
promoteLongs: false,
381389
proxyOptions: this._proxyOptions,
382-
tlsOptions: this._tlsOptions
390+
tlsOptions: this._tlsOptions,
391+
socketOptions: this._socketOptions
383392
});
384393

385394
return deserialize(await stateMachine.execute(this, context), {
@@ -399,7 +408,8 @@ export class AutoEncrypter {
399408
const stateMachine = new StateMachine({
400409
...options,
401410
proxyOptions: this._proxyOptions,
402-
tlsOptions: this._tlsOptions
411+
tlsOptions: this._tlsOptions,
412+
socketOptions: this._socketOptions
403413
});
404414

405415
return await stateMachine.execute(this, context);

src/client-side-encryption/client_encryption.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import {
2828
type KMSProviders,
2929
refreshKMSCredentials
3030
} from './providers/index';
31-
import { type CSFLEKMSTlsOptions, StateMachine } from './state_machine';
31+
import {
32+
type ClientEncryptionSocketOptions,
33+
type CSFLEKMSTlsOptions,
34+
StateMachine
35+
} from './state_machine';
3236

3337
/**
3438
* @public
@@ -62,6 +66,8 @@ export class ClientEncryption {
6266
_tlsOptions: CSFLEKMSTlsOptions;
6367
/** @internal */
6468
_kmsProviders: KMSProviders;
69+
/** @internal */
70+
_socketOptions: ClientEncryptionSocketOptions;
6571

6672
/** @internal */
6773
_mongoCrypt: MongoCrypt;
@@ -108,6 +114,15 @@ export class ClientEncryption {
108114
this._proxyOptions = options.proxyOptions ?? {};
109115
this._tlsOptions = options.tlsOptions ?? {};
110116
this._kmsProviders = options.kmsProviders || {};
117+
this._socketOptions = {};
118+
119+
if ('autoSelectFamily' in client.s.options) {
120+
this._socketOptions.autoSelectFamily = client.s.options.autoSelectFamily;
121+
}
122+
if ('autoSelectFamilyAttemptTimeout' in client.s.options) {
123+
this._socketOptions.autoSelectFamilyAttemptTimeout =
124+
client.s.options.autoSelectFamilyAttemptTimeout;
125+
}
111126

112127
if (options.keyVaultNamespace == null) {
113128
throw new MongoCryptInvalidArgumentError('Missing required option `keyVaultNamespace`');
@@ -199,7 +214,8 @@ export class ClientEncryption {
199214

200215
const stateMachine = new StateMachine({
201216
proxyOptions: this._proxyOptions,
202-
tlsOptions: this._tlsOptions
217+
tlsOptions: this._tlsOptions,
218+
socketOptions: this._socketOptions
203219
});
204220

205221
const dataKey = deserialize(await stateMachine.execute(this, context)) as DataKey;
@@ -256,7 +272,8 @@ export class ClientEncryption {
256272
const context = this._mongoCrypt.makeRewrapManyDataKeyContext(filterBson, keyEncryptionKeyBson);
257273
const stateMachine = new StateMachine({
258274
proxyOptions: this._proxyOptions,
259-
tlsOptions: this._tlsOptions
275+
tlsOptions: this._tlsOptions,
276+
socketOptions: this._socketOptions
260277
});
261278

262279
const { v: dataKeys } = deserialize(await stateMachine.execute(this, context));
@@ -637,7 +654,8 @@ export class ClientEncryption {
637654

638655
const stateMachine = new StateMachine({
639656
proxyOptions: this._proxyOptions,
640-
tlsOptions: this._tlsOptions
657+
tlsOptions: this._tlsOptions,
658+
socketOptions: this._socketOptions
641659
});
642660

643661
const { v } = deserialize(await stateMachine.execute(this, context));
@@ -715,7 +733,8 @@ export class ClientEncryption {
715733
const valueBuffer = serialize({ v: value });
716734
const stateMachine = new StateMachine({
717735
proxyOptions: this._proxyOptions,
718-
tlsOptions: this._tlsOptions
736+
tlsOptions: this._tlsOptions,
737+
socketOptions: this._socketOptions
719738
});
720739
const context = this._mongoCrypt.makeExplicitEncryptionContext(valueBuffer, contextOptions);
721740

src/client-side-encryption/state_machine.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ export type CSFLEKMSTlsOptions = {
114114
[key: string]: ClientEncryptionTlsOptions | undefined;
115115
};
116116

117+
/**
118+
* @public
119+
*
120+
* Socket options to use for KMS requests.
121+
*/
122+
export type ClientEncryptionSocketOptions = Pick<
123+
MongoClientOptions,
124+
'autoSelectFamily' | 'autoSelectFamilyAttemptTimeout'
125+
>;
126+
117127
/**
118128
* This is kind of a hack. For `rewrapManyDataKey`, we have tests that
119129
* guarantee that when there are no matching keys, `rewrapManyDataKey` returns
@@ -153,6 +163,9 @@ export type StateMachineOptions = {
153163

154164
/** TLS options for KMS requests, if set. */
155165
tlsOptions: CSFLEKMSTlsOptions;
166+
167+
/** Socket specific options we support. */
168+
socketOptions: ClientEncryptionSocketOptions;
156169
} & Pick<BSONSerializeOptions, 'promoteLongs' | 'promoteValues'>;
157170

158171
/**
@@ -289,14 +302,27 @@ export class StateMachine {
289302
async kmsRequest(request: MongoCryptKMSRequest): Promise<void> {
290303
const parsedUrl = request.endpoint.split(':');
291304
const port = parsedUrl[1] != null ? Number.parseInt(parsedUrl[1], 10) : HTTPS_PORT;
292-
const options: tls.ConnectionOptions & { host: string; port: number } = {
305+
const options: tls.ConnectionOptions & {
306+
host: string;
307+
port: number;
308+
autoSelectFamily?: boolean;
309+
autoSelectFamilyAttemptTimeout?: number;
310+
} = {
293311
host: parsedUrl[0],
294312
servername: parsedUrl[0],
295313
port
296314
};
297315
const message = request.message;
298316
const buffer = new BufferPool();
299317

318+
const socketOptions = this.options.socketOptions || {};
319+
if ('autoSelectFamily' in socketOptions) {
320+
options.autoSelectFamily = socketOptions.autoSelectFamily;
321+
}
322+
if ('autoSelectFamilyAttemptTimeout' in socketOptions) {
323+
options.autoSelectFamilyAttemptTimeout = socketOptions.autoSelectFamilyAttemptTimeout;
324+
}
325+
300326
const netSocket: net.Socket = new net.Socket();
301327
let socket: tls.TLSSocket;
302328

src/cmap/connect.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,6 @@ function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts {
289289
}
290290
}
291291

292-
if (!('autoSelectFamily' in result)) {
293-
result.autoSelectFamily = true;
294-
}
295-
296292
if (typeof hostAddress.socketPath === 'string') {
297293
result.path = hostAddress.socketPath;
298294
return result as net.IpcNetConnectOpts;

src/connection_string.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,8 @@ export const OPTIONS = {
741741
type: 'record'
742742
},
743743
autoSelectFamily: {
744-
type: 'boolean'
744+
type: 'boolean',
745+
default: true
745746
},
746747
autoSelectFamilyAttemptTimeout: {
747748
type: 'uint'

src/encrypter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { callbackify } from 'util';
22

33
import { AutoEncrypter, type AutoEncryptionOptions } from './client-side-encryption/auto_encrypter';
4+
import { type ClientEncryptionSocketOptions } from './client-side-encryption/state_machine';
45
import { MONGO_CLIENT_EVENTS } from './constants';
56
import { getMongoDBClientEncryption } from './deps';
67
import { MongoInvalidArgumentError, MongoMissingDependencyError } from './error';
@@ -56,6 +57,15 @@ export class Encrypter {
5657
};
5758
}
5859

60+
const socketOptions: ClientEncryptionSocketOptions = {};
61+
if ('autoSelectFamily' in options) {
62+
socketOptions.autoSelectFamily = options.autoSelectFamily;
63+
}
64+
if ('autoSelectFamilyAttemptTimeout' in options) {
65+
socketOptions.autoSelectFamilyAttemptTimeout = options.autoSelectFamilyAttemptTimeout;
66+
}
67+
options.autoEncryption.socketOptions = socketOptions;
68+
5969
this.autoEncrypter = new AutoEncrypter(client, options.autoEncryption);
6070
}
6171

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export type {
248248
LocalKMSProviderConfiguration
249249
} from './client-side-encryption/providers/index';
250250
export type {
251+
ClientEncryptionSocketOptions,
251252
ClientEncryptionTlsOptions,
252253
CSFLEKMSTlsOptions,
253254
StateMachineExecutable

test/unit/client-side-encryption/client_encryption.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ import { Binary, BSON, deserialize } from '../../mongodb';
1919
const { EJSON } = BSON;
2020

2121
class MockClient {
22+
s: any;
23+
24+
constructor(options?: any) {
25+
this.s = { options: options || {} };
26+
}
2227
db(dbName) {
2328
return {
2429
async createCollection(name, options) {

test/unit/client-side-encryption/state_machine.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,30 @@ describe('StateMachine', function () {
148148
});
149149
});
150150

151+
context('when socket options are provided', function () {
152+
const stateMachine = new StateMachine({
153+
socketOptions: { autoSelectFamily: true, autoSelectFamilyAttemptTimeout: 300 }
154+
} as any);
155+
const request = new MockRequest(Buffer.from('foobar'), -1);
156+
let connectOptions;
157+
158+
it('passes them through to the socket', async function () {
159+
sandbox.stub(tls, 'connect').callsFake((options, callback) => {
160+
connectOptions = options;
161+
this.fakeSocket = new MockSocket(callback);
162+
return this.fakeSocket;
163+
});
164+
const kmsRequestPromise = stateMachine.kmsRequest(request);
165+
166+
await setTimeoutAsync(0);
167+
this.fakeSocket.emit('data', Buffer.alloc(0));
168+
169+
await kmsRequestPromise;
170+
expect(connectOptions.autoSelectFamily).to.equal(true);
171+
expect(connectOptions.autoSelectFamilyAttemptTimeout).to.equal(300);
172+
});
173+
});
174+
151175
context('when tls options are provided', function () {
152176
context('when the options are insecure', function () {
153177
[

0 commit comments

Comments
 (0)