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