Skip to main content

rns_net/common/
event.rs

1//! Event types for the driver loop — generic over the writer type.
2
3use std::fmt;
4use std::sync::mpsc;
5
6use rns_core::transport::types::{InterfaceId, InterfaceInfo};
7
8/// Policy for handling incoming direct-connect proposals.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum HolePunchPolicy {
11    /// Reject all proposals.
12    Reject,
13    /// Accept all proposals automatically.
14    AcceptAll,
15    /// Ask the application callback.
16    AskApp,
17}
18
19impl Default for HolePunchPolicy {
20    fn default() -> Self {
21        HolePunchPolicy::AcceptAll
22    }
23}
24
25/// Events sent to the driver thread.
26///
27/// `W` is the writer type (e.g. `Box<dyn Writer>` for sync,
28/// or a channel sender for async).
29pub enum Event<W: Send> {
30    /// A decoded frame arrived from an interface.
31    Frame {
32        interface_id: InterfaceId,
33        data: Vec<u8>,
34    },
35    /// An interface came online after (re)connecting.
36    /// Carries a new writer if the connection was re-established.
37    /// Carries InterfaceInfo if this is a new dynamic interface (e.g. TCP server client).
38    InterfaceUp(InterfaceId, Option<W>, Option<InterfaceInfo>),
39    /// An interface went offline (socket closed, error).
40    InterfaceDown(InterfaceId),
41    /// Periodic maintenance tick (1s interval).
42    Tick,
43    /// Shut down the driver loop.
44    Shutdown,
45    /// Send an outbound packet.
46    SendOutbound {
47        raw: Vec<u8>,
48        dest_type: u8,
49        attached_interface: Option<InterfaceId>,
50    },
51    /// Register a local destination.
52    RegisterDestination { dest_hash: [u8; 16], dest_type: u8 },
53    /// Deregister a local destination.
54    DeregisterDestination { dest_hash: [u8; 16] },
55    /// Deregister a link destination (stop accepting incoming links).
56    DeregisterLinkDestination { dest_hash: [u8; 16] },
57    /// Query driver state. Response is sent via the provided channel.
58    Query(QueryRequest, mpsc::Sender<QueryResponse>),
59    /// Register a link destination (accepts incoming LINKREQUEST).
60    RegisterLinkDestination {
61        dest_hash: [u8; 16],
62        sig_prv_bytes: [u8; 32],
63        sig_pub_bytes: [u8; 32],
64        resource_strategy: u8,
65    },
66    /// Register a request handler for a path on established links.
67    RegisterRequestHandler {
68        path: String,
69        allowed_list: Option<Vec<[u8; 16]>>,
70        handler: Box<
71            dyn Fn([u8; 16], &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>> + Send,
72        >,
73    },
74    /// Create an outbound link. Response sends (link_id) back.
75    CreateLink {
76        dest_hash: [u8; 16],
77        dest_sig_pub_bytes: [u8; 32],
78        response_tx: mpsc::Sender<[u8; 16]>,
79    },
80    /// Send a request on an established link.
81    SendRequest {
82        link_id: [u8; 16],
83        path: String,
84        data: Vec<u8>,
85    },
86    /// Identify on a link (send identity to remote peer).
87    IdentifyOnLink {
88        link_id: [u8; 16],
89        identity_prv_key: [u8; 64],
90    },
91    /// Tear down a link.
92    TeardownLink { link_id: [u8; 16] },
93    /// Send a resource on a link.
94    SendResource {
95        link_id: [u8; 16],
96        data: Vec<u8>,
97        metadata: Option<Vec<u8>>,
98    },
99    /// Set the resource acceptance strategy for a link.
100    SetResourceStrategy { link_id: [u8; 16], strategy: u8 },
101    /// Accept or reject a pending resource (for AcceptApp strategy).
102    AcceptResource {
103        link_id: [u8; 16],
104        resource_hash: Vec<u8>,
105        accept: bool,
106    },
107    /// Send a channel message on a link.
108    SendChannelMessage {
109        link_id: [u8; 16],
110        msgtype: u16,
111        payload: Vec<u8>,
112    },
113    /// Send generic data on a link with a given context.
114    SendOnLink {
115        link_id: [u8; 16],
116        data: Vec<u8>,
117        context: u8,
118    },
119    /// Request a path to a destination from the network.
120    RequestPath { dest_hash: [u8; 16] },
121    /// Register a proof strategy for a destination.
122    RegisterProofStrategy {
123        dest_hash: [u8; 16],
124        strategy: rns_core::types::ProofStrategy,
125        /// Full identity private key (64 bytes) for signing proofs.
126        signing_key: Option<[u8; 64]>,
127    },
128    /// Propose a direct connection to a peer via hole punching.
129    ProposeDirectConnect { link_id: [u8; 16] },
130    /// Set the direct-connect policy.
131    SetDirectConnectPolicy { policy: HolePunchPolicy },
132    /// (Internal) Probe result arrived from a worker thread.
133    HolePunchProbeResult {
134        link_id: [u8; 16],
135        session_id: [u8; 16],
136        observed_addr: std::net::SocketAddr,
137        socket: std::net::UdpSocket,
138        /// The probe server that responded successfully.
139        probe_server: std::net::SocketAddr,
140    },
141    /// (Internal) Probe failed.
142    HolePunchProbeFailed {
143        link_id: [u8; 16],
144        session_id: [u8; 16],
145    },
146    /// An interface's configuration changed (placeholder for future use).
147    InterfaceConfigChanged(InterfaceId),
148    /// Load a WASM hook at runtime.
149    LoadHook {
150        name: String,
151        wasm_bytes: Vec<u8>,
152        attach_point: String,
153        priority: i32,
154        response_tx: mpsc::Sender<Result<(), String>>,
155    },
156    /// Unload a WASM hook at runtime.
157    UnloadHook {
158        name: String,
159        attach_point: String,
160        response_tx: mpsc::Sender<Result<(), String>>,
161    },
162    /// Reload a WASM hook at runtime (detach + recompile + reattach with same priority).
163    ReloadHook {
164        name: String,
165        attach_point: String,
166        wasm_bytes: Vec<u8>,
167        response_tx: mpsc::Sender<Result<(), String>>,
168    },
169    /// List all loaded hooks.
170    ListHooks {
171        response_tx: mpsc::Sender<Vec<HookInfo>>,
172    },
173}
174
175/// Information about a loaded hook program.
176#[derive(Debug, Clone)]
177pub struct HookInfo {
178    pub name: String,
179    pub attach_point: String,
180    pub priority: i32,
181    pub enabled: bool,
182    pub consecutive_traps: u32,
183}
184
185/// Queries that can be sent to the driver.
186#[derive(Debug)]
187pub enum QueryRequest {
188    /// Get interface statistics and transport info.
189    InterfaceStats,
190    /// Get path table entries, optionally filtered by max hops.
191    PathTable { max_hops: Option<u8> },
192    /// Get rate table entries.
193    RateTable,
194    /// Look up the next hop for a destination.
195    NextHop { dest_hash: [u8; 16] },
196    /// Look up the next hop interface name for a destination.
197    NextHopIfName { dest_hash: [u8; 16] },
198    /// Get link table entry count.
199    LinkCount,
200    /// Drop a specific path.
201    DropPath { dest_hash: [u8; 16] },
202    /// Drop all paths that route via a given transport hash.
203    DropAllVia { transport_hash: [u8; 16] },
204    /// Drop all announce queues.
205    DropAnnounceQueues,
206    /// Get the transport identity hash.
207    TransportIdentity,
208    /// Get all blackholed identities.
209    GetBlackholed,
210    /// Add an identity to the blackhole list.
211    BlackholeIdentity {
212        identity_hash: [u8; 16],
213        duration_hours: Option<f64>,
214        reason: Option<String>,
215    },
216    /// Remove an identity from the blackhole list.
217    UnblackholeIdentity { identity_hash: [u8; 16] },
218    /// Check if a path exists to a destination.
219    HasPath { dest_hash: [u8; 16] },
220    /// Get hop count to a destination.
221    HopsTo { dest_hash: [u8; 16] },
222    /// Recall identity info for a destination.
223    RecallIdentity { dest_hash: [u8; 16] },
224    /// Get locally registered destinations.
225    LocalDestinations,
226    /// Get active links.
227    Links,
228    /// Get active resource transfers.
229    Resources,
230    /// Inject a path entry into the path table.
231    InjectPath {
232        dest_hash: [u8; 16],
233        next_hop: [u8; 16],
234        hops: u8,
235        expires: f64,
236        interface_name: String,
237        packet_hash: [u8; 32],
238    },
239    /// Inject an identity into the known destinations cache.
240    InjectIdentity {
241        dest_hash: [u8; 16],
242        identity_hash: [u8; 16],
243        public_key: [u8; 64],
244        app_data: Option<Vec<u8>>,
245        hops: u8,
246        received_at: f64,
247    },
248    /// Get discovered interfaces.
249    DiscoveredInterfaces {
250        only_available: bool,
251        only_transport: bool,
252    },
253    /// Send a probe packet to a destination and return (packet_hash, hops).
254    SendProbe {
255        dest_hash: [u8; 16],
256        payload_size: usize,
257    },
258    /// Check if a proof was received for a probe packet.
259    CheckProof { packet_hash: [u8; 32] },
260}
261
262/// Responses to queries.
263#[derive(Debug)]
264pub enum QueryResponse {
265    InterfaceStats(InterfaceStatsResponse),
266    PathTable(Vec<PathTableEntry>),
267    RateTable(Vec<RateTableEntry>),
268    NextHop(Option<NextHopResponse>),
269    NextHopIfName(Option<String>),
270    LinkCount(usize),
271    DropPath(bool),
272    DropAllVia(usize),
273    DropAnnounceQueues,
274    TransportIdentity(Option<[u8; 16]>),
275    Blackholed(Vec<BlackholeInfo>),
276    BlackholeResult(bool),
277    UnblackholeResult(bool),
278    HasPath(bool),
279    HopsTo(Option<u8>),
280    RecallIdentity(Option<crate::common::destination::AnnouncedIdentity>),
281    LocalDestinations(Vec<LocalDestinationEntry>),
282    Links(Vec<LinkInfoEntry>),
283    Resources(Vec<ResourceInfoEntry>),
284    InjectPath(bool),
285    InjectIdentity(bool),
286    /// List of discovered interfaces.
287    DiscoveredInterfaces(Vec<crate::common::discovery::DiscoveredInterface>),
288    /// Probe sent: (packet_hash, hops) or None if identity unknown.
289    SendProbe(Option<([u8; 32], u8)>),
290    /// Proof check: RTT if received, None if still pending.
291    CheckProof(Option<f64>),
292}
293
294/// Interface statistics response.
295#[derive(Debug, Clone)]
296pub struct InterfaceStatsResponse {
297    pub interfaces: Vec<SingleInterfaceStat>,
298    pub transport_id: Option<[u8; 16]>,
299    pub transport_enabled: bool,
300    pub transport_uptime: f64,
301    /// Total received bytes across all interfaces.
302    pub total_rxb: u64,
303    /// Total transmitted bytes across all interfaces.
304    pub total_txb: u64,
305    /// Probe responder destination hash (if enabled).
306    pub probe_responder: Option<[u8; 16]>,
307}
308
309/// Statistics for a single interface.
310#[derive(Debug, Clone)]
311pub struct SingleInterfaceStat {
312    pub name: String,
313    pub status: bool,
314    pub mode: u8,
315    pub rxb: u64,
316    pub txb: u64,
317    pub rx_packets: u64,
318    pub tx_packets: u64,
319    pub bitrate: Option<u64>,
320    pub ifac_size: Option<usize>,
321    pub started: f64,
322    /// Incoming announce frequency (per second).
323    pub ia_freq: f64,
324    /// Outgoing announce frequency (per second).
325    pub oa_freq: f64,
326    /// Human-readable interface type string (e.g. "TCPClientInterface").
327    pub interface_type: String,
328}
329
330/// A locally registered destination.
331#[derive(Debug, Clone)]
332pub struct LocalDestinationEntry {
333    pub hash: [u8; 16],
334    pub dest_type: u8,
335}
336
337/// Information about an active link.
338#[derive(Debug, Clone)]
339pub struct LinkInfoEntry {
340    pub link_id: [u8; 16],
341    pub state: String,
342    pub is_initiator: bool,
343    pub dest_hash: [u8; 16],
344    pub remote_identity: Option<[u8; 16]>,
345    pub rtt: Option<f64>,
346}
347
348/// Information about an active resource transfer.
349#[derive(Debug, Clone)]
350pub struct ResourceInfoEntry {
351    pub link_id: [u8; 16],
352    pub direction: String,
353    pub total_parts: usize,
354    pub transferred_parts: usize,
355    pub complete: bool,
356}
357
358/// A single path table entry for query responses.
359#[derive(Debug, Clone)]
360pub struct PathTableEntry {
361    pub hash: [u8; 16],
362    pub timestamp: f64,
363    pub via: [u8; 16],
364    pub hops: u8,
365    pub expires: f64,
366    pub interface: InterfaceId,
367    pub interface_name: String,
368}
369
370/// A single rate table entry for query responses.
371#[derive(Debug, Clone)]
372pub struct RateTableEntry {
373    pub hash: [u8; 16],
374    pub last: f64,
375    pub rate_violations: u32,
376    pub blocked_until: f64,
377    pub timestamps: Vec<f64>,
378}
379
380/// A blackholed identity for query responses.
381#[derive(Debug, Clone)]
382pub struct BlackholeInfo {
383    pub identity_hash: [u8; 16],
384    pub created: f64,
385    pub expires: f64,
386    pub reason: Option<String>,
387}
388
389/// Next hop lookup result.
390#[derive(Debug, Clone)]
391pub struct NextHopResponse {
392    pub next_hop: [u8; 16],
393    pub hops: u8,
394    pub interface: InterfaceId,
395}
396
397impl<W: Send> fmt::Debug for Event<W> {
398    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399        match self {
400            Event::Frame { interface_id, data } => f
401                .debug_struct("Frame")
402                .field("interface_id", interface_id)
403                .field("data_len", &data.len())
404                .finish(),
405            Event::InterfaceUp(id, writer, info) => f
406                .debug_tuple("InterfaceUp")
407                .field(id)
408                .field(&writer.is_some())
409                .field(&info.is_some())
410                .finish(),
411            Event::InterfaceDown(id) => f.debug_tuple("InterfaceDown").field(id).finish(),
412            Event::Tick => write!(f, "Tick"),
413            Event::Shutdown => write!(f, "Shutdown"),
414            Event::SendOutbound { raw, dest_type, .. } => f
415                .debug_struct("SendOutbound")
416                .field("raw_len", &raw.len())
417                .field("dest_type", dest_type)
418                .finish(),
419            Event::RegisterDestination {
420                dest_hash,
421                dest_type,
422            } => f
423                .debug_struct("RegisterDestination")
424                .field("dest_hash", dest_hash)
425                .field("dest_type", dest_type)
426                .finish(),
427            Event::DeregisterDestination { dest_hash } => f
428                .debug_struct("DeregisterDestination")
429                .field("dest_hash", dest_hash)
430                .finish(),
431            Event::DeregisterLinkDestination { dest_hash } => f
432                .debug_struct("DeregisterLinkDestination")
433                .field("dest_hash", dest_hash)
434                .finish(),
435            Event::Query(req, _) => f.debug_tuple("Query").field(req).finish(),
436            Event::RegisterLinkDestination { dest_hash, .. } => f
437                .debug_struct("RegisterLinkDestination")
438                .field("dest_hash", dest_hash)
439                .finish(),
440            Event::RegisterRequestHandler { path, .. } => f
441                .debug_struct("RegisterRequestHandler")
442                .field("path", path)
443                .finish(),
444            Event::CreateLink { dest_hash, .. } => f
445                .debug_struct("CreateLink")
446                .field("dest_hash", dest_hash)
447                .finish(),
448            Event::SendRequest { link_id, path, .. } => f
449                .debug_struct("SendRequest")
450                .field("link_id", link_id)
451                .field("path", path)
452                .finish(),
453            Event::IdentifyOnLink { link_id, .. } => f
454                .debug_struct("IdentifyOnLink")
455                .field("link_id", link_id)
456                .finish(),
457            Event::TeardownLink { link_id } => f
458                .debug_struct("TeardownLink")
459                .field("link_id", link_id)
460                .finish(),
461            Event::SendResource { link_id, data, .. } => f
462                .debug_struct("SendResource")
463                .field("link_id", link_id)
464                .field("data_len", &data.len())
465                .finish(),
466            Event::SetResourceStrategy { link_id, strategy } => f
467                .debug_struct("SetResourceStrategy")
468                .field("link_id", link_id)
469                .field("strategy", strategy)
470                .finish(),
471            Event::AcceptResource {
472                link_id, accept, ..
473            } => f
474                .debug_struct("AcceptResource")
475                .field("link_id", link_id)
476                .field("accept", accept)
477                .finish(),
478            Event::SendChannelMessage {
479                link_id,
480                msgtype,
481                payload,
482            } => f
483                .debug_struct("SendChannelMessage")
484                .field("link_id", link_id)
485                .field("msgtype", msgtype)
486                .field("payload_len", &payload.len())
487                .finish(),
488            Event::SendOnLink {
489                link_id,
490                data,
491                context,
492            } => f
493                .debug_struct("SendOnLink")
494                .field("link_id", link_id)
495                .field("data_len", &data.len())
496                .field("context", context)
497                .finish(),
498            Event::RequestPath { dest_hash } => f
499                .debug_struct("RequestPath")
500                .field("dest_hash", dest_hash)
501                .finish(),
502            Event::RegisterProofStrategy {
503                dest_hash,
504                strategy,
505                ..
506            } => f
507                .debug_struct("RegisterProofStrategy")
508                .field("dest_hash", dest_hash)
509                .field("strategy", strategy)
510                .finish(),
511            Event::ProposeDirectConnect { link_id } => f
512                .debug_struct("ProposeDirectConnect")
513                .field("link_id", link_id)
514                .finish(),
515            Event::SetDirectConnectPolicy { .. } => {
516                write!(f, "SetDirectConnectPolicy")
517            }
518            Event::HolePunchProbeResult {
519                link_id,
520                session_id,
521                observed_addr,
522                probe_server,
523                ..
524            } => f
525                .debug_struct("HolePunchProbeResult")
526                .field("link_id", link_id)
527                .field("session_id", session_id)
528                .field("observed_addr", observed_addr)
529                .field("probe_server", probe_server)
530                .finish(),
531            Event::HolePunchProbeFailed {
532                link_id,
533                session_id,
534            } => f
535                .debug_struct("HolePunchProbeFailed")
536                .field("link_id", link_id)
537                .field("session_id", session_id)
538                .finish(),
539            Event::InterfaceConfigChanged(id) => {
540                f.debug_tuple("InterfaceConfigChanged").field(id).finish()
541            }
542            Event::LoadHook {
543                name,
544                attach_point,
545                priority,
546                ..
547            } => f
548                .debug_struct("LoadHook")
549                .field("name", name)
550                .field("attach_point", attach_point)
551                .field("priority", priority)
552                .finish(),
553            Event::UnloadHook {
554                name, attach_point, ..
555            } => f
556                .debug_struct("UnloadHook")
557                .field("name", name)
558                .field("attach_point", attach_point)
559                .finish(),
560            Event::ReloadHook {
561                name, attach_point, ..
562            } => f
563                .debug_struct("ReloadHook")
564                .field("name", name)
565                .field("attach_point", attach_point)
566                .finish(),
567            Event::ListHooks { .. } => write!(f, "ListHooks"),
568        }
569    }
570}