-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(NODE-6289): allow valid srv hostnames with less than 3 parts #4197
Open
aditi-khare-mongoDB
wants to merge
21
commits into
main
Choose a base branch
from
uri-validate-less
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+368
−50
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
73ca011
drivers-2922 downstream changes first pass
aditi-khare-mongoDB ad151f7
temp commit
aditi-khare-mongoDB 15dc8ee
added more prose tests
aditi-khare-mongoDB 586f7c0
wording fix
aditi-khare-mongoDB 36b0b49
Merge branch 'main' into uri-validate-less
aditi-khare-mongoDB bea037e
remove stray comment
aditi-khare-mongoDB ae79ac5
fix up
aditi-khare-mongoDB b2f843e
add back in comment
aditi-khare-mongoDB 154e2ad
fix failing unit tests
aditi-khare-mongoDB 1059175
Merge branch 'main' into uri-validate-less
aditi-khare-mongoDB d1209df
Merge branch 'main' into uri-validate-less
aditi-khare-mongoDB 9dd438e
requested changes 1
aditi-khare-mongoDB f680380
requested changes + review from drivers ticket updates
aditi-khare-mongoDB 4d3efd8
fix failing tests
aditi-khare-mongoDB 062c139
fix failing tests 2
aditi-khare-mongoDB a3b489c
Merge branch 'main' into uri-validate-less
aditi-khare-mongoDB 7895835
await close
aditi-khare-mongoDB 4879b78
Merge branch 'main' into uri-validate-less
aditi-khare-mongoDB 79f41e4
ready for rereivew
aditi-khare-mongoDB eafaf61
ready for rereivew
aditi-khare-mongoDB 1de28de
Merge branch 'main' into uri-validate-less
aditi-khare-mongoDB File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
255 changes: 255 additions & 0 deletions
255
test/integration/initial-dns-seedlist-discovery/initial_dns_seedlist_discovery.prose.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
import { expect } from 'chai'; | ||
import * as dns from 'dns'; | ||
import * as sinon from 'sinon'; | ||
|
||
import { MongoAPIError, Server, ServerDescription, Topology } from '../../mongodb'; | ||
import { topologyWithPlaceholderClient } from '../../tools/utils'; | ||
|
||
describe('Initial DNS Seedlist Discovery (Prose Tests)', () => { | ||
context('1. When running validation on an SRV string before DNS resolution', function () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For easy audit purposes (e.g. in evg), we should match the number and title of the prose test in the context block and capture the full instructions text in comments. |
||
let client; | ||
|
||
beforeEach(async function () { | ||
// this fn stubs DNS resolution to always pass - so we are only checking pre-DNS validation | ||
|
||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'resolved.mongo.localhost', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
|
||
sinon.stub(dns.promises, 'resolveTxt').callsFake(async () => { | ||
throw { code: 'ENODATA' }; | ||
}); | ||
|
||
sinon.stub(Topology.prototype, 'selectServer').callsFake(async () => { | ||
return new Server( | ||
topologyWithPlaceholderClient([], {} as any), | ||
new ServerDescription('a:1'), | ||
{} as any | ||
); | ||
}); | ||
}); | ||
|
||
afterEach(async function () { | ||
sinon.restore(); | ||
await client.close(); | ||
}); | ||
|
||
it('does not error on an SRV because it has one domain level', async function () { | ||
client = await this.configuration.newClient('mongodb+srv://localhost', {}); | ||
await client.connect(); | ||
}); | ||
|
||
it('does not error on an SRV because it has two domain levels', async function () { | ||
client = await this.configuration.newClient('mongodb+srv://mongo.localhost', {}); | ||
await client.connect(); | ||
}); | ||
}); | ||
|
||
context( | ||
'2. When given a host from DNS resolution that does NOT end with the original SRVs domain name', | ||
function () { | ||
beforeEach(async function () { | ||
sinon.stub(dns.promises, 'resolveTxt').callsFake(async () => { | ||
throw { code: 'ENODATA' }; | ||
}); | ||
}); | ||
|
||
afterEach(async function () { | ||
sinon.restore(); | ||
}); | ||
|
||
it('an SRV with one domain level causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'localhost.mongodb', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://localhost', {}) | ||
.connect() | ||
.catch((e: any) => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal('Server record does not share hostname with parent URI'); | ||
}); | ||
|
||
it('an SRV with two domain levels causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'test_1.evil.local', // this string only ends with part of the domain, not all of it! | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://mongo.local', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal('Server record does not share hostname with parent URI'); | ||
}); | ||
|
||
it('an SRV with three or more domain levels causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'blogs.evil.com', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://blogs.mongodb.com', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal('Server record does not share hostname with parent URI'); | ||
}); | ||
} | ||
); | ||
|
||
context( | ||
'3. When given a host from DNS resolution that is identical to the original SRVs hostname', | ||
function () { | ||
beforeEach(async function () { | ||
sinon.stub(dns.promises, 'resolveTxt').callsFake(async () => { | ||
throw { code: 'ENODATA' }; | ||
}); | ||
}); | ||
|
||
afterEach(async function () { | ||
sinon.restore(); | ||
}); | ||
|
||
it('an SRV with one domain level causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'localhost', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://localhost', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal( | ||
'Server record does not have at least one more domain level than parent URI' | ||
); | ||
}); | ||
|
||
it('an SRV with two domain levels causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'mongo.local', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://mongo.local', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal( | ||
'Server record does not have at least one more domain level than parent URI' | ||
); | ||
}); | ||
} | ||
); | ||
|
||
context( | ||
'4. When given a returned address that does NOT share the domain name of the SRV record because its missing a `.`', | ||
function () { | ||
beforeEach(async function () { | ||
sinon.stub(dns.promises, 'resolveTxt').callsFake(async () => { | ||
throw { code: 'ENODATA' }; | ||
}); | ||
}); | ||
|
||
afterEach(async function () { | ||
sinon.restore(); | ||
}); | ||
|
||
it('an SRV with one domain level causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'test_1.cluster_1localhost', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://localhost', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal('Server record does not share hostname with parent URI'); | ||
}); | ||
|
||
it('an SRV with two domain levels causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'test_1.my_hostmongo.local', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://mongo.local', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal('Server record does not share hostname with parent URI'); | ||
}); | ||
|
||
it('an SRV with three domain levels causes a runtime error', async function () { | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
return [ | ||
{ | ||
name: 'cluster.testmongodb.com', | ||
port: 27017, | ||
weight: 0, | ||
priority: 0 | ||
} | ||
]; | ||
}); | ||
const err = await this.configuration | ||
.newClient('mongodb+srv://blogs.mongodb.com', {}) | ||
.connect() | ||
.catch(e => e); | ||
expect(err).to.be.instanceOf(MongoAPIError); | ||
expect(err.message).to.equal('Server record does not share hostname with parent URI'); | ||
}); | ||
} | ||
); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find a kickoff document for this work, so I am not sure if this is something that has already been discussed, but how frequently is this function invoked? Is it in the hot path for anything? This construction, while cleaner, is less performant (since try catch is more expensive than a simple if/then, and the additional function call for the no-op may or may not get optimized).