1 module snmp.varbind;
2 
3 import c.net_snmp;
4 import snmp.oid : OID;
5 
6 private alias size_t = object.size_t;
7 
8 /// Cursor over a netsnmp_variable_list linked list.
9 struct VarBind
10 {
11     netsnmp_variable_list* handle;
12 
13     bool isValid() const @nogc nothrow @safe
14     {
15         return handle !is null;
16     }
17 
18     VarBind next() const @trusted @nogc nothrow
19     {
20         return handle
21             ? VarBind(cast(netsnmp_variable_list*) handle.next_variable) : VarBind(null);
22     }
23 
24     const(oid)* namePtr() const @trusted @nogc nothrow
25     {
26         return handle ? handle.name : null;
27     }
28 
29     size_t nameLength() const @trusted @nogc nothrow
30     {
31         return handle ? handle.name_length : 0;
32     }
33 
34     ubyte type() const @trusted @nogc nothrow
35     {
36         return handle ? handle.type : 0;
37     }
38 
39     int valueStr(scope char[] buf) const @trusted @nogc nothrow
40     {
41         if (!isValid || buf.length == 0)
42             return 0;
43         auto h = cast(netsnmp_variable_list*) handle;
44         return snprint_value(buf.ptr, buf.length, h.name, h.name_length, h);
45     }
46 
47     int oidStr(scope char[] buf) const @trusted @nogc nothrow
48     {
49         if (!isValid || buf.length == 0)
50             return 0;
51         return snprint_objid(buf.ptr, buf.length, handle.name, handle.name_length);
52     }
53 
54     string oidString() const @trusted nothrow
55     {
56         char[512] buf = void;
57         int n = oidStr(buf[]);
58         return n > 0 ? buf[0 .. n].idup : "";
59     }
60 
61     string valueString() const @trusted nothrow
62     {
63         char[512] buf = void;
64         int n = valueStr(buf[]);
65         return n > 0 ? buf[0 .. n].idup : "";
66     }
67 
68     OID toOID() const @trusted @nogc nothrow
69     {
70         import core.stdc.string : memcpy;
71 
72         OID o;
73         if (!isValid)
74             return o;
75         o.length = handle.name_length < MAX_OID_LEN ? handle.name_length : MAX_OID_LEN;
76         memcpy(o.data.ptr, handle.name, o.length * oid.sizeof);
77         return o;
78     }
79 
80     /// Returns 0 for non-integer or invalid bindings.
81     long asLong() const @trusted @nogc nothrow
82     {
83         if (!isValid || handle.type != ASN_INTEGER || !handle.val.integer)
84             return 0;
85         return *handle.val.integer;
86     }
87 
88     /// Counter32 / Gauge32 / TimeTicks. Returns 0 on type mismatch.
89     uint asUint() const @trusted @nogc nothrow
90     {
91         if (!isValid || !handle.val.integer)
92             return 0;
93         ubyte t = handle.type;
94         if (t != ASN_COUNTER && t != ASN_GAUGE && t != ASN_TIMETICKS && t != ASN_UNSIGNED)
95             return 0;
96         return cast(uint) *handle.val.integer;
97     }
98 
99     /// Returns zeroed struct on type mismatch.
100     counter64 asCounter64() const @trusted @nogc nothrow
101     {
102         counter64 zero;
103         if (!isValid || handle.type != ASN_COUNTER64 || !handle.val.counter64)
104             return zero;
105         return *handle.val.counter64;
106     }
107 
108     /// OctetStr / IpAddress / BitStr raw bytes. Returns null on type mismatch.
109     const(ubyte)[] asBytes() const @trusted @nogc nothrow
110     {
111         if (!isValid)
112             return null;
113         ubyte t = handle.type;
114         if (t != ASN_OCTET_STR && t != ASN_IPADDRESS && t != ASN_BIT_STR)
115             return null;
116         // `string` is a D keyword — access via __traits
117         auto p = __traits(getMember, handle.val, "string");
118         if (!p)
119             return null;
120         return p[0 .. handle.val_len];
121     }
122 
123     int opApply(scope int delegate(VarBind) dg)
124     {
125         for (auto v = this; v.isValid; v = v.next)
126             if (auto r = dg(v))
127                 return r;
128         return 0;
129     }
130 }
131 
132 unittest
133 {
134     VarBind v;
135     assert(!v.isValid);
136     assert(!v.next.isValid);
137     assert(v.namePtr is null);
138     assert(v.nameLength == 0);
139     assert(v.type == 0);
140 
141     char[64] buf;
142     assert(v.valueStr(buf[]) == 0);
143     assert(v.oidStr(buf[]) == 0);
144     assert(v.oidString == "");
145     assert(v.valueString == "");
146 
147     assert(!v.toOID.isValid);
148     assert(v.asLong  == 0);
149     assert(v.asUint  == 0);
150     assert(v.asBytes is null);
151     counter64 zero;
152     assert(v.asCounter64.high == zero.high && v.asCounter64.low == zero.low);
153 }