Skip to content

Commit 63c5ffe

Browse files
Allow valid SRV hostnames with less than 3 parts
1 parent 098ae16 commit 63c5ffe

File tree

3 files changed

+331
-170
lines changed

3 files changed

+331
-170
lines changed

driver-core/src/main/com/mongodb/connection/ClusterSettings.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import static com.mongodb.assertions.Assertions.isTrueArgument;
3737
import static com.mongodb.assertions.Assertions.notNull;
3838
import static com.mongodb.internal.connection.ServerAddressHelper.createServerAddress;
39-
import static java.lang.String.format;
4039
import static java.util.Collections.singletonList;
4140
import static java.util.Collections.unmodifiableList;
4241
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -607,11 +606,6 @@ private ClusterSettings(final Builder builder) {
607606
if (builder.srvHost.contains(":")) {
608607
throw new IllegalArgumentException("The srvHost can not contain a host name that specifies a port");
609608
}
610-
611-
if (builder.srvHost.split("\\.").length < 3) {
612-
throw new IllegalArgumentException(format("An SRV host name '%s' was provided that does not contain at least three parts. "
613-
+ "It must contain a hostname, domain name and a top level domain.", builder.srvHost));
614-
}
615609
}
616610

617611
if (builder.hosts.size() > 1 && builder.requiredClusterType == ClusterType.STANDALONE) {
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
# Initial DNS Seedlist Discovery
2+
3+
- Status: Accepted
4+
- Minimum Server Version: N/A
5+
6+
______________________________________________________________________
7+
8+
## Abstract
9+
10+
Presently, seeding a driver with an initial list of ReplicaSet or MongoS addresses is somewhat cumbersome, requiring a
11+
comma-delimited list of host names to attempt connections to. A standardized answer to this problem exists in the form
12+
of SRV records, which allow administrators to configure a single SRV record to return a list of host names. Supporting
13+
this feature would assist our users by decreasing maintenance load, primarily by removing the need to maintain seed
14+
lists at an application level.
15+
16+
This specification builds on the [Connection String](../connection-string/connection-string-spec.md) specification. It
17+
adds a new protocol scheme and modifies how the
18+
[Host Information](../connection-string/connection-string-spec.md#host-information) is interpreted.
19+
20+
## META
21+
22+
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
23+
"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
24+
25+
## Specification
26+
27+
### Connection String Format
28+
29+
The connection string parser in the driver is extended with a new protocol `mongodb+srv` as a logical pre-processing
30+
step before it considers the connection string and SDAM specifications. In this protocol, the comma separated list of
31+
host names is replaced with a single host name. The format is:
32+
33+
```
34+
mongodb+srv://{hostname}/{options}
35+
```
36+
37+
`{options}` refers to the optional elements from the [Connection String](../connection-string/connection-string-spec.md)
38+
specification following the `Host Information`. This includes the `Auth database` and `Connection Options`.
39+
40+
For the purposes of this document, `{hostname}` will be divided using the following terminology. If an SRV `{hostname}`
41+
has:
42+
43+
1. Three or more `.` separated parts, then the left-most part is the `{subdomain}` and the remaining portion is the
44+
`{domainname}`.
45+
46+
- Examples:
47+
- `{hostname}` = `cluster_1.tests.mongodb.co.uk`
48+
49+
- `{subdomain}` = `cluster_1`
50+
- `{domainname}` = `tests.mongodb.co.uk`
51+
52+
- `{hostname}` = `hosts_34.example.com`
53+
54+
- `{subdomain}` = `hosts_34`
55+
- `{domainname}` = `example.com`
56+
57+
2. One or two `.` separated part(s), then the `{hostname}` is equivalent to the `{domainname}`, and there is no
58+
subdomain.
59+
60+
- Examples:
61+
- `{hostname}` = `{domainname}` = `localhost`
62+
- `{hostname}` = `{domainname}` = `mongodb.local`
63+
64+
Only `{domainname}` is used during SRV record verification and `{subdomain}` is ignored.
65+
66+
### MongoClient Configuration
67+
68+
#### srvMaxHosts
69+
70+
This option is used to limit the number of mongos connections that may be created for sharded topologies. This option
71+
limits the number of SRV records used to populate the seedlist during initial discovery, as well as the number of
72+
additional hosts that may be added during
73+
[SRV polling](../polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md). This option
74+
requires a non-negative integer and defaults to zero (i.e. no limit). This option MUST only be configurable at the level
75+
of a `MongoClient`.
76+
77+
#### srvServiceName
78+
79+
This option specifies a valid SRV service name according to
80+
[RFC 6335](https://datatracker.ietf.org/doc/html/rfc6335#section-5.1), with the exception that it may exceed 15
81+
characters as long as the 63rd (62nd with prepended underscore) character DNS query limit is not surpassed. This option
82+
requires a string value and defaults to "mongodb". This option MUST only be configurable at the level of a
83+
`MongoClient`.
84+
85+
#### URI Validation
86+
87+
The driver MUST report an error if either the `srvServiceName` or `srvMaxHosts` URI options are specified with a non-SRV
88+
URI (i.e. scheme other than `mongodb+srv`). The driver MUST allow specifying the `srvServiceName` and `srvMaxHosts` URI
89+
options with an SRV URI (i.e. `mongodb+srv` scheme).
90+
91+
If `srvMaxHosts` is a positive integer, the driver MUST throw an error in the following cases:
92+
93+
- The connection string contains a `replicaSet` option.
94+
- The connection string contains a `loadBalanced` option with a value of `true`.
95+
96+
When validating URI options, the driver MUST first do the SRV and TXT lookup and then perform the validation. For
97+
drivers that do SRV lookup asynchronously this may result in a `MongoClient` being instantiated but erroring later
98+
during operation execution.
99+
100+
### Seedlist Discovery
101+
102+
#### Validation Before Querying DNS
103+
104+
It is an error to specify a port in a connection string with the `mongodb+srv` protocol, and the driver MUST raise a
105+
parse error and MUST NOT do DNS resolution or contact hosts.
106+
107+
It is an error to specify more than one host name in a connection string with the `mongodb+srv` protocol, and the driver
108+
MUST raise a parse error and MUST NOT do DNS resolution or contact hosts.
109+
110+
If `mongodb+srv` is used, a driver MUST implicitly also enable TLS. Clients can turn this off by passing `tls=false` in
111+
either the Connection String, or options passed in as parameters in code to the MongoClient constructor (or equivalent
112+
API for each driver), but not through a TXT record (discussed in a later section).
113+
114+
#### Querying DNS
115+
116+
In this preprocessing step, the driver will query the DNS server for SRV records on the hostname, prefixed with the SRV
117+
service name and protocol. The SRV service name is provided in the `srvServiceName` URI option and defaults to
118+
`mongodb`. The protocol is always `tcp`. After prefixing, the URI should look like: `_{srvServiceName}._tcp.{hostname}`.
119+
This DNS query is expected to respond with one or more SRV records.
120+
121+
The priority and weight fields in returned SRV records MUST be ignored.
122+
123+
If the DNS result returns no SRV records, or no records at all, or a DNS error happens, an error MUST be raised
124+
indicating that the URI could not be used to find hostnames. The error SHALL include the reason why they could not be
125+
found.
126+
127+
A driver MUST verify that the host names returned through SRV records share the original SRV's `{domainname}`. In
128+
addition, SRV records with fewer than three `.` separated parts, the returned hostname MUST have at least one more
129+
domain level than the SRV record hostname. Drivers MUST raise an error and MUST NOT initiate a connection to any
130+
returned hostname which does not fulfill these requirements.
131+
132+
The driver MUST NOT attempt to connect to any hosts until the DNS query has returned its results.
133+
134+
If `srvMaxHosts` is zero or greater than or equal to the number of hosts in the DNS result, the driver MUST populate the
135+
seedlist with all hosts.
136+
137+
If `srvMaxHosts` is greater than zero and less than the number of hosts in the DNS result, the driver MUST randomly
138+
select that many hosts and use them to populate the seedlist. Drivers SHOULD use the
139+
[Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) for
140+
randomization.
141+
142+
### Default Connection String Options
143+
144+
As a second preprocessing step, a Client MUST also query the DNS server for TXT records on `{hostname}`. If available, a
145+
TXT record provides default connection string options. The maximum length of a TXT record string is 255 characters, but
146+
there can be multiple strings per TXT record. A Client MUST support multiple TXT record strings and concatenate them as
147+
if they were one single string in the order they are defined in each TXT record. The order of multiple character strings
148+
in each TXT record is guaranteed. A Client MUST NOT allow multiple TXT records for the same host name and MUST raise an
149+
error when multiple TXT records are encountered.
150+
151+
Information returned within a TXT record is a simple URI string, just like the `{options}` in a connection string.
152+
153+
A Client MUST only support the `authSource`, `replicaSet`, and `loadBalanced` options through a TXT record, and MUST
154+
raise an error if any other option is encountered. Although using `mongodb+srv://` implicitly enables TLS, a Client MUST
155+
NOT allow the `ssl` option to be set through a TXT record option.
156+
157+
TXT records MAY be queried either before, in parallel, or after SRV records. Clients MUST query both the SRV and the TXT
158+
records before attempting any connection to MongoDB.
159+
160+
A Client MUST use options specified in the Connection String, and options passed in as parameters in code to the
161+
MongoClient constructor (or equivalent API for each driver), to override options provided through TXT records.
162+
163+
If any connection string option in a TXT record is incorrectly formatted, a Client MUST throw a parse exception.
164+
165+
This specification does not change the behaviour of handling unknown keys or incorrect values as is set out in the
166+
[Connection String spec](../connection-string/connection-string-spec.md#defining-connection-options). Unknown keys or
167+
incorrect values in default options specified through TXT records MUST be handled in the same way as unknown keys or
168+
incorrect values directly specified through a Connection String. For example, if a driver that does not support the
169+
`authSource` option finds `authSource=db` in a TXT record, it MUST handle the unknown option according to the rules in
170+
the Connection String spec.
171+
172+
### CNAME not supported
173+
174+
The use of DNS CNAME records is not supported. Clients MUST NOT check for a CNAME record on `{hostname}`. A system's DNS
175+
resolver could transparently handle CNAME, but because of how clients validate records returned from SRV queries, use of
176+
CNAME could break validation. Seedlist discovery therefore does not recommend or support the use of CNAME records in
177+
concert with SRV or TXT records.
178+
179+
## Example
180+
181+
If we provide the following URI:
182+
183+
```
184+
mongodb+srv://server.mongodb.com/
185+
```
186+
187+
The driver needs to request the DNS server for the SRV record `_mongodb._tcp.server.mongodb.com`. This could return:
188+
189+
```
190+
Record TTL Class Priority Weight Port Target
191+
_mongodb._tcp.server.mongodb.com. 86400 IN SRV 0 5 27317 mongodb1.mongodb.com.
192+
_mongodb._tcp.server.mongodb.com. 86400 IN SRV 0 5 27017 mongodb2.mongodb.com.
193+
```
194+
195+
The returned host names (`mongodb1.mongodb.com` and `mongodb2.mongodb.com`) must share the same domainname
196+
(`mongodb.com`) as the provided host name (`server.mongodb.com`).
197+
198+
The driver also needs to request the DNS server for the TXT records on `server.mongodb.com`. This could return:
199+
200+
```
201+
Record TTL Class Text
202+
server.mongodb.com. 86400 IN TXT "replicaSet=replProduction&authSource=authDB"
203+
```
204+
205+
From the DNS results, the driver now MUST treat the host information as if the following URI was used instead:
206+
207+
```
208+
mongodb://mongodb1.mongodb.com:27317,mongodb2.mongodb.com:27107/?ssl=true&replicaSet=replProduction&authSource=authDB
209+
```
210+
211+
If we provide the following URI with the same DNS (SRV and TXT) records:
212+
213+
```
214+
mongodb+srv://server.mongodb.com/?authSource=otherDB
215+
```
216+
217+
Then the default in the TXT record for `authSource` is not used as the value in the connection string overrides it. The
218+
Client MUST treat the host information as if the following URI was used instead:
219+
220+
```
221+
mongodb://mongodb1.mongodb.com:27317,mongodb2.mongodb.com:27107/?ssl=true&replicaSet=replProduction&authSource=otherDB
222+
```
223+
224+
## Test Plan
225+
226+
### Prose Tests
227+
228+
See README.md in the accompanying [test directory](tests/README.md).
229+
230+
### Spec Tests
231+
232+
See README.md in the accompanying [test directory](tests/README.md).
233+
234+
Additionally, see the `mongodb+srv` test `invalid-uris.yml` in the
235+
[Connection String Spec tests](../connection-string/tests/README.md).
236+
237+
## Motivation
238+
239+
Several of our users have asked for this through tickets:
240+
241+
- <https://jira.mongodb.org/browse/DRIVERS-201>
242+
- <https://jira.mongodb.org/browse/NODE-865>
243+
- <https://jira.mongodb.org/browse/CSHARP-536>
244+
245+
## Design Rationale
246+
247+
The design specifically calls for a pre-processing stage of the processing of connection URLs to minimize the impact on
248+
existing functionality.
249+
250+
## Justifications
251+
252+
### Why Are Multiple Key-Value Pairs Allowed in One TXT Record?
253+
254+
One could imagine an alternative design in which each TXT record would allow only one URI option. No `&` character would
255+
be allowed as a delimiter within TXT records.
256+
257+
In this spec we allow multiple key-value pairs within one TXT record, delimited by `&`, because it will be common for
258+
all options to fit in a single 255-character TXT record, and it is much more convenient to configure one record in this
259+
case than to configure several.
260+
261+
Secondly, in some cases the order in which options occur is important. For example, readPreferenceTags can appear both
262+
multiple times, and the order in which they appear is significant. Because DNS servers may return TXT records in any
263+
order, it is only possible to guarantee the order in which readPreferenceTags keys appear by having them in the same TXT
264+
record.
265+
266+
### Why Is There No Mention of UTF-8 Characters?
267+
268+
Although DNS TXT records allow any octet to exist in its value, many DNS providers do not allow non-ASCII characters to
269+
be configured. As it is unlikely that any option names or values in the connection string have non-ASCII characters, we
270+
left the behaviour of supporting UTF-8 characters as unspecified.
271+
272+
## Reference Implementation
273+
274+
None yet.
275+
276+
## Backwards Compatibility
277+
278+
There are no backwards compatibility concerns.
279+
280+
## Future Work
281+
282+
In the future we could consider using the priority and weight fields of the SRV records.
283+
284+
## ChangeLog
285+
286+
- 2024-09-24: Removed requirement for URI to have three '.' separated parts; these SRVs have stricter parent domain
287+
matching requirements for security. Create terminology section. Remove usage of term `{TLD}`. The `{hostname}` now
288+
refers to the entire hostname, not just the `{subdomain}`.
289+
290+
- 2024-03-06: Migrated from reStructuredText to Markdown.
291+
292+
- 2022-10-05: Revise spec front matter and reformat changelog.
293+
294+
- 2021-10-14: Add `srvMaxHosts` MongoClient option and restructure Seedlist Discovery section. Improve documentation for
295+
the `srvServiceName` MongoClient option and add a new URI Validation section.
296+
297+
- 2021-09-15: Clarify that service name only defaults to `mongodb`, and should be defined by the `srvServiceName` URI
298+
option.
299+
300+
- 2021-04-15: Adding in behaviour for load balancer mode.
301+
302+
- 2019-03-07: Clarify that CNAME is not supported
303+
304+
- 2018-02-08: Clarify that `{options}}` in the [Specification](#specification) section includes all the optional
305+
elements from the Connection String specification.
306+
307+
- 2017-11-21: Add clause that using `mongodb+srv://` implies enabling TLS. Add restriction that only `authSource` and
308+
`replicaSet` are allows in TXT records. Add restriction that only one TXT record is supported share the same parent
309+
domain name as the given host name.
310+
311+
- 2017-11-17: Add new rule that indicates that host names in returned SRV records MUST share the same parent domain name
312+
as the given host name. Remove language and tests for non-ASCII characters.
313+
314+
- 2017-11-07: Clarified that all parts of listable options such as readPreferenceTags are ignored if they are also
315+
present in options to the MongoClient constructor. Clarified which host names to use for SRV and TXT DNS queries.
316+
317+
- 2017-11-01: Clarified that individual TXT records can have multiple strings.
318+
319+
- 2017-10-31: Added a clause that specifying two host names with a `mongodb+srv://` URI is not allowed. Added a few more
320+
test cases.
321+
322+
- 2017-10-18: Removed prohibition of raising DNS related errors when parsing the URI.
323+
324+
- 2017-10-04: Removed from [Future Work](#future-work) the line about multiple MongoS discovery. The current
325+
specification already allows for it, as multiple host names which are all MongoS servers is already allowed under
326+
SDAM. And this specification does not modify SDAM. Added support for connection string options through TXT records.
327+
328+
- 2017-09-19: Clarify that host names in `mongodb+srv://` URLs work like normal host specifications.
329+
330+
- 2017-09-01: Updated test plan with YAML tests, and moved prose tests for URI parsing into invalid-uris.yml in the
331+
Connection String Spec tests.

0 commit comments

Comments
 (0)