1 module snmp.agent;
2 
3 import c.net_snmp;
4 import c.net_snmp_agent;
5 import snmp.types;
6 
7 private alias size_t = object.size_t;
8 import snmp.oid;
9 
10 /// Auth-protocol OID pointers exported by net-snmp.
11 struct AuthProto
12 {
13     static const(oid)* md5()  @trusted @nogc nothrow { return usmHMACMD5AuthProtocol.ptr; }
14     static const(oid)* sha1() @trusted @nogc nothrow { return usmHMACSHA1AuthProtocol.ptr; }
15     static size_t md5Len()    @trusted @nogc nothrow { return usmHMACMD5AuthProtocol.length; }
16     static size_t sha1Len()   @trusted @nogc nothrow { return usmHMACSHA1AuthProtocol.length; }
17 }
18 
19 struct PrivProto
20 {
21     static const(oid)* des() @trusted @nogc nothrow { return usmDESPrivProtocol.ptr; }
22     static const(oid)* aes() @trusted @nogc nothrow { return usmAESPrivProtocol.ptr; }
23     static size_t desLen()   @trusted @nogc nothrow { return usmDESPrivProtocol.length; }
24     static size_t aesLen()   @trusted @nogc nothrow { return usmAESPrivProtocol.length; }
25 }
26 
27 /// Passed to scalar handler callbacks; wraps value-setting for the response.
28 struct ScalarRequest
29 {
30     netsnmp_agent_request_info* reqinfo;
31     netsnmp_request_info*       requests;
32 
33     bool isGet() const @trusted @nogc nothrow
34     {
35         return reqinfo !is null && reqinfo.mode == cast(int) PDUType.get;
36     }
37 
38     /// Counter32 (wrapping 32-bit unsigned counter).
39     void setCounter(uint val) @trusted @nogc nothrow
40     {
41         setValue(cast(int) ASNType.counter32, &val, val.sizeof);
42     }
43 
44     /// Gauge32 (non-wrapping unsigned integer).
45     void setGauge(uint val) @trusted @nogc nothrow
46     {
47         setValue(cast(int) ASNType.gauge32, &val, val.sizeof);
48     }
49 
50     /// TimeTicks (hundredths of a second since some epoch).
51     void setTimeTicks(uint val) @trusted @nogc nothrow
52     {
53         setValue(cast(int) ASNType.timeticks, &val, val.sizeof);
54     }
55 
56     /// Integer (signed 32-bit).
57     void setInteger(int val) @trusted @nogc nothrow
58     {
59         long v = val;
60         setValue(cast(int) ASNType.integer, &v, v.sizeof);
61     }
62 
63     /// OctetString (bytes, string data, IP address, etc.).
64     void setOctetStr(scope const(ubyte)[] val) @trusted @nogc nothrow
65     {
66         if (val !is null)
67             setValue(cast(int) ASNType.octetStr, val.ptr, val.length);
68     }
69 
70     /// Counter64.
71     void setCounter64(counter64 val) @trusted @nogc nothrow
72     {
73         setValue(cast(int) ASNType.counter64, &val, val.sizeof);
74     }
75 
76     private void setValue(int asnType, scope const(void)* valPtr, size_t len) @trusted @nogc nothrow
77     {
78         if (!requests)
79             return;
80         snmp_set_var_typed_value(cast(netsnmp_variable_list*) requests.requestvb,
81             cast(ubyte) asnType, valPtr, cast(ulong) len);
82     }
83 }
84 
85 /// Embedded SNMP agent or AgentX subagent (RAII, non-copyable).
86 struct SNMPAgent
87 {
88     private const(char)* _name;
89     bool initialised;
90 
91     @disable this(this);
92 
93     /// `name` must outlive the agent.
94     static SNMPAgent create(scope const(char)* name,
95         bool agentx = false) @trusted @nogc nothrow
96     {
97         SNMPAgent a;
98         if (!name)
99             return a;
100 
101         if (agentx)
102             netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
103 
104         init_snmp(name);
105         int rc = init_agent(name);
106         if (rc == 0)
107         {
108             a._name = name;
109             a.initialised = true;
110         }
111         return a;
112     }
113 
114     ~this() @trusted @nogc nothrow { shutdown(); }
115 
116     void shutdown() @trusted @nogc nothrow
117     {
118         if (initialised)
119         {
120             snmp_shutdown(_name);
121             _name = null;
122             initialised = false;
123         }
124     }
125 
126     bool isReady() const @nogc nothrow @safe
127     {
128         return initialised;
129     }
130 
131     /// Register a read-only scalar OID. net-snmp copies the OID internally.
132     bool registerScalar(const(char)* name, Netsnmp_Node_Handler* fn,
133         scope const OID o) @trusted @nogc nothrow
134     {
135         if (!initialised || !name || !fn || !o.isValid)
136             return false;
137         auto reg = netsnmp_create_handler_registration(
138             name, fn, o.data.ptr, o.length, HANDLER_CAN_RONLY);
139         if (!reg)
140             return false;
141         return netsnmp_register_scalar(reg) == 0;
142     }
143 
144     /// Process one round of pending requests.
145     /// block=true waits until a request arrives; false returns immediately.
146     void process(bool block = true) @trusted @nogc nothrow
147     {
148         if (initialised)
149             agent_check_and_process(block ? 1 : 0);
150     }
151 }
152 
153 unittest
154 {
155     auto agent = SNMPAgent.create("snmp-d-agent-test");
156     agent.shutdown();
157     assert(!agent.isReady);
158 
159     agent.shutdown();
160     assert(!agent.isReady);
161 }