/src/ntopng/third-party/snmp/snmp.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "_snmp.h" |
2 | | |
3 | | #include <stdlib.h> |
4 | | #include <string.h> |
5 | | #include <assert.h> |
6 | | #include <stdio.h> |
7 | | |
8 | | #include "_asn1.h" |
9 | | |
10 | | |
11 | | typedef struct VarbindList |
12 | | { |
13 | | char *oid; |
14 | | int value_type; |
15 | | |
16 | | /** Will be a primtive ASN.1 type; effects how value will be interpreted. */ |
17 | | int render_as_type; |
18 | | |
19 | | Value value; |
20 | | |
21 | | struct VarbindList *next; |
22 | | } VarbindList; |
23 | | |
24 | | struct SNMPMessage |
25 | | { |
26 | | int version; |
27 | | char *community; |
28 | | int pdu_type; |
29 | | int request_id; |
30 | | int error; |
31 | | int error_index; |
32 | | VarbindList *varbind_list; |
33 | | }; |
34 | | |
35 | | SNMPMessage *snmp_create_message() |
36 | 0 | { |
37 | 0 | SNMPMessage *message = (SNMPMessage*)malloc(sizeof(SNMPMessage)); |
38 | 0 | message->version = 0; |
39 | 0 | message->community = NULL; |
40 | 0 | message->pdu_type = 0; |
41 | 0 | message->request_id = 0; |
42 | 0 | message->error = 0; |
43 | 0 | message->error_index = 0; |
44 | 0 | message->varbind_list = NULL; |
45 | 0 | return message; |
46 | 0 | } |
47 | | |
48 | | static void destroy_varbind_list(VarbindList *list) |
49 | 0 | { |
50 | 0 | if (list == NULL) |
51 | 0 | return; |
52 | | |
53 | 0 | free(list->oid); |
54 | 0 | if (list->render_as_type == NTOP_ASN1_STRING_TYPE) |
55 | 0 | free(list->value.str_value); |
56 | 0 | destroy_varbind_list(list->next); |
57 | 0 | free(list); |
58 | 0 | } |
59 | | |
60 | | void snmp_destroy_message(SNMPMessage *message) |
61 | 0 | { |
62 | 0 | free(message->community); |
63 | 0 | destroy_varbind_list(message->varbind_list); |
64 | 0 | } |
65 | | |
66 | | void snmp_set_version(SNMPMessage *message, int version) |
67 | 0 | { |
68 | 0 | message->version = version; |
69 | 0 | } |
70 | | |
71 | | void snmp_set_community(SNMPMessage *message, char *community) |
72 | 0 | { |
73 | 0 | free(message->community); |
74 | 0 | message->community = strdup(community); |
75 | 0 | } |
76 | | |
77 | | void snmp_set_pdu_type(SNMPMessage *message, int type) |
78 | 0 | { |
79 | 0 | message->pdu_type = type; |
80 | 0 | } |
81 | | |
82 | | void snmp_set_request_id(SNMPMessage *message, int request_id) |
83 | 0 | { |
84 | 0 | message->request_id = request_id; |
85 | 0 | } |
86 | | |
87 | | void snmp_set_error(SNMPMessage *message, int error) |
88 | 0 | { |
89 | 0 | message->error = error; |
90 | 0 | } |
91 | | |
92 | | void snmp_set_error_index(SNMPMessage *message, int error_index) |
93 | 0 | { |
94 | 0 | message->error_index = error_index; |
95 | 0 | } |
96 | | |
97 | | void snmp_add_varbind(SNMPMessage *message, VarbindList *vb) |
98 | 0 | { |
99 | 0 | if (message->varbind_list == NULL) |
100 | 0 | message->varbind_list = vb; |
101 | 0 | else |
102 | 0 | { |
103 | 0 | VarbindList *parent = message->varbind_list; |
104 | | |
105 | 0 | while (parent->next != NULL) |
106 | 0 | parent = parent->next; |
107 | | |
108 | 0 | parent->next = vb; |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | void snmp_add_varbind_null(SNMPMessage *message, char *oid) |
113 | 0 | { |
114 | 0 | VarbindList *vb = (VarbindList*)malloc(sizeof (VarbindList)); |
115 | 0 | vb->oid = strdup(oid); |
116 | 0 | vb->value_type = NTOP_ASN1_NULL_TYPE; |
117 | 0 | vb->render_as_type = NTOP_ASN1_NULL_TYPE; |
118 | 0 | vb->next = NULL; |
119 | | |
120 | 0 | snmp_add_varbind(message, vb); |
121 | 0 | } |
122 | | |
123 | | void snmp_add_varbind_integer_type(SNMPMessage *message, char *oid, int type, int64_t value) |
124 | 0 | { |
125 | 0 | VarbindList *vb = (VarbindList*)malloc(sizeof (VarbindList)); |
126 | 0 | vb->oid = strdup(oid); |
127 | 0 | vb->value_type = type; |
128 | 0 | vb->render_as_type = NTOP_ASN1_INTEGER_TYPE; |
129 | 0 | vb->value.int_value = value; |
130 | 0 | vb->next = NULL; |
131 | | |
132 | 0 | snmp_add_varbind(message, vb); |
133 | 0 | } |
134 | | |
135 | | void snmp_add_varbind_integer(SNMPMessage *message, char *oid, int value) |
136 | 0 | { |
137 | 0 | snmp_add_varbind_integer_type(message, oid, NTOP_ASN1_INTEGER_TYPE, value); |
138 | 0 | } |
139 | | |
140 | | void snmp_add_varbind_string(SNMPMessage *message, char *oid, char *value) |
141 | 0 | { |
142 | 0 | VarbindList *vb = (VarbindList*)malloc(sizeof (VarbindList)); |
143 | 0 | vb->oid = strdup(oid); |
144 | 0 | vb->value_type = NTOP_ASN1_STRING_TYPE; |
145 | 0 | vb->render_as_type = NTOP_ASN1_STRING_TYPE; |
146 | 0 | vb->value.str_value = strdup(value); |
147 | 0 | vb->next = NULL; |
148 | | |
149 | 0 | snmp_add_varbind(message, vb); |
150 | 0 | } |
151 | | |
152 | | static void get_msg_lens(SNMPMessage *message, int *msg_len, int *pdu_len, int *vbl_len) |
153 | 0 | { |
154 | 0 | *vbl_len = 0; |
155 | 0 | VarbindList *vb = message->varbind_list; |
156 | 0 | while (vb != NULL) |
157 | 0 | { |
158 | 0 | int oid_obj_len = object_length(oid_length(vb->oid)); |
159 | 0 | int value_obj_len = object_length(value_length(vb->render_as_type, vb->value)); |
160 | 0 | *vbl_len += object_length(oid_obj_len + value_obj_len); |
161 | | |
162 | 0 | vb = vb->next; |
163 | 0 | } |
164 | | |
165 | 0 | *pdu_len = object_length(integer_length(message->request_id)); |
166 | 0 | *pdu_len += object_length(integer_length(message->error)); |
167 | 0 | *pdu_len += object_length(integer_length(message->error_index)); |
168 | 0 | *pdu_len += sequence_header_length(*vbl_len) + *vbl_len; |
169 | | |
170 | 0 | *msg_len = object_length(integer_length(message->version)); |
171 | 0 | *msg_len += object_length(string_length(message->community)); |
172 | | |
173 | 0 | *msg_len += header_length(message->pdu_type, *pdu_len) + *pdu_len; |
174 | 0 | } |
175 | | |
176 | | int snmp_message_length(SNMPMessage *message) |
177 | 0 | { |
178 | 0 | int msg_len, pdu_len, vbl_len; |
179 | | |
180 | 0 | get_msg_lens(message, &msg_len, &pdu_len, &vbl_len); |
181 | | |
182 | 0 | return sequence_header_length(msg_len) + msg_len; |
183 | 0 | } |
184 | | |
185 | | void snmp_render_message(SNMPMessage *message, void *buffer) |
186 | 0 | { |
187 | 0 | int msg_len, pdu_len, vbl_len; |
188 | 0 | VarbindList *vb; |
189 | 0 | void *p = buffer; |
190 | | |
191 | 0 | get_msg_lens(message, &msg_len, &pdu_len, &vbl_len); |
192 | | |
193 | 0 | p = render_sequence_header(msg_len, p); |
194 | 0 | p = render_integer_object(message->version, p); |
195 | 0 | p = render_string_object(message->community, p); |
196 | | |
197 | 0 | p = render_header(message->pdu_type, pdu_len, p); |
198 | 0 | p = render_integer_object(message->request_id, p); |
199 | 0 | p = render_integer_object(message->error, p); |
200 | 0 | p = render_integer_object(message->error_index, p); |
201 | | |
202 | 0 | p = render_sequence_header(vbl_len, p); |
203 | 0 | vb = message->varbind_list; |
204 | 0 | while (vb != NULL) |
205 | 0 | { |
206 | 0 | int oid_obj_len = object_length(oid_length(vb->oid)); |
207 | 0 | int value_obj_len = object_length(value_length(vb->render_as_type, vb->value)); |
208 | 0 | p = render_sequence_header(oid_obj_len + value_obj_len, p); |
209 | 0 | p = render_oid_object(vb->oid, p); |
210 | 0 | p = render_value_object(vb->value_type, vb->render_as_type, vb->value, (char*)p); |
211 | | |
212 | 0 | vb = vb->next; |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | SNMPMessage *snmp_parse_message(void *buffer, int len) |
217 | 0 | { |
218 | 0 | SNMPMessage *message = snmp_create_message(); |
219 | 0 | ASN1Parser *parser = asn1_create_parser(buffer, len); |
220 | | |
221 | 0 | asn1_parse_sequence(parser); |
222 | 0 | asn1_parse_integer(parser, &message->version); |
223 | 0 | asn1_parse_string(parser, &message->community); |
224 | 0 | asn1_parse_structure(parser, &message->pdu_type); |
225 | 0 | asn1_parse_integer(parser, &message->request_id); |
226 | 0 | asn1_parse_integer(parser, &message->error); |
227 | 0 | asn1_parse_integer(parser, &message->error_index); |
228 | 0 | asn1_parse_sequence(parser); |
229 | 0 | while (asn1_parse_sequence(parser)) |
230 | 0 | { |
231 | 0 | char *oid = NULL, *oid1 = NULL; |
232 | 0 | int type = 0; |
233 | 0 | Value value; |
234 | 0 | asn1_parse_oid(parser, &oid); |
235 | 0 | asn1_parse_peek(parser, &type, NULL); |
236 | | |
237 | 0 | switch (type) |
238 | 0 | { |
239 | 0 | case NTOP_SNMP_NOSUCHINSTANCE: |
240 | 0 | case NTOP_SNMP_NOSUCHOBJECT: |
241 | 0 | case NTOP_ASN1_NULL_TYPE: |
242 | 0 | asn1_parse_primitive_value(parser, NULL, &value); |
243 | 0 | snmp_add_varbind_null(message, oid); |
244 | 0 | break; |
245 | | |
246 | 0 | case NTOP_ASN1_OID_TYPE: |
247 | 0 | asn1_parse_oid(parser, &oid1); |
248 | 0 | asn1_parse_primitive_value(parser, NULL, &value); |
249 | 0 | snmp_add_varbind_string(message, oid, oid1); |
250 | 0 | break; |
251 | | |
252 | 0 | case NTOP_SNMP_GAUGE_TYPE: |
253 | 0 | case NTOP_SNMP_COUNTER_TYPE: |
254 | 0 | case NTOP_SNMP_COUNTER64_TYPE: |
255 | 0 | case NTOP_SNMP_TIMETICKS_TYPE: |
256 | 0 | case NTOP_ASN1_INTEGER_TYPE: |
257 | 0 | asn1_parse_integer_type(parser, NULL, &value.int_value); |
258 | 0 | snmp_add_varbind_integer_type(message, oid, type, value.int_value); |
259 | 0 | break; |
260 | | |
261 | 0 | case NTOP_ASN1_STRING_TYPE: |
262 | 0 | asn1_parse_string_type(parser, NULL, &value.str_value); |
263 | 0 | snmp_add_varbind_string(message, oid, value.str_value); |
264 | 0 | free(value.str_value); |
265 | 0 | break; |
266 | 0 | } |
267 | | |
268 | 0 | if(oid) free(oid); |
269 | 0 | if(oid1) free(oid1); |
270 | 0 | asn1_parse_pop(parser); |
271 | 0 | } |
272 | 0 | asn1_parse_pop(parser); |
273 | 0 | asn1_parse_pop(parser); |
274 | 0 | asn1_parse_pop(parser); |
275 | 0 | asn1_destroy_parser(parser); |
276 | | |
277 | 0 | return message; |
278 | 0 | } |
279 | | |
280 | | void snmp_print_message(SNMPMessage *message, FILE *stream) |
281 | 0 | { |
282 | 0 | VarbindList *vb; |
283 | | |
284 | 0 | fprintf(stream, "SNMP Message:\n"); |
285 | 0 | fprintf(stream, " Version: %d\n", message->version); |
286 | 0 | fprintf(stream, " Community: %s\n", message->community); |
287 | 0 | fprintf(stream, " PDU Type: %d\n", message->pdu_type); |
288 | 0 | fprintf(stream, " Request ID: %d\n", message->request_id); |
289 | 0 | fprintf(stream, " Error: %d\n", message->error); |
290 | 0 | fprintf(stream, " Error Index: %d\n", message->error_index); |
291 | | |
292 | 0 | vb = message->varbind_list; |
293 | 0 | while (vb) |
294 | 0 | { |
295 | 0 | char type_str[20] = ""; |
296 | 0 | if (vb->value_type != vb->render_as_type) |
297 | 0 | snprintf(type_str, sizeof(type_str), " (type 0x%02x)", vb->value_type); |
298 | | |
299 | 0 | fprintf(stream, " OID: %s\n", vb->oid); |
300 | 0 | switch (vb->render_as_type) |
301 | 0 | { |
302 | 0 | case NTOP_ASN1_NULL_TYPE: |
303 | 0 | fprintf(stream, " Null%s\n", type_str); |
304 | 0 | break; |
305 | 0 | case NTOP_ASN1_INTEGER_TYPE: |
306 | 0 | fprintf(stream, " Integer%s: %lu PRId64\n", type_str, (unsigned long)vb->value.int_value); |
307 | 0 | break; |
308 | 0 | case NTOP_ASN1_STRING_TYPE: |
309 | 0 | fprintf(stream, " String%s: %s\n", type_str, vb->value.str_value); |
310 | 0 | break; |
311 | 0 | default: |
312 | 0 | abort(); |
313 | 0 | } |
314 | 0 | vb = vb->next; |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | int snmp_get_pdu_type(SNMPMessage *message) |
319 | 0 | { |
320 | 0 | return message->pdu_type; |
321 | 0 | } |
322 | | |
323 | | static VarbindList *get_varbind(SNMPMessage *message, int num) |
324 | 0 | { |
325 | 0 | int i = 0; |
326 | 0 | VarbindList *vb = message->varbind_list; |
327 | | |
328 | 0 | while (vb) |
329 | 0 | { |
330 | 0 | if (i == num) |
331 | 0 | return vb; |
332 | | |
333 | 0 | vb = vb->next; |
334 | 0 | i++; |
335 | 0 | } |
336 | | |
337 | 0 | return NULL; |
338 | 0 | } |
339 | | |
340 | | static int get_varbind_value(SNMPMessage *message, int num, char **oid, int *type, int *render_as_type, Value *value) |
341 | 0 | { |
342 | 0 | VarbindList *vb = get_varbind(message, num); |
343 | 0 | if (!vb) |
344 | 0 | return 0; |
345 | | |
346 | 0 | if (oid) |
347 | 0 | *oid = vb->oid; |
348 | | |
349 | 0 | if (type) |
350 | 0 | *type = vb->value_type; |
351 | | |
352 | 0 | if (render_as_type) |
353 | 0 | *render_as_type = vb->render_as_type; |
354 | | |
355 | 0 | if (value) |
356 | 0 | *value = vb->value; |
357 | | |
358 | 0 | return 1; |
359 | 0 | } |
360 | | |
361 | | int snmp_get_varbind_integer(SNMPMessage *message, int num, char **oid, int *type, int *int_value) |
362 | 0 | { |
363 | 0 | int render_as_type; |
364 | 0 | Value value; |
365 | | |
366 | 0 | if (!get_varbind_value(message, num, oid, type, &render_as_type, &value)) |
367 | 0 | return 0; |
368 | | |
369 | | //TODO return value of 0 also indicates end of list |
370 | | //handle non-integer data differently? |
371 | 0 | if (render_as_type != NTOP_ASN1_INTEGER_TYPE) |
372 | 0 | return 0; |
373 | | |
374 | 0 | if (int_value) |
375 | 0 | *int_value = (int)value.int_value; |
376 | | |
377 | 0 | return 1; |
378 | 0 | } |
379 | | |
380 | | int snmp_get_varbind_string(SNMPMessage *message, int num, char **oid, int *type, char **str_value) |
381 | 0 | { |
382 | 0 | int render_as_type; |
383 | 0 | Value value; |
384 | | |
385 | 0 | if (!get_varbind_value(message, num, oid, type, &render_as_type, &value)) |
386 | 0 | return 0; |
387 | | |
388 | | //TODO return value of 0 also indicates end of list |
389 | | //handle non-string data differently? |
390 | 0 | if (render_as_type != NTOP_ASN1_STRING_TYPE) |
391 | 0 | return 0; |
392 | | |
393 | 0 | if (str_value) |
394 | 0 | *str_value = value.str_value; |
395 | | |
396 | 0 | return 1; |
397 | 0 | } |
398 | | |
399 | | int snmp_get_varbind_as_string(SNMPMessage *message, int num, char **oid, int *type, char **value_str) |
400 | 0 | { |
401 | 0 | int render_as_type; |
402 | 0 | Value value; |
403 | 0 | char buf[20]; |
404 | | |
405 | 0 | if (!get_varbind_value(message, num, oid, type, &render_as_type, &value)) |
406 | 0 | return 0; |
407 | | |
408 | 0 | if (!value_str) |
409 | 0 | return 1; |
410 | | |
411 | 0 | switch (render_as_type) |
412 | 0 | { |
413 | 0 | case NTOP_ASN1_NULL_TYPE: |
414 | 0 | *value_str = strdup(""); |
415 | 0 | break; |
416 | 0 | case NTOP_ASN1_INTEGER_TYPE: |
417 | | // snprintf(buf, sizeof(buf), "%d", value.int_value); |
418 | | // FIX: integer types always assumed unsigned |
419 | 0 | snprintf(buf, sizeof(buf), "%lu", (unsigned long)value.int_value); |
420 | 0 | *value_str = strdup(buf); |
421 | 0 | break; |
422 | 0 | case NTOP_ASN1_STRING_TYPE: |
423 | 0 | *value_str = strdup(value.str_value); |
424 | 0 | break; |
425 | 0 | default: |
426 | 0 | abort(); |
427 | 0 | } |
428 | | |
429 | 0 | return 1; |
430 | 0 | } |