Skip to content

Commit dda2da1

Browse files
DRIVERS 2922 Allow valid SRV hostnames with less than 3 parts (#2972)
* Original solution * Updating tests and implementation to allow 3 parts to SRV string * Fixing tests * updating tests * typo * Making changes according to copilot autoreview * reverting changes to invalid-uris * responding to comments - rewriting tests and moving logic
1 parent ed49a89 commit dda2da1

File tree

4 files changed

+90
-40
lines changed

4 files changed

+90
-40
lines changed

lib/mongo/srv/result.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,27 @@ def normalize_hostname(host)
110110
# A hostname's domain name consists of each of the '.' delineated
111111
# parts after the first. For example, the hostname 'foo.bar.baz'
112112
# has the domain name 'bar.baz'.
113+
#
114+
# If the hostname has less than three parts, its domain name is the hostname itself.
113115
#
114116
# @param [ String ] record_host The host of the SRV record.
115117
#
116118
# @raise [ Mongo::Error::MismatchedDomain ] If the record's domain name doesn't match that of
117119
# the hostname.
118120
def validate_same_origin!(record_host)
119-
domain_name ||= query_hostname.split('.')[1..-1]
120-
host_parts = record_host.split('.')
121+
srv_host_domain = query_hostname.split('.')
122+
srv_is_less_than_three_parts = srv_host_domain.length < 3
123+
unless srv_is_less_than_three_parts
124+
srv_host_domain = srv_host_domain[1..-1]
125+
end
126+
record_host_parts = record_host.split('.')
127+
128+
if (srv_is_less_than_three_parts && record_host_parts.length <= srv_host_domain.length)
129+
raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, srv_host_domain])
130+
end
121131

122-
unless (host_parts.size > domain_name.size) && (domain_name == host_parts[-domain_name.length..-1])
123-
raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, domain_name])
132+
unless (record_host_parts.size > srv_host_domain.size) && (srv_host_domain == record_host_parts[-srv_host_domain.size..-1])
133+
raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, srv_host_domain])
124134
end
125135
end
126136
end

lib/mongo/uri/srv_protocol.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,7 @@ def parse!(remaining)
168168
# The hostname cannot include a port.
169169
#
170170
# The hostname must not begin with a dot, end with a dot, or have
171-
# consecutive dots. The hostname must have a minimum of 3 total
172-
# components (foo.bar.tld).
171+
# consecutive dots. The hostname must have a minimum of 1 component
173172
#
174173
# Raises Error::InvalidURI if validation fails.
175174
def validate_srv_hostname(hostname)
@@ -185,8 +184,8 @@ def validate_srv_hostname(hostname)
185184
if parts.any?(&:empty?)
186185
raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}")
187186
end
188-
if parts.length < 3
189-
raise_invalid_error!("Hostname must have a minimum of 3 components (foo.bar.tld): #{hostname}")
187+
if parts.length < 1
188+
raise_invalid_error!("Hostname cannot be empty: #{hostname}")
190189
end
191190
end
192191

spec/mongo/srv/result_spec.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,75 @@
2323
expect(result.address_strs).to eq(['foo.bar.com:42'])
2424
end
2525
end
26+
27+
example_srv_names = ['i-love-rb', 'i-love-rb.mongodb', 'i-love-ruby.mongodb.io'];
28+
example_host_names = [
29+
'rb-00.i-love-rb',
30+
'rb-00.i-love-rb.mongodb',
31+
'i-love-ruby-00.mongodb.io'
32+
];
33+
example_host_names_that_do_not_match_parent = [
34+
'rb-00.i-love-rb-a-little',
35+
'rb-00.i-love-rb-a-little.mongodb',
36+
'i-love-ruby-00.evil-mongodb.io'
37+
];
38+
39+
(0..2).each do |i|
40+
context "when srvName has #{i+1} part#{i != 0 ? 's' : ''}" do
41+
let(:srv_name) { example_srv_names[i] }
42+
let(:host_name) { example_host_names[i] }
43+
let(:mismatched_host_name) { example_host_names_that_do_not_match_parent[i] }
44+
context 'when address does not match parent domain' do
45+
let(:record) do
46+
double('record').tap do |record|
47+
allow(record).to receive(:target).and_return(mismatched_host_name)
48+
allow(record).to receive(:port).and_return(42)
49+
allow(record).to receive(:ttl).and_return(1)
50+
end
51+
end
52+
it 'raises MismatchedDomain error' do
53+
expect {
54+
result = described_class.new(srv_name)
55+
result.add_record(record)
56+
}.to raise_error(Mongo::Error::MismatchedDomain)
57+
end
58+
end
59+
60+
context 'when address matches parent domain' do
61+
let(:record) do
62+
double('record').tap do |record|
63+
allow(record).to receive(:target).and_return(host_name)
64+
allow(record).to receive(:port).and_return(42)
65+
allow(record).to receive(:ttl).and_return(1)
66+
end
67+
end
68+
it 'adds the record' do
69+
result = described_class.new(srv_name)
70+
result.add_record(record)
71+
72+
expect(result.address_strs).to eq([host_name + ':42'])
73+
end
74+
end
75+
76+
if i < 2
77+
context 'when the address is less than 3 parts' do
78+
let(:record) do
79+
double('record').tap do |record|
80+
allow(record).to receive(:target).and_return(srv_name)
81+
allow(record).to receive(:port).and_return(42)
82+
allow(record).to receive(:ttl).and_return(1)
83+
end
84+
end
85+
it 'does not accept address if it does not contain an extra domain level' do
86+
expect {
87+
result = described_class.new(srv_name)
88+
result.add_record(record)
89+
}.to raise_error(Mongo::Error::MismatchedDomain)
90+
end
91+
end
92+
end
93+
end
94+
end
2695
end
2796

2897
describe '#normalize_hostname' do

spec/mongo/uri/srv_protocol_spec.rb

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,6 @@
5656
end
5757
end
5858

59-
context 'when the host in URI does not have {hostname}, {domainname} and {tld}' do
60-
61-
let(:string) { "#{scheme}#{hosts}" }
62-
let(:hosts) { '10gen.cc/' }
63-
64-
it 'raises an error' do
65-
expect { uri }.to raise_error(Mongo::Error::InvalidURI)
66-
end
67-
end
68-
6959
context 'when the {tld} is empty' do
7060

7161
let(:string) { "#{scheme}#{hosts}" }
@@ -220,24 +210,6 @@
220210
expect { uri }.to raise_error(Mongo::Error::InvalidURI)
221211
end
222212
end
223-
224-
context 'mongodb+srv://example.com?w=1' do
225-
226-
let(:string) { "#{scheme}example.com?w=1" }
227-
228-
it 'raises an error' do
229-
expect { uri }.to raise_error(Mongo::Error::InvalidURI)
230-
end
231-
end
232-
233-
context 'mongodb+srv://example.com/?w' do
234-
235-
let(:string) { "#{scheme}example.com/?w" }
236-
237-
it 'raises an error' do
238-
expect { uri }.to raise_error(Mongo::Error::InvalidURI)
239-
end
240-
end
241213
end
242214

243215
describe 'valid uris' do
@@ -1302,8 +1274,8 @@
13021274
'a'
13031275
end
13041276

1305-
it 'raises an error' do
1306-
expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1277+
it 'does not raise an error' do
1278+
expect { validate }.to_not raise_error
13071279
end
13081280
end
13091281

@@ -1313,8 +1285,8 @@
13131285
'a.b'
13141286
end
13151287

1316-
it 'raises an error' do
1317-
expect { validate }.to raise_error(Mongo::Error::InvalidURI)
1288+
it 'validates the hostname' do
1289+
expect { validate }.not_to raise_error
13181290
end
13191291
end
13201292

0 commit comments

Comments
 (0)