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 7fb2ce14..8ef97f14 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt @@ -9,6 +9,8 @@ import android.os.ParcelUuid import android.util.Log import androidx.core.app.ActivityCompat import com.bitchat.android.protocol.BitchatPacket +import com.bitchat.android.protocol.SpecialRecipients +import com.bitchat.android.model.RoutedPacket import kotlinx.coroutines.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -53,6 +55,7 @@ class BluetoothConnectionManager( // Simplified connection tracking - reduced memory footprint private val connectedDevices = ConcurrentHashMap() private val subscribedDevices = CopyOnWriteArrayList() + public val addressPeerMap = ConcurrentHashMap() // Connection attempt tracking with automatic cleanup private val pendingConnections = ConcurrentHashMap() @@ -199,47 +202,106 @@ class BluetoothConnectionManager( powerManager.setAppBackgroundState(inBackground) } + // Function to send data to a single device (server side) + private fun notifyDevice(device: BluetoothDevice, data: ByteArray): Boolean { + return try { + characteristic?.let { char -> + char.value = data + val result = gattServer?.notifyCharacteristicChanged(device, char, false) ?: false + result + } ?: false + } catch (e: Exception) { + Log.w(TAG, "Error sending to server connection ${device.address}: ${e.message}") + connectionScope.launch { + delay(CLEANUP_DELAY) + subscribedDevices.remove(device) + addressPeerMap.remove(device.address) + } + false + } + } + + // Function to send data to a single device (client side) + private fun writeToDeviceConn(deviceConn: DeviceConnection, data: ByteArray): Boolean { + return try { + deviceConn.characteristic?.let { char -> + char.value = data + val result = deviceConn.gatt?.writeCharacteristic(char) ?: false + result + } ?: false + } catch (e: Exception) { + Log.w(TAG, "Error sending to client connection ${deviceConn.device.address}: ${e.message}") + connectionScope.launch { + delay(CLEANUP_DELAY) + cleanupDeviceConnection(deviceConn.device.address) + } + false + } + } + /** * Broadcast packet to connected devices with connection limit enforcement */ - fun broadcastPacket(packet: BitchatPacket) { + fun broadcastPacket(routed: RoutedPacket) { + val packet = routed.packet + if (!isActive) return val data = packet.toBinaryData() ?: return + if (packet.recipientID != SpecialRecipients.BROADCAST) { + val recipientID = packet.recipientID?.let { + String(it).replace("\u0000", "").trim() + } ?: "" + + // Try to find the recipient in server connections (subscribedDevices) + val targetDevice = subscribedDevices.firstOrNull { addressPeerMap[it.address] == recipientID } + // If found, send directly + if (targetDevice != null) { + Log.d(TAG, "Send packet type ${packet.type} directly to target device for recipient $recipientID: ${targetDevice.address}") + if (notifyDevice(targetDevice, data)) + return // Sent, no need to continue + } + + // Try to find the recipient in client connections (connectedDevices) + val targetDeviceConn = connectedDevices.values.firstOrNull { addressPeerMap[it.device.address] == recipientID } + // If found, send directly + if (targetDeviceConn != null) { + Log.d(TAG, "Send packet type ${packet.type} directly to target client connection for recipient $recipientID: ${targetDeviceConn.device.address}") + if (writeToDeviceConn(targetDeviceConn, data)) + return // Sent, no need to continue + } + } + + // Else, continue with broadcasting to all devices Log.d(TAG, "Broadcasting packet type ${packet.type} to ${subscribedDevices.size} server + ${connectedDevices.size} client connections") - + + val senderID = String(packet.senderID).replace("\u0000", "") // Send to server connections (devices connected to our GATT server) subscribedDevices.forEach { device -> - try { - characteristic?.let { char -> - char.value = data - gattServer?.notifyCharacteristicChanged(device, char, false) - } - } catch (e: Exception) { - Log.w(TAG, "Error sending to server connection ${device.address}: ${e.message}") - // Clean up failed connection - connectionScope.launch { - delay(CLEANUP_DELAY) - subscribedDevices.remove(device) - } + if (device.address == routed.relayAddress) { + Log.d(TAG, "Skipping broadcast back to relayer: ${device.address}") + return@forEach } + if (addressPeerMap[device.address] == senderID) { + Log.d(TAG, "Skipping broadcast back to sender: ${device.address}") + return@forEach + } + notifyDevice(device, data) } // Send to client connections connectedDevices.values.forEach { deviceConn -> if (deviceConn.isClient && deviceConn.gatt != null && deviceConn.characteristic != null) { - try { - deviceConn.characteristic.value = data - deviceConn.gatt.writeCharacteristic(deviceConn.characteristic) - } catch (e: Exception) { - Log.w(TAG, "Error sending to client connection ${deviceConn.device.address}: ${e.message}") - // Clean up failed connection - connectionScope.launch { - delay(CLEANUP_DELAY) - cleanupDeviceConnection(deviceConn.device.address) - } + if (deviceConn.device.address == routed.relayAddress) { + Log.d(TAG, "Skipping broadcast back to relayer: ${deviceConn.device.address}") + return@forEach } + if (addressPeerMap[deviceConn.device.address] == senderID) { + Log.d(TAG, "Skipping broadcast back to sender: ${deviceConn.device.address}") + return@forEach + } + writeToDeviceConn(deviceConn, data) } } } @@ -916,6 +978,7 @@ class BluetoothConnectionManager( private fun cleanupDeviceConnection(deviceAddress: String) { connectedDevices.remove(deviceAddress)?.let { deviceConn -> subscribedDevices.removeAll { it.address == deviceAddress } + addressPeerMap.remove(deviceAddress) } // CRITICAL FIX: Always remove from pending connections when cleaning up // This prevents failed connections from blocking future attempts @@ -944,6 +1007,7 @@ class BluetoothConnectionManager( private fun clearAllConnections() { connectedDevices.clear() subscribedDevices.clear() + addressPeerMap.clear() pendingConnections.clear() } } diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt index 7732fd0f..87d3cb1d 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -5,6 +5,7 @@ import android.util.Log import com.bitchat.android.crypto.EncryptionService import com.bitchat.android.crypto.MessagePadding import com.bitchat.android.model.BitchatMessage +import com.bitchat.android.model.RoutedPacket import com.bitchat.android.model.DeliveryAck import com.bitchat.android.model.ReadReceipt import com.bitchat.android.protocol.BitchatPacket @@ -101,10 +102,14 @@ class BluetoothMeshService(private val context: Context) { // SecurityManager delegate for key exchange notifications securityManager.delegate = object : SecurityManagerDelegate { - override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) { + override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray, receivedAddress: String?) { // Notify delegate about key exchange completion so it can register peer fingerprint delegate?.registerPeerPublicKey(peerID, peerPublicKeyData) + receivedAddress?.let { address -> + connectionManager.addressPeerMap[address] = peerID + } + // Send announcement and cached messages after key exchange serviceScope.launch { delay(100) @@ -127,7 +132,7 @@ class BluetoothMeshService(private val context: Context) { } override fun sendPacket(packet: BitchatPacket) { - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) } } @@ -160,11 +165,11 @@ class BluetoothMeshService(private val context: Context) { // Packet operations override fun sendPacket(packet: BitchatPacket) { - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) } - override fun relayPacket(packet: BitchatPacket) { - connectionManager.broadcastPacket(packet) + override fun relayPacket(routed: RoutedPacket) { + connectionManager.broadcastPacket(routed) } override fun getBroadcastRecipient(): ByteArray { @@ -221,32 +226,32 @@ class BluetoothMeshService(private val context: Context) { peerManager.updatePeerLastSeen(peerID) } - override fun handleKeyExchange(packet: BitchatPacket, peerID: String): Boolean { - return runBlocking { securityManager.handleKeyExchange(packet, peerID) } + override fun handleKeyExchange(routed: RoutedPacket): Boolean { + return runBlocking { securityManager.handleKeyExchange(routed) } } - override fun handleAnnounce(packet: BitchatPacket, peerID: String) { - serviceScope.launch { messageHandler.handleAnnounce(packet, peerID) } + override fun handleAnnounce(routed: RoutedPacket) { + serviceScope.launch { messageHandler.handleAnnounce(routed) } } - override fun handleMessage(packet: BitchatPacket, peerID: String) { - serviceScope.launch { messageHandler.handleMessage(packet, peerID) } + override fun handleMessage(routed: RoutedPacket) { + serviceScope.launch { messageHandler.handleMessage(routed) } } - override fun handleLeave(packet: BitchatPacket, peerID: String) { - serviceScope.launch { messageHandler.handleLeave(packet, peerID) } + override fun handleLeave(routed: RoutedPacket) { + serviceScope.launch { messageHandler.handleLeave(routed) } } override fun handleFragment(packet: BitchatPacket): BitchatPacket? { return fragmentManager.handleFragment(packet) } - override fun handleDeliveryAck(packet: BitchatPacket, peerID: String) { - serviceScope.launch { messageHandler.handleDeliveryAck(packet, peerID) } + override fun handleDeliveryAck(routed: RoutedPacket) { + serviceScope.launch { messageHandler.handleDeliveryAck(routed) } } - override fun handleReadReceipt(packet: BitchatPacket, peerID: String) { - serviceScope.launch { messageHandler.handleReadReceipt(packet, peerID) } + override fun handleReadReceipt(routed: RoutedPacket) { + serviceScope.launch { messageHandler.handleReadReceipt(routed) } } override fun sendAnnouncementToPeer(peerID: String) { @@ -257,15 +262,15 @@ class BluetoothMeshService(private val context: Context) { storeForwardManager.sendCachedMessages(peerID) } - override fun relayPacket(packet: BitchatPacket) { - connectionManager.broadcastPacket(packet) + override fun relayPacket(routed: RoutedPacket) { + connectionManager.broadcastPacket(routed) } } // BluetoothConnectionManager delegates connectionManager.delegate = object : BluetoothConnectionManagerDelegate { override fun onPacketReceived(packet: BitchatPacket, peerID: String, device: android.bluetooth.BluetoothDevice?) { - packetProcessor.processPacket(packet, peerID) + packetProcessor.processPacket(RoutedPacket(packet, peerID, device?.address)) } override fun onDeviceConnected(device: android.bluetooth.BluetoothDevice) { @@ -370,7 +375,7 @@ class BluetoothMeshService(private val context: Context) { // Send with random delay and retry for reliability // delay(Random.nextLong(50, 500)) - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) } } } @@ -423,7 +428,7 @@ class BluetoothMeshService(private val context: Context) { // Send with delay delay(Random.nextLong(50, 500)) - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) } } catch (e: Exception) { @@ -449,13 +454,13 @@ class BluetoothMeshService(private val context: Context) { // Send multiple times for reliability delay(Random.nextLong(0, 500)) - connectionManager.broadcastPacket(announcePacket) + connectionManager.broadcastPacket(RoutedPacket(announcePacket)) delay(500 + Random.nextLong(0, 500)) - connectionManager.broadcastPacket(announcePacket) + connectionManager.broadcastPacket(RoutedPacket(announcePacket)) delay(1000 + Random.nextLong(0, 500)) - connectionManager.broadcastPacket(announcePacket) + connectionManager.broadcastPacket(RoutedPacket(announcePacket)) } } @@ -473,7 +478,7 @@ class BluetoothMeshService(private val context: Context) { payload = nickname.toByteArray() ) - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) peerManager.markPeerAsAnnouncedTo(peerID) } @@ -489,7 +494,7 @@ class BluetoothMeshService(private val context: Context) { payload = publicKeyData ) - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) Log.d(TAG, "Sent key exchange") } @@ -505,7 +510,7 @@ class BluetoothMeshService(private val context: Context) { payload = nickname.toByteArray() ) - connectionManager.broadcastPacket(packet) + connectionManager.broadcastPacket(RoutedPacket(packet)) } /** 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 d25caf1c..2cff0e6c 100644 --- a/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt +++ b/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt @@ -5,6 +5,7 @@ import com.bitchat.android.crypto.MessagePadding import com.bitchat.android.model.BitchatMessage import com.bitchat.android.model.DeliveryAck import com.bitchat.android.model.ReadReceipt +import com.bitchat.android.model.RoutedPacket import com.bitchat.android.protocol.BitchatPacket import com.bitchat.android.protocol.MessageType import kotlinx.coroutines.* @@ -30,7 +31,10 @@ class MessageHandler(private val myPeerID: String) { /** * Handle announce message */ - suspend fun handleAnnounce(packet: BitchatPacket, peerID: String): Boolean { + suspend fun handleAnnounce(routed: RoutedPacket): Boolean { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" + if (peerID == myPeerID) return false val nickname = String(packet.payload, Charsets.UTF_8) @@ -43,7 +47,7 @@ class MessageHandler(private val myPeerID: String) { if (packet.ttl > 1u) { val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte()) delay(Random.nextLong(100, 300)) - delegate?.relayPacket(relayPacket) + delegate?.relayPacket(RoutedPacket(relayPacket, peerID, routed.relayAddress)) } return isFirstAnnounce @@ -52,27 +56,31 @@ class MessageHandler(private val myPeerID: String) { /** * Handle broadcast or private message */ - suspend fun handleMessage(packet: BitchatPacket, peerID: String) { + suspend fun handleMessage(routed: RoutedPacket) { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" if (peerID == myPeerID) return val recipientID = packet.recipientID?.takeIf { !it.contentEquals(delegate?.getBroadcastRecipient()) } if (recipientID == null) { // BROADCAST MESSAGE - handleBroadcastMessage(packet, peerID) + handleBroadcastMessage(routed) } else if (String(recipientID).replace("\u0000", "") == myPeerID) { // PRIVATE MESSAGE FOR US handlePrivateMessage(packet, peerID) } else if (packet.ttl > 0u) { // RELAY MESSAGE - relayMessage(packet) + relayMessage(routed) } } /** * Handle broadcast message */ - private suspend fun handleBroadcastMessage(packet: BitchatPacket, peerID: String) { + private suspend fun handleBroadcastMessage(routed: RoutedPacket) { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" try { // Parse message val message = BitchatMessage.fromBinaryPayload(packet.payload) @@ -104,7 +112,7 @@ class MessageHandler(private val myPeerID: String) { } // Relay broadcast messages - relayMessage(packet) + relayMessage(routed) } catch (e: Exception) { Log.e(TAG, "Failed to process broadcast message: ${e.message}") @@ -162,7 +170,9 @@ class MessageHandler(private val myPeerID: String) { /** * Handle leave message */ - suspend fun handleLeave(packet: BitchatPacket, peerID: String) { + suspend fun handleLeave(routed: RoutedPacket) { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" val content = String(packet.payload, Charsets.UTF_8) if (content.startsWith("#")) { @@ -180,14 +190,16 @@ class MessageHandler(private val myPeerID: String) { // Relay if TTL > 0 if (packet.ttl > 1u) { val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte()) - delegate?.relayPacket(relayPacket) + delegate?.relayPacket(routed.copy(packet = relayPacket)) } } /** * Handle delivery acknowledgment */ - suspend fun handleDeliveryAck(packet: BitchatPacket, peerID: String) { + suspend fun handleDeliveryAck(routed: RoutedPacket) { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" if (packet.recipientID != null && String(packet.recipientID).replace("\u0000", "") == myPeerID) { try { val decryptedData = delegate?.decryptFromPeer(packet.payload, peerID) @@ -200,17 +212,19 @@ class MessageHandler(private val myPeerID: String) { } catch (e: Exception) { Log.e(TAG, "Failed to decrypt delivery ACK: ${e.message}") } - } else if (packet.ttl > 0u) { + } else if (packet.ttl > 0u && String(packet.senderID).replace("\u0000", "") != myPeerID) { // Relay val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte()) - delegate?.relayPacket(relayPacket) + delegate?.relayPacket(routed.copy(packet = relayPacket)) } } /** * Handle read receipt */ - suspend fun handleReadReceipt(packet: BitchatPacket, peerID: String) { + suspend fun handleReadReceipt(routed: RoutedPacket) { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" if (packet.recipientID != null && String(packet.recipientID).replace("\u0000", "") == myPeerID) { try { val decryptedData = delegate?.decryptFromPeer(packet.payload, peerID) @@ -223,17 +237,18 @@ class MessageHandler(private val myPeerID: String) { } catch (e: Exception) { Log.e(TAG, "Failed to decrypt read receipt: ${e.message}") } - } else if (packet.ttl > 0u) { + } else if (packet.ttl > 0u && String(packet.senderID).replace("\u0000", "") != myPeerID) { // Relay val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte()) - delegate?.relayPacket(relayPacket) + delegate?.relayPacket(routed.copy(packet = relayPacket)) } } /** * Relay message with adaptive probability (same as iOS) */ - private suspend fun relayMessage(packet: BitchatPacket) { + private suspend fun relayMessage(routed: RoutedPacket) { + val packet = routed.packet if (packet.ttl == 0u.toUByte()) return val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte()) @@ -253,7 +268,7 @@ class MessageHandler(private val myPeerID: String) { if (shouldRelay) { val delay = Random.nextLong(50, 500) // Random delay like iOS delay(delay) - delegate?.relayPacket(relayPacket) + delegate?.relayPacket(routed.copy(packet = relayPacket)) } } @@ -326,7 +341,7 @@ interface MessageHandlerDelegate { // Packet operations fun sendPacket(packet: BitchatPacket) - fun relayPacket(packet: BitchatPacket) + fun relayPacket(routed: RoutedPacket) fun getBroadcastRecipient(): ByteArray // Cryptographic operations diff --git a/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt b/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt index 35084d27..dc43ed7a 100644 --- a/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt +++ b/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt @@ -3,6 +3,7 @@ package com.bitchat.android.mesh import android.util.Log import com.bitchat.android.protocol.BitchatPacket import com.bitchat.android.protocol.MessageType +import com.bitchat.android.model.RoutedPacket import kotlinx.coroutines.* /** @@ -24,16 +25,19 @@ class PacketProcessor(private val myPeerID: String) { /** * Process received packet - main entry point for all incoming packets */ - fun processPacket(packet: BitchatPacket, peerID: String) { + fun processPacket(routed: RoutedPacket) { processorScope.launch { - handleReceivedPacket(packet, peerID) + handleReceivedPacket(routed) } } /** * Handle received packet - core protocol logic (exact same as iOS) */ - private suspend fun handleReceivedPacket(packet: BitchatPacket, peerID: String) { + private suspend fun handleReceivedPacket(routed: RoutedPacket) { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" + // Basic validation and security checks if (!delegate?.validatePacketSecurity(packet, peerID)!!) { Log.d(TAG, "Packet failed security validation from $peerID") @@ -47,15 +51,15 @@ class PacketProcessor(private val myPeerID: String) { // Process based on message type (exact same logic as iOS) when (MessageType.fromValue(packet.type)) { - MessageType.KEY_EXCHANGE -> handleKeyExchange(packet, peerID) - MessageType.ANNOUNCE -> handleAnnounce(packet, peerID) - MessageType.MESSAGE -> handleMessage(packet, peerID) - MessageType.LEAVE -> handleLeave(packet, peerID) + MessageType.KEY_EXCHANGE -> handleKeyExchange(routed) + MessageType.ANNOUNCE -> handleAnnounce(routed) + MessageType.MESSAGE -> handleMessage(routed) + MessageType.LEAVE -> handleLeave(routed) MessageType.FRAGMENT_START, MessageType.FRAGMENT_CONTINUE, - MessageType.FRAGMENT_END -> handleFragment(packet, peerID) - MessageType.DELIVERY_ACK -> handleDeliveryAck(packet, peerID) - MessageType.READ_RECEIPT -> handleReadReceipt(packet, peerID) + MessageType.FRAGMENT_END -> handleFragment(routed) + MessageType.DELIVERY_ACK -> handleDeliveryAck(routed) + MessageType.READ_RECEIPT -> handleReadReceipt(routed) else -> { Log.w(TAG, "Unknown message type: ${packet.type}") } @@ -65,10 +69,11 @@ class PacketProcessor(private val myPeerID: String) { /** * Handle key exchange message */ - private suspend fun handleKeyExchange(packet: BitchatPacket, peerID: String) { + private suspend fun handleKeyExchange(routed: RoutedPacket) { + val peerID = routed.peerID ?: "unknown" Log.d(TAG, "Processing key exchange from $peerID") - val success = delegate?.handleKeyExchange(packet, peerID) ?: false + val success = delegate?.handleKeyExchange(routed) ?: false if (success) { // Key exchange successful, send announce and cached messages @@ -83,60 +88,60 @@ class PacketProcessor(private val myPeerID: String) { /** * Handle announce message */ - private suspend fun handleAnnounce(packet: BitchatPacket, peerID: String) { - Log.d(TAG, "Processing announce from $peerID") - delegate?.handleAnnounce(packet, peerID) + private suspend fun handleAnnounce(routed: RoutedPacket) { + Log.d(TAG, "Processing announce from ${routed.peerID}") + delegate?.handleAnnounce(routed) } /** * Handle regular message */ - private suspend fun handleMessage(packet: BitchatPacket, peerID: String) { - Log.d(TAG, "Processing message from $peerID") - delegate?.handleMessage(packet, peerID) + private suspend fun handleMessage(routed: RoutedPacket) { + Log.d(TAG, "Processing message from ${routed.peerID}") + delegate?.handleMessage(routed) } /** * Handle leave message */ - private suspend fun handleLeave(packet: BitchatPacket, peerID: String) { - Log.d(TAG, "Processing leave from $peerID") - delegate?.handleLeave(packet, peerID) + private suspend fun handleLeave(routed: RoutedPacket) { + Log.d(TAG, "Processing leave from ${routed.peerID}") + delegate?.handleLeave(routed) } /** * Handle message fragments */ - private suspend fun handleFragment(packet: BitchatPacket, peerID: String) { - Log.d(TAG, "Processing fragment from $peerID") + private suspend fun handleFragment(routed: RoutedPacket) { + Log.d(TAG, "Processing fragment from ${routed.peerID}") - val reassembledPacket = delegate?.handleFragment(packet) + val reassembledPacket = delegate?.handleFragment(routed.packet) if (reassembledPacket != null) { Log.d(TAG, "Fragment reassembled, processing complete message") - handleReceivedPacket(reassembledPacket, peerID) + handleReceivedPacket(RoutedPacket(reassembledPacket, routed.peerID, routed.relayAddress)) } // Relay fragment regardless of reassembly - if (packet.ttl > 0u) { - val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte()) - delegate?.relayPacket(relayPacket) + if (routed.packet.ttl > 0u) { + val relayPacket = routed.packet.copy(ttl = (routed.packet.ttl - 1u).toUByte()) + delegate?.relayPacket(RoutedPacket(relayPacket, routed.peerID, routed.relayAddress)) } } /** * Handle delivery acknowledgment */ - private suspend fun handleDeliveryAck(packet: BitchatPacket, peerID: String) { - Log.d(TAG, "Processing delivery ACK from $peerID") - delegate?.handleDeliveryAck(packet, peerID) + private suspend fun handleDeliveryAck(routed: RoutedPacket) { + Log.d(TAG, "Processing delivery ACK from ${routed.peerID}") + delegate?.handleDeliveryAck(routed) } /** * Handle read receipt */ - private suspend fun handleReadReceipt(packet: BitchatPacket, peerID: String) { - Log.d(TAG, "Processing read receipt from $peerID") - delegate?.handleReadReceipt(packet, peerID) + private suspend fun handleReadReceipt(routed: RoutedPacket) { + Log.d(TAG, "Processing read receipt from ${routed.peerID}") + delegate?.handleReadReceipt(routed) } /** @@ -169,16 +174,16 @@ interface PacketProcessorDelegate { fun updatePeerLastSeen(peerID: String) // Message type handlers - fun handleKeyExchange(packet: BitchatPacket, peerID: String): Boolean - fun handleAnnounce(packet: BitchatPacket, peerID: String) - fun handleMessage(packet: BitchatPacket, peerID: String) - fun handleLeave(packet: BitchatPacket, peerID: String) + fun handleKeyExchange(routed: RoutedPacket): Boolean + fun handleAnnounce(routed: RoutedPacket) + fun handleMessage(routed: RoutedPacket) + fun handleLeave(routed: RoutedPacket) fun handleFragment(packet: BitchatPacket): BitchatPacket? - fun handleDeliveryAck(packet: BitchatPacket, peerID: String) - fun handleReadReceipt(packet: BitchatPacket, peerID: String) + fun handleDeliveryAck(routed: RoutedPacket) + fun handleReadReceipt(routed: RoutedPacket) // Communication fun sendAnnouncementToPeer(peerID: String) fun sendCachedMessages(peerID: String) - fun relayPacket(packet: BitchatPacket) + fun relayPacket(routed: RoutedPacket) } 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 65b74e42..50c0ce92 100644 --- a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt @@ -4,6 +4,7 @@ import android.util.Log import com.bitchat.android.crypto.EncryptionService import com.bitchat.android.protocol.BitchatPacket import com.bitchat.android.protocol.MessageType +import com.bitchat.android.model.RoutedPacket import kotlinx.coroutines.* import java.util.* import kotlin.collections.mutableSetOf @@ -88,7 +89,10 @@ class SecurityManager(private val encryptionService: EncryptionService, private /** * Handle key exchange packet */ - suspend fun handleKeyExchange(packet: BitchatPacket, peerID: String): Boolean { + suspend fun handleKeyExchange(routed: RoutedPacket): Boolean { + val packet = routed.packet + val peerID = routed.peerID ?: "unknown" + if (peerID == myPeerID) return false if (packet.payload.isEmpty()) { @@ -113,7 +117,7 @@ class SecurityManager(private val encryptionService: EncryptionService, private Log.d(TAG, "Successfully processed key exchange from $peerID") // Notify delegate - delegate?.onKeyExchangeCompleted(peerID, packet.payload) + delegate?.onKeyExchangeCompleted(peerID, packet.payload, routed.relayAddress) return true @@ -315,5 +319,5 @@ class SecurityManager(private val encryptionService: EncryptionService, private * Delegate interface for security manager callbacks */ interface SecurityManagerDelegate { - fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) + fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray, receivedAddress: String?) } diff --git a/app/src/main/java/com/bitchat/android/model/RoutedPacket.kt b/app/src/main/java/com/bitchat/android/model/RoutedPacket.kt new file mode 100644 index 00000000..59697360 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/model/RoutedPacket.kt @@ -0,0 +1,13 @@ +package com.bitchat.android.model + +import com.bitchat.android.protocol.BitchatPacket + +/** + * Represents a routed packet with additional metadata + * Used for processing and routing packets in the mesh network + */ +data class RoutedPacket( + val packet: BitchatPacket, + val peerID: String? = null, // Who sent it (parsed from packet.senderID) + val relayAddress: String? = null // Address it came from (for avoiding loopback) +) \ No newline at end of file