From ca51e66951767090e59ba214d0cdaff423bc3b4d Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:23:33 +0200 Subject: [PATCH] fix: sync own messages --- .../android/mesh/BluetoothConnectionManager.kt | 9 ++++++++- .../com/bitchat/android/mesh/MessageHandler.kt | 12 +++++++----- .../bitchat/android/mesh/SecurityManager.kt | 12 +++++++++--- .../bitchat/android/sync/GossipSyncManager.kt | 18 +++++++++++++++++- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt index 19c103a3c..c96fdd7ae 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt @@ -51,7 +51,14 @@ class BluetoothConnectionManager( } } - if (peerID == myPeerID) return // Ignore messages from self + // Normally ignore messages from self, but accept local-only sync responses (TTL=0) + if (peerID == myPeerID) { + if (packet.ttl == 0u.toUByte()) { + Log.d(TAG, "Accepting self-sender packet with TTL=0 (likely SYNC response)") + } else { + return // Ignore non-sync self packets + } + } delegate?.onPacketReceived(packet, peerID, device) } diff --git a/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt b/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt index c2faffea2..cda51b1f6 100644 --- a/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt +++ b/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt @@ -179,7 +179,7 @@ class MessageHandler(private val myPeerID: String) { val packet = routed.packet val peerID = routed.peerID ?: "unknown" - if (peerID == myPeerID) return false + if (peerID == myPeerID && routed.packet.ttl != 0u.toUByte()) return false // Try to decode as iOS-compatible IdentityAnnouncement with TLV format val announcement = IdentityAnnouncement.decode(packet.payload) @@ -304,7 +304,8 @@ class MessageHandler(private val myPeerID: String) { suspend fun handleMessage(routed: RoutedPacket) { val packet = routed.packet val peerID = routed.peerID ?: "unknown" - if (peerID == myPeerID) return + // IMPORTANT: Allow TTL=0 self packets (sync responses) to flow through + if (peerID == myPeerID && packet.ttl != 0u.toUByte()) return val senderNickname = delegate?.getPeerNickname(peerID) if (senderNickname != null) { Log.d(TAG, "Received message from $senderNickname") @@ -329,10 +330,11 @@ class MessageHandler(private val myPeerID: String) { private suspend fun handleBroadcastMessage(routed: RoutedPacket) { val packet = routed.packet val peerID = routed.peerID ?: "unknown" - + // Enforce: only accept public messages from verified peers we know + // EXCEPTION: allow our own messages (peerID == myPeerID), e.g. TTL=0 sync responses val peerInfo = delegate?.getPeerInfo(peerID) - if (peerInfo == null || !peerInfo.isVerifiedNickname) { + if (peerID != myPeerID && (peerInfo == null || !peerInfo.isVerifiedNickname)) { Log.w(TAG, "🚫 Dropping public message from unverified or unknown peer ${peerID.take(8)}...") return } @@ -340,7 +342,7 @@ class MessageHandler(private val myPeerID: String) { try { // Parse message val message = BitchatMessage( - sender = delegate?.getPeerNickname(peerID) ?: "unknown", + sender = if (peerID == myPeerID) (delegate?.getMyNickname() ?: myPeerID) else (delegate?.getPeerNickname(peerID) ?: "unknown"), content = String(packet.payload, Charsets.UTF_8), senderPeerID = peerID, timestamp = Date(packet.timestamp.toLong()) diff --git a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt index f763424a7..57790b86c 100644 --- a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt @@ -44,10 +44,16 @@ class SecurityManager(private val encryptionService: EncryptionService, private * Validate packet security (timestamp, replay attacks, duplicates, signatures) */ fun validatePacket(packet: BitchatPacket, peerID: String): Boolean { - // Skip validation for our own packets + // Allow our own packets only when they are local-only (TTL=0) — used by SYNC responses if (peerID == myPeerID) { - Log.d(TAG, "Skipping validation for our own packet") - return false + val isLocalOnly = packet.ttl == 0u.toUByte() + if (isLocalOnly) { + Log.d(TAG, "Accepting self packet due to TTL=0 (SYNC/local-only)") + // Intentionally continue to duplicate detection to avoid showing duplicates + } else { + Log.d(TAG, "Dropping non-local self packet during validation") + return false + } } // Validate packet payload diff --git a/app/src/main/java/com/bitchat/android/sync/GossipSyncManager.kt b/app/src/main/java/com/bitchat/android/sync/GossipSyncManager.kt index 38d23a917..898a7e36a 100644 --- a/app/src/main/java/com/bitchat/android/sync/GossipSyncManager.kt +++ b/app/src/main/java/com/bitchat/android/sync/GossipSyncManager.kt @@ -156,8 +156,12 @@ class GossipSyncManager( return GCSFilter.contains(sorted, v) } + // Track what we send per original sender peerID (hex) + data class SentStats(var announce: Boolean = false, var msgCount: Int = 0) + val sentBySender = mutableMapOf() + // 1) Announcements: send latest per peerID if remote doesn't have them - for ((_, pair) in latestAnnouncementByPeer.entries) { + for ((senderHex, pair) in latestAnnouncementByPeer.entries) { val (id, pkt) = pair val idBytes = hexToBytes(id) if (!mightContain(idBytes)) { @@ -165,6 +169,7 @@ class GossipSyncManager( val toSend = pkt.copy(ttl = 0u) delegate?.sendPacketToPeer(fromPeerID, toSend) Log.d(TAG, "Sent sync announce: Type ${toSend.type} from ${toSend.senderID.toHexString()} to $fromPeerID packet id ${idBytes.toHexString()}") + sentBySender.getOrPut(senderHex) { SentStats() }.apply { announce = true } } } @@ -176,6 +181,17 @@ class GossipSyncManager( val toSend = pkt.copy(ttl = 0u) delegate?.sendPacketToPeer(fromPeerID, toSend) Log.d(TAG, "Sent sync message: Type ${toSend.type} to $fromPeerID packet id ${idBytes.toHexString()}") + val senderHex = pkt.senderID.joinToString("") { b -> "%02x".format(b) } + sentBySender.getOrPut(senderHex) { SentStats() }.apply { msgCount += 1 } + } + } + + // Summary log per sender (original packet senderID) + if (sentBySender.isEmpty()) { + Log.d(TAG, "SYNC_RESPONSE summary to $fromPeerID: nothing to send") + } else { + for ((senderHex, stats) in sentBySender) { + Log.d(TAG, "SYNC_RESPONSE summary to $fromPeerID for sender $senderHex: announce=${stats.announce}, messages=${stats.msgCount}") } } }