From a7b094a1284fd1fa9c8aaf84598e5ee12ed6041d Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Wed, 25 Mar 2026 11:18:25 +0100
Subject: [PATCH] fix: cache discovered host to prevent repeated subnet scan floods

---
 lib/services/mqtt_service.dart |   36 ++++++++++++++++++++----------------
 1 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart
index 60930a5..b74b7e1 100644
--- a/lib/services/mqtt_service.dart
+++ b/lib/services/mqtt_service.dart
@@ -44,6 +44,7 @@
   ConnectionStatus _status = ConnectionStatus.disconnected;
   bool _intentionalClose = false;
   String? _clientId;
+  String? _lastDiscoveredHost;
   StreamSubscription? _updatesSub;
 
   // Message deduplication
@@ -104,12 +105,15 @@
 
     final clientId = await _getClientId();
 
-    // Connection order: local → Bonjour → VPN → remote
+    // Connection order: local → cached discovery → Bonjour/scan → VPN → remote
     final attempts = <MapEntry<String, int>>[];  // host → timeout ms
     if (config.localHost != null && config.localHost!.isNotEmpty) {
       attempts.add(MapEntry(config.localHost!, 2500));
     }
-    // Bonjour placeholder — inserted dynamically below
+    // Try cached discovered host before scanning again
+    if (_lastDiscoveredHost != null) {
+      attempts.add(MapEntry(_lastDiscoveredHost!, 3000));
+    }
     if (config.vpnHost != null && config.vpnHost!.isNotEmpty) {
       attempts.add(MapEntry(config.vpnHost!, 3000));
     }
@@ -118,7 +122,6 @@
     }
     _mqttLog('MQTT: attempts=${attempts.map((e) => e.key).join(", ")} port=${config.port}');
 
-    // Try configured local host first
     for (final attempt in attempts) {
       if (_intentionalClose) return;
       _mqttLog('MQTT: trying ${attempt.key}:${config.port}');
@@ -127,21 +130,22 @@
       } catch (e) {
         _mqttLog('MQTT: ${attempt.key} error=$e');
       }
+    }
 
-      // After local host fails, try Bonjour discovery before VPN/remote
-      if (attempt.key == config.localHost && !_intentionalClose) {
-        _mqttLog('MQTT: trying Bonjour discovery...');
-        final bonjourHost = await _discoverViaMdns();
-        if (bonjourHost != null && !_intentionalClose) {
-          _mqttLog('MQTT: Bonjour found $bonjourHost');
-          try {
-            if (await _tryConnect(bonjourHost, clientId, timeout: 3000)) return;
-          } catch (e) {
-            _mqttLog('MQTT: Bonjour host $bonjourHost error=$e');
-          }
-        } else {
-          _mqttLog('MQTT: Bonjour discovery returned nothing');
+    // All configured hosts failed — try Bonjour/subnet scan (only once, not on retry)
+    if (_lastDiscoveredHost == null && !_intentionalClose) {
+      _mqttLog('MQTT: trying Bonjour/subnet discovery...');
+      final discovered = await _discoverViaMdns();
+      if (discovered != null && !_intentionalClose) {
+        _lastDiscoveredHost = discovered;
+        _mqttLog('MQTT: discovered $discovered, connecting...');
+        try {
+          if (await _tryConnect(discovered, clientId, timeout: 3000)) return;
+        } catch (e) {
+          _mqttLog('MQTT: discovered host $discovered error=$e');
         }
+      } else {
+        _mqttLog('MQTT: discovery returned nothing');
       }
     }
 

--
Gitblit v1.3.1