[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1107064: unblock: mongo-c-driver/1.30.4-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Please unblock package mongo-c-driver

[ Reason ]
I uploaded mongo-c-driver/1.30.4-1 on 2025-05-08, believing that under
the soft freeze policy in effect at the time that the package would
automatically migrate after 10 days without any intervention required.
However, it seems that when the hard freeze was announced on 2025-05-18
that mongo-c-driver/1.30.4-1 was retroactively blocked. (My recollection
from previous releases was that packages are treated based on the freeze
policy in effect at the time of upload, hence why I thought that the
package would migrate under the soft freeze policy.)

This version is a new upstream point release which fixes two minor
issues. One issue relates only to non-Linux platforms, and the other is
related to conformance with upstream's protocol specification. This
latter change is the only change which would produce observable
differences between mongo-c-driver 1.30.3-1 and 1.30.4-1 as built for
Debian. (The specific protocol conformance changes to which I am
referring are in src/libmongoc/src/mongoc/mongoc-bulkwrite.c for the
functional changes and src/libmongoc/tests/test-mongoc-bulkwrite.c and
src/libmongoc/tests/test-mongoc-crud.c for the associated unit and
integration tests, as seen in the attached debdiff.)

[ Impact ]
Unblocking this package ensures that users have access to a more
spec-conformant mongo-c-driver package and it will allow the migration
of syslog-ng, which was uploaded to unstable and built against this
version of mongo-c-driver. Note that the syslog-ng migration is needed
because it fixes a security bug. (This is noted in #1106898.)

[ Tests ]
The upstream development process includes multiple peer code reviews, as
well as extensive CI across a great many hardware architectures and OS
platforms. The functional changes in this particular version of
mongo-c-driver were also accompanied buy specific unit and integration
tests (as noted above).

[ Risks ]
This is a very low risk change. Upstream has a rather conservative
policy when it comes to changes in their point releases, and these
changes have undergone extensive review and testing.

[ Checklist ]
  [?] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
I am not sure about the "all changes are documented in the d/changelog"
bit, as I uploaded while under the impression that the package would not
end up being blocked. As a result, the changelog entry is simply "New
upstream release". I hope that the Release Team is willing to overlook
that minor issue and grant the unblock request.

unblock mongo-c-driver/1.30.4-1


-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEEIYZ1DR4ae5UL01q7ldFmTdL1kUIFAmg7cMcACgkQldFmTdL1
kUK59w/+KPWfnQof0EMhGQDlzBcbXKXIvbkMPXlwzgsWFYDyvPTuncR8DVNA7fbe
91hJvtCbmESMmgEYRLy4xHv8zbR+gn1fCN9AuGwinlm90R5GCxD6RhUzfbVJqW5y
d1YfR56zTo0hZkLrNPfWjva8Drt/LfC8s5WzjYfsDzBRpQ8EunZzq6FGCQt2iCcQ
A+g9sTO8glqlrO7YiOZnk15caiOmztRc8LfEcBF6H2ZrI8clZP0pb9e/WD2Fwcdi
nsVlqwGq5ab4JOMT4W53EqeWnc6TlKddicYa/kr3JkvtLqCiYtG1LFyGwtB6P1ii
dwzE9NiRg+AHpXcWwF4ZMn6ilhoqoP4hGXKNHYXahkVLSP7UthWUAH0KsaLnMhyH
xbFDTZ3o1DXwQZR742JORXBaEXDapgaWGPhUCDDGsnLQdih2ZyRcxXn+jb5Kadtv
9gyRqEDY9KG1aUIHp2eIG4r2UP6r8woz8fH56Gm95GLXd+awOvj9wWM7hi9p7F4u
BQiUrOEMzZ+gJENVtnUbtSiS9VquRymWeZDmOvZPcr4TgVuTXL8PBj5yYRcyLD4F
kCxU392HoIJOJqLxDjrmeXIagSdGnz0usO9ypV2IR82mGBYBFZb05HxmrTB343IX
xr63PEaAkedYpwFKUNu2FiSuoD6ViSh4MRNfOxsukySyx8Fq33U=
=JLPP
-----END PGP SIGNATURE-----
diff -Nru mongo-c-driver-1.30.3/.evergreen/config_generator/components/funcs/prepare_kerberos.py mongo-c-driver-1.30.4/.evergreen/config_generator/components/funcs/prepare_kerberos.py
--- mongo-c-driver-1.30.3/.evergreen/config_generator/components/funcs/prepare_kerberos.py	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/.evergreen/config_generator/components/funcs/prepare_kerberos.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,36 +0,0 @@
-from shrub.v3.evg_command import EvgCommandType
-
-from config_generator.etc.function import Function
-from config_generator.etc.utils import bash_exec
-
-
-class PrepareKerberos(Function):
-    name = 'prepare-kerberos'
-    commands = [
-        bash_exec(
-            command_type=EvgCommandType.SETUP,
-            working_dir='mongoc',
-            silent=True,
-            script='''\
-            if test "${keytab|}" && command -v kinit >/dev/null; then
-                echo "${keytab}" > /tmp/drivers.keytab.base64
-                base64 --decode /tmp/drivers.keytab.base64 > /tmp/drivers.keytab
-                if touch /etc/krb5.conf 2>/dev/null; then
-                    cat .evergreen/etc/kerberos.realm | tee -a /etc/krb5.conf
-                elif command sudo true 2>/dev/null; then
-                    cat .evergreen/etc/kerberos.realm | sudo tee -a /etc/krb5.conf
-                else
-                    echo "Cannot append kerberos.realm to /etc/krb5.conf; skipping." 1>&2
-                fi
-            fi
-            '''
-        ),
-    ]
-
-    @classmethod
-    def call(cls, **kwargs):
-        return cls.default_call(**kwargs)
-
-
-def functions():
-    return PrepareKerberos.defn()
diff -Nru mongo-c-driver-1.30.3/.evergreen/generated_configs/functions.yml mongo-c-driver-1.30.4/.evergreen/generated_configs/functions.yml
--- mongo-c-driver-1.30.3/.evergreen/generated_configs/functions.yml	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/.evergreen/generated_configs/functions.yml	2025-05-07 15:11:51.000000000 -0400
@@ -327,27 +327,6 @@
         args:
           - -c
           - .evergreen/scripts/compile-openssl-static.sh
-  prepare-kerberos:
-    - command: subprocess.exec
-      type: setup
-      params:
-        binary: bash
-        working_dir: mongoc
-        silent: true
-        args:
-          - -c
-          - |
-            if test "${keytab|}" && command -v kinit >/dev/null; then
-                echo "${keytab}" > /tmp/drivers.keytab.base64
-                base64 --decode /tmp/drivers.keytab.base64 > /tmp/drivers.keytab
-                if touch /etc/krb5.conf 2>/dev/null; then
-                    cat .evergreen/etc/kerberos.realm | tee -a /etc/krb5.conf
-                elif command sudo true 2>/dev/null; then
-                    cat .evergreen/etc/kerberos.realm | sudo tee -a /etc/krb5.conf
-                else
-                    echo "Cannot append kerberos.realm to /etc/krb5.conf; skipping." 1>&2
-                fi
-            fi
   restore-instance-profile:
     - command: subprocess.exec
       params:
diff -Nru mongo-c-driver-1.30.3/.evergreen/generated_configs/legacy-config.yml mongo-c-driver-1.30.4/.evergreen/generated_configs/legacy-config.yml
--- mongo-c-driver-1.30.3/.evergreen/generated_configs/legacy-config.yml	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/.evergreen/generated_configs/legacy-config.yml	2025-05-07 15:11:51.000000000 -0400
@@ -1245,7 +1245,6 @@
   - func: fetch-build
     vars:
       BUILD_NAME: debug-compile-sasl-openssl
-  - func: prepare-kerberos
   - func: run auth tests
 - name: authentication-tests-darwinssl
   tags:
@@ -1258,7 +1257,6 @@
   - func: fetch-build
     vars:
       BUILD_NAME: debug-compile-sasl-darwinssl
-  - func: prepare-kerberos
   - func: run auth tests
 - name: authentication-tests-winssl
   tags:
@@ -1271,7 +1269,6 @@
   - func: fetch-build
     vars:
       BUILD_NAME: debug-compile-sspi-winssl
-  - func: prepare-kerberos
   - func: run auth tests
 - name: authentication-tests-openssl-nosasl
   tags:
@@ -1284,7 +1281,6 @@
   - func: fetch-build
     vars:
       BUILD_NAME: debug-compile-nosasl-openssl
-  - func: prepare-kerberos
   - func: run auth tests
 - name: test-mongohouse
   depends_on:
@@ -1312,7 +1308,6 @@
       script: |-
         set -o errexit
         env SANITIZE=address SASL=AUTO SSL=OPENSSL EXTRA_CONFIGURE_FLAGS='-DENABLE_EXTRA_ALIGNMENT=OFF' .evergreen/scripts/compile.sh
-  - func: prepare-kerberos
   - func: run auth tests
     vars:
       ASAN: 'on'
diff -Nru mongo-c-driver-1.30.3/.evergreen/legacy_config_generator/evergreen_config_lib/tasks.py mongo-c-driver-1.30.4/.evergreen/legacy_config_generator/evergreen_config_lib/tasks.py
--- mongo-c-driver-1.30.3/.evergreen/legacy_config_generator/evergreen_config_lib/tasks.py	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/.evergreen/legacy_config_generator/evergreen_config_lib/tasks.py	2025-05-07 15:11:51.000000000 -0400
@@ -649,7 +649,6 @@
 
     def post_commands(self) -> Iterable[Value]:
         yield func("fetch-build", BUILD_NAME=self.build_task_name)
-        yield func("prepare-kerberos")
         yield func("run auth tests")
 
     @property
@@ -701,7 +700,6 @@
             """,
                     add_expansions_to_env=True,
                 ),
-                func("prepare-kerberos"),
                 func("run auth tests", ASAN="on"),
             ],
         )
diff -Nru mongo-c-driver-1.30.3/.evergreen/scripts/run-auth-tests.sh mongo-c-driver-1.30.4/.evergreen/scripts/run-auth-tests.sh
--- mongo-c-driver-1.30.3/.evergreen/scripts/run-auth-tests.sh	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/.evergreen/scripts/run-auth-tests.sh	2025-05-07 15:11:51.000000000 -0400
@@ -18,6 +18,40 @@
 declare install_dir="${mongoc_dir}/install-dir"
 declare openssl_install_dir="${mongoc_dir}/openssl-install-dir"
 
+# Create directory for secrets within Evergreen task directory. Task directory is cleaned up between tasks.
+declare secrets_dir
+secrets_dir="$(to_absolute "${mongoc_dir}/../secrets")"
+mkdir -p "${secrets_dir}"
+chmod 700 "${secrets_dir}"
+
+# Create certificate to test X509 auth with Atlas:
+atlas_x509_path="${secrets_dir:?}/atlas_x509.pem"
+echo "${atlas_x509_cert_base64:?}" | base64 --decode > "${secrets_dir:?}/atlas_x509.pem"
+# On Windows, convert certificate to PKCS#1 to work around CDRIVER-4269:
+if $IS_WINDOWS; then
+    openssl pkey -in "${secrets_dir:?}/atlas_x509.pem" -traditional > "${secrets_dir:?}/atlas_x509_pkcs1.pem"
+    openssl x509 -in "${secrets_dir:?}/atlas_x509.pem" >> "${secrets_dir:?}/atlas_x509_pkcs1.pem"
+    atlas_x509_path="$(cygpath -m "${secrets_dir:?}/atlas_x509_pkcs1.pem")"
+fi
+
+# Create Kerberos config and keytab files.
+echo "Setting up Kerberos ... begin"
+if command -v kinit >/dev/null; then
+    # Copy host config and append realm:
+    if [ -e /etc/krb5.conf ]; then
+      cat /etc/krb5.conf > "${secrets_dir:?}/krb5.conf"
+    fi
+    cat "${mongoc_dir}/.evergreen/etc/kerberos.realm" >> "${secrets_dir:?}/krb5.conf"
+    # Set up keytab:
+    echo "${keytab:?}" | base64 --decode > "${secrets_dir:?}/drivers.keytab"
+    # Initialize kerberos:
+    KRB5_CONFIG="${secrets_dir:?}/krb5.conf" kinit -k -t "${secrets_dir:?}/drivers.keytab" -p drivers@LDAPTEST.10GEN.CC
+    echo "Setting up Kerberos ... done"
+else
+    echo "No 'kinit' detected"
+    echo "Setting up Kerberos ... skipping"
+fi
+
 declare c_timeout="connectTimeoutMS=30000&serverSelectionTryOnce=false"
 
 declare sasl="OFF"
@@ -62,10 +96,6 @@
 : "${test_gssapi:?}"
 : "${ip_addr:?}"
 
-if command -v kinit >/dev/null && [[ -f /tmp/drivers.keytab ]]; then
-  kinit -k -t /tmp/drivers.keytab -p drivers@LDAPTEST.10GEN.CC || true
-fi
-
 # Archlinux (which we use for testing various self-installed OpenSSL versions)
 # stores their trust list under /etc/ca-certificates/extracted/.
 # We need to copy it to our custom installed OpenSSL/LibreSSL trust store.
@@ -142,6 +172,10 @@
     echo "Connecting to Atlas Serverless"
     LD_LIBRARY_PATH="${openssl_lib_prefix}" "${ping}" "${atlas_serverless:?}&${c_timeout}"
   fi
+
+  echo "Connecting to Atlas with X509"
+  LD_LIBRARY_PATH="${openssl_lib_prefix}" "${ping}" "${atlas_x509:?}&tlsCertificateKeyFile=${atlas_x509_path}&${c_timeout}"
+
 fi
 
 echo "Authenticating using PLAIN"
diff -Nru mongo-c-driver-1.30.3/.github/CODEOWNERS mongo-c-driver-1.30.4/.github/CODEOWNERS
--- mongo-c-driver-1.30.3/.github/CODEOWNERS	1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/.github/CODEOWNERS	2025-05-07 15:11:51.000000000 -0400
@@ -0,0 +1,2 @@
+# Listing code owners is required by DRIVERS-3098
+* @mongodb/dbx-c-cxx
diff -Nru mongo-c-driver-1.30.3/CONTRIBUTING.md mongo-c-driver-1.30.4/CONTRIBUTING.md
--- mongo-c-driver-1.30.3/CONTRIBUTING.md	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/CONTRIBUTING.md	2025-05-07 15:11:51.000000000 -0400
@@ -148,7 +148,7 @@
 In another terminal, use the `mongosh` shell to create a user:
 
 ```
-$ mongosh --eval "db.createUser({user: 'admin', pwd: 'pass', roles: ['root']})" admin
+$ mongosh --eval "db.createUser({user: 'bob', pwd: 'pwd123', roles: ['root']})" admin
 ```
 
 Authentication in MongoDB 3.0 and later uses SCRAM-SHA-1, which in turn
@@ -157,8 +157,8 @@
 Set the user and password environment variables, then build and run the tests:
 
 ```
-$ export MONGOC_TEST_USER=admin
-$ export MONGOC_TEST_PASSWORD=pass
+$ export MONGOC_TEST_USER=bob
+$ export MONGOC_TEST_PASSWORD=pwd123
 $ ./test-libmongoc
 ```
 
diff -Nru mongo-c-driver-1.30.3/NEWS mongo-c-driver-1.30.4/NEWS
--- mongo-c-driver-1.30.3/NEWS	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/NEWS	2025-05-07 15:11:51.000000000 -0400
@@ -1,3 +1,17 @@
+libmongoc 1.30.4
+================
+
+Fixes:
+
+  * Fix username handling for MONGODB-X509 authentication when C driver is configured to use Secure Transport (CMake option `ENABLE_SSL=DARWIN`) or Secure Channel (CMake option `ENABLE_SSL=WINDOWS`).
+  * Do not set empty partial result on client error for `mongoc_bulkwrite_execute`.
+
+Thanks to everyone who contributed to the development of this release.
+
+  * Kevin Albertson
+
+
+
 libmongoc 1.30.3
 ================
 
diff -Nru mongo-c-driver-1.30.3/VERSION_CURRENT mongo-c-driver-1.30.4/VERSION_CURRENT
--- mongo-c-driver-1.30.3/VERSION_CURRENT	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/VERSION_CURRENT	2025-05-07 15:11:51.000000000 -0400
@@ -1 +1 @@
-1.30.3
+1.30.4
diff -Nru mongo-c-driver-1.30.3/build/calc_release_version.py mongo-c-driver-1.30.4/build/calc_release_version.py
--- mongo-c-driver-1.30.3/build/calc_release_version.py	1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/build/calc_release_version.py	2025-05-07 15:11:51.000000000 -0400
@@ -0,0 +1,403 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#
+# Copyright 2009-present MongoDB, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+A script that calculates the release version number (based on the current Git
+branch and/or recent tags in history) to assign to a tarball generated from the
+current Git commit.
+
+This script needs to remain compatible with its target platforms, which currently
+includes RHEL 6, which uses Python 2.6!
+"""
+
+# XXX NOTE XXX NOTE XXX NOTE XXX
+# After modifying this script it is advisable to execute the self-tests in this directory:
+# - calc_release_version_selftest.sh
+# - calc_release_version_selftest.py
+# XXX NOTE XXX NOTE XXX NOTE XXX
+
+# pyright: reportTypeCommentUsage=false
+
+import datetime
+import errno
+import re
+import subprocess
+import optparse  # No 'argparse' on Python 2.6
+import sys
+
+
+class Version:
+    def __init__(self, s):
+        pat = r'(\d+)\.(\d+)\.(\d+)(\-\S+)?'
+        match = re.match(pat, s)
+        assert match, "Unrecognized version string %s" % s
+        self.major, self.minor, self.micro = (
+            map(int, (match.group(1), match.group(2), match.group(3))))
+
+        if match.group(4):
+            self.prerelease = match.group(4)[1:]
+        else:
+            self.prerelease = ''
+
+    def __lt__(self, other):
+        if self.major != other.major:
+            return self.major < other.major
+        if self.minor != other.minor:
+            return self.minor < other.minor
+        if self.micro != other.micro:
+            return self.micro < other.micro
+        if self.prerelease != other.prerelease:
+            if self.prerelease != '' and other.prerelease == '':
+                # Consider a prerelease less than non-prerelease.
+                return True
+            # For simplicity, compare prerelease versions lexicographically.
+            return self.prerelease < other.prerelease
+
+        # Versions are equal.
+        return False
+
+    def __eq__(self, other):
+        self_tuple = self.major, self.minor, self.micro, self.prerelease
+        other_tuple = other.major, other.minor, other.micro, other.prerelease
+        return self_tuple == other_tuple
+
+
+def parse_version(ver):
+    return Version(ver)
+
+
+parser = optparse.OptionParser(description=__doc__)
+parser.add_option("--debug", "-d", action="store_true", help="Enable debug output")
+parser.add_option("--previous", "-p", action="store_true", help="Calculate the previous version instead of the current")
+parser.add_option("--next-minor", action="store_true", help="Calculate the next minor version instead of the current")
+args, pos = parser.parse_args()
+assert not pos, "No positional arguments are expected"
+
+
+_DEBUG = args.debug  # type: bool
+
+
+def debug(msg):  # type: (str) -> None
+    if _DEBUG:
+        sys.stderr.write(msg)
+        sys.stderr.write("\n")
+        sys.stderr.flush()
+
+
+debug("Debugging output enabled.")
+
+# This option indicates we are to determine the previous release version
+PREVIOUS = args.previous  # type: bool
+# This options indicates to output the next minor release version
+NEXT_MINOR = args.next_minor  # type: bool
+
+# fmt: off
+
+PREVIOUS_TAG_RE = re.compile('(?P<ver>(?P<vermaj>[0-9]+)\\.(?P<vermin>[0-9]+)'
+                             '\\.(?P<verpatch>[0-9]+)(?:-(?P<verpre>.*))?)')
+RELEASE_TAG_RE = re.compile('(?P<ver>(?P<vermaj>[0-9]+)\\.(?P<vermin>[0-9]+)'
+                            '\\.(?P<verpatch>[0-9]+)(?:-(?P<verpre>.*))?)')
+RELEASE_BRANCH_RE = re.compile('(?:(?:refs/remotes/)?origin/)?(?P<brname>r'
+                               '(?P<vermaj>[0-9]+)\\.(?P<vermin>[0-9]+))')
+
+
+def check_output(args):  # type: (list[str]) -> str
+    """
+    Delegates to subprocess.check_output() if it is available, otherwise
+    provides a reasonable facsimile.
+    """
+    debug('Run command: {0}'.format(args))
+    try:
+        proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+    except OSError as e:
+        suppl = ''
+        if e.errno == errno.ENOENT:
+            suppl = 'Does the executable “{0}” not exist?'.format(args[0])
+        raise RuntimeError("Failed to execute subprocess {0}: {1} [{2}]".format(args, e, suppl))
+    out = proc.communicate()[0]
+    ret = proc.poll()
+    if ret:
+        raise subprocess.CalledProcessError(ret, args[0])
+
+    # git isn't guaranteed to always return UTF-8, but for our purposes
+    # this should be fine as tags and hashes should be ASCII only.
+    out = out.decode('utf-8')
+
+    return out
+
+
+def check_head_tag():  # type: () -> str | None
+    """
+    Checks the current HEAD to see if it has been tagged with a tag that matches
+    the pattern for a release tag.  Returns release version calculated from the
+    tag, or None if there is no matching tag associated with HEAD.
+    """
+
+    found_tag = False
+    version_str = '0.0.0'
+    version_parsed = parse_version(version_str)
+
+    # have git tell us if any tags that look like release tags point at HEAD;
+    # based on our policy, a commit should never have more than one release tag
+    tags = check_output(['git', 'tag', '--points-at', 'HEAD', '--list', '1.*']).split()
+    tag = ''
+    if len(tags) == 1:
+        tag = tags[0]
+    elif len(tags) > 1:
+        raise Exception('Expected 1 or 0 tags on HEAD, got: {}'.format(tags))
+
+    release_tag_match = RELEASE_TAG_RE.match(tag)
+    if release_tag_match:
+        new_version_str = release_tag_match.group('ver')
+        new_version_parsed = parse_version(new_version_str)
+        if new_version_parsed > version_parsed: # type: ignore
+            debug('HEAD release tag: ' + new_version_str)
+            version_str = new_version_str
+            version_parsed = new_version_parsed
+            found_tag = True
+
+    if found_tag:
+        debug('Calculated version: ' + version_str)
+        return version_str
+
+    return None
+
+def get_next_minor(prerelease_marker):  # type: (str) -> str
+    """
+    get_next_minor does the following:
+        - Inspect the branches that fit the convention for a release branch.
+        - Choose the latest and increment the minor version.
+        - Append .0 to form the new version (e.g., r1.21 becomes 1.22.0)
+        - Append a pre-release marker. (e.g. 1.22.0 becomes 1.22.0-20220201+gitf6e6a7025d)
+    """
+
+    version_str = '0.0.0'
+    version_parsed = parse_version(version_str)
+
+    version_new = {}
+    # Use refs (not branches) to get local branches plus remote branches
+    refs = check_output(['git', 'show-ref']).splitlines()
+    for ref in refs:
+        release_branch_match = RELEASE_BRANCH_RE.match(ref.split()[1])
+        if release_branch_match:
+            # Construct a candidate version from this branch name
+            version_new['major'] = int(release_branch_match.group('vermaj'))
+            version_new['minor'] = int(release_branch_match.group('vermin')) + 1
+            version_new['patch'] = 0
+            version_new['prerelease'] = prerelease_marker
+            new_version_str = str(version_new['major']) + '.' + \
+                              str(version_new['minor']) + '.' + \
+                              str(version_new['patch']) + '-' + \
+                              version_new['prerelease']
+            new_version_parsed = parse_version(new_version_str)
+            if new_version_parsed > version_parsed: # type: ignore
+                version_str = new_version_str
+                version_parsed = new_version_parsed
+                debug('Found new best version "' + version_str \
+                            + '" based on branch "' \
+                            + release_branch_match.group('brname') + '"')
+    return version_str
+
+def get_branch_tags(active_branch_name):  # type: (str) -> list[str]
+    """
+    Returns a list of tags corresponding to the current branch, which must not
+    be master.  If the specified branch is a release branch then return all tags
+    based on the major/minor X.Y release version.  If the specified branch is
+    neither master nor a release branch, then walk backwards in history until
+    the first tag matching the glob '1.*' and return that tag.
+    """
+
+    if active_branch_name == 'master':
+        raise Exception('this method is not meant to be called while on "master"')
+
+    release_branch_match = RELEASE_BRANCH_RE.match(active_branch_name)
+    if release_branch_match:
+        # This is a release branch, so look for tags only on this branch
+        tag_glob = release_branch_match.group('vermaj') + '.' \
+                + release_branch_match.group('vermin') + '.*'
+        return check_output(['git', 'tag', '--list', tag_glob]).splitlines()
+
+    # Not a release branch, so look for the most recent tag in history
+    commits = check_output(['git', 'log', '--pretty=format:%H', '--no-merges'])
+    tags_by_obj = get_object_tags()
+    for commit in commits.splitlines():
+        got = tags_by_obj.get(commit)
+        if got:
+            return got
+    # No tags
+    return []
+
+
+def iter_tag_lines():
+    """
+    Generate a list of pairs of strings, where the first is a commit hash, and
+    the second is a tag that is associated with that commit. Duplicate commits
+    are possible.
+    """
+    output = check_output(['git', 'tag', '--list', '1.*', '--format=%(*objectname)|%(tag)'])
+    lines = output.splitlines()
+    for l in lines:
+        obj, tag = l.split('|', 1)
+        if tag:
+            yield obj, tag
+
+
+def get_object_tags():  # type: () -> dict[str, list[str]]
+    """
+    Obtain a mapping between commit hashes and a list of tags that point to
+    that commit. Untagged commits will not be included in the resulting map.
+    """
+    ret = {}  # type: dict[str, list[str]]
+    for obj, tag in iter_tag_lines():
+        ret.setdefault(obj, []).append(tag)
+    return ret
+
+
+def process_and_sort_tags(tags):  # type: (list[str]) -> list[str]
+    """
+    Given a string (as returned from get_branch_tags), return a sorted list of
+    zero or more tags (sorted based on the Version comparison) which meet
+    the following criteria:
+        - a final release tag (i.e., 1.x.y without any pre-release suffix)
+        - a pre-release tag which is not superseded by a release tag (i.e.,
+          1.x.y-preX iff 1.x.y does not already exist)
+    """
+
+    processed_and_sorted_tags = []  # type: list[str]
+    if not tags or len(tags) == 0:
+        return processed_and_sorted_tags
+
+    # find all the final release tags
+    for tag in tags:
+        release_tag_match = RELEASE_TAG_RE.match(tag)
+        if release_tag_match and not release_tag_match.group('verpre'):
+            processed_and_sorted_tags.append(tag)
+    # collect together final release tags and pre-release tags for
+    # versions that have not yet had a final release
+    for tag in tags:
+        tag_parts = tag.split('-')
+        if len(tag_parts) >= 2 and tag_parts[0] not in processed_and_sorted_tags:
+            processed_and_sorted_tags.append(tag)
+    processed_and_sorted_tags.sort(key=Version)  # type: ignore
+
+    return processed_and_sorted_tags
+
+def main():
+    """
+    The algorithm is roughly:
+
+        - Is the --next-minor flag passed? If "yes", then return the next minor
+           release with a pre-release marker.
+        - Is the current HEAD associated with a tag that looks like a release
+           version?
+        - If "yes" then use that as the version
+        - If "no" then is the current branch master?
+        - If "yes" the current branch is master, then return the next minor
+           release with a pre-release marker.
+        - If "no" the current branch is not master, then determine the most
+           recent tag in history; strip any pre-release marker, increment the
+           patch version, and append a new pre-release marker
+    """
+
+    version_str = '0.0.0'
+    version_parsed = parse_version(version_str)
+    head_commit_short = check_output(['git', 'rev-parse', '--revs-only',
+                                      '--short=10', 'HEAD^{commit}']).strip()
+    prerelease_marker = datetime.date.today().strftime('%Y%m%d') \
+            + '+git' + head_commit_short
+
+    if NEXT_MINOR:
+        debug('Calculating next minor release')
+        return get_next_minor(prerelease_marker)
+
+    head_tag_ver = check_head_tag()
+    if head_tag_ver:
+        return head_tag_ver
+
+    active_branch_name = check_output(['git', 'rev-parse',
+                                       '--abbrev-ref', 'HEAD']).strip()
+    debug('Calculating release version for branch: ' + active_branch_name)
+    if active_branch_name == 'master':
+        return get_next_minor(prerelease_marker)
+
+    branch_tags = get_branch_tags(active_branch_name)
+    tags = process_and_sort_tags(branch_tags)
+
+    tag = tags[-1] if len(tags) > 0 else ''
+    # at this point the RE match is redundant, but convenient for accessing
+    # the components of the version string
+    release_tag_match = RELEASE_TAG_RE.match(tag)
+    if release_tag_match:
+        version_new = {}
+        version_new['major'] = int(release_tag_match.group('vermaj'))
+        version_new['minor'] = int(release_tag_match.group('vermin'))
+        version_new['patch'] = int(release_tag_match.group('verpatch')) + 1
+        version_new['prerelease'] = prerelease_marker
+        new_version_str = str(version_new['major']) + '.' + \
+                          str(version_new['minor']) + '.' + \
+                          str(version_new['patch']) + '-' + \
+                          version_new['prerelease']
+        new_version_parsed = parse_version(new_version_str)
+        if new_version_parsed > version_parsed: # type: ignore
+            version_str = new_version_str
+            version_parsed = new_version_parsed
+            debug('Found new best version "' + version_str \
+                        + '" from tag "' + release_tag_match.group('ver') + '"')
+
+    return version_str
+
+def previous(rel_ver):  # type: (str) -> str
+    """
+    Given a release version, find the previous version based on the latest Git
+    tag that is strictly a lower version than the given release version.
+    """
+    debug('Calculating previous release version (option -p was specified).')
+    version_str = '0.0.0'
+    version_parsed = parse_version(version_str)
+    rel_ver_str = rel_ver
+    rel_ver_parsed = parse_version(rel_ver_str)
+    tags = check_output(['git', 'tag', '--list', '1.*']).splitlines()
+    processed_and_sorted_tags = process_and_sort_tags(tags)
+    for tag in processed_and_sorted_tags:
+        previous_tag_match = PREVIOUS_TAG_RE.match(tag)
+        if previous_tag_match:
+            version_new = {}
+            version_new['major'] = int(previous_tag_match.group('vermaj'))
+            version_new['minor'] = int(previous_tag_match.group('vermin'))
+            version_new['patch'] = int(previous_tag_match.group('verpatch'))
+            version_new['prerelease'] = previous_tag_match.group('verpre')
+            new_version_str = str(version_new['major']) + '.' + \
+                              str(version_new['minor']) + '.' + \
+                              str(version_new['patch'])
+            if version_new['prerelease'] is not None:
+                new_version_str += '-' + version_new['prerelease']
+            new_version_parsed = parse_version(new_version_str)
+            if new_version_parsed < rel_ver_parsed and new_version_parsed > version_parsed: # type: ignore
+                version_str = new_version_str
+                version_parsed = new_version_parsed
+                debug('Found new best version "' + version_str \
+                            + '" from tag "' + tag + '"')
+
+    return version_str
+
+if __name__ == "__main__":
+    RELEASE_VER = previous(main()) if PREVIOUS else main()
+
+    debug('Final calculated release version:')
+    print(RELEASE_VER)
diff -Nru mongo-c-driver-1.30.3/debian/changelog mongo-c-driver-1.30.4/debian/changelog
--- mongo-c-driver-1.30.3/debian/changelog	2025-04-14 13:21:52.000000000 -0400
+++ mongo-c-driver-1.30.4/debian/changelog	2025-05-07 15:11:43.000000000 -0400
@@ -1,3 +1,9 @@
+mongo-c-driver (1.30.4-1) unstable; urgency=medium
+
+  * New upstream release
+
+ -- Roberto C. Sanchez <roberto@connexer.com>  Wed, 07 May 2025 15:11:43 -0400
+
 mongo-c-driver (1.30.3-1) unstable; urgency=medium
 
   * New upstream release
diff -Nru mongo-c-driver-1.30.3/etc/prior_version.txt mongo-c-driver-1.30.4/etc/prior_version.txt
--- mongo-c-driver-1.30.3/etc/prior_version.txt	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/etc/prior_version.txt	2025-05-07 15:11:51.000000000 -0400
@@ -1 +1 @@
-1.30.2
\ No newline at end of file
+1.30.3
\ No newline at end of file
diff -Nru mongo-c-driver-1.30.3/src/libbson/NEWS mongo-c-driver-1.30.4/src/libbson/NEWS
--- mongo-c-driver-1.30.3/src/libbson/NEWS	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libbson/NEWS	2025-05-07 15:11:51.000000000 -0400
@@ -1,3 +1,10 @@
+libbson 1.30.4
+==============
+
+No changes since 1.30.3. Version incremented to match the libmongoc version.
+
+
+
 libbson 1.30.3
 ==============
 
Binary files /tmp/user/2000/VbGtCRjZ_u/mongo-c-driver-1.30.3/src/libmongoc/doc/includes/libbson.inv and /tmp/user/2000/dmOl9HcoDL/mongo-c-driver-1.30.4/src/libmongoc/doc/includes/libbson.inv differ
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-bulkwrite.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-bulkwrite.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-bulkwrite.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-bulkwrite.c	2025-05-07 15:11:51.000000000 -0400
@@ -863,6 +863,9 @@
    bson_t updateresults;
    bson_t deleteresults;
    bool verboseresults;
+   // `parsed_some_results` becomes true if an ok:1 reply to `bulkWrite` is successfully parsed.
+   // Used to determine whether some writes were successful.
+   bool parsed_some_results;
 };
 
 int64_t
@@ -1332,6 +1335,8 @@
       _bulkwriteexception_append_writeconcernerror (self->exc, code, errmsg, &errInfo);
    }
 
+   self->res->parsed_some_results = true;
+
    return true;
 }
 
@@ -1512,6 +1517,7 @@
    BSON_ASSERT_PARAM (self);
    BSON_OPTIONAL_PARAM (opts);
 
+   // `has_successful_results` is set to true if any `bulkWrite` reply indicates some writes succeeded.
    bool has_successful_results = false;
    mongoc_bulkwritereturn_t ret = {0};
    bson_error_t error = {0};
@@ -1946,16 +1952,18 @@
    }
 
 fail:
-   if (is_ordered) {
-      // Ordered writes stop on first error. If the error reported is for an index > 0, assume some writes suceeded.
-      if (ret.res->errorscount == 0 || (ret.res->first_error_index.isset && ret.res->first_error_index.index > 0)) {
-         has_successful_results = true;
-      }
-   } else {
-      BSON_ASSERT (mcommon_in_range_size_t_signed (ret.res->errorscount));
-      size_t errorscount_sz = (size_t) ret.res->errorscount;
-      if (errorscount_sz < self->n_ops) {
-         has_successful_results = true;
+   if (ret.res->parsed_some_results) {
+      if (is_ordered) {
+         // Ordered writes stop on first error. If the error reported is for an index > 0, assume some writes suceeded.
+         if (ret.res->errorscount == 0 || (ret.res->first_error_index.isset && ret.res->first_error_index.index > 0)) {
+            has_successful_results = true;
+         }
+      } else {
+         BSON_ASSERT (mcommon_in_range_size_t_signed (ret.res->errorscount));
+         size_t errorscount_sz = (size_t) ret.res->errorscount;
+         if (errorscount_sz < self->n_ops) {
+            has_successful_results = true;
+         }
       }
    }
    if (!is_acknowledged || !has_successful_results) {
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-cluster-private.h mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-cluster-private.h
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-cluster-private.h	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-cluster-private.h	2025-05-07 15:11:51.000000000 -0400
@@ -235,10 +235,7 @@
                                       mongoc_stream_t *stream);
 
 bool
-_mongoc_cluster_get_auth_cmd_x509 (const mongoc_uri_t *uri,
-                                   const mongoc_ssl_opt_t *ssl_opts,
-                                   bson_t *cmd /* OUT */,
-                                   bson_error_t *error /* OUT */);
+_mongoc_cluster_get_auth_cmd_x509 (const mongoc_uri_t *uri, bson_t *cmd /* OUT */, bson_error_t *error /* OUT */);
 
 /* Returns true if a versioned server API has been selected, otherwise returns
  * false. */
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-cluster.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-cluster.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-cluster.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-cluster.c	2025-05-07 15:11:51.000000000 -0400
@@ -150,7 +150,7 @@
 _int32_from_le (const void *data)
 {
    BSON_ASSERT_PARAM (data);
-   return bson_iter_int32_unsafe (&(bson_iter_t){.raw = data});
+   return bson_iter_int32_unsafe (&(bson_iter_t) {.raw = data});
 }
 
 
@@ -813,12 +813,7 @@
    _mongoc_topology_dup_handshake_cmd (cluster->client->topology, &handshake_command);
 
    if (cluster->requires_auth && speculative_auth_response) {
-      mongoc_ssl_opt_t *ssl_opts = NULL;
-#ifdef MONGOC_ENABLE_SSL
-      ssl_opts = &cluster->client->ssl_opts;
-#endif
-
-      _mongoc_topology_scanner_add_speculative_authentication (&handshake_command, cluster->uri, ssl_opts, scram);
+      _mongoc_topology_scanner_add_speculative_authentication (&handshake_command, cluster->uri, scram);
    }
 
    if (negotiate_sasl_supported_mechs) {
@@ -1059,10 +1054,7 @@
 }
 
 bool
-_mongoc_cluster_get_auth_cmd_x509 (const mongoc_uri_t *uri,
-                                   const mongoc_ssl_opt_t *ssl_opts,
-                                   bson_t *cmd /* OUT */,
-                                   bson_error_t *error /* OUT */)
+_mongoc_cluster_get_auth_cmd_x509 (const mongoc_uri_t *uri, bson_t *cmd /* OUT */, bson_error_t *error /* OUT */)
 {
 #ifndef MONGOC_ENABLE_SSL
    bson_set_error (error,
@@ -1073,41 +1065,21 @@
    return false;
 #else
    const char *username_from_uri = NULL;
-   char *username_from_subject = NULL;
 
    BSON_ASSERT (uri);
+   BSON_UNUSED (error);
 
    username_from_uri = mongoc_uri_get_username (uri);
    if (username_from_uri) {
       TRACE ("%s", "X509: got username from URI");
-   } else {
-      if (!ssl_opts || !ssl_opts->pem_file) {
-         bson_set_error (error,
-                         MONGOC_ERROR_CLIENT,
-                         MONGOC_ERROR_CLIENT_AUTHENTICATE,
-                         "cannot determine username for "
-                         "X-509 authentication.");
-         return false;
-      }
-
-      username_from_subject = mongoc_ssl_extract_subject (ssl_opts->pem_file, ssl_opts->pem_pwd);
-      if (!username_from_subject) {
-         bson_set_error (error,
-                         MONGOC_ERROR_CLIENT,
-                         MONGOC_ERROR_CLIENT_AUTHENTICATE,
-                         "No username provided for X509 authentication.");
-         return false;
-      }
-
-      TRACE ("%s", "X509: got username from certificate");
    }
 
    bson_init (cmd);
    BSON_APPEND_INT32 (cmd, "authenticate", 1);
    BSON_APPEND_UTF8 (cmd, "mechanism", "MONGODB-X509");
-   BSON_APPEND_UTF8 (cmd, "user", username_from_uri ? username_from_uri : username_from_subject);
-
-   bson_free (username_from_subject);
+   if (username_from_uri) {
+      BSON_APPEND_UTF8 (cmd, "user", username_from_uri);
+   }
 
    return true;
 #endif
@@ -1138,7 +1110,7 @@
    BSON_ASSERT (cluster);
    BSON_ASSERT (stream);
 
-   if (!_mongoc_cluster_get_auth_cmd_x509 (cluster->uri, &cluster->client->ssl_opts, &cmd, error)) {
+   if (!_mongoc_cluster_get_auth_cmd_x509 (cluster->uri, &cmd, error)) {
       return false;
    }
 
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-openssl-private.h mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-openssl-private.h
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-openssl-private.h	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-openssl-private.h	2025-05-07 15:11:51.000000000 -0400
@@ -38,8 +38,6 @@
 _mongoc_openssl_check_peer_hostname (SSL *ssl, const char *host, bool allow_invalid_hostname);
 SSL_CTX *
 _mongoc_openssl_ctx_new (mongoc_ssl_opt_t *opt);
-char *
-_mongoc_openssl_extract_subject (const char *filename, const char *passphrase);
 void
 _mongoc_openssl_init (void);
 void
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-openssl.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-openssl.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-openssl.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-openssl.c	2025-05-07 15:11:51.000000000 -0400
@@ -1011,57 +1011,6 @@
    return ctx;
 }
 
-
-char *
-_mongoc_openssl_extract_subject (const char *filename, const char *passphrase)
-{
-   X509_NAME *subject = NULL;
-   X509 *cert = NULL;
-   BIO *certbio = NULL;
-   BIO *strbio = NULL;
-   char *str = NULL;
-   int ret;
-
-   BSON_UNUSED (passphrase);
-
-   if (!filename) {
-      return NULL;
-   }
-
-   certbio = BIO_new (BIO_s_file ());
-   strbio = BIO_new (BIO_s_mem ());
-
-   BSON_ASSERT (certbio);
-   BSON_ASSERT (strbio);
-
-
-   if (BIO_read_filename (certbio, filename) && (cert = PEM_read_bio_X509 (certbio, NULL, 0, NULL))) {
-      if ((subject = X509_get_subject_name (cert))) {
-         ret = X509_NAME_print_ex (strbio, subject, 0, XN_FLAG_RFC2253);
-
-         if ((ret > 0) && (ret < INT_MAX)) {
-            str = (char *) bson_malloc (ret + 2);
-            BIO_gets (strbio, str, ret + 1);
-            str[ret] = '\0';
-         }
-      }
-   }
-
-   if (cert) {
-      X509_free (cert);
-   }
-
-   if (certbio) {
-      BIO_free (certbio);
-   }
-
-   if (strbio) {
-      BIO_free (strbio);
-   }
-
-   return str;
-}
-
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
 #ifdef _WIN32
 
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h	2025-05-07 15:11:51.000000000 -0400
@@ -32,10 +32,6 @@
 
 BSON_BEGIN_DECLS
 
-
-char *
-_mongoc_secure_channel_extract_subject (const char *filename, const char *passphrase);
-
 bool
 mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt);
 
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-channel.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-channel.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-channel.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-channel.c	2025-05-07 15:11:51.000000000 -0400
@@ -240,42 +240,6 @@
    return mongoc_secure_channel_setup_certificate_from_file (opt->pem_file);
 }
 
-static void
-_bson_append_szoid (mcommon_string_append_t *retval, PCCERT_CONTEXT cert, const char *label, void *oid)
-{
-   DWORD oid_len = CertGetNameString (cert, CERT_NAME_ATTR_TYPE, 0, oid, NULL, 0);
-
-   if (oid_len > 1) {
-      char *tmp = bson_malloc0 (oid_len);
-
-      CertGetNameString (cert, CERT_NAME_ATTR_TYPE, 0, oid, tmp, oid_len);
-      mcommon_string_append_printf (retval, "%s%s", label, tmp);
-      bson_free (tmp);
-   }
-}
-
-char *
-_mongoc_secure_channel_extract_subject (const char *filename, const char *passphrase)
-{
-   PCCERT_CONTEXT cert;
-   cert = mongoc_secure_channel_setup_certificate_from_file (filename);
-   if (!cert) {
-      return NULL;
-   }
-
-   mcommon_string_append_t retval;
-   mcommon_string_new_as_append (&retval);
-
-   _bson_append_szoid (&retval, cert, "C=", szOID_COUNTRY_NAME);
-   _bson_append_szoid (&retval, cert, ",ST=", szOID_STATE_OR_PROVINCE_NAME);
-   _bson_append_szoid (&retval, cert, ",L=", szOID_LOCALITY_NAME);
-   _bson_append_szoid (&retval, cert, ",O=", szOID_ORGANIZATION_NAME);
-   _bson_append_szoid (&retval, cert, ",OU=", szOID_ORGANIZATIONAL_UNIT_NAME);
-   _bson_append_szoid (&retval, cert, ",CN=", szOID_COMMON_NAME);
-   _bson_append_szoid (&retval, cert, ",STREET=", szOID_STREET_ADDRESS);
-
-   return mcommon_string_from_append_destroy_with_steal (&retval);
-}
 
 bool
 mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt)
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-transport-private.h mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-transport-private.h
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-transport-private.h	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-transport-private.h	2025-05-07 15:11:51.000000000 -0400
@@ -31,9 +31,6 @@
 char *
 _mongoc_cfstringref_to_cstring (CFStringRef ref);
 
-char *
-_mongoc_secure_transport_extract_subject (const char *filename, const char *passphrase);
-
 OSStatus
 mongoc_secure_transport_write (SSLConnectionRef connection, const void *data, size_t *data_length);
 OSStatus
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-transport.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-transport.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-secure-transport.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-secure-transport.c	2025-05-07 15:11:51.000000000 -0400
@@ -80,23 +80,6 @@
    return NULL;
 }
 
-static void
-_bson_append_cftyperef (mcommon_string_append_t *retval, const char *label, CFTypeRef str)
-{
-   char *cs;
-
-   if (str) {
-      cs = _mongoc_cfstringref_to_cstring (str);
-
-      if (cs) {
-         mcommon_string_append_printf (retval, "%s%s", label, cs);
-         bson_free (cs);
-      } else {
-         mcommon_string_append_printf (retval, "%s(null)", label);
-      }
-   }
-}
-
 CFTypeRef
 _mongoc_secure_transport_dict_get (CFArrayRef values, CFStringRef label)
 {
@@ -121,76 +104,6 @@
    return NULL;
 }
 
-char *
-_mongoc_secure_transport_RFC2253_from_cert (SecCertificateRef cert)
-{
-   CFTypeRef value;
-   CFTypeRef subject_name;
-   CFDictionaryRef cert_dict;
-
-   cert_dict = SecCertificateCopyValues (cert, NULL, NULL);
-   if (!cert_dict) {
-      return NULL;
-   }
-
-   subject_name = CFDictionaryGetValue (cert_dict, kSecOIDX509V1SubjectName);
-   if (!subject_name) {
-      CFRelease (cert_dict);
-      return NULL;
-   }
-
-   subject_name = CFDictionaryGetValue (subject_name, kSecPropertyKeyValue);
-   if (!subject_name) {
-      CFRelease (cert_dict);
-      return NULL;
-   }
-
-   mcommon_string_append_t retval;
-   mcommon_string_new_as_append (&retval);
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDCountryName);
-   _bson_append_cftyperef (&retval, "C=", value);
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDStateProvinceName);
-   _bson_append_cftyperef (&retval, ",ST=", value);
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDLocalityName);
-   _bson_append_cftyperef (&retval, ",L=", value);
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDOrganizationName);
-   _bson_append_cftyperef (&retval, ",O=", value);
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDOrganizationalUnitName);
-   if (value) {
-      /* Can be either one unit name, or array of unit names */
-      if (CFGetTypeID (value) == CFStringGetTypeID ()) {
-         _bson_append_cftyperef (&retval, ",OU=", value);
-      } else if (CFGetTypeID (value) == CFArrayGetTypeID ()) {
-         CFIndex len = CFArrayGetCount (value);
-
-         if (len > 0) {
-            _bson_append_cftyperef (&retval, ",OU=", CFArrayGetValueAtIndex (value, 0));
-         }
-         if (len > 1) {
-            _bson_append_cftyperef (&retval, ",", CFArrayGetValueAtIndex (value, 1));
-         }
-         if (len > 2) {
-            _bson_append_cftyperef (&retval, ",", CFArrayGetValueAtIndex (value, 2));
-         }
-      }
-   }
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDCommonName);
-   _bson_append_cftyperef (&retval, ",CN=", value);
-
-   value = _mongoc_secure_transport_dict_get (subject_name, kSecOIDStreetAddress);
-   _bson_append_cftyperef (&retval, ",STREET", value);
-
-   CFRelease (cert_dict);
-   return mcommon_string_from_append_destroy_with_steal (&retval);
-}
-
-
 static void
 safe_release (CFTypeRef ref)
 {
@@ -273,41 +186,6 @@
    return r;
 }
 
-char *
-_mongoc_secure_transport_extract_subject (const char *filename, const char *passphrase)
-{
-   bool success;
-   char *retval = NULL;
-   CFArrayRef items = NULL;
-   SecExternalItemType type = kSecItemTypeCertificate;
-
-
-   success = _mongoc_secure_transport_import_pem (filename, passphrase, &items, &type);
-
-   if (!success) {
-      return NULL;
-   }
-
-   if (type == kSecItemTypeAggregate) {
-      for (CFIndex i = 0; i < CFArrayGetCount (items); ++i) {
-         CFTypeID item_id = CFGetTypeID (CFArrayGetValueAtIndex (items, i));
-
-         if (item_id == SecCertificateGetTypeID ()) {
-            retval = _mongoc_secure_transport_RFC2253_from_cert ((SecCertificateRef) CFArrayGetValueAtIndex (items, i));
-            break;
-         }
-      }
-   } else if (type == kSecItemTypeCertificate) {
-      retval = _mongoc_secure_transport_RFC2253_from_cert ((SecCertificateRef) items);
-   }
-
-   if (items) {
-      CFRelease (items);
-   }
-
-   return retval;
-}
-
 static const char *
 SecExternalItemType_to_string (SecExternalItemType value)
 {
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-ssl-private.h mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-ssl-private.h
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-ssl-private.h	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-ssl-private.h	2025-05-07 15:11:51.000000000 -0400
@@ -31,9 +31,6 @@
    bool tls_disable_ocsp_endpoint_check;
 } _mongoc_internal_tls_opts_t;
 
-char *
-mongoc_ssl_extract_subject (const char *filename, const char *passphrase);
-
 void
 _mongoc_ssl_opts_from_uri (mongoc_ssl_opt_t *ssl_opt, _mongoc_internal_tls_opts_t *internal, mongoc_uri_t *uri);
 void
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-ssl.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-ssl.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-ssl.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-ssl.c	2025-05-07 15:11:51.000000000 -0400
@@ -58,44 +58,6 @@
    return &gMongocSslOptDefault;
 }
 
-char *
-mongoc_ssl_extract_subject (const char *filename, const char *passphrase)
-{
-   char *retval;
-
-   if (!filename) {
-      MONGOC_ERROR ("No filename provided to extract subject from");
-      return NULL;
-   }
-
-#ifdef _WIN32
-   if (_access (filename, 0) != 0) {
-#else
-   if (access (filename, R_OK) != 0) {
-#endif
-      MONGOC_ERROR ("Can't extract subject from unreadable file: '%s'", filename);
-      return NULL;
-   }
-
-#if defined(MONGOC_ENABLE_SSL_OPENSSL)
-   retval = _mongoc_openssl_extract_subject (filename, passphrase);
-#elif defined(MONGOC_ENABLE_SSL_LIBRESSL)
-   MONGOC_WARNING ("libtls doesn't support automatically extracting subject from "
-                   "certificate to use with authentication");
-   retval = NULL;
-#elif defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT)
-retval = _mongoc_secure_transport_extract_subject (filename, passphrase);
-#elif defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL)
-retval = _mongoc_secure_channel_extract_subject (filename, passphrase);
-#endif
-
-   if (!retval) {
-      MONGOC_ERROR ("Can't extract subject from file '%s'", filename);
-   }
-
-   return retval;
-}
-
 void
 _mongoc_ssl_opts_from_uri (mongoc_ssl_opt_t *ssl_opt, _mongoc_internal_tls_opts_t *internal, mongoc_uri_t *uri)
 {
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h	2025-05-07 15:11:51.000000000 -0400
@@ -206,7 +206,6 @@
 void
 _mongoc_topology_scanner_add_speculative_authentication (bson_t *cmd,
                                                          const mongoc_uri_t *uri,
-                                                         const mongoc_ssl_opt_t *ssl_opts,
                                                          mongoc_scram_t *scram /* OUT */);
 
 void
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-topology-scanner.c mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-topology-scanner.c
--- mongo-c-driver-1.30.3/src/libmongoc/src/mongoc/mongoc-topology-scanner.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/src/mongoc/mongoc-topology-scanner.c	2025-05-07 15:11:51.000000000 -0400
@@ -170,7 +170,6 @@
 void
 _mongoc_topology_scanner_add_speculative_authentication (bson_t *cmd,
                                                          const mongoc_uri_t *uri,
-                                                         const mongoc_ssl_opt_t *ssl_opts,
                                                          mongoc_scram_t *scram /* OUT */)
 {
    bson_t auth_cmd;
@@ -186,7 +185,7 @@
       /* Ignore errors while building authentication document: we proceed with
        * the handshake as usual and let the subsequent authenticate command
        * fail. */
-      if (_mongoc_cluster_get_auth_cmd_x509 (uri, ssl_opts, &auth_cmd, &error)) {
+      if (_mongoc_cluster_get_auth_cmd_x509 (uri, &auth_cmd, &error)) {
          has_auth = true;
          BSON_APPEND_UTF8 (&auth_cmd, "db", "$external");
       }
@@ -375,13 +374,7 @@
 
    if (node->ts->speculative_authentication && !node->has_auth && bson_empty (&node->speculative_auth_response) &&
        node->scram.step == 0) {
-      mongoc_ssl_opt_t *ssl_opts = NULL;
-
-#ifdef MONGOC_ENABLE_SSL
-      ssl_opts = ts->ssl_opts;
-#endif
-
-      _mongoc_topology_scanner_add_speculative_authentication (&cmd, ts->uri, ssl_opts, &node->scram);
+      _mongoc_topology_scanner_add_speculative_authentication (&cmd, ts->uri, &node->scram);
    }
 
    if (!bson_empty (&ts->cluster_time)) {
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/tests/test-mongoc-bulkwrite.c mongo-c-driver-1.30.4/src/libmongoc/tests/test-mongoc-bulkwrite.c
--- mongo-c-driver-1.30.3/src/libmongoc/tests/test-mongoc-bulkwrite.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/tests/test-mongoc-bulkwrite.c	2025-05-07 15:11:51.000000000 -0400
@@ -219,6 +219,7 @@
    // Execute.
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       mongoc_bulkwriteresult_destroy (bwr.res);
       mongoc_bulkwriteexception_destroy (bwr.exc);
@@ -251,6 +252,7 @@
 
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+      ASSERT (!bwr.res); // No result due to no successful writes.
       ASSERT (bwr.exc);
       ASSERT (mongoc_bulkwriteexception_error (bwr.exc, &error));
       ASSERT_ERROR_CONTAINS (
@@ -309,6 +311,7 @@
    // Execute.
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, bwo);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       // Expect the selected server is reported as used.
       uint32_t used_serverid = mongoc_bulkwriteresult_serverid (bwr.res);
@@ -381,6 +384,7 @@
    // Execute.
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, bwo);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       // Expect a different server was used due to retry.
       uint32_t used_serverid = mongoc_bulkwriteresult_serverid (bwr.res);
@@ -444,6 +448,7 @@
    // Execute.
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, bwo);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       mongoc_bulkwriteresult_destroy (bwr.res);
       mongoc_bulkwriteexception_destroy (bwr.exc);
@@ -486,6 +491,7 @@
    // Execute.
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL /* opts */);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       // Expect no verbose results.
       ASSERT (NULL == mongoc_bulkwriteresult_insertresults (bwr.res));
@@ -558,6 +564,7 @@
    // Execute.
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL /* opts */);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       mongoc_bulkwriteresult_destroy (bwr.res);
       mongoc_bulkwriteexception_destroy (bwr.exc);
@@ -606,6 +613,7 @@
    // Attempt execution without assigning a client
    {
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+      ASSERT (!bwr.res); // No result due to no successful writes.
       ASSERT (bwr.exc);
       ASSERT (mongoc_bulkwriteexception_error (bwr.exc, &error));
       ASSERT_ERROR_CONTAINS (error,
@@ -620,6 +628,7 @@
    {
       mongoc_bulkwrite_set_client (bw, client);
       mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+      ASSERT (bwr.res);
       ASSERT_NO_BULKWRITEEXCEPTION (bwr);
       mongoc_bulkwriteresult_destroy (bwr.res);
       mongoc_bulkwriteexception_destroy (bwr.exc);
@@ -667,8 +676,8 @@
    ASSERT_OR_PRINT (mongoc_bulkwrite_append_insertone (bw, "db.coll", docs[1], NULL, &error), error);
 
    mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, bw_opts);
-   ASSERT_NO_BULKWRITEEXCEPTION (bwr);
    ASSERT (bwr.res);
+   ASSERT_NO_BULKWRITEEXCEPTION (bwr);
    const bson_t *insertresults = mongoc_bulkwriteresult_insertresults (bwr.res);
    ASSERT_MATCH (insertresults,
                  BSON_STR ({"0" : {"insertedId" : "over_2mib_1"}}, {"1" : {"insertedId" : "over_2mib_2"}}));
@@ -682,6 +691,40 @@
    bson_free (large_string);
 }
 
+
+// `test_bulkwrite_client_error_no_result` is a regression test for CDRIVER-5969.
+static void
+test_bulkwrite_client_error_no_result (void *unused)
+{
+   BSON_UNUSED (unused);
+
+   bson_error_t error;
+   mongoc_client_t *client = test_framework_new_default_client ();
+   // Trigger a client-side error by adding a too-big document.
+   {
+      mongoc_bulkwrite_t *bw = mongoc_client_bulkwrite_new (client);
+      bson_t too_big = BSON_INITIALIZER;
+      const size_t maxMessageSizeByte = 48000000;
+      char *big_string = bson_malloc (maxMessageSizeByte + 1);
+      memset (big_string, 'a', maxMessageSizeByte);
+      big_string[maxMessageSizeByte] = '\0';
+      BSON_APPEND_UTF8 (&too_big, "big", big_string);
+      ASSERT_OR_PRINT (mongoc_bulkwrite_append_insertone (bw, "db.coll", &too_big, NULL, &error), error);
+      mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+      ASSERT (!bwr.res); // No result due to no successful writes.
+      ASSERT (bwr.exc);
+      ASSERT (mongoc_bulkwriteexception_error (bwr.exc, &error));
+      ASSERT_ERROR_CONTAINS (
+         error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_COMMAND_INVALID_ARG, "Sending would exceed maxMessageSizeBytes");
+      bson_free (big_string);
+      bson_destroy (&too_big);
+      mongoc_bulkwriteresult_destroy (bwr.res);
+      mongoc_bulkwriteexception_destroy (bwr.exc);
+      mongoc_bulkwrite_destroy (bw);
+   }
+
+   mongoc_client_destroy (client);
+}
 void
 test_bulkwrite_install (TestSuite *suite)
 {
@@ -782,5 +825,13 @@
                       NULL /* dtor */,
                       NULL /* ctx */,
                       test_framework_skip_if_max_wire_version_less_than_25 // require server 8.0
+   );
+
+   TestSuite_AddFull (suite,
+                      "/bulkwrite/client_error_no_result",
+                      test_bulkwrite_client_error_no_result,
+                      NULL /* dtor */,
+                      NULL /* ctx */,
+                      test_framework_skip_if_max_wire_version_less_than_25 // require server 8.0
    );
 }
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/tests/test-mongoc-crud.c mongo-c-driver-1.30.4/src/libmongoc/tests/test-mongoc-crud.c
--- mongo-c-driver-1.30.3/src/libmongoc/tests/test-mongoc-crud.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/tests/test-mongoc-crud.c	2025-05-07 15:11:51.000000000 -0400
@@ -366,8 +366,8 @@
    }
 
    mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, NULL /* options */);
-   ASSERT_NO_BULKWRITEEXCEPTION (ret);
    ASSERT (ret.res);
+   ASSERT_NO_BULKWRITEEXCEPTION (ret);
    ASSERT_CMPINT64 (mongoc_bulkwriteresult_insertedcount (ret.res), ==, numModels);
    mongoc_bulkwriteexception_destroy (ret.exc);
    mongoc_bulkwriteresult_destroy (ret.res);
@@ -465,6 +465,7 @@
    }
 
    mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, NULL /* options */);
+   ASSERT (ret.res); // Has partial results.
    ASSERT (ret.exc);
 
    // Expect no top-level error.
@@ -535,6 +536,7 @@
       mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
       mongoc_bulkwriteopts_set_ordered (opts, false);
       mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
+      ASSERT (!ret.res); // No result due to no successful writes.
       ASSERT (ret.exc);
 
       if (mongoc_bulkwriteexception_error (ret.exc, &error)) {
@@ -574,6 +576,7 @@
       mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
       mongoc_bulkwriteopts_set_ordered (opts, true);
       mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
+      ASSERT (!ret.res); // No result due to no successful writes.
       ASSERT (ret.exc);
 
       if (mongoc_bulkwriteexception_error (ret.exc, &error)) {
@@ -655,7 +658,7 @@
    mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
    mongoc_bulkwriteopts_set_verboseresults (opts, true);
    mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
-
+   ASSERT (ret.res);
    ASSERT_NO_BULKWRITEEXCEPTION (ret);
 
    ASSERT_CMPINT64 (mongoc_bulkwriteresult_upsertedcount (ret.res), ==, 2);
@@ -741,7 +744,7 @@
    mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
    mongoc_bulkwriteopts_set_verboseresults (opts, true);
    mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
-
+   ASSERT (ret.res);
    ASSERT_NO_BULKWRITEEXCEPTION (ret);
 
    ASSERT_CMPINT64 (mongoc_bulkwriteresult_upsertedcount (ret.res), ==, 2);
@@ -842,13 +845,13 @@
    mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
    mongoc_bulkwriteopts_set_verboseresults (opts, true);
    mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
+   ASSERT (ret.res);
    ASSERT (ret.exc);
 
    if (!mongoc_bulkwriteexception_error (ret.exc, &error)) {
       test_error ("Expected top-level error but got:\n%s", test_bulkwriteexception_str (ret.exc));
    }
    ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_QUERY, 8, "Failing command via 'failCommand' failpoint");
-   ASSERT (ret.res);
    ASSERT_CMPSIZE_T ((size_t) mongoc_bulkwriteresult_upsertedcount (ret.res), ==, numModels);
 
    // Check length of update results.
@@ -911,12 +914,12 @@
       ASSERT_OR_PRINT (ok, error);
 
       mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
+      ASSERT (!ret.res); // No result due to unacknowledged write concern.
       ASSERT (ret.exc);
       if (!mongoc_bulkwriteexception_error (ret.exc, &error)) {
          test_error ("Expected top-level error but got:\n%s", test_bulkwriteexception_str (ret.exc));
       }
       ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_COMMAND_INVALID_ARG, "of size");
-
       mongoc_bulkwriteexception_destroy (ret.exc);
       mongoc_bulkwriteresult_destroy (ret.res);
       mongoc_bulkwrite_destroy (bw);
@@ -929,12 +932,12 @@
       ASSERT_OR_PRINT (ok, error);
 
       mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, opts);
+      ASSERT (!ret.res); // No result due to unacknowledged write concern.
       ASSERT (ret.exc);
       if (!mongoc_bulkwriteexception_error (ret.exc, &error)) {
          test_error ("Expected top-level error but got:\n%s", test_bulkwriteexception_str (ret.exc));
       }
       ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_COMMAND_INVALID_ARG, "of size");
-
       mongoc_bulkwriteexception_destroy (ret.exc);
       mongoc_bulkwriteresult_destroy (ret.res);
       mongoc_bulkwrite_destroy (bw);
@@ -1075,6 +1078,7 @@
       // Execute.
       {
          mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (tf->bw, NULL /* opts */);
+         ASSERT (bwr.res);
          ASSERT_NO_BULKWRITEEXCEPTION (bwr);
          ASSERT (mcommon_in_range_int64_t_unsigned (tf->numModels));
          ASSERT_CMPINT64 (mongoc_bulkwriteresult_insertedcount (bwr.res), ==, (int64_t) tf->numModels + 1);
@@ -1125,6 +1129,7 @@
       // Execute.
       {
          mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (tf->bw, NULL /* opts */);
+         ASSERT (bwr.res);
          ASSERT_NO_BULKWRITEEXCEPTION (bwr);
          ASSERT (mcommon_in_range_int64_t_unsigned (tf->numModels));
          ASSERT_CMPINT64 (mongoc_bulkwriteresult_insertedcount (bwr.res), ==, (int64_t) tf->numModels + 1);
@@ -1205,6 +1210,7 @@
       // Execute.
       {
          mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+         ASSERT (!bwr.res); // No result due to no successful writes.
          ASSERT (bwr.exc);
          if (!mongoc_bulkwriteexception_error (bwr.exc, &error)) {
             test_error ("Expected top-level error but got:\n%s", test_bulkwriteexception_str (bwr.exc));
@@ -1233,6 +1239,7 @@
       // Execute.
       {
          mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+         ASSERT (!bwr.res); // No result due to no successful writes.
          ASSERT (bwr.exc);
          if (!mongoc_bulkwriteexception_error (bwr.exc, &error)) {
             test_error ("Expected top-level error but got:\n%s", test_bulkwriteexception_str (bwr.exc));
@@ -1282,6 +1289,7 @@
       // Execute.
       {
          mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, NULL);
+         ASSERT (!bwr.res); // No result due to no successful writes.
          ASSERT (bwr.exc);
          if (!mongoc_bulkwriteexception_error (bwr.exc, &error)) {
             test_error ("Expected top-level error but got:\n%s", test_bulkwriteexception_str (bwr.exc));
diff -Nru mongo-c-driver-1.30.3/src/libmongoc/tests/test-mongoc-x509.c mongo-c-driver-1.30.4/src/libmongoc/tests/test-mongoc-x509.c
--- mongo-c-driver-1.30.3/src/libmongoc/tests/test-mongoc-x509.c	2025-04-14 13:22:10.000000000 -0400
+++ mongo-c-driver-1.30.4/src/libmongoc/tests/test-mongoc-x509.c	2025-05-07 15:11:51.000000000 -0400
@@ -7,22 +7,7 @@
 
 #include "TestSuite.h"
 #include "test-libmongoc.h"
-
-#if defined(MONGOC_ENABLE_SSL) && !defined(MONGOC_ENABLE_SSL_LIBRESSL)
-static void
-test_extract_subject (void)
-{
-   char *subject;
-
-   subject = mongoc_ssl_extract_subject (CERT_SERVER, NULL);
-   ASSERT_CMPSTR (subject, "C=US,ST=New York,L=New York City,O=MongoDB,OU=Drivers,CN=localhost");
-   bson_free (subject);
-
-   subject = mongoc_ssl_extract_subject (CERT_CLIENT, NULL);
-   ASSERT_CMPSTR (subject, "C=US,ST=New York,L=New York City,O=MDB,OU=Drivers,CN=client");
-   bson_free (subject);
-}
-#endif
+#include "test-conveniences.h" // tmp_bson
 
 #ifdef MONGOC_ENABLE_OCSP_OPENSSL
 /* Test parsing a DER encoded tlsfeature extension contents for the
@@ -83,11 +68,209 @@
 }
 #endif /* MONGOC_ENABLE_OCSP_OPENSSL */
 
+#ifdef MONGOC_ENABLE_SSL
+static void
+create_x509_user (void)
+{
+   bson_error_t error;
+
+   mongoc_client_t *client = test_framework_new_default_client ();
+   bool ok =
+      mongoc_client_command_simple (client,
+                                    "$external",
+                                    tmp_bson (BSON_STR ({
+                                       "createUser" : "C=US,ST=New York,L=New York City,O=MDB,OU=Drivers,CN=client",
+                                       "roles" : [ {"role" : "readWrite", "db" : "db"} ]
+                                    })),
+                                    NULL /* read_prefs */,
+                                    NULL /* reply */,
+                                    &error);
+   ASSERT_OR_PRINT (ok, error);
+   mongoc_client_destroy (client);
+}
+
+static void
+drop_x509_user (bool ignore_notfound)
+{
+   bson_error_t error;
+
+   mongoc_client_t *client = test_framework_new_default_client ();
+   bool ok = mongoc_client_command_simple (
+      client,
+      "$external",
+      tmp_bson (BSON_STR ({"dropUser" : "C=US,ST=New York,L=New York City,O=MDB,OU=Drivers,CN=client"})),
+      NULL /* read_prefs */,
+      NULL /* reply */,
+      &error);
+
+   if (!ok) {
+      ASSERT_OR_PRINT (ignore_notfound && NULL != strstr (error.message, "not found"), error);
+   }
+   mongoc_client_destroy (client);
+}
+
+static mongoc_uri_t *
+get_x509_uri (void)
+{
+   bson_error_t error;
+   char *uristr_noauth = test_framework_get_uri_str_no_auth ("db");
+   mongoc_uri_t *uri = mongoc_uri_new_with_error (uristr_noauth, &error);
+   ASSERT_OR_PRINT (uri, error);
+   ASSERT (mongoc_uri_set_auth_mechanism (uri, "MONGODB-X509"));
+   ASSERT (mongoc_uri_set_auth_source (uri, "$external"));
+   bson_free (uristr_noauth);
+   return uri;
+}
+
+static bool
+try_insert (mongoc_client_t *client, bson_error_t *error)
+{
+   mongoc_collection_t *coll = mongoc_client_get_collection (client, "db", "coll");
+   bool ok = mongoc_collection_insert_one (coll, tmp_bson ("{}"), NULL, NULL, error);
+   mongoc_collection_destroy (coll);
+   return ok;
+}
+
+static void
+test_x509_auth (void *unused)
+{
+   BSON_UNUSED (unused);
+
+   drop_x509_user (true /* ignore "not found" error */);
+   create_x509_user ();
+
+   // Test auth works:
+   {
+      // Create URI:
+      mongoc_uri_t *uri = get_x509_uri ();
+      {
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCERTIFICATEKEYFILE, CERT_CLIENT));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA));
+      }
+
+      // Try auth:
+      bson_error_t error = {0};
+      bool ok;
+      {
+         mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL);
+         ok = try_insert (client, &error);
+         mongoc_client_destroy (client);
+      }
+
+      ASSERT_OR_PRINT (ok, error);
+      mongoc_uri_destroy (uri);
+   }
+
+   // Test auth fails with no client certificate:
+   {
+      // Create URI:
+      mongoc_uri_t *uri = get_x509_uri ();
+      {
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA));
+      }
+
+      // Try auth:
+      bson_error_t error = {0};
+      bool ok;
+      {
+         mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL);
+         ok = try_insert (client, &error);
+         mongoc_client_destroy (client);
+      }
+
+      ASSERT (!ok);
+      ASSERT_ERROR_CONTAINS (error,
+                             MONGOC_ERROR_CLIENT,
+                             MONGOC_ERROR_CLIENT_AUTHENTICATE,
+                             "" /* message differs between server versions */);
+      mongoc_uri_destroy (uri);
+   }
+
+   // Test auth works with explicit username:
+   {
+      // Create URI:
+      mongoc_uri_t *uri = get_x509_uri ();
+      {
+         ASSERT (mongoc_uri_set_username (uri, "C=US,ST=New York,L=New York City,O=MDB,OU=Drivers,CN=client"));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCERTIFICATEKEYFILE, CERT_CLIENT));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA));
+      }
+
+      // Try auth:
+      bson_error_t error = {0};
+      bool ok;
+      {
+         mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL);
+         ok = try_insert (client, &error);
+         mongoc_client_destroy (client);
+      }
+
+      ASSERT_OR_PRINT (ok, error);
+      mongoc_uri_destroy (uri);
+   }
+
+   // Test auth fails with wrong username:
+   {
+      // Create URI:
+      mongoc_uri_t *uri = get_x509_uri ();
+      {
+         ASSERT (mongoc_uri_set_username (uri, "bad"));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCERTIFICATEKEYFILE, CERT_CLIENT));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA));
+      }
+
+      // Try auth:
+      bson_error_t error = {0};
+      bool ok;
+      {
+         mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL);
+         ok = try_insert (client, &error);
+         mongoc_client_destroy (client);
+      }
+
+      ASSERT (!ok);
+      ASSERT_ERROR_CONTAINS (error,
+                             MONGOC_ERROR_CLIENT,
+                             MONGOC_ERROR_CLIENT_AUTHENTICATE,
+                             "" /* message differs between server versions */);
+      mongoc_uri_destroy (uri);
+   }
+
+   // Test auth fails with correct username but wrong certificate:
+   {
+      // Create URI:
+      mongoc_uri_t *uri = get_x509_uri ();
+      {
+         ASSERT (mongoc_uri_set_username (uri, "C=US,ST=New York,L=New York City,O=MDB,OU=Drivers,CN=client"));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCERTIFICATEKEYFILE, CERT_SERVER));
+         ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA));
+      }
+
+      // Try auth:
+      bson_error_t error = {0};
+      bool ok;
+      {
+         mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL);
+         ok = try_insert (client, &error);
+         mongoc_client_destroy (client);
+      }
+
+      ASSERT (!ok);
+      ASSERT_ERROR_CONTAINS (error,
+                             MONGOC_ERROR_CLIENT,
+                             MONGOC_ERROR_CLIENT_AUTHENTICATE,
+                             "" /* message differs between server versions */);
+      mongoc_uri_destroy (uri);
+   }
+   drop_x509_user (false);
+}
+#endif // MONGOC_ENABLE_SSL
+
 void
 test_x509_install (TestSuite *suite)
 {
-#if defined(MONGOC_ENABLE_SSL) && !defined(MONGOC_ENABLE_SSL_LIBRESSL)
-   TestSuite_Add (suite, "/X509/extract_subject", test_extract_subject);
+#ifdef MONGOC_ENABLE_SSL
+   TestSuite_AddFull (suite, "/X509/auth", test_x509_auth, NULL, NULL, test_framework_skip_if_no_auth);
 #endif
 
 #ifdef MONGOC_ENABLE_OCSP_OPENSSL

Reply to: