Mark a session defunct if an attempt to send a message on it fails outright. (#26778)

This is most likely a "No route to host" error or something like that, which
_may_ get cleared up with a new dns-sd resolve and session re-establishment.
diff --git a/src/messaging/ExchangeContext.cpp b/src/messaging/ExchangeContext.cpp
index bcdd4ea..05d2b5f 100644
--- a/src/messaging/ExchangeContext.cpp
+++ b/src/messaging/ExchangeContext.cpp
@@ -203,6 +203,7 @@
             return CHIP_ERROR_MISSING_SECURE_SESSION;
         }
 
+        SessionHandle session = GetSessionHandle();
         CHIP_ERROR err;
 
 #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
@@ -213,17 +214,29 @@
         else
         {
 #endif
-            err = mDispatch.SendMessage(GetExchangeMgr()->GetSessionManager(), mSession.Get().Value(), mExchangeId, IsInitiator(),
+            err = mDispatch.SendMessage(GetExchangeMgr()->GetSessionManager(), session, mExchangeId, IsInitiator(),
                                         GetReliableMessageContext(), reliableTransmissionRequested, protocolId, msgType,
                                         std::move(msgBuf));
 #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
         }
 #endif
-        // We should only cancel the response timer if the ExchangeContext fails to send the message that starts the response timer.
-        if (err != CHIP_NO_ERROR && startedResponseTimer)
+        if (err != CHIP_NO_ERROR)
         {
-            CancelResponseTimer();
-            SetResponseExpected(false);
+            // We should only cancel the response timer if the ExchangeContext fails to send the message that starts the response
+            // timer.
+            if (startedResponseTimer)
+            {
+                CancelResponseTimer();
+                SetResponseExpected(false);
+            }
+
+            // If we can't even send a message (send failed with a non-transient
+            // error), mark the session as defunct, just like we would if we
+            // thought we sent the message and never got a response.
+            if (session->IsSecureSession() && session->AsSecureSession()->IsCASESession())
+            {
+                session->AsSecureSession()->MarkAsDefunct();
+            }
         }
 
         // Standalone acks are not application-level message sends.