/src/net-snmp/snmplib/snmp_client.c
Line | Count | Source |
1 | | /* |
2 | | * snmp_client.c - a toolkit of common functions for an SNMP client. |
3 | | * |
4 | | */ |
5 | | /* Portions of this file are subject to the following copyright(s). See |
6 | | * the Net-SNMP's COPYING file for more details and other copyrights |
7 | | * that may apply: |
8 | | */ |
9 | | /********************************************************************** |
10 | | Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University |
11 | | |
12 | | All Rights Reserved |
13 | | |
14 | | Permission to use, copy, modify, and distribute this software and its |
15 | | documentation for any purpose and without fee is hereby granted, |
16 | | provided that the above copyright notice appear in all copies and that |
17 | | both that copyright notice and this permission notice appear in |
18 | | supporting documentation, and that the name of CMU not be |
19 | | used in advertising or publicity pertaining to distribution of the |
20 | | software without specific, written prior permission. |
21 | | |
22 | | CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
23 | | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
24 | | CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
25 | | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
26 | | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
27 | | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
28 | | SOFTWARE. |
29 | | ******************************************************************/ |
30 | | /* |
31 | | * Portions of this file are copyrighted by: |
32 | | * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
33 | | * Use is subject to license terms specified in the COPYING file |
34 | | * distributed with the Net-SNMP package. |
35 | | * |
36 | | * Portions of this file are copyrighted by: |
37 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
38 | | * Use is subject to license terms specified in the COPYING file |
39 | | * distributed with the Net-SNMP package. |
40 | | */ |
41 | | |
42 | | /** @defgroup snmp_client various PDU processing routines |
43 | | * @ingroup library |
44 | | * |
45 | | * @{ |
46 | | */ |
47 | | #include <net-snmp/net-snmp-config.h> |
48 | | #include <net-snmp/net-snmp-features.h> |
49 | | |
50 | | #include <stdio.h> |
51 | | #include <errno.h> |
52 | | #ifdef HAVE_INTTYPES_H |
53 | | #include <inttypes.h> |
54 | | #endif |
55 | | #ifdef HAVE_STDLIB_H |
56 | | #include <stdlib.h> |
57 | | #endif |
58 | | #ifdef HAVE_STRING_H |
59 | | #include <string.h> |
60 | | #else |
61 | | #include <strings.h> |
62 | | #endif |
63 | | #ifdef HAVE_UNISTD_H |
64 | | #include <unistd.h> |
65 | | #endif |
66 | | #include <sys/types.h> |
67 | | #ifdef TIME_WITH_SYS_TIME |
68 | | # include <sys/time.h> |
69 | | # include <time.h> |
70 | | #else |
71 | | # ifdef HAVE_SYS_TIME_H |
72 | | # include <sys/time.h> |
73 | | # else |
74 | | # include <time.h> |
75 | | # endif |
76 | | #endif |
77 | | #ifdef HAVE_SYS_PARAM_H |
78 | | #include <sys/param.h> |
79 | | #endif |
80 | | #ifdef HAVE_NETINET_IN_H |
81 | | #include <netinet/in.h> |
82 | | #endif |
83 | | #ifdef HAVE_ARPA_INET_H |
84 | | #include <arpa/inet.h> |
85 | | #endif |
86 | | #ifdef HAVE_SYS_SELECT_H |
87 | | #include <sys/select.h> |
88 | | #endif |
89 | | #ifdef HAVE_SYSLOG_H |
90 | | #include <syslog.h> |
91 | | #endif |
92 | | |
93 | | #include <net-snmp/types.h> |
94 | | |
95 | | #include <net-snmp/agent/ds_agent.h> |
96 | | #include <net-snmp/library/default_store.h> |
97 | | #include <net-snmp/library/snmp.h> |
98 | | #include <net-snmp/library/snmp-tc.h> |
99 | | #include <net-snmp/library/snmp_api.h> |
100 | | #include <net-snmp/library/snmp_client.h> |
101 | | #include <net-snmp/library/snmp_impl.h> |
102 | | #include <net-snmp/library/snmp_secmod.h> |
103 | | #include <net-snmp/library/snmpusm.h> |
104 | | #include <net-snmp/library/mib.h> |
105 | | #include <net-snmp/library/snmp_logging.h> |
106 | | #include <net-snmp/library/snmp_assert.h> |
107 | | #include <net-snmp/library/large_fd_set.h> |
108 | | #include <net-snmp/library/tools.h> |
109 | | #include <net-snmp/pdu_api.h> |
110 | | |
111 | | netsnmp_feature_child_of(snmp_client_all, libnetsnmp); |
112 | | |
113 | | netsnmp_feature_child_of(snmp_split_pdu, snmp_client_all); |
114 | | netsnmp_feature_child_of(snmp_reset_var_types, snmp_client_all); |
115 | | netsnmp_feature_child_of(query_set_default_session, snmp_client_all); |
116 | | netsnmp_feature_child_of(row_create, snmp_client_all); |
117 | | |
118 | | #ifndef BSD4_3 |
119 | | #define BSD4_2 |
120 | | #endif |
121 | | |
122 | | |
123 | | /* |
124 | | * Prototype definitions |
125 | | */ |
126 | | static int snmp_synch_input(int op, netsnmp_session * session, |
127 | | int reqid, netsnmp_pdu *pdu, void *magic); |
128 | | |
129 | | netsnmp_pdu * |
130 | | snmp_pdu_create(int command) |
131 | 0 | { |
132 | 0 | netsnmp_pdu *pdu; |
133 | |
|
134 | 0 | pdu = calloc(1, sizeof(netsnmp_pdu)); |
135 | 0 | if (pdu) { |
136 | 0 | pdu->version = SNMP_DEFAULT_VERSION; |
137 | 0 | pdu->command = command; |
138 | 0 | pdu->errstat = SNMP_DEFAULT_ERRSTAT; |
139 | 0 | pdu->errindex = SNMP_DEFAULT_ERRINDEX; |
140 | 0 | pdu->securityModel = SNMP_DEFAULT_SECMODEL; |
141 | 0 | pdu->transport_data = NULL; |
142 | 0 | pdu->transport_data_length = 0; |
143 | 0 | pdu->securityNameLen = 0; |
144 | 0 | pdu->contextNameLen = 0; |
145 | 0 | pdu->time = 0; |
146 | 0 | pdu->reqid = snmp_get_next_reqid(); |
147 | 0 | pdu->msgid = snmp_get_next_msgid(); |
148 | 0 | } |
149 | 0 | return pdu; |
150 | |
|
151 | 0 | } |
152 | | |
153 | | |
154 | | /* |
155 | | * Add a null variable with the requested name to the end of the list of |
156 | | * variables for this pdu. |
157 | | */ |
158 | | netsnmp_variable_list * |
159 | | snmp_add_null_var(netsnmp_pdu *pdu, const oid * name, size_t name_length) |
160 | 0 | { |
161 | 0 | return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0); |
162 | 0 | } |
163 | | |
164 | | |
165 | | #include <net-snmp/library/snmp_debug.h> |
166 | | static int |
167 | | snmp_synch_input(int op, |
168 | | netsnmp_session * session, |
169 | | int reqid, netsnmp_pdu *pdu, void *magic) |
170 | 0 | { |
171 | 0 | struct synch_state *state = (struct synch_state *) magic; |
172 | 0 | int rpt_type; |
173 | |
|
174 | 0 | if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) { |
175 | 0 | DEBUGMSGTL(("snmp_synch", "Unexpected response (ReqID: %d,%d - Cmd %d)\n", |
176 | 0 | reqid, state->reqid, pdu->command )); |
177 | 0 | return 0; |
178 | 0 | } |
179 | | |
180 | 0 | state->waiting = 0; |
181 | 0 | DEBUGMSGTL(("snmp_synch", "Response (ReqID: %d - Cmd %d)\n", |
182 | 0 | reqid, (pdu ? pdu->command : -1))); |
183 | |
|
184 | 0 | if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE && pdu) { |
185 | 0 | if (pdu->command == SNMP_MSG_REPORT) { |
186 | 0 | rpt_type = snmpv3_get_report_type(pdu); |
187 | 0 | if (SNMPV3_IGNORE_UNAUTH_REPORTS || |
188 | 0 | rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) { |
189 | 0 | state->waiting = 1; |
190 | 0 | } |
191 | 0 | state->pdu = NULL; |
192 | 0 | state->status = STAT_ERROR; |
193 | 0 | session->s_snmp_errno = rpt_type; |
194 | 0 | SET_SNMP_ERROR(rpt_type); |
195 | 0 | } else if (pdu->command == SNMP_MSG_RESPONSE) { |
196 | | /* |
197 | | * clone the pdu to return to snmp_synch_response |
198 | | */ |
199 | 0 | state->pdu = snmp_clone_pdu(pdu); |
200 | 0 | state->status = STAT_SUCCESS; |
201 | 0 | session->s_snmp_errno = SNMPERR_SUCCESS; |
202 | 0 | } |
203 | 0 | else { |
204 | 0 | char msg_buf[50]; |
205 | 0 | state->status = STAT_ERROR; |
206 | 0 | session->s_snmp_errno = SNMPERR_PROTOCOL; |
207 | 0 | SET_SNMP_ERROR(SNMPERR_PROTOCOL); |
208 | 0 | snprintf(msg_buf, sizeof(msg_buf), "Expected RESPONSE-PDU but got %s-PDU", |
209 | 0 | snmp_pdu_type(pdu->command)); |
210 | 0 | snmp_set_detail(msg_buf); |
211 | 0 | return 0; |
212 | 0 | } |
213 | 0 | } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) { |
214 | 0 | state->pdu = NULL; |
215 | 0 | state->status = STAT_TIMEOUT; |
216 | 0 | session->s_snmp_errno = SNMPERR_TIMEOUT; |
217 | 0 | SET_SNMP_ERROR(SNMPERR_TIMEOUT); |
218 | 0 | } else if (op == NETSNMP_CALLBACK_OP_SEC_ERROR) { |
219 | 0 | state->pdu = NULL; |
220 | | /* |
221 | | * If we already have an error in status, then leave it alone. |
222 | | */ |
223 | 0 | if (state->status == STAT_SUCCESS) { |
224 | 0 | state->status = STAT_ERROR; |
225 | 0 | session->s_snmp_errno = SNMPERR_GENERR; |
226 | 0 | SET_SNMP_ERROR(SNMPERR_GENERR); |
227 | 0 | } |
228 | 0 | } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) { |
229 | 0 | state->pdu = NULL; |
230 | 0 | state->status = STAT_ERROR; |
231 | 0 | session->s_snmp_errno = SNMPERR_ABORT; |
232 | 0 | SET_SNMP_ERROR(SNMPERR_ABORT); |
233 | 0 | } |
234 | 0 | DEBUGMSGTL(("snmp_synch", "status = %d errno = %d\n", |
235 | 0 | state->status, session->s_snmp_errno)); |
236 | |
|
237 | 0 | return 1; |
238 | 0 | } |
239 | | |
240 | | |
241 | | /* |
242 | | * Clone an SNMP variable data structure. |
243 | | * Sets pointers to structure private storage, or |
244 | | * allocates larger object identifiers and values as needed. |
245 | | * |
246 | | * Caller must make list association for cloned variable. |
247 | | * |
248 | | * Returns 0 if successful. |
249 | | */ |
250 | | int |
251 | | snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar) |
252 | 0 | { |
253 | 0 | if (!newvar || !var) |
254 | 0 | return 1; |
255 | | |
256 | 0 | memmove(newvar, var, sizeof(netsnmp_variable_list)); |
257 | 0 | newvar->next_variable = NULL; |
258 | 0 | newvar->name = NULL; |
259 | 0 | newvar->val.string = NULL; |
260 | 0 | newvar->data = NULL; |
261 | 0 | newvar->dataFreeHook = NULL; |
262 | 0 | newvar->index = 0; |
263 | | |
264 | | /* |
265 | | * Clone the object identifier and the value. |
266 | | * Allocate memory iff original will not fit into local storage. |
267 | | */ |
268 | 0 | if (snmp_set_var_objid(newvar, var->name, var->name_length)) |
269 | 0 | return 1; |
270 | | |
271 | | /* |
272 | | * need a pointer to copy a string value. |
273 | | */ |
274 | 0 | if (var->val.string) { |
275 | 0 | if (var->val.string != &var->buf[0]) { |
276 | 0 | if (var->val_len < sizeof(var->buf)) { |
277 | 0 | newvar->val.string = newvar->buf; |
278 | 0 | memmove(newvar->val.string, var->val.string, var->val_len); |
279 | 0 | newvar->val.string[var->val_len] = 0; |
280 | 0 | } else { |
281 | 0 | newvar->val.string = (u_char *) malloc(var->val_len + 1); |
282 | 0 | if (!newvar->val.string) |
283 | 0 | return 1; |
284 | 0 | memmove(newvar->val.string, var->val.string, var->val_len); |
285 | 0 | newvar->val.string[var->val_len] = 0; |
286 | 0 | } |
287 | 0 | } else { /* fix the pointer to new local store */ |
288 | 0 | newvar->val.string = newvar->buf; |
289 | | /* |
290 | | * no need for a memmove, since we copied the whole var |
291 | | * struct (and thus var->buf) at the beginning of this function. |
292 | | */ |
293 | 0 | } |
294 | 0 | } else { |
295 | 0 | newvar->val.string = NULL; |
296 | 0 | newvar->val_len = 0; |
297 | 0 | } |
298 | | |
299 | 0 | return 0; |
300 | 0 | } |
301 | | |
302 | | |
303 | | /* |
304 | | * Possibly make a copy of source memory buffer. |
305 | | * Will reset destination pointer if source pointer is NULL. |
306 | | * Returns 0 if successful, 1 if memory allocation fails. |
307 | | */ |
308 | | int |
309 | | snmp_clone_mem(void **dstPtr, const void *srcPtr, unsigned len) |
310 | 0 | { |
311 | 0 | *dstPtr = NULL; |
312 | 0 | if (srcPtr) { |
313 | 0 | *dstPtr = malloc(len + 1); |
314 | 0 | if (!*dstPtr) { |
315 | 0 | return 1; |
316 | 0 | } |
317 | 0 | memmove(*dstPtr, srcPtr, len); |
318 | | /* |
319 | | * this is for those routines that expect 0-terminated strings!!! |
320 | | * someone should rather have called strdup |
321 | | */ |
322 | 0 | ((char *) *dstPtr)[len] = 0; |
323 | 0 | } |
324 | 0 | return 0; |
325 | 0 | } |
326 | | |
327 | | |
328 | | /* |
329 | | * Walks through a list of varbinds and frees and allocated memory, |
330 | | * restoring pointers to local buffers |
331 | | */ |
332 | | void |
333 | | snmp_reset_var_buffers(netsnmp_variable_list * var) |
334 | 0 | { |
335 | 0 | while (var) { |
336 | 0 | if (var->name != var->name_loc) { |
337 | 0 | if(NULL != var->name) |
338 | 0 | free(var->name); |
339 | 0 | var->name = var->name_loc; |
340 | 0 | var->name_length = 0; |
341 | 0 | } |
342 | 0 | if (var->val.string != var->buf) { |
343 | 0 | if (NULL != var->val.string) |
344 | 0 | free(var->val.string); |
345 | 0 | var->val.string = var->buf; |
346 | 0 | var->val_len = 0; |
347 | 0 | } |
348 | 0 | var = var->next_variable; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | /* |
353 | | * Creates and allocates a clone of the input PDU, |
354 | | * but does NOT copy the variables. |
355 | | * This function should be used with another function, |
356 | | * such as _copy_pdu_vars. |
357 | | * |
358 | | * Returns a pointer to the cloned PDU if successful. |
359 | | * Returns 0 if failure. |
360 | | */ |
361 | | static |
362 | | netsnmp_pdu * |
363 | | _clone_pdu_header(netsnmp_pdu *pdu) |
364 | 0 | { |
365 | 0 | netsnmp_pdu *newpdu; |
366 | 0 | struct snmp_secmod_def *sptr; |
367 | 0 | int ret; |
368 | |
|
369 | 0 | if (!pdu) |
370 | 0 | return NULL; |
371 | | |
372 | 0 | newpdu = netsnmp_memdup(pdu, sizeof(netsnmp_pdu)); |
373 | 0 | if (!newpdu) |
374 | 0 | return NULL; |
375 | | |
376 | | /* |
377 | | * reset copied pointers if copy fails |
378 | | */ |
379 | 0 | newpdu->variables = NULL; |
380 | 0 | newpdu->enterprise = NULL; |
381 | 0 | newpdu->community = NULL; |
382 | 0 | newpdu->securityEngineID = NULL; |
383 | 0 | newpdu->securityName = NULL; |
384 | 0 | newpdu->contextEngineID = NULL; |
385 | 0 | newpdu->contextName = NULL; |
386 | 0 | newpdu->transport_data = NULL; |
387 | 0 | newpdu->securityStateRef = NULL; |
388 | | |
389 | | /* |
390 | | * copy buffers individually. If any copy fails, all are freed. |
391 | | */ |
392 | 0 | if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise, |
393 | 0 | sizeof(oid) * pdu->enterprise_length) || |
394 | 0 | snmp_clone_mem((void **) &newpdu->community, pdu->community, |
395 | 0 | pdu->community_len) || |
396 | 0 | snmp_clone_mem((void **) &newpdu->contextEngineID, |
397 | 0 | pdu->contextEngineID, pdu->contextEngineIDLen) |
398 | 0 | || snmp_clone_mem((void **) &newpdu->securityEngineID, |
399 | 0 | pdu->securityEngineID, pdu->securityEngineIDLen) |
400 | 0 | || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName, |
401 | 0 | pdu->contextNameLen) |
402 | 0 | || snmp_clone_mem((void **) &newpdu->securityName, |
403 | 0 | pdu->securityName, pdu->securityNameLen) |
404 | 0 | || snmp_clone_mem((void **) &newpdu->transport_data, |
405 | 0 | pdu->transport_data, |
406 | 0 | pdu->transport_data_length)) { |
407 | 0 | snmp_free_pdu(newpdu); |
408 | 0 | return NULL; |
409 | 0 | } |
410 | | |
411 | 0 | sptr = find_sec_mod(newpdu->securityModel); |
412 | 0 | if (sptr && sptr->pdu_clone) { |
413 | | /* call security model if it needs to know about this */ |
414 | 0 | ret = sptr->pdu_clone(pdu, newpdu); |
415 | 0 | if (ret) { |
416 | 0 | snmp_free_pdu(newpdu); |
417 | 0 | return NULL; |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | 0 | return newpdu; |
422 | 0 | } |
423 | | |
424 | | static |
425 | | netsnmp_variable_list * |
426 | | _copy_varlist(netsnmp_variable_list * var, /* source varList */ |
427 | | int errindex, /* index of variable to drop (if any) */ |
428 | | int copy_count) |
429 | 0 | { /* !=0 number variables to copy */ |
430 | 0 | netsnmp_variable_list *newhead, *newvar, *oldvar; |
431 | 0 | int ii = 0; |
432 | |
|
433 | 0 | newhead = NULL; |
434 | 0 | oldvar = NULL; |
435 | |
|
436 | 0 | while (var && (copy_count-- > 0)) { |
437 | | /* |
438 | | * Drop the specified variable (if applicable) |
439 | | * xxx hmm, is it intentional that dropping the errindex |
440 | | * counts towards copy_count? |
441 | | */ |
442 | 0 | if (++ii == errindex) { |
443 | 0 | var = var->next_variable; |
444 | 0 | continue; |
445 | 0 | } |
446 | | |
447 | | /* |
448 | | * clone the next variable. Cleanup if alloc fails |
449 | | */ |
450 | 0 | newvar = (netsnmp_variable_list *) |
451 | 0 | malloc(sizeof(netsnmp_variable_list)); |
452 | 0 | if (snmp_clone_var(var, newvar)) { |
453 | 0 | if (newvar) |
454 | 0 | free(newvar); |
455 | 0 | snmp_free_varbind(newhead); |
456 | 0 | return NULL; |
457 | 0 | } |
458 | | |
459 | | /* |
460 | | * add cloned variable to new list |
461 | | */ |
462 | 0 | if (NULL == newhead) |
463 | 0 | newhead = newvar; |
464 | 0 | if (oldvar) |
465 | 0 | oldvar->next_variable = newvar; |
466 | 0 | oldvar = newvar; |
467 | |
|
468 | 0 | var = var->next_variable; |
469 | 0 | } |
470 | 0 | return newhead; |
471 | 0 | } |
472 | | |
473 | | |
474 | | /* |
475 | | * Copy some or all variables from source PDU to target PDU. |
476 | | * This function consolidates many of the needs of PDU variables: |
477 | | * Clone PDU : copy all the variables. |
478 | | * Split PDU : skip over some variables to copy other variables. |
479 | | * Fix PDU : remove variable associated with error index. |
480 | | * |
481 | | * Designed to work with _clone_pdu_header. |
482 | | * |
483 | | * If drop_err is set, drop any variable associated with errindex. |
484 | | * If skip_count is set, skip the number of variable in pdu's list. |
485 | | * While copy_count is greater than zero, copy pdu variables to newpdu. |
486 | | * |
487 | | * If an error occurs, newpdu is freed and pointer is set to 0. |
488 | | * |
489 | | * Returns a pointer to the cloned PDU if successful. |
490 | | * Returns 0 if failure. |
491 | | */ |
492 | | static |
493 | | netsnmp_pdu * |
494 | | _copy_pdu_vars(netsnmp_pdu *pdu, /* source PDU */ |
495 | | netsnmp_pdu *newpdu, /* target PDU */ |
496 | | int drop_err, /* !=0 drop errored variable */ |
497 | | int skip_count, /* !=0 number of variables to skip */ |
498 | | int copy_count) |
499 | 0 | { /* !=0 number of variables to copy */ |
500 | 0 | netsnmp_variable_list *var; |
501 | | #ifdef TEMPORARILY_DISABLED |
502 | | int copied; |
503 | | #endif |
504 | 0 | int drop_idx; |
505 | |
|
506 | 0 | if (!newpdu) |
507 | 0 | return NULL; /* where is PDU to copy to ? */ |
508 | | |
509 | 0 | if (drop_err) |
510 | 0 | drop_idx = pdu->errindex - skip_count; |
511 | 0 | else |
512 | 0 | drop_idx = 0; |
513 | |
|
514 | 0 | var = pdu->variables; |
515 | 0 | while (var && (skip_count-- > 0)) /* skip over pdu variables */ |
516 | 0 | var = var->next_variable; |
517 | |
|
518 | | #ifdef TEMPORARILY_DISABLED |
519 | | copied = 0; |
520 | | if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY) |
521 | | copied = 1; /* We're interested in 'empty' responses too */ |
522 | | #endif |
523 | |
|
524 | 0 | newpdu->variables = _copy_varlist(var, drop_idx, copy_count); |
525 | | #ifdef TEMPORARILY_DISABLED |
526 | | if (newpdu->variables) |
527 | | copied = 1; |
528 | | #endif |
529 | |
|
530 | | #ifdef ALSO_TEMPORARILY_DISABLED |
531 | | /* |
532 | | * Error if bad errindex or if target PDU has no variables copied |
533 | | */ |
534 | | if ((drop_err && (ii < pdu->errindex)) |
535 | | #ifdef TEMPORARILY_DISABLED |
536 | | /* |
537 | | * SNMPv3 engineID probes are allowed to be empty. |
538 | | * See the comment in snmp_api.c for further details |
539 | | */ |
540 | | || copied == 0 |
541 | | #endif |
542 | | ) { |
543 | | snmp_free_pdu(newpdu); |
544 | | return 0; |
545 | | } |
546 | | #endif |
547 | 0 | return newpdu; |
548 | 0 | } |
549 | | |
550 | | |
551 | | /* |
552 | | * Creates (allocates and copies) a clone of the input PDU. |
553 | | * If drop_err is set, don't copy any variable associated with errindex. |
554 | | * This function is called by snmp_clone_pdu and snmp_fix_pdu. |
555 | | * |
556 | | * Returns a pointer to the cloned PDU if successful. |
557 | | * Returns 0 if failure. |
558 | | */ |
559 | | static |
560 | | netsnmp_pdu * |
561 | | _clone_pdu(netsnmp_pdu *pdu, int drop_err) |
562 | 0 | { |
563 | 0 | netsnmp_pdu *newpdu; |
564 | |
|
565 | 0 | newpdu = _clone_pdu_header(pdu); |
566 | 0 | if (!newpdu) |
567 | 0 | return newpdu; |
568 | 0 | newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000); /* skip none, copy all */ |
569 | |
|
570 | 0 | return newpdu; |
571 | 0 | } |
572 | | |
573 | | |
574 | | /* |
575 | | * This function will clone a full varbind list |
576 | | * |
577 | | * Returns a pointer to the cloned varbind list if successful. |
578 | | * Returns 0 if failure |
579 | | */ |
580 | | netsnmp_variable_list * |
581 | | snmp_clone_varbind(netsnmp_variable_list * varlist) |
582 | 0 | { |
583 | 0 | return _copy_varlist(varlist, 0, 10000); /* skip none, copy all */ |
584 | 0 | } |
585 | | |
586 | | /* |
587 | | * This function will clone a PDU including all of its variables. |
588 | | * |
589 | | * Returns a pointer to the cloned PDU if successful. |
590 | | * Returns 0 if failure |
591 | | */ |
592 | | netsnmp_pdu * |
593 | | snmp_clone_pdu(netsnmp_pdu *pdu) |
594 | 0 | { |
595 | 0 | return _clone_pdu(pdu, 0); /* copies all variables */ |
596 | 0 | } |
597 | | |
598 | | |
599 | | /* |
600 | | * This function will clone a PDU including some of its variables. |
601 | | * |
602 | | * If skip_count is not zero, it defines the number of variables to skip. |
603 | | * If copy_count is not zero, it defines the number of variables to copy. |
604 | | * |
605 | | * Returns a pointer to the cloned PDU if successful. |
606 | | * Returns 0 if failure. |
607 | | */ |
608 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU |
609 | | netsnmp_pdu * |
610 | | snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count) |
611 | 0 | { |
612 | 0 | netsnmp_pdu *newpdu; |
613 | |
|
614 | 0 | newpdu = _clone_pdu_header(pdu); |
615 | 0 | if (!newpdu) |
616 | 0 | return newpdu; |
617 | 0 | newpdu = _copy_pdu_vars(pdu, newpdu, 0, /* don't drop any variables */ |
618 | 0 | skip_count, copy_count); |
619 | |
|
620 | 0 | return newpdu; |
621 | 0 | } |
622 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU */ |
623 | | |
624 | | |
625 | | /* |
626 | | * If there was an error in the input pdu, creates a clone of the pdu |
627 | | * that includes all the variables except the one marked by the errindex. |
628 | | * The command is set to the input command and the reqid, errstat, and |
629 | | * errindex are set to default values. |
630 | | * If the error status didn't indicate an error, the error index didn't |
631 | | * indicate a variable, the pdu wasn't a get response message, the |
632 | | * marked variable was not present in the initial request, or there |
633 | | * would be no remaining variables, this function will return 0. |
634 | | * If everything was successful, a pointer to the fixed cloned pdu will |
635 | | * be returned. |
636 | | */ |
637 | | netsnmp_pdu * |
638 | | snmp_fix_pdu(netsnmp_pdu *pdu, int command) |
639 | 0 | { |
640 | 0 | netsnmp_pdu *newpdu; |
641 | |
|
642 | 0 | if ((pdu->command != SNMP_MSG_RESPONSE) |
643 | 0 | || (pdu->errstat == SNMP_ERR_NOERROR) |
644 | 0 | || (NULL == pdu->variables) |
645 | 0 | || (pdu->errindex > (int)snmp_varbind_len(pdu)) |
646 | 0 | || (pdu->errindex <= 0)) { |
647 | 0 | return NULL; /* pre-condition tests fail */ |
648 | 0 | } |
649 | | |
650 | 0 | newpdu = _clone_pdu(pdu, 1); /* copies all except errored variable */ |
651 | 0 | if (!newpdu) |
652 | 0 | return NULL; |
653 | 0 | if (!newpdu->variables) { |
654 | 0 | snmp_free_pdu(newpdu); |
655 | 0 | return NULL; /* no variables. "should not happen" */ |
656 | 0 | } |
657 | 0 | newpdu->command = command; |
658 | 0 | newpdu->reqid = snmp_get_next_reqid(); |
659 | 0 | newpdu->msgid = snmp_get_next_msgid(); |
660 | 0 | newpdu->errstat = SNMP_DEFAULT_ERRSTAT; |
661 | 0 | newpdu->errindex = SNMP_DEFAULT_ERRINDEX; |
662 | |
|
663 | 0 | return newpdu; |
664 | 0 | } |
665 | | |
666 | | |
667 | | /* |
668 | | * Returns the number of variables bound to a PDU structure |
669 | | */ |
670 | | unsigned long |
671 | | snmp_varbind_len(netsnmp_pdu *pdu) |
672 | 0 | { |
673 | 0 | register netsnmp_variable_list *vars; |
674 | 0 | unsigned long retVal = 0; |
675 | 0 | if (pdu) |
676 | 0 | for (vars = pdu->variables; vars; vars = vars->next_variable) { |
677 | 0 | retVal++; |
678 | 0 | } |
679 | |
|
680 | 0 | return retVal; |
681 | 0 | } |
682 | | |
683 | | /* |
684 | | * Add object identifier name to SNMP variable. |
685 | | * If the name is large, additional memory is allocated. |
686 | | * Returns 0 if successful. |
687 | | */ |
688 | | |
689 | | int |
690 | | snmp_set_var_objid(netsnmp_variable_list * vp, |
691 | | const oid * objid, size_t name_length) |
692 | 0 | { |
693 | 0 | size_t len = sizeof(oid) * name_length; |
694 | |
|
695 | 0 | if (vp->name != vp->name_loc && vp->name != NULL) { |
696 | | /* |
697 | | * Probably previously-allocated "big storage". Better free it |
698 | | * else memory leaks possible. |
699 | | */ |
700 | 0 | free(vp->name); |
701 | 0 | } |
702 | | |
703 | | /* |
704 | | * use built-in storage for smaller values |
705 | | */ |
706 | 0 | if (len <= sizeof(vp->name_loc)) { |
707 | 0 | vp->name = vp->name_loc; |
708 | 0 | } else { |
709 | 0 | vp->name = (oid *) malloc(len); |
710 | 0 | if (!vp->name) |
711 | 0 | return 1; |
712 | 0 | } |
713 | 0 | if (objid) |
714 | 0 | memmove(vp->name, objid, len); |
715 | 0 | vp->name_length = name_length; |
716 | 0 | return 0; |
717 | 0 | } |
718 | | |
719 | | /** |
720 | | * snmp_set_var_typed_value is used to set data into the netsnmp_variable_list |
721 | | * structure. Used to return data to the snmp request via the |
722 | | * netsnmp_request_info structure's requestvb pointer. |
723 | | * |
724 | | * @param newvar the structure gets populated with the given data, type, |
725 | | * val_str, and val_len. |
726 | | * @param type is the asn data type to be copied |
727 | | * @param val_str is a buffer containing the value to be copied into the |
728 | | * newvar structure. |
729 | | * @param val_len the length of val_str |
730 | | * |
731 | | * @return returns 0 on success and 1 on a malloc error |
732 | | */ |
733 | | |
734 | | int |
735 | | snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type, |
736 | | const void * val_str, size_t val_len) |
737 | 0 | { |
738 | 0 | newvar->type = type; |
739 | 0 | return snmp_set_var_value(newvar, val_str, val_len); |
740 | 0 | } |
741 | | |
742 | | int |
743 | | snmp_set_var_typed_integer(netsnmp_variable_list * newvar, |
744 | | u_char type, long val) |
745 | 0 | { |
746 | 0 | newvar->type = type; |
747 | 0 | return snmp_set_var_value(newvar, &val, sizeof(long)); |
748 | 0 | } |
749 | | |
750 | | int |
751 | | count_varbinds(netsnmp_variable_list * var_ptr) |
752 | 0 | { |
753 | 0 | int count = 0; |
754 | |
|
755 | 0 | for (; var_ptr != NULL; var_ptr = var_ptr->next_variable) |
756 | 0 | count++; |
757 | |
|
758 | 0 | return count; |
759 | 0 | } |
760 | | |
761 | | netsnmp_feature_child_of(count_varbinds_of_type, netsnmp_unused); |
762 | | #ifndef NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE |
763 | | int |
764 | | count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type) |
765 | 0 | { |
766 | 0 | int count = 0; |
767 | |
|
768 | 0 | for (; var_ptr != NULL; var_ptr = var_ptr->next_variable) |
769 | 0 | if (var_ptr->type == type) |
770 | 0 | count++; |
771 | |
|
772 | 0 | return count; |
773 | 0 | } |
774 | | #endif /* NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE */ |
775 | | |
776 | | netsnmp_feature_child_of(find_varind_of_type, netsnmp_unused); |
777 | | #ifndef NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE |
778 | | netsnmp_variable_list * |
779 | | find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type) |
780 | 0 | { |
781 | 0 | for (; var_ptr != NULL && var_ptr->type != type; |
782 | 0 | var_ptr = var_ptr->next_variable); |
783 | |
|
784 | 0 | return var_ptr; |
785 | 0 | } |
786 | | #endif /* NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE */ |
787 | | |
788 | | netsnmp_variable_list* |
789 | | find_varbind_in_list( netsnmp_variable_list *vblist, |
790 | | const oid *name, size_t len) |
791 | 0 | { |
792 | 0 | for (; vblist != NULL; vblist = vblist->next_variable) |
793 | 0 | if (!snmp_oid_compare(vblist->name, vblist->name_length, name, len)) |
794 | 0 | return vblist; |
795 | | |
796 | 0 | return NULL; |
797 | 0 | } |
798 | | |
799 | | /* |
800 | | * Add some value to SNMP variable. |
801 | | * If the value is large, additional memory is allocated. |
802 | | * Returns 0 if successful. |
803 | | */ |
804 | | |
805 | | int |
806 | | snmp_set_var_value(netsnmp_variable_list * vars, |
807 | | const void * value, size_t len) |
808 | 0 | { |
809 | 0 | int largeval = 1; |
810 | | |
811 | | /* |
812 | | * xxx-rks: why the unconditional free? why not use existing |
813 | | * memory, if len < vars->val_len ? |
814 | | */ |
815 | 0 | if (vars->val.string && vars->val.string != vars->buf) { |
816 | 0 | free(vars->val.string); |
817 | 0 | } |
818 | 0 | vars->val.string = NULL; |
819 | 0 | vars->val_len = 0; |
820 | |
|
821 | 0 | if (value == NULL && len > 0) { |
822 | 0 | snmp_log(LOG_ERR, "bad size for NULL value\n"); |
823 | 0 | return 1; |
824 | 0 | } |
825 | | |
826 | | /* |
827 | | * use built-in storage for smaller values |
828 | | */ |
829 | 0 | if (len <= sizeof(vars->buf)) { |
830 | 0 | vars->val.string = (u_char *) vars->buf; |
831 | 0 | largeval = 0; |
832 | 0 | } |
833 | |
|
834 | 0 | if ((0 == len) || (NULL == value)) { |
835 | 0 | vars->val.string[0] = 0; |
836 | 0 | return 0; |
837 | 0 | } |
838 | | |
839 | 0 | vars->val_len = len; |
840 | 0 | switch (vars->type) { |
841 | 0 | case ASN_INTEGER: |
842 | 0 | case ASN_UNSIGNED: |
843 | 0 | case ASN_TIMETICKS: |
844 | 0 | case ASN_COUNTER: |
845 | 0 | case ASN_UINTEGER: |
846 | 0 | if (vars->val_len == sizeof(int)) { |
847 | 0 | if (ASN_INTEGER == vars->type) { |
848 | 0 | const int *val_int |
849 | 0 | = (const int *) value; |
850 | 0 | *(vars->val.integer) = (long) *val_int; |
851 | 0 | } else { |
852 | 0 | const u_int *val_uint |
853 | 0 | = (const u_int *) value; |
854 | 0 | *(vars->val.integer) = (unsigned long) *val_uint; |
855 | 0 | } |
856 | 0 | } |
857 | 0 | else if (vars->val_len == sizeof(long)){ |
858 | 0 | const u_long *val_ulong |
859 | 0 | = (const u_long *) value; |
860 | 0 | *(vars->val.integer) = *val_ulong; |
861 | 0 | if (*(vars->val.integer) > 0xffffffff) { |
862 | 0 | NETSNMP_LOGONCE((LOG_INFO, |
863 | 0 | "truncating integer value > 32 bits\n")); |
864 | 0 | *(vars->val.integer) &= 0xffffffff; |
865 | 0 | } |
866 | 0 | } |
867 | 0 | else if (vars->val_len == sizeof(long long)){ |
868 | 0 | const unsigned long long *val_ullong |
869 | 0 | = (const unsigned long long *) value; |
870 | 0 | *(vars->val.integer) = (long) *val_ullong; |
871 | 0 | if (*(vars->val.integer) > 0xffffffff) { |
872 | 0 | NETSNMP_LOGONCE((LOG_INFO, |
873 | 0 | "truncating integer value > 32 bits\n")); |
874 | 0 | *(vars->val.integer) &= 0xffffffff; |
875 | 0 | } |
876 | 0 | } |
877 | 0 | else if (vars->val_len == sizeof(intmax_t)){ |
878 | 0 | const uintmax_t *val_uintmax_t |
879 | 0 | = (const uintmax_t *) value; |
880 | 0 | *(vars->val.integer) = (long) *val_uintmax_t; |
881 | 0 | if (*(vars->val.integer) > 0xffffffff) { |
882 | 0 | NETSNMP_LOGONCE((LOG_INFO, |
883 | 0 | "truncating integer value > 32 bits\n")); |
884 | 0 | *(vars->val.integer) &= 0xffffffff; |
885 | 0 | } |
886 | 0 | } |
887 | 0 | else if (vars->val_len == sizeof(short)) { |
888 | 0 | if (ASN_INTEGER == vars->type) { |
889 | 0 | const short *val_short |
890 | 0 | = (const short *) value; |
891 | 0 | *(vars->val.integer) = (long) *val_short; |
892 | 0 | } else { |
893 | 0 | const u_short *val_ushort |
894 | 0 | = (const u_short *) value; |
895 | 0 | *(vars->val.integer) = (unsigned long) *val_ushort; |
896 | 0 | } |
897 | 0 | } |
898 | 0 | else if (vars->val_len == sizeof(char)) { |
899 | 0 | if (ASN_INTEGER == vars->type) { |
900 | 0 | const signed char *val_char |
901 | 0 | = (const signed char *) value; |
902 | 0 | *(vars->val.integer) = (long) *val_char; |
903 | 0 | } else { |
904 | 0 | const u_char *val_uchar |
905 | 0 | = (const u_char *) value; |
906 | 0 | *(vars->val.integer) = (unsigned long) *val_uchar; |
907 | 0 | } |
908 | 0 | } |
909 | 0 | else { |
910 | 0 | snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n", |
911 | 0 | (int)vars->val_len); |
912 | 0 | return (1); |
913 | 0 | } |
914 | 0 | vars->val_len = sizeof(long); |
915 | 0 | break; |
916 | | |
917 | 0 | case ASN_OBJECT_ID: |
918 | 0 | case ASN_PRIV_IMPLIED_OBJECT_ID: |
919 | 0 | case ASN_PRIV_INCL_RANGE: |
920 | 0 | case ASN_PRIV_EXCL_RANGE: |
921 | 0 | if (largeval) { |
922 | 0 | vars->val.objid = (oid *) malloc(vars->val_len); |
923 | 0 | } |
924 | 0 | if (vars->val.objid == NULL) { |
925 | 0 | snmp_log(LOG_ERR,"no storage for OID\n"); |
926 | 0 | return 1; |
927 | 0 | } |
928 | 0 | memmove(vars->val.objid, value, vars->val_len); |
929 | 0 | break; |
930 | | |
931 | 0 | case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */ |
932 | 0 | if (4 != vars->val_len) { |
933 | 0 | netsnmp_assert("ipaddress length == 4"); |
934 | 0 | } |
935 | 0 | NETSNMP_FALLTHROUGH; |
936 | 0 | case ASN_PRIV_IMPLIED_OCTET_STR: |
937 | 0 | case ASN_OCTET_STR: |
938 | 0 | case ASN_BIT_STR: |
939 | 0 | case ASN_OPAQUE: |
940 | 0 | case ASN_NSAP: |
941 | 0 | if (vars->val_len >= sizeof(vars->buf)) { |
942 | 0 | vars->val.string = (u_char *) malloc(vars->val_len + 1); |
943 | 0 | } |
944 | 0 | if (vars->val.string == NULL) { |
945 | 0 | snmp_log(LOG_ERR,"no storage for string\n"); |
946 | 0 | return 1; |
947 | 0 | } |
948 | 0 | memmove(vars->val.string, value, vars->val_len); |
949 | | /* |
950 | | * Make sure the string is zero-terminated; some bits of code make |
951 | | * this assumption. Easier to do this here than fix all these wrong |
952 | | * assumptions. |
953 | | */ |
954 | 0 | vars->val.string[vars->val_len] = '\0'; |
955 | 0 | break; |
956 | | |
957 | 0 | case SNMP_NOSUCHOBJECT: |
958 | 0 | case SNMP_NOSUCHINSTANCE: |
959 | 0 | case SNMP_ENDOFMIBVIEW: |
960 | 0 | case ASN_NULL: |
961 | 0 | vars->val_len = 0; |
962 | 0 | vars->val.string = NULL; |
963 | 0 | break; |
964 | | |
965 | 0 | #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES |
966 | 0 | case ASN_OPAQUE_U64: |
967 | 0 | case ASN_OPAQUE_I64: |
968 | 0 | #endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */ |
969 | 0 | case ASN_COUNTER64: |
970 | 0 | if (largeval || vars->val_len != sizeof(struct counter64)) { |
971 | 0 | snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n", |
972 | 0 | (int)vars->val_len); |
973 | 0 | return (1); |
974 | 0 | } |
975 | 0 | vars->val_len = sizeof(struct counter64); |
976 | 0 | memmove(vars->val.counter64, value, vars->val_len); |
977 | 0 | break; |
978 | | |
979 | 0 | #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES |
980 | 0 | case ASN_OPAQUE_FLOAT: |
981 | 0 | if (largeval) { |
982 | 0 | snmp_log(LOG_ERR,"bad size for opaque float (%d)\n", |
983 | 0 | (int)vars->val_len); |
984 | 0 | return (1); |
985 | 0 | } |
986 | 0 | vars->val_len = sizeof(float); |
987 | 0 | memmove(vars->val.floatVal, value, vars->val_len); |
988 | 0 | break; |
989 | | |
990 | 0 | case ASN_OPAQUE_DOUBLE: |
991 | 0 | if (largeval) { |
992 | 0 | snmp_log(LOG_ERR,"bad size for opaque double (%d)\n", |
993 | 0 | (int)vars->val_len); |
994 | 0 | return (1); |
995 | 0 | } |
996 | 0 | vars->val_len = sizeof(double); |
997 | 0 | memmove(vars->val.doubleVal, value, vars->val_len); |
998 | 0 | break; |
999 | | |
1000 | 0 | #endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */ |
1001 | | |
1002 | 0 | default: |
1003 | 0 | snmp_log(LOG_ERR,"Internal error in type switching\n"); |
1004 | 0 | snmp_set_detail("Internal error in type switching\n"); |
1005 | 0 | return (1); |
1006 | 0 | } |
1007 | | |
1008 | 0 | return 0; |
1009 | 0 | } |
1010 | | |
1011 | | void |
1012 | | snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type, |
1013 | | u_char new_type) |
1014 | 0 | { |
1015 | 0 | while (vbl) { |
1016 | 0 | if (vbl->type == old_type) { |
1017 | 0 | snmp_set_var_typed_value(vbl, new_type, NULL, 0); |
1018 | 0 | } |
1019 | 0 | vbl = vbl->next_variable; |
1020 | 0 | } |
1021 | 0 | } |
1022 | | |
1023 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES |
1024 | | void |
1025 | | snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type) |
1026 | 0 | { |
1027 | 0 | while (vbl) { |
1028 | 0 | snmp_set_var_typed_value(vbl, new_type, NULL, 0); |
1029 | 0 | vbl = vbl->next_variable; |
1030 | 0 | } |
1031 | 0 | } |
1032 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES */ |
1033 | | |
1034 | | int |
1035 | | snmp_synch_response_cb(netsnmp_session * ss, |
1036 | | netsnmp_pdu *pdu, |
1037 | | netsnmp_pdu **response, snmp_callback pcb) |
1038 | 0 | { |
1039 | 0 | struct synch_state lstate, *state; |
1040 | 0 | snmp_callback cbsav; |
1041 | 0 | void *cbmagsav; |
1042 | 0 | int numfds, count; |
1043 | 0 | netsnmp_large_fd_set fdset; |
1044 | 0 | struct timeval timeout, *tvp; |
1045 | 0 | int block; |
1046 | |
|
1047 | 0 | memset((void *) &lstate, 0, sizeof(lstate)); |
1048 | 0 | state = &lstate; |
1049 | 0 | cbsav = ss->callback; |
1050 | 0 | cbmagsav = ss->callback_magic; |
1051 | 0 | ss->callback = pcb; |
1052 | 0 | ss->callback_magic = (void *) state; |
1053 | 0 | netsnmp_large_fd_set_init(&fdset, FD_SETSIZE); |
1054 | |
|
1055 | 0 | if (snmp_send(ss, pdu) == 0) { |
1056 | 0 | snmp_free_pdu(pdu); |
1057 | 0 | state->status = STAT_ERROR; |
1058 | 0 | } else { |
1059 | 0 | state->reqid = pdu->reqid; |
1060 | 0 | state->waiting = 1; |
1061 | 0 | } |
1062 | |
|
1063 | 0 | while (state->waiting) { |
1064 | 0 | snmp_poll(); |
1065 | |
|
1066 | 0 | numfds = 0; |
1067 | 0 | NETSNMP_LARGE_FD_ZERO(&fdset); |
1068 | 0 | block = NETSNMP_SNMPBLOCK; |
1069 | 0 | tvp = &timeout; |
1070 | 0 | timerclear(tvp); |
1071 | 0 | snmp_sess_select_info2_flags(NULL, &numfds, &fdset, tvp, &block, |
1072 | 0 | NETSNMP_SELECT_NOALARMS); |
1073 | 0 | if (block == 1) |
1074 | 0 | tvp = NULL; /* block without timeout */ |
1075 | 0 | count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp); |
1076 | 0 | if (count > 0) { |
1077 | 0 | snmp_read2(&fdset); |
1078 | 0 | } else { |
1079 | 0 | switch (count) { |
1080 | 0 | case 0: |
1081 | 0 | snmp_timeout(); |
1082 | 0 | break; |
1083 | 0 | case -1: |
1084 | 0 | if (errno == EINTR) { |
1085 | 0 | continue; |
1086 | 0 | } else { |
1087 | 0 | snmp_errno = SNMPERR_GENERR; /*MTCRITICAL_RESOURCE */ |
1088 | | /* |
1089 | | * CAUTION! if another thread closed the socket(s) |
1090 | | * waited on here, the session structure was freed. |
1091 | | * It would be nice, but we can't rely on the pointer. |
1092 | | * ss->s_snmp_errno = SNMPERR_GENERR; |
1093 | | * ss->s_errno = errno; |
1094 | | */ |
1095 | 0 | snmp_set_detail(strerror(errno)); |
1096 | 0 | } |
1097 | 0 | NETSNMP_FALLTHROUGH; |
1098 | 0 | default: |
1099 | 0 | state->status = STAT_ERROR; |
1100 | 0 | state->waiting = 0; |
1101 | 0 | } |
1102 | 0 | } |
1103 | | |
1104 | 0 | if ( ss->flags & SNMP_FLAGS_RESP_CALLBACK ) { |
1105 | 0 | void (*cb)(void); |
1106 | 0 | cb = (void (*)(void))(ss->myvoid); |
1107 | 0 | cb(); /* Used to invoke 'netsnmp_check_outstanding_agent_requests();' |
1108 | | on internal AgentX queries. */ |
1109 | 0 | } |
1110 | 0 | } |
1111 | 0 | *response = state->pdu; |
1112 | 0 | ss->callback = cbsav; |
1113 | 0 | ss->callback_magic = cbmagsav; |
1114 | 0 | netsnmp_large_fd_set_cleanup(&fdset); |
1115 | 0 | return state->status; |
1116 | 0 | } |
1117 | | |
1118 | | int |
1119 | | snmp_synch_response(netsnmp_session * ss, |
1120 | | netsnmp_pdu *pdu, netsnmp_pdu **response) |
1121 | 0 | { |
1122 | 0 | return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input); |
1123 | 0 | } |
1124 | | |
1125 | | int |
1126 | | snmp_sess_synch_response(struct session_list *slp, |
1127 | | netsnmp_pdu *pdu, netsnmp_pdu **response) |
1128 | 0 | { |
1129 | 0 | netsnmp_session *ss; |
1130 | 0 | struct synch_state lstate, *state; |
1131 | 0 | snmp_callback cbsav; |
1132 | 0 | void *cbmagsav; |
1133 | 0 | int numfds, count; |
1134 | 0 | netsnmp_large_fd_set fdset; |
1135 | 0 | struct timeval timeout, *tvp; |
1136 | 0 | int block; |
1137 | |
|
1138 | 0 | ss = snmp_sess_session(slp); |
1139 | 0 | if (ss == NULL) { |
1140 | 0 | return STAT_ERROR; |
1141 | 0 | } |
1142 | | |
1143 | 0 | memset((void *) &lstate, 0, sizeof(lstate)); |
1144 | 0 | state = &lstate; |
1145 | 0 | cbsav = ss->callback; |
1146 | 0 | cbmagsav = ss->callback_magic; |
1147 | 0 | ss->callback = snmp_synch_input; |
1148 | 0 | ss->callback_magic = (void *) state; |
1149 | 0 | netsnmp_large_fd_set_init(&fdset, FD_SETSIZE); |
1150 | |
|
1151 | 0 | if (snmp_sess_send(slp, pdu) == 0) { |
1152 | 0 | snmp_free_pdu(pdu); |
1153 | 0 | state->status = STAT_ERROR; |
1154 | 0 | } else { |
1155 | 0 | state->waiting = 1; |
1156 | 0 | state->reqid = pdu->reqid; |
1157 | 0 | } |
1158 | |
|
1159 | 0 | while (state->waiting) { |
1160 | 0 | snmp_sess_poll(slp); |
1161 | |
|
1162 | 0 | numfds = 0; |
1163 | 0 | NETSNMP_LARGE_FD_ZERO(&fdset); |
1164 | 0 | block = NETSNMP_SNMPBLOCK; |
1165 | 0 | tvp = &timeout; |
1166 | 0 | timerclear(tvp); |
1167 | 0 | snmp_sess_select_info2_flags(slp, &numfds, &fdset, tvp, &block, |
1168 | 0 | NETSNMP_SELECT_NOALARMS); |
1169 | 0 | if (block == 1) |
1170 | 0 | tvp = NULL; /* block without timeout */ |
1171 | 0 | count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp); |
1172 | 0 | if (count > 0) { |
1173 | 0 | snmp_sess_read2(slp, &fdset); |
1174 | 0 | } else |
1175 | 0 | switch (count) { |
1176 | 0 | case 0: |
1177 | 0 | snmp_sess_timeout(slp); |
1178 | 0 | break; |
1179 | 0 | case -1: |
1180 | 0 | if (errno == EINTR) { |
1181 | 0 | continue; |
1182 | 0 | } else { |
1183 | 0 | snmp_errno = SNMPERR_GENERR; /*MTCRITICAL_RESOURCE */ |
1184 | | /* |
1185 | | * CAUTION! if another thread closed the socket(s) |
1186 | | * waited on here, the session structure was freed. |
1187 | | * It would be nice, but we can't rely on the pointer. |
1188 | | * ss->s_snmp_errno = SNMPERR_GENERR; |
1189 | | * ss->s_errno = errno; |
1190 | | */ |
1191 | 0 | snmp_set_detail(strerror(errno)); |
1192 | 0 | } |
1193 | 0 | NETSNMP_FALLTHROUGH; |
1194 | 0 | default: |
1195 | 0 | state->status = STAT_ERROR; |
1196 | 0 | state->waiting = 0; |
1197 | 0 | } |
1198 | 0 | } |
1199 | 0 | *response = state->pdu; |
1200 | 0 | ss->callback = cbsav; |
1201 | 0 | ss->callback_magic = cbmagsav; |
1202 | 0 | netsnmp_large_fd_set_cleanup(&fdset); |
1203 | 0 | return state->status; |
1204 | 0 | } |
1205 | | |
1206 | | |
1207 | | const char * |
1208 | | snmp_errstring(int errstat) |
1209 | 0 | { |
1210 | 0 | const char * const error_string[19] = { |
1211 | 0 | "(noError) No Error", |
1212 | 0 | "(tooBig) Response message would have been too large.", |
1213 | 0 | "(noSuchName) There is no such variable name in this MIB.", |
1214 | 0 | "(badValue) The value given has the wrong type or length.", |
1215 | 0 | "(readOnly) The two parties used do not have access to use the specified SNMP PDU.", |
1216 | 0 | "(genError) A general failure occurred", |
1217 | 0 | "noAccess", |
1218 | 0 | "wrongType (The set datatype does not match the data type the agent expects)", |
1219 | 0 | "wrongLength (The set value has an illegal length from what the agent expects)", |
1220 | 0 | "wrongEncoding", |
1221 | 0 | "wrongValue (The set value is illegal or unsupported in some way)", |
1222 | 0 | "noCreation (That table does not support row creation or that object can not ever be created)", |
1223 | 0 | "inconsistentValue (The set value is illegal or unsupported in some way)", |
1224 | 0 | "resourceUnavailable (This is likely a out-of-memory failure within the agent)", |
1225 | 0 | "commitFailed", |
1226 | 0 | "undoFailed", |
1227 | 0 | "authorizationError (access denied to that object)", |
1228 | 0 | "notWritable (That object does not support modification)", |
1229 | 0 | "inconsistentName (That object can not currently be created)" |
1230 | 0 | }; |
1231 | |
|
1232 | 0 | if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) { |
1233 | 0 | return error_string[errstat]; |
1234 | 0 | } else { |
1235 | 0 | return "Unknown Error"; |
1236 | 0 | } |
1237 | 0 | } |
1238 | | |
1239 | | |
1240 | | |
1241 | | /* |
1242 | | * |
1243 | | * Convenience routines to make various requests |
1244 | | * over the specified SNMP session. |
1245 | | * |
1246 | | */ |
1247 | | #include <net-snmp/library/snmp_debug.h> |
1248 | | |
1249 | | static netsnmp_session *_def_query_session = NULL; |
1250 | | |
1251 | | #ifndef NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION |
1252 | | void |
1253 | 0 | netsnmp_query_set_default_session( netsnmp_session *sess) { |
1254 | 0 | DEBUGMSGTL(("iquery", "set default session %p\n", sess)); |
1255 | 0 | _def_query_session = sess; |
1256 | 0 | } |
1257 | | #endif /* NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION */ |
1258 | | |
1259 | | void |
1260 | 0 | netsnmp_query_shutdown(void) { |
1261 | 0 | if (_def_query_session) { |
1262 | 0 | snmp_close(_def_query_session); |
1263 | 0 | _def_query_session = NULL; |
1264 | 0 | } |
1265 | 0 | } |
1266 | | |
1267 | | /** |
1268 | | * Return a pointer to the default internal query session. |
1269 | | */ |
1270 | | netsnmp_session * |
1271 | 0 | netsnmp_query_get_default_session_unchecked( void ) { |
1272 | 0 | DEBUGMSGTL(("iquery", "get default session %p\n", _def_query_session)); |
1273 | 0 | return _def_query_session; |
1274 | 0 | } |
1275 | | |
1276 | | /** |
1277 | | * Return a pointer to the default internal query session and log a |
1278 | | * warning message once if this session does not exist. |
1279 | | */ |
1280 | | netsnmp_session * |
1281 | 0 | netsnmp_query_get_default_session( void ) { |
1282 | 0 | static int warning_logged = 0; |
1283 | |
|
1284 | 0 | if (! _def_query_session && ! warning_logged) { |
1285 | 0 | if (! netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, |
1286 | 0 | NETSNMP_DS_AGENT_INTERNAL_SECNAME)) { |
1287 | 0 | snmp_log(LOG_WARNING, |
1288 | 0 | "iquerySecName has not been configured - internal queries will fail\n"); |
1289 | 0 | } else { |
1290 | 0 | snmp_log(LOG_WARNING, |
1291 | 0 | "default session is not available - internal queries will fail\n"); |
1292 | 0 | } |
1293 | 0 | warning_logged = 1; |
1294 | 0 | } |
1295 | |
|
1296 | 0 | return netsnmp_query_get_default_session_unchecked(); |
1297 | 0 | } |
1298 | | |
1299 | | |
1300 | | /* |
1301 | | * Internal utility routine to actually send the query |
1302 | | */ |
1303 | | static int _query(netsnmp_variable_list *list, |
1304 | | int request, |
1305 | 0 | netsnmp_session *session) { |
1306 | |
|
1307 | 0 | netsnmp_pdu *pdu; |
1308 | 0 | netsnmp_pdu *response = NULL; |
1309 | 0 | netsnmp_variable_list *vb1, *vb2, *vtmp; |
1310 | 0 | int ret, count; |
1311 | |
|
1312 | 0 | DEBUGMSGTL(("iquery", "query on session %p\n", session)); |
1313 | |
|
1314 | 0 | if (NULL == list) { |
1315 | 0 | snmp_log(LOG_ERR, "empty variable list in _query\n"); |
1316 | 0 | return SNMP_ERR_GENERR; |
1317 | 0 | } |
1318 | | |
1319 | 0 | pdu = snmp_pdu_create( request ); |
1320 | 0 | if (NULL == pdu) { |
1321 | 0 | snmp_log(LOG_ERR, "could not allocate pdu\n"); |
1322 | 0 | return SNMP_ERR_GENERR; |
1323 | 0 | } |
1324 | | |
1325 | | /* |
1326 | | * Clone the varbind list into the request PDU... |
1327 | | */ |
1328 | 0 | pdu->variables = snmp_clone_varbind( list ); |
1329 | 0 | if (NULL == pdu->variables) { |
1330 | 0 | snmp_log(LOG_ERR, "could not clone variable list\n"); |
1331 | 0 | snmp_free_pdu(pdu); |
1332 | 0 | return SNMP_ERR_GENERR; |
1333 | 0 | } |
1334 | | |
1335 | 0 | #ifndef NETSNMP_NO_WRITE_SUPPORT |
1336 | 0 | retry: |
1337 | 0 | #endif |
1338 | 0 | if ( session ) |
1339 | 0 | ret = snmp_synch_response( session, pdu, &response ); |
1340 | 0 | else if (_def_query_session) |
1341 | 0 | ret = snmp_synch_response( _def_query_session, pdu, &response ); |
1342 | 0 | else { |
1343 | | /* No session specified */ |
1344 | 0 | snmp_free_pdu(pdu); |
1345 | 0 | return SNMP_ERR_GENERR; |
1346 | 0 | } |
1347 | 0 | DEBUGMSGTL(("iquery", "query returned %d\n", ret)); |
1348 | | |
1349 | | /* |
1350 | | * ....then copy the results back into the |
1351 | | * list (assuming the request succeeded!). |
1352 | | * This avoids having to worry about how this |
1353 | | * list was originally allocated. |
1354 | | */ |
1355 | 0 | if ( ret == SNMP_ERR_NOERROR ) { |
1356 | 0 | if ( response->errstat != SNMP_ERR_NOERROR ) { |
1357 | 0 | DEBUGMSGT(("iquery", "Error in packet: %s\n", |
1358 | 0 | snmp_errstring(response->errstat))); |
1359 | | /* |
1360 | | * If the request failed, then remove the |
1361 | | * offending varbind and try again. |
1362 | | * (all except SET requests) |
1363 | | * |
1364 | | * XXX - implement a library version of |
1365 | | * NETSNMP_DS_APP_DONT_FIX_PDUS ?? |
1366 | | */ |
1367 | 0 | ret = response->errstat; |
1368 | 0 | if (response->errindex != 0) { |
1369 | 0 | DEBUGMSGT(("iquery:result", "Failed object:\n")); |
1370 | 0 | for (count = 1, vtmp = response->variables; |
1371 | 0 | vtmp && count != response->errindex; |
1372 | 0 | vtmp = vtmp->next_variable, count++) |
1373 | 0 | /*EMPTY*/; |
1374 | 0 | if (vtmp) |
1375 | 0 | DEBUGMSGVAR(("iquery:result", vtmp)); |
1376 | 0 | DEBUGMSG(("iquery:result", "\n")); |
1377 | 0 | } |
1378 | 0 | #ifndef NETSNMP_NO_WRITE_SUPPORT |
1379 | 0 | if (request != SNMP_MSG_SET && |
1380 | 0 | response->errindex != 0) { |
1381 | 0 | DEBUGMSGTL(("iquery", "retrying query (%d, %ld)\n", ret, response->errindex)); |
1382 | 0 | pdu = snmp_fix_pdu( response, request ); |
1383 | 0 | snmp_free_pdu( response ); |
1384 | 0 | response = NULL; |
1385 | 0 | if ( pdu != NULL ) |
1386 | 0 | goto retry; |
1387 | 0 | } |
1388 | 0 | #endif /* !NETSNMP_NO_WRITE_SUPPORT */ |
1389 | 0 | } else { |
1390 | 0 | for (vb1 = response->variables, vb2 = list; |
1391 | 0 | vb1; |
1392 | 0 | vb1 = vb1->next_variable, vb2 = vb2->next_variable) { |
1393 | 0 | DEBUGMSGVAR(("iquery:result", vb1)); |
1394 | 0 | DEBUGMSG(("iquery:results", "\n")); |
1395 | 0 | if ( !vb2 ) { |
1396 | 0 | ret = SNMP_ERR_GENERR; |
1397 | 0 | break; |
1398 | 0 | } |
1399 | 0 | vtmp = vb2->next_variable; |
1400 | 0 | snmp_free_var_internals( vb2 ); |
1401 | 0 | snmp_clone_var( vb1, vb2 ); /* xxx: check return? */ |
1402 | 0 | vb2->next_variable = vtmp; |
1403 | 0 | } |
1404 | 0 | } |
1405 | 0 | } else { |
1406 | | /* Distinguish snmp_send errors from SNMP errStat errors */ |
1407 | 0 | ret = -ret; |
1408 | 0 | } |
1409 | 0 | snmp_free_pdu( response ); |
1410 | 0 | return ret; |
1411 | 0 | } |
1412 | | |
1413 | | /* |
1414 | | * These are simple wrappers round the internal utility routine |
1415 | | */ |
1416 | | int netsnmp_query_get(netsnmp_variable_list *list, |
1417 | 0 | netsnmp_session *session){ |
1418 | 0 | return _query( list, SNMP_MSG_GET, session ); |
1419 | 0 | } |
1420 | | |
1421 | | |
1422 | | int netsnmp_query_getnext(netsnmp_variable_list *list, |
1423 | 0 | netsnmp_session *session){ |
1424 | 0 | return _query( list, SNMP_MSG_GETNEXT, session ); |
1425 | 0 | } |
1426 | | |
1427 | | |
1428 | | #ifndef NETSNMP_NO_WRITE_SUPPORT |
1429 | | int netsnmp_query_set(netsnmp_variable_list *list, |
1430 | 0 | netsnmp_session *session){ |
1431 | 0 | return _query( list, SNMP_MSG_SET, session ); |
1432 | 0 | } |
1433 | | #endif /* !NETSNMP_NO_WRITE_SUPPORT */ |
1434 | | |
1435 | | /* |
1436 | | * A walk needs a bit more work. |
1437 | | */ |
1438 | | int netsnmp_query_walk(netsnmp_variable_list *list, |
1439 | 0 | netsnmp_session *session) { |
1440 | | /* |
1441 | | * Create a working copy of the original (single) |
1442 | | * varbind, so we can use this varbind parameter |
1443 | | * to check when we've finished walking this subtree. |
1444 | | */ |
1445 | 0 | netsnmp_variable_list *vb = snmp_clone_varbind( list ); |
1446 | 0 | netsnmp_variable_list *res_list = NULL; |
1447 | 0 | netsnmp_variable_list *res_last = NULL; |
1448 | 0 | int ret; |
1449 | | |
1450 | | /* |
1451 | | * Now walk the tree as usual |
1452 | | */ |
1453 | 0 | ret = _query( vb, SNMP_MSG_GETNEXT, session ); |
1454 | 0 | while ( ret == SNMP_ERR_NOERROR && |
1455 | 0 | snmp_oidtree_compare( list->name, list->name_length, |
1456 | 0 | vb->name, vb->name_length ) == 0) { |
1457 | |
|
1458 | 0 | if (vb->type == SNMP_ENDOFMIBVIEW || |
1459 | 0 | vb->type == SNMP_NOSUCHOBJECT || |
1460 | 0 | vb->type == SNMP_NOSUCHINSTANCE) |
1461 | 0 | break; |
1462 | | |
1463 | | /* |
1464 | | * Copy each response varbind to the end of the result list |
1465 | | * and then re-use this to ask for the next entry. |
1466 | | */ |
1467 | 0 | if ( res_last ) { |
1468 | 0 | res_last->next_variable = snmp_clone_varbind( vb ); |
1469 | 0 | res_last = res_last->next_variable; |
1470 | 0 | } else { |
1471 | 0 | res_list = snmp_clone_varbind( vb ); |
1472 | 0 | res_last = res_list; |
1473 | 0 | } |
1474 | 0 | ret = _query( vb, SNMP_MSG_GETNEXT, session ); |
1475 | 0 | } |
1476 | | /* |
1477 | | * Copy the first result back into the original varbind parameter, |
1478 | | * add the rest of the results (if any), and clean up. |
1479 | | */ |
1480 | 0 | if ( res_list ) { |
1481 | 0 | snmp_clone_var( res_list, list ); |
1482 | 0 | list->next_variable = res_list->next_variable; |
1483 | 0 | res_list->next_variable = NULL; |
1484 | 0 | snmp_free_varbind( res_list ); |
1485 | 0 | } |
1486 | 0 | snmp_free_varbind( vb ); |
1487 | 0 | return ret; |
1488 | 0 | } |
1489 | | |
1490 | | /** ************************************************************************** |
1491 | | * |
1492 | | * state machine |
1493 | | * |
1494 | | */ |
1495 | | int |
1496 | | netsnmp_state_machine_run( netsnmp_state_machine_input *input) |
1497 | 0 | { |
1498 | 0 | netsnmp_state_machine_step *current, *last; |
1499 | |
|
1500 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1501 | 0 | netsnmp_require_ptr_LRV( input->steps, SNMPERR_GENERR ); |
1502 | 0 | last = current = input->steps; |
1503 | |
|
1504 | 0 | DEBUGMSGT(("state_machine:run", "starting step: %s\n", current->name)); |
1505 | |
|
1506 | 0 | while (current) { |
1507 | | |
1508 | | /* |
1509 | | * log step and check for required data |
1510 | | */ |
1511 | 0 | DEBUGMSGT(("state_machine:run", "at step: %s\n", current->name)); |
1512 | 0 | if (NULL == current->run) { |
1513 | 0 | DEBUGMSGT(("state_machine:run", "no run step\n")); |
1514 | 0 | current->result = last->result; |
1515 | 0 | break; |
1516 | 0 | } |
1517 | | |
1518 | | /* |
1519 | | * run step |
1520 | | */ |
1521 | 0 | DEBUGMSGT(("state_machine:run", "running step: %s\n", current->name)); |
1522 | 0 | current->result = (*current->run)( input, current ); |
1523 | 0 | ++input->steps_so_far; |
1524 | | |
1525 | | /* |
1526 | | * log result and move to next step |
1527 | | */ |
1528 | 0 | input->last_run = current; |
1529 | 0 | DEBUGMSGT(("state_machine:run:result", "step %s returned %d\n", |
1530 | 0 | current->name, current->result)); |
1531 | 0 | if (SNMPERR_SUCCESS == current->result) |
1532 | 0 | current = current->on_success; |
1533 | 0 | else if (SNMPERR_ABORT == current->result) { |
1534 | 0 | DEBUGMSGT(("state_machine:run:result", "ABORT from %s\n", |
1535 | 0 | current->name)); |
1536 | 0 | break; |
1537 | 0 | } |
1538 | 0 | else |
1539 | 0 | current = current->on_error; |
1540 | 0 | } |
1541 | | |
1542 | | /* |
1543 | | * run cleanup |
1544 | | */ |
1545 | 0 | if ((input->cleanup) && (input->cleanup->run)) |
1546 | 0 | (*input->cleanup->run)( input, input->last_run ); |
1547 | |
|
1548 | 0 | return input->last_run->result; |
1549 | 0 | } |
1550 | | |
1551 | | #ifndef NETSNMP_NO_WRITE_SUPPORT |
1552 | | #ifndef NETSNMP_FEATURE_REMOVE_ROW_CREATE |
1553 | | /** ************************************************************************** |
1554 | | * |
1555 | | * row create state machine steps |
1556 | | * |
1557 | | */ |
1558 | | typedef struct rowcreate_state_s { |
1559 | | |
1560 | | netsnmp_session *session; |
1561 | | netsnmp_variable_list *vars; |
1562 | | int row_status_index; |
1563 | | } rowcreate_state; |
1564 | | |
1565 | | static netsnmp_variable_list * |
1566 | | _get_vb_num(netsnmp_variable_list *vars, int index) |
1567 | 0 | { |
1568 | 0 | for (; vars && index > 0; --index) |
1569 | 0 | vars = vars->next_variable; |
1570 | |
|
1571 | 0 | if (!vars || index > 0) |
1572 | 0 | return NULL; |
1573 | | |
1574 | 0 | return vars; |
1575 | 0 | } |
1576 | | |
1577 | | |
1578 | | /* |
1579 | | * cleanup |
1580 | | */ |
1581 | | static int |
1582 | | _row_status_state_cleanup(netsnmp_state_machine_input *input, |
1583 | | netsnmp_state_machine_step *step) |
1584 | 0 | { |
1585 | 0 | rowcreate_state *ctx; |
1586 | |
|
1587 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_ABORT ); |
1588 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_ABORT ); |
1589 | | |
1590 | 0 | DEBUGMSGT(("row_create:called", "_row_status_state_cleanup, last run step was %s rc %d\n", |
1591 | 0 | step->name, step->result)); |
1592 | |
|
1593 | 0 | ctx = (rowcreate_state *)input->input_context; |
1594 | 0 | if (ctx && ctx->vars) |
1595 | 0 | snmp_free_varbind( ctx->vars ); |
1596 | |
|
1597 | 0 | return SNMPERR_SUCCESS; |
1598 | 0 | } |
1599 | | |
1600 | | /* |
1601 | | * send a request to activate the row |
1602 | | */ |
1603 | | static int |
1604 | | _row_status_state_activate(netsnmp_state_machine_input *input, |
1605 | | netsnmp_state_machine_step *step) |
1606 | 0 | { |
1607 | 0 | rowcreate_state *ctx; |
1608 | 0 | netsnmp_variable_list *rs_var, *var = NULL; |
1609 | 0 | int32_t rc, val = RS_ACTIVE; |
1610 | |
|
1611 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1612 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); |
1613 | 0 | netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); |
1614 | | |
1615 | 0 | ctx = (rowcreate_state *)input->input_context; |
1616 | |
|
1617 | 0 | DEBUGMSGT(("row_create:called", "called %s\n", step->name)); |
1618 | | |
1619 | | /* |
1620 | | * just send the rowstatus varbind |
1621 | | */ |
1622 | 0 | rs_var = _get_vb_num(ctx->vars, ctx->row_status_index); |
1623 | 0 | netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR); |
1624 | | |
1625 | 0 | var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length, |
1626 | 0 | rs_var->type, &val, sizeof(val)); |
1627 | 0 | netsnmp_require_ptr_LRV( var, SNMPERR_GENERR ); |
1628 | | |
1629 | | /* |
1630 | | * send set |
1631 | | */ |
1632 | 0 | rc = netsnmp_query_set( var, ctx->session ); |
1633 | 0 | if (-2 == rc) |
1634 | 0 | rc = SNMPERR_ABORT; |
1635 | |
|
1636 | 0 | snmp_free_varbind(var); |
1637 | |
|
1638 | 0 | return rc; |
1639 | 0 | } |
1640 | | |
1641 | | /* |
1642 | | * send each non-row status column, one at a time |
1643 | | */ |
1644 | | static int |
1645 | | _row_status_state_single_value_cols(netsnmp_state_machine_input *input, |
1646 | | netsnmp_state_machine_step *step) |
1647 | 0 | { |
1648 | 0 | rowcreate_state *ctx; |
1649 | 0 | netsnmp_variable_list *var, *tmp_next, *row_status; |
1650 | 0 | int rc = SNMPERR_GENERR; |
1651 | |
|
1652 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1653 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); |
1654 | 0 | netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); |
1655 | | |
1656 | 0 | ctx = (rowcreate_state *)input->input_context; |
1657 | |
|
1658 | 0 | DEBUGMSGT(("row_create:called", "called %s\n", step->name)); |
1659 | |
|
1660 | 0 | row_status = _get_vb_num(ctx->vars, ctx->row_status_index); |
1661 | 0 | netsnmp_require_ptr_LRV(row_status, SNMPERR_GENERR); |
1662 | | |
1663 | | /* |
1664 | | * try one varbind at a time |
1665 | | */ |
1666 | 0 | for (var = ctx->vars; var; var = var->next_variable) { |
1667 | 0 | if (var == row_status) |
1668 | 0 | continue; |
1669 | | |
1670 | 0 | tmp_next = var->next_variable; |
1671 | 0 | var->next_variable = NULL; |
1672 | | |
1673 | | /* |
1674 | | * send set |
1675 | | */ |
1676 | 0 | rc = netsnmp_query_set( var, ctx->session ); |
1677 | 0 | var->next_variable = tmp_next; |
1678 | 0 | if (-2 == rc) |
1679 | 0 | rc = SNMPERR_ABORT; |
1680 | 0 | if (rc != SNMPERR_SUCCESS) |
1681 | 0 | break; |
1682 | 0 | } |
1683 | |
|
1684 | 0 | return rc; |
1685 | 0 | } |
1686 | | |
1687 | | /* |
1688 | | * send all values except row status |
1689 | | */ |
1690 | | static int |
1691 | | _row_status_state_multiple_values_cols(netsnmp_state_machine_input *input, |
1692 | | netsnmp_state_machine_step *step) |
1693 | 0 | { |
1694 | 0 | rowcreate_state *ctx; |
1695 | 0 | netsnmp_variable_list *vars, *var, *last, *row_status; |
1696 | 0 | int rc; |
1697 | |
|
1698 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1699 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); |
1700 | 0 | netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); |
1701 | | |
1702 | 0 | ctx = (rowcreate_state *)input->input_context; |
1703 | |
|
1704 | 0 | DEBUGMSGT(("row_create:called", "called %s\n", step->name)); |
1705 | |
|
1706 | 0 | vars = snmp_clone_varbind(ctx->vars); |
1707 | 0 | netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR); |
1708 | | |
1709 | 0 | row_status = _get_vb_num(vars, ctx->row_status_index); |
1710 | 0 | if (NULL == row_status) { |
1711 | 0 | snmp_free_varbind(vars); |
1712 | 0 | return SNMPERR_GENERR; |
1713 | 0 | } |
1714 | | |
1715 | | /* |
1716 | | * remove row status varbind |
1717 | | */ |
1718 | 0 | if (row_status == vars) { |
1719 | 0 | vars = row_status->next_variable; |
1720 | 0 | row_status->next_variable = NULL; |
1721 | 0 | } |
1722 | 0 | else { |
1723 | 0 | for (last=vars, var=last->next_variable; |
1724 | 0 | var; |
1725 | 0 | last=var, var = var->next_variable) { |
1726 | 0 | if (var == row_status) { |
1727 | 0 | last->next_variable = var->next_variable; |
1728 | 0 | break; |
1729 | 0 | } |
1730 | 0 | } |
1731 | 0 | } |
1732 | 0 | snmp_free_var(row_status); |
1733 | | |
1734 | | /* |
1735 | | * send set |
1736 | | */ |
1737 | 0 | rc = netsnmp_query_set( vars, ctx->session ); |
1738 | 0 | if (-2 == rc) |
1739 | 0 | rc = SNMPERR_ABORT; |
1740 | |
|
1741 | 0 | snmp_free_varbind(vars); |
1742 | |
|
1743 | 0 | return rc; |
1744 | 0 | } |
1745 | | |
1746 | | /* |
1747 | | * send a createAndWait request with no other values |
1748 | | */ |
1749 | | static int |
1750 | | _row_status_state_single_value_createAndWait(netsnmp_state_machine_input *input, |
1751 | | netsnmp_state_machine_step *step) |
1752 | 0 | { |
1753 | 0 | rowcreate_state *ctx; |
1754 | 0 | netsnmp_variable_list *var = NULL, *rs_var; |
1755 | 0 | int32_t rc, val = RS_CREATEANDWAIT; |
1756 | |
|
1757 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1758 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); |
1759 | 0 | netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); |
1760 | | |
1761 | 0 | ctx = (rowcreate_state *)input->input_context; |
1762 | |
|
1763 | 0 | DEBUGMSGT(("row_create:called", "called %s\n", step->name)); |
1764 | |
|
1765 | 0 | rs_var = _get_vb_num(ctx->vars, ctx->row_status_index); |
1766 | 0 | netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR); |
1767 | | |
1768 | 0 | var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length, |
1769 | 0 | rs_var->type, &val, sizeof(val)); |
1770 | 0 | netsnmp_require_ptr_LRV(var, SNMPERR_GENERR); |
1771 | | |
1772 | | /* |
1773 | | * send set |
1774 | | */ |
1775 | 0 | rc = netsnmp_query_set( var, ctx->session ); |
1776 | 0 | if (-2 == rc) |
1777 | 0 | rc = SNMPERR_ABORT; |
1778 | |
|
1779 | 0 | snmp_free_varbind(var); |
1780 | |
|
1781 | 0 | return rc; |
1782 | 0 | } |
1783 | | |
1784 | | /* |
1785 | | * send a creatAndWait request with all values |
1786 | | */ |
1787 | | static int |
1788 | | _row_status_state_all_values_createAndWait(netsnmp_state_machine_input *input, |
1789 | | netsnmp_state_machine_step *step) |
1790 | 0 | { |
1791 | 0 | rowcreate_state *ctx; |
1792 | 0 | netsnmp_variable_list *vars, *rs_var; |
1793 | 0 | int rc; |
1794 | |
|
1795 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1796 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); |
1797 | 0 | netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); |
1798 | | |
1799 | 0 | ctx = (rowcreate_state *)input->input_context; |
1800 | |
|
1801 | 0 | DEBUGMSGT(("row_create:called", "called %s\n", step->name)); |
1802 | |
|
1803 | 0 | vars = snmp_clone_varbind(ctx->vars); |
1804 | 0 | netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR); |
1805 | | |
1806 | | /* |
1807 | | * make sure row stats is createAndWait |
1808 | | */ |
1809 | 0 | rs_var = _get_vb_num(vars, ctx->row_status_index); |
1810 | 0 | if (NULL == rs_var) { |
1811 | 0 | snmp_free_varbind(vars); |
1812 | 0 | return SNMPERR_GENERR; |
1813 | 0 | } |
1814 | | |
1815 | 0 | if (*rs_var->val.integer != RS_CREATEANDWAIT) |
1816 | 0 | *rs_var->val.integer = RS_CREATEANDWAIT; |
1817 | | |
1818 | | /* |
1819 | | * send set |
1820 | | */ |
1821 | 0 | rc = netsnmp_query_set( vars, ctx->session ); |
1822 | 0 | if (-2 == rc) |
1823 | 0 | rc = SNMPERR_ABORT; |
1824 | |
|
1825 | 0 | snmp_free_varbind(vars); |
1826 | |
|
1827 | 0 | return rc; |
1828 | 0 | } |
1829 | | |
1830 | | |
1831 | | /** |
1832 | | * send createAndGo request with all values |
1833 | | */ |
1834 | | static int |
1835 | | _row_status_state_all_values_createAndGo(netsnmp_state_machine_input *input, |
1836 | | netsnmp_state_machine_step *step) |
1837 | 0 | { |
1838 | 0 | rowcreate_state *ctx; |
1839 | 0 | netsnmp_variable_list *vars, *rs_var; |
1840 | 0 | int rc; |
1841 | |
|
1842 | 0 | netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); |
1843 | 0 | netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); |
1844 | 0 | netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); |
1845 | | |
1846 | 0 | ctx = (rowcreate_state *)input->input_context; |
1847 | |
|
1848 | 0 | DEBUGMSGT(("row_create:called", "called %s\n", step->name)); |
1849 | |
|
1850 | 0 | vars = snmp_clone_varbind(ctx->vars); |
1851 | 0 | netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR); |
1852 | | |
1853 | | /* |
1854 | | * make sure row stats is createAndGo |
1855 | | */ |
1856 | 0 | rs_var = _get_vb_num(vars, ctx->row_status_index + 1); |
1857 | 0 | if (NULL == rs_var) { |
1858 | 0 | snmp_free_varbind(vars); |
1859 | 0 | return SNMPERR_GENERR; |
1860 | 0 | } |
1861 | | |
1862 | 0 | if (*rs_var->val.integer != RS_CREATEANDGO) |
1863 | 0 | *rs_var->val.integer = RS_CREATEANDGO; |
1864 | | |
1865 | | /* |
1866 | | * send set |
1867 | | */ |
1868 | 0 | rc = netsnmp_query_set( vars, ctx->session ); |
1869 | 0 | if (-2 == rc) |
1870 | 0 | rc = SNMPERR_ABORT; |
1871 | |
|
1872 | 0 | snmp_free_varbind(vars); |
1873 | |
|
1874 | 0 | return rc; |
1875 | 0 | } |
1876 | | |
1877 | | /** ************************************************************************** |
1878 | | * |
1879 | | * row api |
1880 | | * |
1881 | | */ |
1882 | | int |
1883 | | netsnmp_row_create(netsnmp_session *sess, netsnmp_variable_list *vars, |
1884 | | int row_status_index) |
1885 | 0 | { |
1886 | 0 | netsnmp_state_machine_step rc_cleanup = |
1887 | 0 | { "row_create_cleanup", 0, _row_status_state_cleanup, |
1888 | 0 | 0, NULL, NULL, 0, NULL }; |
1889 | 0 | netsnmp_state_machine_step rc_activate = |
1890 | 0 | { "row_create_activate", 0, _row_status_state_activate, |
1891 | 0 | 0, NULL, NULL, 0, NULL }; |
1892 | 0 | netsnmp_state_machine_step rc_sv_cols = |
1893 | 0 | { "row_create_single_value_cols", 0, |
1894 | 0 | _row_status_state_single_value_cols, 0, &rc_activate,NULL, 0, NULL }; |
1895 | 0 | netsnmp_state_machine_step rc_mv_cols = |
1896 | 0 | { "row_create_multiple_values_cols", 0, |
1897 | 0 | _row_status_state_multiple_values_cols, 0, &rc_activate, &rc_sv_cols, |
1898 | 0 | 0, NULL }; |
1899 | 0 | netsnmp_state_machine_step rc_sv_caw = |
1900 | 0 | { "row_create_single_value_createAndWait", 0, |
1901 | 0 | _row_status_state_single_value_createAndWait, 0, &rc_mv_cols, NULL, |
1902 | 0 | 0, NULL }; |
1903 | 0 | netsnmp_state_machine_step rc_av_caw = |
1904 | 0 | { "row_create_all_values_createAndWait", 0, |
1905 | 0 | _row_status_state_all_values_createAndWait, 0, &rc_activate, |
1906 | 0 | &rc_sv_caw, 0, NULL }; |
1907 | 0 | netsnmp_state_machine_step rc_av_cag = |
1908 | 0 | { "row_create_all_values_createAndGo", 0, |
1909 | 0 | _row_status_state_all_values_createAndGo, 0, NULL, &rc_av_caw, 0, |
1910 | 0 | NULL }; |
1911 | |
|
1912 | 0 | netsnmp_state_machine_input sm_input = { "row_create_machine", 0, |
1913 | 0 | &rc_av_cag, &rc_cleanup }; |
1914 | 0 | rowcreate_state state; |
1915 | |
|
1916 | 0 | netsnmp_require_ptr_LRV( sess, SNMPERR_GENERR); |
1917 | 0 | netsnmp_require_ptr_LRV( vars, SNMPERR_GENERR); |
1918 | | |
1919 | 0 | state.session = sess; |
1920 | 0 | state.vars = vars; |
1921 | |
|
1922 | 0 | state.row_status_index = row_status_index; |
1923 | 0 | sm_input.input_context = &state; |
1924 | |
|
1925 | 0 | netsnmp_state_machine_run( &sm_input); |
1926 | |
|
1927 | 0 | return SNMPERR_SUCCESS; |
1928 | 0 | } |
1929 | | #endif /* NETSNMP_FEATURE_REMOVE_ROW_CREATE */ |
1930 | | #endif /* NETSNMP_NO_WRITE_SUPPORT */ |
1931 | | |
1932 | | |
1933 | | /** @} */ |