diff --git a/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md b/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md index 92d4de9e77..fb15d0c10b 100644 --- a/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md +++ b/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md @@ -37,7 +37,8 @@ mongodb+srv://{hostname}/{options} `{options}` refers to the optional elements from the [Connection String](../connection-string/connection-string-spec.md) specification following the `Host Information`. This includes the `Auth database` and `Connection Options`. -For the purposes of this document, `{hostname}` will be divided using the following terminology. If an SRV `{hostname}` +For the purposes of this document, `{hostname}` will be divided using the following terminology. If +`srvAllowedHostsSuffix` has been configured, then that will act as the `{domainname}`. Otherwise, if an SRV `{hostname}` has: 1. Three or more `.` separated parts, then the left-most part is the `{subdomain}` and the remaining portion is the @@ -65,6 +66,29 @@ Only `{domainname}` is used during SRV record verification and `{subdomain}` is ### MongoClient Configuration +#### srvAllowedHostsSuffix + +This option is used to validate hosts. If present, its value MUST be treated as the `{domainname}` for +[DNS validation](#querying-dns) and +[SRV polling](../polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md). For example, +`srvAllowedHostsSuffix=.mydomain.net`. Drivers MUST apply the following normalization and validation to the value, in +this order: + +1. Any leading or trailing `.` MUST be stripped. For example, `srvAllowedHostsSuffix=.mydomain.net.` is treated as + `mydomain.net`. If the resulting stripped value is empty, an error MUST be raised. +2. The value MUST be converted to its A-label (Punycode) form, so that it is comparable against the A-label hostnames + returned by DNS. +3. The value MUST be normalized to lowercase using ASCII case folding. +4. The resulting value MUST contain at least two `.` separated labels. For example, `srvAllowedHostsSuffix=net` MUST + raise an error. +5. Drivers SHOULD raise an error if the resulting value is a public suffix, per the algorithm in + [Public Suffix List](../public-suffix-list/public-suffix-list.md). +6. A `.` MUST be prepended. For example, `srvAllowedHostsSuffix=mydomain.net` is treated as `.mydomain.net`. + +If this option is not present, the `{domainname}` MUST be inferred from the `{hostname}` (as described in +[Connection String Format](#connection-string-format)). This option MUST only be configurable at the level of a +`MongoClient`. + #### srvMaxHosts This option is used to limit the number of mongos connections that may be created for sharded topologies. This option @@ -84,9 +108,9 @@ requires a string value and defaults to "mongodb". This option MUST only be conf #### URI Validation -The driver MUST report an error if either the `srvServiceName` or `srvMaxHosts` URI options are specified with a non-SRV -URI (i.e. scheme other than `mongodb+srv`). The driver MUST allow specifying the `srvServiceName` and `srvMaxHosts` URI -options with an SRV URI (i.e. `mongodb+srv` scheme). +The driver MUST report an error if any of `srvServiceName`, `srvMaxHosts`, or `srvAllowedHostsSuffix` URI options are +specified with a non-SRV URI (i.e. scheme other than `mongodb+srv`). The driver MUST allow specifying the +`srvServiceName`, `srvMaxHosts`, and `srvAllowedHostsSuffix` URI options with an SRV URI (i.e. `mongodb+srv` scheme). If `srvMaxHosts` is a positive integer, the driver MUST throw an error in the following cases: @@ -124,10 +148,22 @@ If the DNS result returns no SRV records, or no records at all, or a DNS error h indicating that the URI could not be used to find hostnames. The error SHALL include the reason why they could not be found. +Before validating returned hostnames, drivers MUST normalize them as follows, in this order: + +1. Any trailing `.` MUST be stripped. For example, `host.mydomain.net.` becomes `host.mydomain.net`. +2. The hostname MUST be converted to its A-label (Punycode) form. +3. The hostname MUST be normalized to lowercase using ASCII case folding. + +The `{domainname}` that returned hostnames are validated against MUST have the same three normalizations applied, so +that neither trailing dots, case, nor Unicode/Punycode encoding can affect the comparison. When `{domainname}` comes +from `srvAllowedHostsSuffix`, steps 1-3 of [srvAllowedHostsSuffix](#srvallowedhostssuffix) already apply them. The +leading `.` that step 6 prepends makes the value a label-aligned suffix and is not part of this normalization. + A driver MUST verify that the host names returned through SRV records share the original SRV's `{domainname}`. In -addition, SRV records with fewer than three `.` separated parts, the returned hostname MUST have at least one more -domain level than the SRV record hostname. Drivers MUST raise an error and MUST NOT initiate a connection to any -returned hostname which does not fulfill these requirements. +addition, when `srvAllowedHostsSuffix` is not configured and the SRV record hostname has fewer than three `.` separated +parts, the returned hostname MUST have at least one more domain level than the SRV record hostname. Drivers MUST raise +an error and MUST NOT initiate a connection to any returned hostname which does not fulfill these requirements. This +additional requirement does not apply when `srvAllowedHostsSuffix` is configured. The driver MUST NOT attempt to connect to any hosts until the DNS query has returned its results. @@ -247,6 +283,39 @@ Several of our users have asked for this through tickets: The design specifically calls for a pre-processing stage of the processing of connection URLs to minimize the impact on existing functionality. +### Rationale for `srvAllowedHostsSuffix` + +By default, the parent domain that returned host names are checked against is inferred from the seed by treating its +leftmost label as the `{subdomain}`. A seed of `mongodb.mydomain.net` results in `mongodb1.us-east-1.mydomain.net` being +rejected, even when both are owned by the same user. `srvAllowedHostsSuffix` allows users to work around this limitation +by letting the user state the parent domain directly instead, admitting such nested names and hosts in a different +domain altogether. + +Note that nothing requires the `{hostname}` itself to end with the configured suffix. For example, +`mongodb+srv://cluster.example.org/?srvAllowedHostsSuffix=.hosts.example.net` is valid, even though +`cluster.example.org` does not end with `.hosts.example.net`. This is intentional: the option exists so the user can +state the allowed parent domain explicitly, rather than having it inferred from the connection string. Requiring the two +to match would restore that coupling, and would rule out arrangements such as a vanity alias in one organization's +domain resolving to hosts in a provider's domain. + +Because it replaces that default requirement, this option relaxes a DNS spoofing safeguard. An attacker able to forge +SRV responses is otherwise confined to host names under the seed's `{domainname}`; with the option set, they are +confined to host names under the configured suffix instead. The broader that suffix, the more hosts a forged response +can direct a driver to. + +Requiring that the value not be a public suffix bounds how broad it can get. It rules out values such as `.com` or +`.co.uk`, which would place no meaningful limit on a forged response. It does not, however, make a given configuration +safe: `.example.com` is not a public suffix, but it still admits every host in a large organization's domain. + +Users should be encouraged to configure the narrowest suffix that covers their deployment, and drivers should say so +wherever the option is documented. For a seed hostname of `cluster.test.internal.example.com`, prefer the second of +these: + +```text +mongodb+srv://cluster.test.internal.example.com/?srvAllowedHostsSuffix=.example.com +mongodb+srv://cluster.test.internal.example.com/?srvAllowedHostsSuffix=.internal.example.com +``` + ## Justifications ### Why Are Multiple Key-Value Pairs Allowed in One TXT Record? @@ -283,6 +352,11 @@ In the future we could consider using the priority and weight fields of the SRV ## ChangeLog +- 2026-09-03: Specify that host names returned through SRV records, and the `{domainname}` they are validated against, + are both normalized -- trailing dot stripped, converted to A-label form, ASCII lowercased -- before validation. + +- 2026-08-24: Add `srvAllowedHostsSuffix` MongoClient option. + - 2024-09-24: Removed requirement for URI to have three '.' separated parts; these SRVs have stricter parent domain matching requirements for security. Create terminology section. Remove usage of term `{TLD}`. The `{hostname}` now refers to the entire hostname, not just the `{subdomain}`. diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-case-insensitive.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-case-insensitive.json new file mode 100644 index 0000000000..3b1f67fec9 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-case-insensitive.json @@ -0,0 +1,10 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.BUILD.10GEN.CC", + "seeds": [ + "localhost.build.10gen.cc:27017" + ], + "options": { + "ssl": true + }, + "ping": false +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-case-insensitive.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-case-insensitive.yml new file mode 100644 index 0000000000..7957ba1f8c --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-case-insensitive.yml @@ -0,0 +1,8 @@ +# Tests that srvAllowedHostsSuffix comparison is case-insensitive (ASCII case folding per RFC 4343). +# The suffix .BUILD.10GEN.CC is uppercase but should match the returned host localhost.build.10gen.cc. +uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.BUILD.10GEN.CC" +seeds: + - localhost.build.10gen.cc:27017 +options: + ssl: true +ping: false diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-mismatch.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-mismatch.json new file mode 100644 index 0000000000..56e26524c4 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-mismatch.json @@ -0,0 +1,6 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=test.build.10gen.cc", + "seeds": [], + "hosts": [], + "error": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-mismatch.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-mismatch.yml new file mode 100644 index 0000000000..db6ee9811b --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-mismatch.yml @@ -0,0 +1,5 @@ +# DNS record for test12.test.build.10gen.cc returns localhost.build.10gen.cc which would not match test.build.10gen.cc +uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=test.build.10gen.cc" +seeds: [] +hosts: [] +error: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-period-only.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-period-only.json new file mode 100644 index 0000000000..f3689428c1 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-period-only.json @@ -0,0 +1,6 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=.", + "seeds": [], + "hosts": [], + "error": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-period-only.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-period-only.yml new file mode 100644 index 0000000000..bd6621e833 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-period-only.yml @@ -0,0 +1,7 @@ +# srvAllowedHostsSuffix MUST name at least one label. A value of "." is empty once the +# leading and trailing "." are stripped, so it names no domain at all and MUST raise an +# error rather than being treated as a suffix that every host matches. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=." +seeds: [] +hosts: [] +error: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.json index 30824585ac..d6d02fa51a 100644 --- a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.json @@ -10,8 +10,7 @@ "localhost:27019" ], "options": { - "ssl": true, - "srvAllowedHostsSuffix": "10gen.cc" + "ssl": true }, "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.yml index ecdb22b858..4e20a2d22f 100644 --- a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-not-public-suffix.yml @@ -11,5 +11,4 @@ hosts: - localhost:27019 options: ssl: true - srvAllowedHostsSuffix: "10gen.cc" ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-public-suffix.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-public-suffix.yml index cfdfb8c9ed..c2f246d189 100644 --- a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-public-suffix.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-psl-public-suffix.yml @@ -1,6 +1,4 @@ -# "cc" is an ordinary rule in the Public Suffix List, so it is itself a public suffix -# and must be rejected. The SRV hosts do end with "cc", so the host suffix check -# passes and the public suffix check is the only thing that can fail here. +# "cc" is an ordinary rule in the Public Suffix List and is only one label and therefore MUST be rejected. uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=cc" seeds: [] hosts: [] diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-tld-only.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-tld-only.json new file mode 100644 index 0000000000..12098dbf5e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-tld-only.json @@ -0,0 +1,6 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=.cc", + "seeds": [], + "hosts": [], + "error": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-tld-only.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-tld-only.yml new file mode 100644 index 0000000000..aeb9be44e6 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-tld-only.yml @@ -0,0 +1,6 @@ +# srvAllowedHostsSuffix SHOULD NOT be a public suffix. "cc" is an ordinary rule in the +# Public Suffix List, and isn't two or more `.` separated labels so `.cc` MUST raise an error. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=.cc" +seeds: [] +hosts: [] +error: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-trailing-dot.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-trailing-dot.json new file mode 100644 index 0000000000..9befc30060 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-trailing-dot.json @@ -0,0 +1,10 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.build.10gen.cc.", + "seeds": [ + "localhost.build.10gen.cc:27017" + ], + "options": { + "ssl": true + }, + "ping": false +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-trailing-dot.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-trailing-dot.yml new file mode 100644 index 0000000000..4da9917696 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-trailing-dot.yml @@ -0,0 +1,8 @@ +# A trailing dot in srvAllowedHostsSuffix must be stripped before comparison, +# so ".build.10gen.cc." is equivalent to ".build.10gen.cc". +uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.build.10gen.cc." +seeds: + - localhost.build.10gen.cc:27017 +options: + ssl: true +ping: false diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-with_dot.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-with_dot.json new file mode 100644 index 0000000000..724f813918 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-with_dot.json @@ -0,0 +1,10 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.build.10gen.cc", + "seeds": [ + "localhost.build.10gen.cc:27017" + ], + "options": { + "ssl": true + }, + "ping": false +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-with_dot.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-with_dot.yml new file mode 100644 index 0000000000..bb73a50279 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-with_dot.yml @@ -0,0 +1,8 @@ +# Also tests trailing dot normalization: DNS returns targets with a trailing dot +# (localhost.build.10gen.cc.) which must be stripped before suffix comparison. +uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.build.10gen.cc" +seeds: + - localhost.build.10gen.cc:27017 +options: + ssl: true +ping: false diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_fail.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_fail.json new file mode 100644 index 0000000000..b7544b66f2 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_fail.json @@ -0,0 +1,6 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=uild.10gen.cc", + "seeds": [], + "hosts": [], + "error": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_fail.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_fail.yml new file mode 100644 index 0000000000..57fab7a570 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_fail.yml @@ -0,0 +1,5 @@ +# dot should be prepended to `srvAllowedHostsSuffix` causing the host to be .uild.10gen.cc which does not match any available DNS records +uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=uild.10gen.cc" +seeds: [] +hosts: [] +error: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_pass.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_pass.json new file mode 100644 index 0000000000..ea1f5c8918 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_pass.json @@ -0,0 +1,10 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=build.10gen.cc", + "seeds": [ + "localhost.build.10gen.cc:27017" + ], + "options": { + "ssl": true + }, + "ping": false +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_pass.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_pass.yml new file mode 100644 index 0000000000..feca05f8c8 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvAllowedHostsSuffix-without_dot_pass.yml @@ -0,0 +1,6 @@ +uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=build.10gen.cc" +seeds: + - localhost.build.10gen.cc:27017 +options: + ssl: true +ping: false diff --git a/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md b/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md index 2805a9c23c..8166c9160c 100644 --- a/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md +++ b/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md @@ -53,9 +53,14 @@ initial seedlist discovery. Periodic scan MUST follow these rules: and protocol. The SRV service name is provided in the [srvServiceName](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvservicename) URI option and defaults to `mongodb`. The protocol is always `tcp`. After prefixing, the URI should look like: - `_{srvServiceName}._tcp.{hostname}.{domainname}`. -- A driver MUST verify that the host names returned through SRV records have the same parent `{domainname}`. When this - verification fails, a driver: + `_{srvServiceName}._tcp.{hostname}.{domainname}`. Here `{hostname}.{domainname}` is the host from the connection + string, and is not affected by + [srvAllowedHostsSuffix](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvallowedhostssuffix): + that option changes which parent domain returned host names are verified against, not which records are queried. +- A driver MUST verify that the host names returned through SRV records have the same parent `{domainname}`. When + [srvAllowedHostsSuffix](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvallowedhostssuffix) + is configured, its value is the parent domain used for this verification instead of the `{domainname}` derived from + the connection string. When this verification fails, a driver: - MUST NOT add such a non-compliant host name to the topology - MUST NOT raise an error - SHOULD log the non-compliance, including the host name @@ -165,6 +170,8 @@ No future work is expected. ## Changelog +- 2026-09-03: Account for the `srvAllowedHostsSuffix` MongoClient option when verifying returned host names. + - 2024-08-22: Migrated from reStructuredText to Markdown. - 2022-10-05: Revise spec front matter and reformat changelog. diff --git a/source/public-suffix-list/public-suffix-list.md b/source/public-suffix-list/public-suffix-list.md index d0a14679f4..21875b62e8 100644 --- a/source/public-suffix-list/public-suffix-list.md +++ b/source/public-suffix-list/public-suffix-list.md @@ -84,6 +84,10 @@ vendored file MUST therefore convert one side before comparing, so that a Punyco `xn--` labels) and the Unicode rules it is compared against are in the same form. Comparing the two forms directly will fail to match rules that should match. +Note: The leading `*` and `!` markers in the PSL are not part of a label and MUST NOT be included in that conversion. A +driver MUST strip them before converting a rule and reapply them after, so that a rule such as `!公司.cn` converts to +`!xn--55qx5d.cn` rather than being mangled. + ### Determining the public suffix To determine the public suffix of a domain, a driver using the vendored file MUST follow the @@ -187,4 +191,7 @@ notice does not apply to the rest of this repository. ## Changelog +- 2026-09-03: Clarify that markers shouldn't be converted to Punycode and add a prose test covering internationalized + (Punycode vs. Unicode) rules. + - 2026-08-11: Vendor the Public Suffix List and add a script to sync it. diff --git a/source/public-suffix-list/tests/README.md b/source/public-suffix-list/tests/README.md index f4b16b8996..1a9b16ebcd 100644 --- a/source/public-suffix-list/tests/README.md +++ b/source/public-suffix-list/tests/README.md @@ -36,13 +36,18 @@ This test utilizes the rule `!www.ck` in the PSL, which overrides the `*.ck` rul When no rule matches, the prevailing rule is `*` and the rightmost label alone is the public suffix. Assert that `is_public_suffix("nosuchtld") -> true` and `is_public_suffix("foo.nosuchtld") -> false`. +### 6. An internationalized rule + +This test utilizes the rules `рф` and `公司.cn` in the PSL, which are stored as Unicode while the hostnames being compared +are Punycode-encoded. Assert that `is_public_suffix("xn--p1ai") -> true`, +`is_public_suffix("example.xn--p1ai") -> false`, and `is_public_suffix("xn--55qx5d.cn") -> true`. + ## Connection String Tests The `srvAllowedHostsSuffix-psl-*` tests in the [Initial DNS Seedlist Discovery tests](https://github.com/mongodb/specifications/tree/master/source/initial-dns-seedlist-discovery/tests/replica-set) cover the two cases that are observable through a connection string: a suffix that is a public suffix (`cc`) is -rejected, and one that is not (`10gen.cc`) is accepted. Both use a suffix the test SRV hosts end with, so the host -suffix check passes and the public suffix check is the only thing that can change the outcome. +rejected, and one that is not (`10gen.cc`) is accepted. They live with the seedlist discovery tests because `srvAllowedHostsSuffix` is a connection string option, so a driver should implement the parsing of the PSL and the uri option together. diff --git a/source/uri-options/tests/srv-options.json b/source/uri-options/tests/srv-options.json index 0670612c0d..75bb7d1c5f 100644 --- a/source/uri-options/tests/srv-options.json +++ b/source/uri-options/tests/srv-options.json @@ -111,6 +111,15 @@ "loadBalanced": true, "srvMaxHosts": 0 } + }, + { + "description": "Non-SRV URI with srvAllowedHostsSuffix", + "uri": "mongodb://example.com/?srvAllowedHostsSuffix=.mongodb.net", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null } ] } diff --git a/source/uri-options/tests/srv-options.yml b/source/uri-options/tests/srv-options.yml index 991749b0ef..f8bab2d2b9 100644 --- a/source/uri-options/tests/srv-options.yml +++ b/source/uri-options/tests/srv-options.yml @@ -87,3 +87,10 @@ tests: options: loadBalanced: true srvMaxHosts: 0 + - description: "Non-SRV URI with srvAllowedHostsSuffix" + uri: "mongodb://example.com/?srvAllowedHostsSuffix=.mongodb.net" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/uri-options.md b/source/uri-options/uri-options.md index ca1a1b7cfc..ef052493c2 100644 --- a/source/uri-options/uri-options.md +++ b/source/uri-options/uri-options.md @@ -43,9 +43,9 @@ The driver MUST report an error if the `directConnection=true` URI option is spe The driver MUST report an error if the `directConnection=true` URI option is specified with an SRV URI, because the URI may resolve to multiple hosts. The driver MUST allow specifying `directConnection=false` URI option with an SRV URI. -### srvServiceName and srvMaxHosts URI options +### srvServiceName, srvMaxHosts, and srvAllowedHostsSuffix URI options -For URI option validation pertaining to `srvServiceName` and `srvMaxHosts`, please see the +For URI option validation pertaining to `srvServiceName`, `srvMaxHosts`, and `srvAllowedHostsSuffix`, please see the [Initial DNS Seedlist Discovery spec](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#uri-validation) for details. @@ -104,6 +104,7 @@ to URI options apply here. | serverSelectionTimeoutMS | positive integer; a driver may also accept 0 to be used for a special case, provided that it documents the meaning | defined in [server selection spec](../server-selection/server-selection.md#serverselectiontimeoutms) | no | A timeout in milliseconds to block for server selection before raising an error | | serverSelectionTryOnce | "true" or "false" | defined in [server selection spec](../server-selection/server-selection.md#serverselectiontryonce) | required for single-threaded drivers | Scan the topology only once after a server selection failure instead of repeatedly until the server selection times out | | socketTimeoutMS | non-negative integer; 0 means no timeout | no timeout | no | NOTE: This option is deprecated in favor of [timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms)

Amount of time spent attempting to send or receive on a socket before timing out; note that this only applies to application operations, not SDAM. | +| srvAllowedHostsSuffix | a valid DNS hostname suffix (e.g. ".mydomain.net") | none; domain is inferred from the SRV hostname | no | A hostname suffix used to validate hosts returned via SRV lookup, replacing the domain inferred from the SRV hostname. Defined in the [Initial DNS Seedlist Discovery spec](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvallowedhostssuffix). | | srvMaxHosts | non-negative integer; 0 means no maximum | defined in the [Initial DNS Seedlist Discovery spec](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvmaxhosts) | no | The maximum number of SRV results to randomly select when initially populating the seedlist or, during SRV polling, adding new hosts to the topology. | | srvServiceName | a valid SRV service name according to [RFC 6335](https://datatracker.ietf.org/doc/html/rfc6335#section-5.1) | "mongodb" | no | the service name to use for SRV lookup in [initial DNS seedlist discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvservicename) and [SRV polling](../polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md) | | ssl | "true" or "false" | same as "tls" | no | alias of "tls"; required to ensure that Atlas connection strings continue to work | @@ -184,6 +185,8 @@ changes. ## Changelog +- 2026-09-03: Add `srvAllowedHostsSuffix` option. + - 2026-06-17: Remove pre-4.2 version references. - 2024-05-08: Migrated from reStructuredText to Markdown.