use super::includes::*;
use crate::error::NetworkError;
use crate::proto::node_result::DeRegistration;
use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet;
use citadel_crypt::stacked_ratchet::StackedRatchet;
use std::sync::atomic::Ordering;
#[cfg_attr(feature = "localhost-testing", tracing::instrument(target = "citadel", skip_all, ret, err, fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))]
pub async fn process_deregister(
session_ref: &HdpSession,
packet: HdpPacket,
header_drill_vers: u32,
) -> Result<PrimaryProcessorResult, NetworkError> {
let session = session_ref.clone();
if session.state.load(Ordering::Relaxed) != SessionState::Connected {
log::error!(target: "citadel", "disconnect packet received, but session state is not connected. Must be connected to deregister. Dropping");
return Ok(PrimaryProcessorResult::Void);
}
let task = async move {
let session = &session;
let hr = {
let state_container = inner_state!(session.state_container);
return_if_none!(
get_proper_hyper_ratchet(header_drill_vers, &state_container, None),
"Could not get proper HR [deregister]"
)
};
let timestamp = session.time_tracker.get_global_time_ns();
let (header, payload, _, _) = packet.decompose();
let (header, _payload, hyper_ratchet) = return_if_none!(
validation::aead::validate(hr, &header, payload),
"Unable to validate dereg packet"
);
let header = &header;
let implicated_cid = header.session_cid.get();
let security_level = header.security_level.into();
match header.cmd_aux {
packet_flags::cmd::aux::do_deregister::STAGE0 => {
log::trace!(target: "citadel", "STAGE 0 DEREGISTER PACKET RECV");
deregister_client_from_self(
implicated_cid,
session,
&hyper_ratchet,
timestamp,
security_level,
)
.await
}
packet_flags::cmd::aux::do_deregister::SUCCESS => {
log::trace!(target: "citadel", "STAGE SUCCESS DEREGISTER PACKET RECV");
deregister_from_hyperlan_server_as_client(implicated_cid, session).await
}
packet_flags::cmd::aux::do_deregister::FAILURE => {
log::trace!(target: "citadel", "STAGE FAILURE DEREGISTER PACKET RECV");
let state_container = inner_state!(session.state_container);
let ticket = state_container.deregister_state.current_ticket;
std::mem::drop(state_container);
let cid =
return_if_none!(session.implicated_cid.get(), "implicated CID not loaded");
session
.kernel_tx
.unbounded_send(NodeResult::DeRegistration(DeRegistration {
implicated_cid: cid,
ticket_opt: ticket,
success: false,
}))?;
log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin", cid);
Ok(PrimaryProcessorResult::EndSession(
"Deregistration failure. Closing connection anyways",
))
}
_ => {
log::error!(target: "citadel", "Invalid auxiliary command");
Ok(PrimaryProcessorResult::Void)
}
}
};
to_concurrent_processor!(task)
}
async fn deregister_client_from_self(
implicated_cid: u64,
session_ref: &HdpSession,
hyper_ratchet: &StackedRatchet,
timestamp: i64,
security_level: SecurityLevel,
) -> Result<PrimaryProcessorResult, NetworkError> {
let session = session_ref;
let (acc_mgr, ticket) = {
let state_container = inner_state!(session.state_container);
let ticket = state_container.deregister_state.current_ticket;
let acc_manager = session.account_manager.clone();
std::mem::drop(state_container);
(acc_manager, ticket)
};
let (ret, success) = match acc_mgr.delete_client_by_cid(implicated_cid).await {
Ok(_) => {
log::trace!(target: "citadel", "Successfully purged account {} locally!", implicated_cid);
let stage_success_packet = packet_crafter::do_deregister::craft_final(
hyper_ratchet,
true,
timestamp,
security_level,
);
(
PrimaryProcessorResult::ReplyToSender(stage_success_packet),
true,
)
}
Err(err) => {
log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin ({:?})", implicated_cid, err);
let stage_failure_packet = packet_crafter::do_deregister::craft_final(
hyper_ratchet,
false,
timestamp,
security_level,
);
(
PrimaryProcessorResult::ReplyToSender(stage_failure_packet),
false,
)
}
};
let session = session_ref;
session.send_to_kernel(NodeResult::DeRegistration(DeRegistration {
implicated_cid,
ticket_opt: ticket,
success,
}))?;
session
.state
.store(SessionState::NeedsRegister, Ordering::Relaxed);
session.send_session_dc_signal(
ticket,
success,
"Deregistration occurred. Session disconnected",
);
Ok(ret)
}
async fn deregister_from_hyperlan_server_as_client(
implicated_cid: u64,
session_ref: &HdpSession,
) -> Result<PrimaryProcessorResult, NetworkError> {
let session = session_ref;
let (acc_manager, dereg_ticket) = {
let state_container = inner_state!(session.state_container);
let acc_manager = session.account_manager.clone();
let dereg_ticket = state_container.deregister_state.current_ticket;
(acc_manager, dereg_ticket)
};
let success = match acc_manager.delete_client_by_cid(implicated_cid).await {
Ok(_) => {
log::trace!(target: "citadel", "Successfully purged account {} locally!", implicated_cid);
true
}
Err(err) => {
log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin. Reason: {:?}", implicated_cid, err);
false
}
};
session.send_to_kernel(NodeResult::DeRegistration(DeRegistration {
implicated_cid,
ticket_opt: dereg_ticket,
success: true,
}))?;
session.send_session_dc_signal(
dereg_ticket,
success,
"Deregistration occurred. Session disconnected",
);
session.shutdown();
Ok(PrimaryProcessorResult::Void)
}