1 module snmp.session;
2 
3 import c.net_snmp;
4 import snmp.types;
5 import snmp.oid;
6 import snmp.pdu : SNMPResponse;
7 
8 /// Lightweight SNMP session wrapper (RAII, non-copyable).
9 struct SNMPSession
10 {
11     netsnmp_session* handle;
12 
13     @disable this(this);
14 
15     static SNMPSession open(
16         scope const(char)* peername,
17         scope const(char)* community,
18         SNMPVersion ver = SNMPVersion.v2c) @trusted @nogc nothrow
19     {
20         SNMPSession s;
21 
22         if (!peername || !community)
23             return s;
24 
25         netsnmp_session sess;
26         snmp_sess_init(&sess);
27 
28         // `version` is a D keyword — access via __traits
29         __traits(getMember, sess, "version") = cast(long) ver;
30         sess.peername = cast(char*) peername;
31         sess.community = cast(ubyte*) community;
32 
33         import core.stdc.string : strlen;
34 
35         sess.community_len = strlen(community);
36 
37         s.handle = snmp_open(&sess);
38         return s;
39     }
40 
41     static SNMPSession open(
42         string peername,
43         string community,
44         SNMPVersion ver = SNMPVersion.v2c)
45     {
46         import std.string : toStringz;
47 
48         return open(peername.toStringz, community.toStringz, ver);
49     }
50 
51     ~this() @trusted @nogc nothrow { close(); }
52 
53     void close() @trusted @nogc nothrow
54     {
55         if (handle)
56         {
57             snmp_close(handle);
58             handle = null;
59         }
60     }
61 
62     bool isOpen() const @nogc nothrow @safe
63     {
64         return handle !is null;
65     }
66 
67     SNMPResponse get(scope const OID o) @trusted @nogc nothrow
68     {
69         if (!isOpen || !o.isValid)
70             return SNMPResponse(null, STAT_ERROR);
71 
72         auto pdu = snmp_pdu_create(cast(int) PDUType.get);
73         snmp_add_null_var(pdu, o.data.ptr, o.length);
74 
75         netsnmp_pdu* resp;
76         int stat = snmp_synch_response(handle, pdu, &resp);
77         return SNMPResponse(resp, stat);
78     }
79 
80     SNMPResponse getNext(scope const OID o) @trusted @nogc nothrow
81     {
82         if (!isOpen || !o.isValid)
83             return SNMPResponse(null, STAT_ERROR);
84 
85         auto pdu = snmp_pdu_create(cast(int) PDUType.getNext);
86         snmp_add_null_var(pdu, o.data.ptr, o.length);
87 
88         netsnmp_pdu* resp;
89         int stat = snmp_synch_response(handle, pdu, &resp);
90         return SNMPResponse(resp, stat);
91     }
92 
93     /// SNMPv2c/v3 only. non_repeaters/max_repetitions map to errstat/errindex.
94     SNMPResponse getBulk(scope const OID o,
95         int nonRepeaters   = 0,
96         int maxRepetitions = 10) @trusted @nogc nothrow
97     {
98         if (!isOpen || !o.isValid)
99             return SNMPResponse(null, STAT_ERROR);
100 
101         auto pdu = snmp_pdu_create(cast(int) PDUType.getBulk);
102         pdu.errstat  = nonRepeaters;
103         pdu.errindex = maxRepetitions;
104         snmp_add_null_var(pdu, o.data.ptr, o.length);
105 
106         netsnmp_pdu* resp;
107         int stat = snmp_synch_response(handle, pdu, &resp);
108         return SNMPResponse(resp, stat);
109     }
110 }
111 
112 unittest
113 {
114     init_snmp("snmp-d-test");
115 
116     auto bad = SNMPSession.open(cast(const(char)*) null, "public", SNMPVersion.v2c);
117     assert(!bad.isOpen);
118 
119     bad.close();
120     bad.close();
121     assert(!bad.isOpen);
122 }