1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Handler types and traits for SNMP MIB operations.
//!
//! Defines the interface for implementing SNMP agent handlers:
//!
//! - [`MibHandler`] - Trait for handling GET, GETNEXT, and SET operations
//! - [`RequestContext`] - Information about the incoming request
//! - [`PreparedSet`], [`SetTestResult`] - Request-owned SET transaction state
//! - [`GetResult`], [`GetNextResult`] - Read operation results
//! - [`SetTestResult`], [`SetCommitResult`], [`SetUndoResult`] - SET phase results
//! - [`HandlerError`], [`HandlerResult`] - Processing failures, reported as `genErr`
//! - [`OidTable`] - Helper for implementing GETNEXT with sorted OID storage
//!
//! # Overview
//!
//! Handlers are registered with an [`Agent`](crate::agent::Agent) using a prefix OID.
//! When the agent receives a request, it dispatches to the handler with the longest
//! matching prefix. Each handler implements the [`MibHandler`] trait to respond to
//! GET, GETNEXT, and optionally SET operations.
//!
//! GET and GETNEXT return [`HandlerResult`], so `?` works on any
//! [`std::error::Error`] inside a handler. `Ok` carries the protocol answer —
//! including the "doesn't exist" exception values — while `Err` means the
//! handler failed to produce one (e.g. its backing store was unreachable) and
//! makes the agent answer the request with `genErr` (RFC 3416 Section 4.2.1).
//!
//! # Basic handler example
//!
//! A minimal handler that provides two scalar values:
//!
//! ```rust
//! use async_snmp::handler::{MibHandler, RequestContext, GetResult, GetNextResult, HandlerResult, BoxFuture};
//! use async_snmp::{Oid, Value, VarBind, oid};
//!
//! struct MyHandler;
//!
//! impl MibHandler for MyHandler {
//! fn get<'a>(&'a self, _ctx: &'a RequestContext, oid: &'a Oid) -> BoxFuture<'a, HandlerResult<GetResult>> {
//! Box::pin(async move {
//! if oid == &oid!(1, 3, 6, 1, 4, 1, 99999, 1, 0) {
//! return Ok(GetResult::Value(Value::Integer(42)));
//! }
//! Ok(GetResult::NoSuchObject)
//! })
//! }
//!
//! fn get_next<'a>(&'a self, _ctx: &'a RequestContext, oid: &'a Oid) -> BoxFuture<'a, HandlerResult<GetNextResult>> {
//! Box::pin(async move {
//! let my_oid = oid!(1, 3, 6, 1, 4, 1, 99999, 1, 0);
//! if oid < &my_oid {
//! return Ok(GetNextResult::Value(VarBind::new(my_oid, Value::Integer(42))));
//! }
//! Ok(GetNextResult::EndOfMibView)
//! })
//! }
//! }
//! ```
//!
//! Choose `GetResult::NoSuchObject` or `GetResult::NoSuchInstance`, and
//! `GetNextResult::EndOfMibView`, explicitly. `From<Value>` and
//! `From<VarBind>` provide conversions for unambiguous value results.
//!
//! # SET operations and multi-phase protocol
//!
//! SET operations follow a multi-phase protocol as defined in RFC 3416, modeled
//! after net-snmp's RESERVE/ACTION/COMMIT/FREE/UNDO phases:
//!
//! 1. **Test Phase**: [`MibHandler::test_set`] is called for ALL varbinds before any
//! commits. Each successful test returns a request-owned [`PreparedSet`]. If a
//! test fails, [`PreparedSet::free`] cleans earlier successful reservations
//! in reverse order; a failing test must not leave resources behind.
//!
//! 2. **Commit Phase**: [`PreparedSet::commit`] is called for each varbind in order.
//! A failed commit may have partially mutated state, so [`PreparedSet::undo`]
//! cleans every attempted binding in reverse order, including the failed
//! attempt. Later prepared bindings receive [`PreparedSet::free`] in reverse
//! order. Cleanup continues if undo fails.
//!
//! 3. **Finalization Phase**: after all commits succeed, [`PreparedSet::finalize`]
//! releases rollback data and reservations in reverse order.
//!
//! Explicit terminal callbacks perform normal protocol cleanup. Prepared
//! objects may implement idempotent `Drop` for synchronous reservation/resource
//! fallback on cancellation or panic, but `Drop` cannot await rollback or
//! promise transactional atomicity after commit starts.
//! By default, handlers are read-only (returning [`SetTestError::NotWritable`]).
//! See [`MibHandler`] documentation for implementation details.
//!
//! # Using `OidTable` for GETNEXT
//!
//! For handlers with static or slowly-changing data, [`OidTable`] simplifies
//! GETNEXT implementation by maintaining OIDs in sorted order:
//!
//! ```rust
//! use async_snmp::handler::{MibHandler, RequestContext, GetResult, GetNextResult, HandlerResult, OidTable, BoxFuture};
//! use async_snmp::{Oid, Value, VarBind, oid};
//!
//! struct StaticHandler {
//! table: OidTable<Value>,
//! }
//!
//! impl StaticHandler {
//! fn new() -> Self {
//! let mut table = OidTable::new();
//! table.insert(oid!(1, 3, 6, 1, 4, 1, 99999, 1, 0), Value::Integer(100));
//! table.insert(oid!(1, 3, 6, 1, 4, 1, 99999, 2, 0), Value::OctetString("test".into()));
//! Self { table }
//! }
//! }
//!
//! impl MibHandler for StaticHandler {
//! fn get<'a>(&'a self, _ctx: &'a RequestContext, oid: &'a Oid) -> BoxFuture<'a, HandlerResult<GetResult>> {
//! Box::pin(async move {
//! Ok(self.table.get(oid)
//! .cloned()
//! .map(GetResult::Value)
//! .unwrap_or(GetResult::NoSuchObject))
//! })
//! }
//!
//! fn get_next<'a>(&'a self, _ctx: &'a RequestContext, oid: &'a Oid) -> BoxFuture<'a, HandlerResult<GetNextResult>> {
//! Box::pin(async move {
//! Ok(self.table.get_next(oid)
//! .map(|(o, v)| GetNextResult::Value(VarBind::new(o.clone(), v.clone())))
//! .unwrap_or(GetNextResult::EndOfMibView))
//! })
//! }
//! }
pub use ;
pub use ;
pub use OidTable;
pub use ;
pub use ;
/// Concrete security model used to authenticate an SNMP request (RFC 3411).