//! Connection handling and send/receive queues pub mod handshake; pub mod packet; pub mod socket; use ::std::{rc::Rc, vec::Vec}; pub use crate::connection::{ handshake::Handshake, packet::{ConnectionID as ID, Packet, PacketData}, }; use crate::{ dnssec, enc::{ asym::PubKey, hkdf::HkdfSha3, sym::{CipherKind, CipherRecv, CipherSend}, Random, }, inner::ThreadTracker, }; /// strong typedef for receiving connection id #[derive(Debug, Copy, Clone, PartialEq)] pub struct IDRecv(pub ID); /// strong typedef for sending connection id #[derive(Debug, Copy, Clone, PartialEq)] pub struct IDSend(pub ID); /// Version of the fenrir protocol in use #[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)] #[repr(u8)] pub enum ProtocolVersion { /// First Fenrir Protocol Version V0 = 0, } impl ProtocolVersion { /// actual length of the protocol version field pub const fn len() -> usize { 1 } /// Serialize into raw bytes pub fn serialize(&self, out: &mut u8) { *out = *self as u8; } } /// A single connection and its data #[derive(Debug)] pub struct Connection { /// Receiving Connection ID pub id_recv: IDRecv, /// Sending Connection ID pub id_send: IDSend, /// The main hkdf used for all secrets in this connection pub hkdf: HkdfSha3, /// Cipher for decrypting data pub cipher_recv: CipherRecv, /// Cipher for encrypting data pub cipher_send: CipherSend, } /// Role: used to set the correct secrets /// * Server: Connection is Incoming /// * Client: Connection is Outgoing #[derive(Debug, Copy, Clone)] #[repr(u8)] pub enum Role { /// Server: we receive the connection Server = 0, /// Client: we initate the connection Client, } impl Connection { pub(crate) fn new( hkdf: HkdfSha3, cipher: CipherKind, role: Role, rand: &Random, ) -> Self { let (secret_recv, secret_send) = match role { Role::Server => { (hkdf.get_secret(b"to_server"), hkdf.get_secret(b"to_client")) } Role::Client => { (hkdf.get_secret(b"to_client"), hkdf.get_secret(b"to_server")) } }; let cipher_recv = CipherRecv::new(cipher, secret_recv); let cipher_send = CipherSend::new(cipher, secret_send, rand); Self { id_recv: IDRecv(ID::Handshake), id_send: IDSend(ID::Handshake), hkdf, cipher_recv, cipher_send, } } } // PERF: Arc> loks a bit too much, need to find // faster ways to do this pub(crate) struct ConnList { thread_id: ThreadTracker, connections: Vec>>, /// Bitmap to track which connection ids are used or free ids_used: Vec<::bitmaps::Bitmap<1024>>, } impl ConnList { pub(crate) fn new(thread_id: ThreadTracker) -> Self { let bitmap_id = ::bitmaps::Bitmap::<1024>::new(); const INITIAL_CAP: usize = 128; let mut ret = Self { thread_id, connections: Vec::with_capacity(INITIAL_CAP), ids_used: vec![bitmap_id], }; ret.connections.resize_with(INITIAL_CAP, || None); ret } pub fn len(&self) -> usize { let mut total: usize = 0; for bitmap in self.ids_used.iter() { total = total + bitmap.len() } total } /// Only *Reserve* a connection, /// without actually tracking it in self.connections pub(crate) fn reserve_first( &mut self, mut conn: Connection, ) -> Rc { // uhm... bad things are going on here: // * id must be initialized, but only because: // * rust does not understand that after the `!found` id is always // initialized // * `ID::new_u64` is really safe only with >0, but here it always is // ...we should probably rewrite it in better, safer rust let mut id_in_thread: usize = 0; let mut found = false; for (i, b) in self.ids_used.iter_mut().enumerate() { match b.first_false_index() { Some(idx) => { b.set(idx, true); id_in_thread = (i * 1024) + idx; found = true; break; } None => {} } } if !found { let mut new_bitmap = ::bitmaps::Bitmap::<1024>::new(); new_bitmap.set(0, true); id_in_thread = self.ids_used.len() * 1024; self.ids_used.push(new_bitmap); } // make sure we have enough space in self.connections let curr_capacity = self.connections.capacity(); if self.connections.capacity() <= id_in_thread { // Fill with "None", assure 64 connections without reallocations let multiple = 64 + curr_capacity - 1; let new_capacity = multiple - (multiple % curr_capacity); self.connections.resize_with(new_capacity, || None); } // calculate the actual connection ID let actual_id = ((id_in_thread as u64) * (self.thread_id.total as u64)) + (self.thread_id.id as u64); let new_id = IDRecv(ID::new_u64(actual_id)); conn.id_recv = new_id; // Return the new connection without tracking it Rc::new(conn) } /// NOTE: does NOT check if the connection has been previously reserved! pub(crate) fn track(&mut self, conn: Rc) -> Result<(), ()> { let conn_id = match conn.id_recv { IDRecv(ID::Handshake) => { return Err(()); } IDRecv(ID::ID(conn_id)) => conn_id, }; let id_in_thread: usize = (conn_id.get() / (self.thread_id.total as u64)) as usize; self.connections[id_in_thread] = Some(conn); Ok(()) } pub(crate) fn delete(&mut self, id: IDRecv) { if let IDRecv(ID::ID(raw_id)) = id { let id_in_thread: usize = (raw_id.get() / (self.thread_id.total as u64)) as usize; let vec_index = id_in_thread / 1024; let bitmask_index = id_in_thread % 1024; if let Some(bitmask) = self.ids_used.get_mut(vec_index) { bitmask.set(bitmask_index, false); self.connections[id_in_thread] = None; } } } } use ::std::collections::HashMap; enum MapEntry { Present(IDSend), Reserved, } /// return wether we already have a connection, we are waiting for one, or you /// can start one #[derive(Debug, Clone, Copy)] pub(crate) enum Reservation { /// we already have a connection. use this ID. Present(IDSend), /// we don't have a connection, but we are waiting for one to be established. Waiting, /// we have reserved a spot for your connection. Reserved, } /// Link the public key of the authentication server to a connection id /// so that we can reuse that connection to ask for more authentications /// /// Note that a server can have multiple public keys, /// and the handshake will only ever verify one. /// To avoid malicious publication fo keys that are not yours, /// on connection we: /// * reserve all public keys of the server /// * wait for the connection to finish /// * remove all those reservations, exept the one key that actually succeded /// While searching, we return a connection ID if just one key is a match pub(crate) struct AuthServerConnections { conn_map: HashMap, next_reservation: u64, } impl AuthServerConnections { pub(crate) fn new() -> Self { Self { conn_map: HashMap::with_capacity(32), next_reservation: 0, } } /// add an ID to the reserved spot, /// and unlock the other pubkeys which have not been verified pub(crate) fn add( &mut self, pubkey: &PubKey, id: IDSend, record: &dnssec::Record, ) { let _ = self.conn_map.insert(*pubkey, MapEntry::Present(id)); for (_, pk) in record.public_keys.iter() { if pk == pubkey { continue; } let _ = self.conn_map.remove(pk); } } /// remove a dropped connection pub(crate) fn remove_reserved(&mut self, record: &dnssec::Record) { for (_, pk) in record.public_keys.iter() { let _ = self.conn_map.remove(pk); } } /// remove a dropped connection pub(crate) fn remove_conn(&mut self, pubkey: &PubKey) { let _ = self.conn_map.remove(pubkey); } /// each dnssec::Record has multiple Pubkeys. reserve and ID for them all. /// later on, when `add` is called we will delete /// those that have not actually benn used pub(crate) fn get_or_reserve( &mut self, record: &dnssec::Record, ) -> Reservation { for (_, pk) in record.public_keys.iter() { match self.conn_map.get(pk) { None => {} Some(MapEntry::Reserved) => return Reservation::Waiting, Some(MapEntry::Present(id)) => { return Reservation::Present(id.clone()) } } } for (_, pk) in record.public_keys.iter() { let _ = self.conn_map.insert(*pk, MapEntry::Reserved); } Reservation::Reserved } }