Skip to content

Commit d3e9693

Browse files
sanityclaude
andcommitted
fix(transport): handle duplicate intro packets during gateway handshake
When a peer restarts with a new identity, the gateway correctly detects this (issue #2277 fix) and creates a new gateway_connection. However, the peer sends multiple RSA intro packets for NAT traversal reliability. After the first intro triggers the new gateway_connection, subsequent intro packets get routed to the ongoing handler instead of being ignored. The handler expected a symmetric ACK response but received a 256-byte RSA intro packet, causing "invalid symmetric key" error. This fix modifies the gateway_connection to: 1. Wait for packets in a loop with a deadline 2. Ignore 256-byte packets (likely duplicate RSA intros) 3. Continue waiting for the real symmetric ACK response 4. Only error out after the deadline expires Fixes #2292 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6d8f026 commit d3e9693

File tree

1 file changed

+58
-22
lines changed

1 file changed

+58
-22
lines changed

crates/core/src/transport/connection_handler.rs

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -893,32 +893,68 @@ impl<S: Socket> UdpPacketsListener<S> {
893893
.await
894894
.map_err(|_| TransportError::ChannelClosed)?;
895895

896-
// wait until the remote sends the ack packet
897-
let timeout = tokio::time::timeout(Duration::from_secs(5), next_inbound.recv_async());
898-
match timeout.await {
899-
Ok(Ok(packet)) => {
900-
let _ = packet.try_decrypt_sym(&inbound_key).map_err(|_| {
901-
tracing::debug!(
902-
peer_addr = %remote_addr,
903-
direction = "inbound",
904-
packet_len = packet.data().len(),
905-
"Failed to decrypt packet with inbound key"
906-
);
907-
TransportError::ConnectionEstablishmentFailure {
908-
cause: "invalid symmetric key".into(),
909-
}
910-
})?;
911-
}
912-
Ok(Err(_)) => {
913-
return Err(TransportError::ConnectionEstablishmentFailure {
914-
cause: "connection closed".into(),
915-
});
916-
}
917-
Err(_) => {
896+
// Wait for the remote to send the ACK packet.
897+
// Issue #2292: The peer may send multiple RSA intro packets for NAT traversal reliability.
898+
// These duplicate intro packets (256 bytes) arrive on the ongoing_gw_connections channel
899+
// and would fail symmetric decryption. We must ignore them and wait for the real ACK.
900+
let deadline = tokio::time::Instant::now() + Duration::from_secs(5);
901+
loop {
902+
let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
903+
if remaining.is_zero() {
918904
return Err(TransportError::ConnectionEstablishmentFailure {
919905
cause: "connection timed out waiting for response".into(),
920906
});
921907
}
908+
909+
let timeout_result =
910+
tokio::time::timeout(remaining, next_inbound.recv_async()).await;
911+
912+
match timeout_result {
913+
Ok(Ok(packet)) => {
914+
// Issue #2292: Check if this is a duplicate RSA intro packet (256 bytes).
915+
// The peer sends multiple intro packets for reliability during NAT traversal.
916+
// After we detect a new identity and create a new gateway_connection,
917+
// these duplicate intros get routed here instead of creating another connection.
918+
// We must ignore them and continue waiting for the actual ACK response.
919+
if packet.data().len() == RSA_INTRO_PACKET_SIZE {
920+
tracing::debug!(
921+
peer_addr = %remote_addr,
922+
direction = "inbound",
923+
packet_len = packet.data().len(),
924+
"Ignoring 256-byte packet during gateway handshake (likely duplicate RSA intro)"
925+
);
926+
continue;
927+
}
928+
929+
// Try to decrypt as symmetric ACK response
930+
match packet.try_decrypt_sym(&inbound_key) {
931+
Ok(_) => {
932+
// Successfully decrypted - this is the ACK we're waiting for
933+
break;
934+
}
935+
Err(_) => {
936+
tracing::debug!(
937+
peer_addr = %remote_addr,
938+
direction = "inbound",
939+
packet_len = packet.data().len(),
940+
"Failed to decrypt packet with inbound key, continuing to wait"
941+
);
942+
// Continue waiting for a valid ACK
943+
continue;
944+
}
945+
}
946+
}
947+
Ok(Err(_)) => {
948+
return Err(TransportError::ConnectionEstablishmentFailure {
949+
cause: "connection closed".into(),
950+
});
951+
}
952+
Err(_) => {
953+
return Err(TransportError::ConnectionEstablishmentFailure {
954+
cause: "connection timed out waiting for response".into(),
955+
});
956+
}
957+
}
922958
}
923959

924960
let sent_tracker = Arc::new(parking_lot::Mutex::new(SentPacketTracker::new()));

0 commit comments

Comments
 (0)