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