mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-12 05:29:57 +02:00
fix(core): tear down shares on forget/block and fail-closed blocks
Cancel targeted protocol shares synchronously before await, and treat block lookup errors as denied so store failures cannot admit blocked peers. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -183,7 +183,7 @@ impl ApprovalService {
|
|||||||
.contacts()
|
.contacts()
|
||||||
.is_blocked(&remote_endpoint_id)
|
.is_blocked(&remote_endpoint_id)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(false)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
// Indistinguishable from other refusals so probing cannot detect blocks.
|
// Indistinguishable from other refusals so probing cannot detect blocks.
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -129,10 +129,11 @@ impl DeviceRelationshipService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn is_blocked(&self, endpoint_id: &str) -> bool {
|
pub(super) async fn is_blocked(&self, endpoint_id: &str) -> bool {
|
||||||
|
// Fail closed: a store error must not admit blocked traffic.
|
||||||
self.contacts()
|
self.contacts()
|
||||||
.is_blocked(endpoint_id)
|
.is_blocked(endpoint_id)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(false)
|
.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drop orphaned relationship grant secrets and disable rows whose secrets are gone.
|
/// Drop orphaned relationship grant secrets and disable rows whose secrets are gone.
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ impl CoreInner {
|
|||||||
.contacts()
|
.contacts()
|
||||||
.is_blocked(&peer_endpoint_id)
|
.is_blocked(&peer_endpoint_id)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(false)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ impl CoreInner {
|
|||||||
.device_relationships
|
.device_relationships
|
||||||
.forget(peer_endpoint_id.clone())
|
.forget(peer_endpoint_id.clone())
|
||||||
.await?;
|
.await?;
|
||||||
// Targeted transfers for this relationship only (ticket 10 fills in).
|
// Targeted transfers for this relationship only.
|
||||||
// Invitation-domain shares are deliberately not cancelled here.
|
// Invitation-domain shares are deliberately not cancelled here.
|
||||||
self.cancel_targeted_transfers_for_peer(&peer_endpoint_id)
|
self.cancel_targeted_transfers_for_peer(&peer_endpoint_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -62,6 +62,17 @@ impl CoreInner {
|
|||||||
.push(peer_endpoint_id.to_string());
|
.push(peer_endpoint_id.to_string());
|
||||||
}
|
}
|
||||||
self.targeted_offers.discard_from(peer_endpoint_id).await;
|
self.targeted_offers.discard_from(peer_endpoint_id).await;
|
||||||
|
let protocol_ids = self
|
||||||
|
.targeted_store()
|
||||||
|
.protocol_ids_for_peer(peer_endpoint_id)
|
||||||
|
.await?;
|
||||||
|
// Signal active transfers synchronously before awaiting share teardown.
|
||||||
|
for protocol_transfer_id in &protocol_ids {
|
||||||
|
let _ = self.take_active_transfer(*protocol_transfer_id);
|
||||||
|
}
|
||||||
|
for protocol_transfer_id in &protocol_ids {
|
||||||
|
let _ = self.cancel_idle_or_share(*protocol_transfer_id).await;
|
||||||
|
}
|
||||||
self.targeted_store().cancel_by_peer(peer_endpoint_id).await
|
self.targeted_store().cancel_by_peer(peer_endpoint_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,10 +187,6 @@ impl TargetedOfferInbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(
|
|
||||||
dead_code,
|
|
||||||
reason = "called via cancel_targeted_transfers_for_peer for ticket 09"
|
|
||||||
)]
|
|
||||||
pub(crate) async fn discard_from(&self, endpoint_id: &str) {
|
pub(crate) async fn discard_from(&self, endpoint_id: &str) {
|
||||||
let ids: Vec<String> = {
|
let ids: Vec<String> = {
|
||||||
let pending = self.pending.lock().await;
|
let pending = self.pending.lock().await;
|
||||||
|
|||||||
@@ -159,10 +159,6 @@ impl TargetedTransferStore {
|
|||||||
rows.into_iter().map(row_to_transfer).collect()
|
rows.into_iter().map(row_to_transfer).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(
|
|
||||||
dead_code,
|
|
||||||
reason = "called via cancel_targeted_transfers_for_peer for ticket 09"
|
|
||||||
)]
|
|
||||||
pub(crate) async fn cancel_by_peer(&self, peer_endpoint_id: &str) -> Result<u64, VnidropError> {
|
pub(crate) async fn cancel_by_peer(&self, peer_endpoint_id: &str) -> Result<u64, VnidropError> {
|
||||||
let now = now_ms();
|
let now = now_ms();
|
||||||
let result = sqlx::query(
|
let result = sqlx::query(
|
||||||
@@ -180,6 +176,26 @@ impl TargetedTransferStore {
|
|||||||
.map_err(VnidropError::repository)?;
|
.map_err(VnidropError::repository)?;
|
||||||
Ok(result.rows_affected())
|
Ok(result.rows_affected())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn protocol_ids_for_peer(
|
||||||
|
&self,
|
||||||
|
peer_endpoint_id: &str,
|
||||||
|
) -> Result<Vec<u64>, VnidropError> {
|
||||||
|
let rows = sqlx::query(
|
||||||
|
r#"
|
||||||
|
SELECT protocol_transfer_id FROM targeted_transfers
|
||||||
|
WHERE sender_endpoint_id = ?1 OR receiver_endpoint_id = ?1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(peer_endpoint_id)
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(VnidropError::repository)?;
|
||||||
|
Ok(rows
|
||||||
|
.into_iter()
|
||||||
|
.map(|row| row.get::<i64, _>(0) as u64)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
Reference in New Issue
Block a user