//******************************************************************************** //******************************************************************************** static void delayed_response(unsigned int clientreg, void * clientarg) { syslog(LOG_DEBUG, "Enter: delayed_response, %s.", HANDLER_NAME); netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; if(!netsnmp_handler_check_cache(cache)) { syslog(LOG_ERR, "%s", "illegal call to return delayed response."); return; } syslog(LOG_DEBUG, "delayed_instance, continuing delayed request, mode = %d.", cache->reqinfo->mode); cache->requests->delegated = 0; switch(cache->reqinfo->mode) { case MODE_GET: case MODE_GETNEXT: int32_t redirect = redirect_count(); snmp_set_var_typed_integer(cache->requests->requestvb, ASN_COUNTER, redirect); } netsnmp_free_delegated_cache(cache); syslog(LOG_DEBUG, "Exit: delayed_response, %s.", HANDLER_NAME); syslog(LOG_DEBUG,"%s", "==============================================================="); return; }
void return_delayed_response(unsigned int clientreg, void *clientarg) { /* * extract the cache from the passed argument */ netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; netsnmp_request_info *requests; netsnmp_agent_request_info *reqinfo; u_long *delay_time_cache = NULL; /* * here we double check that the cache we created earlier is still * * valid. If not, the request timed out for some reason and we * * don't need to keep processing things. Should never happen, but * * this double checks. */ cache = netsnmp_handler_check_cache(cache); if (!cache) { snmp_log(LOG_ERR, "illegal call to return delayed response\n"); return; } /* * re-establish the previous pointers we are used to having */ reqinfo = cache->reqinfo; requests = cache->requests; DEBUGMSGTL(("delayed_instance", "continuing delayed request, mode = %d\n", cache->reqinfo->mode)); /* * mention that it's no longer delegated, and we've now answered * the query (which we'll do down below). */ requests->delegated = 0; switch (cache->reqinfo->mode) { /* * registering as an instance means we don't need to deal with * getnext processing, so we don't handle it here at all. * * However, since the instance handler already reset the mode * back to GETNEXT from the faked GET mode, we need to do the * same thing in both cases. This should be fixed in future * versions of net-snmp hopefully. */ case MODE_GET: case MODE_GETNEXT: /* * return the currend delay time */ snmp_set_var_typed_value(cache->requests->requestvb, ASN_INTEGER, (u_char *) & delay_time, sizeof(delay_time)); break; #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: /* * check type */ if (requests->requestvb->type != ASN_INTEGER) { /* * not an integer. Bad dog, no bone. */ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); /* * we don't need the cache any longer */ netsnmp_free_delegated_cache(cache); return; } break; case MODE_SET_RESERVE2: /* * store old value for UNDO support in the future. */ delay_time_cache = netsnmp_memdup(&delay_time, sizeof(delay_time)); /* * malloc failed */ if (delay_time_cache == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); netsnmp_free_delegated_cache(cache); return; } /* * Add our temporary information to the request itself. * This is then retrivable later. The free function * passed auto-frees it when the request is later * deleted. */ netsnmp_request_add_list_data(requests, netsnmp_create_data_list (DELAYED_INSTANCE_SET_NAME, delay_time_cache, free)); break; case MODE_SET_ACTION: /* * update current value */ delay_time = *(requests->requestvb->val.integer); DEBUGMSGTL(("testhandler", "updated delay_time -> %ld\n", delay_time)); break; case MODE_SET_UNDO: /* * ack, something somewhere failed. We reset back to the * previously old value by extracting the previosuly * stored information back out of the request */ delay_time = *((u_long *) netsnmp_request_get_list_data(requests, DELAYED_INSTANCE_SET_NAME)); break; case MODE_SET_COMMIT: case MODE_SET_FREE: /* * the only thing to do here is free the old memdup'ed * value, but it's auto-freed by the datalist recovery, so * we don't have anything to actually do here */ break; #endif /* NETSNMP_NO_WRITE_SUPPORT */ } /* * free the information cache */ netsnmp_free_delegated_cache(cache); }
/* * Handle the response from an AgentX subagent, * merging the answers back into the original query */ int agentx_got_response(int operation, netsnmp_session * session, int reqid, netsnmp_pdu *pdu, void *magic) { netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) magic; int i, ret; netsnmp_request_info *requests, *request; netsnmp_variable_list *var; netsnmp_session *ax_session; cache = netsnmp_handler_check_cache(cache); if (!cache) { DEBUGMSGTL(("agentx/master", "response too late on session %8p\n", session)); return 0; } requests = cache->requests; switch (operation) { case NETSNMP_CALLBACK_OP_TIMED_OUT:{ void *s = snmp_sess_pointer(session); DEBUGMSGTL(("agentx/master", "timeout on session %8p\n", session)); netsnmp_handler_mark_requests_as_delegated(requests, REQUEST_IS_NOT_DELEGATED); netsnmp_set_request_error(cache->reqinfo, requests, /* XXXWWW: should be index=0 */ SNMP_ERR_GENERR); /* * This is a bit sledgehammer because the other sessions on this * transport may be okay (e.g. some thread in the subagent has * wedged, but the others are alright). OTOH the overwhelming * probability is that the whole agent has died somehow. */ if (s != NULL) { netsnmp_transport *t = snmp_sess_transport(s); close_agentx_session(session, -1); if (t != NULL) { DEBUGMSGTL(("agentx/master", "close transport\n")); t->f_close(t); } else { DEBUGMSGTL(("agentx/master", "NULL transport??\n")); } } else { DEBUGMSGTL(("agentx/master", "NULL sess_pointer??\n")); } ax_session = (netsnmp_session *) cache->localinfo; netsnmp_free_agent_snmp_session_by_session(ax_session, NULL); netsnmp_free_delegated_cache(cache); return 0; } case NETSNMP_CALLBACK_OP_DISCONNECT: case NETSNMP_CALLBACK_OP_SEND_FAILED: if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) { DEBUGMSGTL(("agentx/master", "disconnect on session %8p\n", session)); } else { DEBUGMSGTL(("agentx/master", "send failed on session %8p\n", session)); } close_agentx_session(session, -1); netsnmp_handler_mark_requests_as_delegated(requests, REQUEST_IS_NOT_DELEGATED); netsnmp_set_request_error(cache->reqinfo, requests, /* XXXWWW: should be index=0 */ SNMP_ERR_GENERR); netsnmp_free_delegated_cache(cache); return 0; case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: /* * This session is alive */ CLEAR_SNMP_STRIKE_FLAGS(session->flags); break; default: snmp_log(LOG_ERR, "Unknown operation %d in agentx_got_response\n", operation); netsnmp_free_delegated_cache(cache); return 0; } DEBUGMSGTL(("agentx/master", "got response errstat=%ld, (req=0x%x,trans=" "0x%x,sess=0x%x)\n", pdu->errstat, (unsigned)pdu->reqid, (unsigned)pdu->transid, (unsigned)pdu->sessid)); if (pdu->errstat != AGENTX_ERR_NOERROR) { /* [RFC 2471 - 7.2.5.2.] * * 1) For any received AgentX response PDU, if res.error is * not `noError', the SNMP response PDU's error code is * set to this value. If res.error contains an AgentX * specific value (e.g. `parseError'), the SNMP response * PDU's error code is set to a value of genErr instead. * Also, the SNMP response PDU's error index is set to * the index of the variable binding corresponding to the * failed VarBind in the subagent's AgentX response PDU. * * All other AgentX response PDUs received due to * processing this SNMP request are ignored. Processing * is complete; the SNMP Response PDU is ready to be sent * (see section 7.2.6, "Sending the SNMP Response-PDU"). */ int err; DEBUGMSGTL(("agentx/master", "agentx_got_response() error branch\n")); switch (pdu->errstat) { case AGENTX_ERR_PARSE_FAILED: case AGENTX_ERR_REQUEST_DENIED: case AGENTX_ERR_PROCESSING_ERROR: err = SNMP_ERR_GENERR; break; default: err = pdu->errstat; } ret = 0; for (request = requests, i = 1; request; request = request->next, i++) { if (i == pdu->errindex) { /* * Mark this varbind as the one generating the error. * Note that the AgentX errindex may not match the * position in the original SNMP PDU (request->index) */ netsnmp_set_request_error(cache->reqinfo, request, err); ret = 1; } request->delegated = REQUEST_IS_NOT_DELEGATED; } if (!ret) { /* * ack, unknown, mark the first one */ netsnmp_set_request_error(cache->reqinfo, requests, SNMP_ERR_GENERR); } netsnmp_free_delegated_cache(cache); DEBUGMSGTL(("agentx/master", "end error branch\n")); return 1; } else if (cache->reqinfo->mode == MODE_GET || cache->reqinfo->mode == MODE_GETNEXT || cache->reqinfo->mode == MODE_GETBULK) { /* * Replace varbinds for data request types, but not SETs. */ DEBUGMSGTL(("agentx/master", "agentx_got_response() beginning...\n")); for (var = pdu->variables, request = requests; request && var; request = request->next, var = var->next_variable) { /* * Otherwise, process successful requests */ DEBUGMSGTL(("agentx/master", " handle_agentx_response: processing: ")); DEBUGMSGOID(("agentx/master", var->name, var->name_length)); DEBUGMSG(("agentx/master", "\n")); if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE)) { DEBUGMSGTL(("snmp_agent", " >> ")); DEBUGMSGVAR(("snmp_agent", var)); DEBUGMSG(("snmp_agent", "\n")); } /* * update the oid in the original request */ if (var->type != SNMP_ENDOFMIBVIEW) { snmp_set_var_typed_value(request->requestvb, var->type, var->val.string, var->val_len); snmp_set_var_objid(request->requestvb, var->name, var->name_length); } request->delegated = REQUEST_IS_NOT_DELEGATED; } if (request || var) { /* * ack, this is bad. The # of varbinds don't match and * there is no way to fix the problem */ snmp_log(LOG_ERR, "response to agentx request illegal. bailing out.\n"); netsnmp_set_request_error(cache->reqinfo, requests, SNMP_ERR_GENERR); } if (cache->reqinfo->mode == MODE_GETBULK) netsnmp_bulk_to_next_fix_requests(requests); } else { /* * mark set requests as handled */ for (request = requests; request; request = request->next) { request->delegated = REQUEST_IS_NOT_DELEGATED; } } DEBUGMSGTL(("agentx/master", "handle_agentx_response() finishing...\n")); netsnmp_free_delegated_cache(cache); return 1; }
int proxy_got_response(int operation, netsnmp_session * sess, int reqid, netsnmp_pdu *pdu, void *cb_data) { netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) cb_data; netsnmp_request_info *requests, *request = NULL; netsnmp_variable_list *vars, *var = NULL; struct simple_proxy *sp; oid myname[MAX_OID_LEN]; size_t myname_len = MAX_OID_LEN; cache = netsnmp_handler_check_cache(cache); if (!cache) { DEBUGMSGTL(("proxy", "a proxy request was no longer valid.\n")); return SNMP_ERR_NOERROR; } requests = cache->requests; sp = (struct simple_proxy *) cache->localinfo; if (!sp) { DEBUGMSGTL(("proxy", "a proxy request was no longer valid.\n")); return SNMP_ERR_NOERROR; } switch (operation) { case NETSNMP_CALLBACK_OP_TIMED_OUT: /* * WWWXXX: don't leave requests delayed if operation is * something like TIMEOUT */ DEBUGMSGTL(("proxy", "got timed out... requests = %08p\n", requests)); netsnmp_handler_mark_requests_as_delegated(requests, REQUEST_IS_NOT_DELEGATED); if(cache->reqinfo->mode != MODE_GETNEXT) { DEBUGMSGTL(("proxy", " ignoring timeout\n")); netsnmp_set_request_error(cache->reqinfo, requests, /* XXXWWW: should be index = 0 */ SNMP_ERR_GENERR); } netsnmp_free_delegated_cache(cache); return 0; case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: vars = pdu->variables; if (pdu->errstat != SNMP_ERR_NOERROR) { /* * If we receive an error from the proxy agent, pass it on up. * The higher-level processing seems to Do The Right Thing. * * 2005/06 rks: actually, it doesn't do the right thing for * a get-next request that returns NOSUCHNAME. If we do nothing, * it passes that error back to the comman initiator. What it should * do is ignore the error and move on to the next tree. To * accomplish that, all we need to do is clear the delegated flag. * Not sure if any other error codes need the same treatment. Left * as an exercise to the reader... */ DEBUGMSGTL(("proxy", "got error response (%d)\n", pdu->errstat)); if((cache->reqinfo->mode == MODE_GETNEXT) && (SNMP_ERR_NOSUCHNAME == pdu->errstat)) { DEBUGMSGTL(("proxy", " ignoring error response\n")); netsnmp_handler_mark_requests_as_delegated(requests, REQUEST_IS_NOT_DELEGATED); } else netsnmp_set_request_error(cache->reqinfo, requests, pdu->errstat); /* * update the original request varbinds with the results */ } else for (var = vars, request = requests; request && var; request = request->next, var = var->next_variable) { /* * XXX - should this be done here? * Or wait until we know it's OK? */ snmp_set_var_typed_value(request->requestvb, var->type, var->val.string, var->val_len); DEBUGMSGTL(("proxy", "got response... ")); DEBUGMSGOID(("proxy", var->name, var->name_length)); DEBUGMSG(("proxy", "\n")); request->delegated = 0; /* * Check the response oid is legitimate, * and discard the value if not. * * XXX - what's the difference between these cases? */ if (sp->base_len && (var->name_length < sp->base_len || snmp_oid_compare(var->name, sp->base_len, sp->base, sp->base_len) != 0)) { DEBUGMSGTL(( "proxy", "out of registered range... ")); DEBUGMSGOID(("proxy", var->name, sp->base_len)); DEBUGMSG(( "proxy", " (%d) != ", sp->base_len)); DEBUGMSGOID(("proxy", sp->base, sp->base_len)); DEBUGMSG(( "proxy", "\n")); snmp_set_var_typed_value(request->requestvb, ASN_NULL, NULL, 0); continue; } else if (!sp->base_len && (var->name_length < sp->name_len || snmp_oid_compare(var->name, sp->name_len, sp->name, sp->name_len) != 0)) { DEBUGMSGTL(( "proxy", "out of registered base range... ")); DEBUGMSGOID(("proxy", var->name, sp->name_len)); DEBUGMSG(( "proxy", " (%d) != ", sp->name_len)); DEBUGMSGOID(("proxy", sp->name, sp->name_len)); DEBUGMSG(( "proxy", "\n")); snmp_set_var_typed_value(request->requestvb, ASN_NULL, NULL, 0); continue; } else { /* * If the returned OID is legitimate, then update * the original request varbind accordingly. */ if (sp->base_len) { /* * XXX: oid size maxed? */ memcpy(myname, sp->name, sizeof(oid) * sp->name_len); myname_len = sp->name_len + var->name_length - sp->base_len; if (myname_len > MAX_OID_LEN) { snmp_log(LOG_WARNING, "proxy OID return length too long.\n"); netsnmp_set_request_error(cache->reqinfo, requests, SNMP_ERR_GENERR); if (pdu) snmp_free_pdu(pdu); netsnmp_free_delegated_cache(cache); return 1; } if (var->name_length > sp->base_len) memcpy(&myname[sp->name_len], &var->name[sp->base_len], sizeof(oid) * (var->name_length - sp->base_len)); snmp_set_var_objid(request->requestvb, myname, myname_len); } else { snmp_set_var_objid(request->requestvb, var->name, var->name_length); } } } if (request || var) { /* * ack, this is bad. The # of varbinds don't match and * there is no way to fix the problem */ if (pdu) snmp_free_pdu(pdu); snmp_log(LOG_ERR, "response to proxy request illegal. We're screwed.\n"); netsnmp_set_request_error(cache->reqinfo, requests, SNMP_ERR_GENERR); } /* fix bulk_to_next operations */ if (cache->reqinfo->mode == MODE_GETBULK) netsnmp_bulk_to_next_fix_requests(requests); /* * free the response */ if (pdu && 0) snmp_free_pdu(pdu); break; default: DEBUGMSGTL(("proxy", "no response received: op = %d\n", operation)); break; } netsnmp_free_delegated_cache(cache); return 1; }
void get_status(unsigned int clientreg, void *clientarg) { /* * This function returns the value of the status variable. * If the value of status is 0 (no collection is running), it * calles collect_data to start one. * */ /* * Extract the cache from the passed argument. */ netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; netsnmp_request_info *requests; netsnmp_agent_request_info *reqinfo; u_long *delay_time_cache = NULL; /* * Make sure the cache created earlier is still * valid. If not, the request timed out for some reason and we * do not need to keep processing things. Should never happen, but * this double checks. */ cache = netsnmp_handler_check_cache(cache); if (!cache) { snmp_log(LOG_ERR, "illegal call to return delayed response\n"); return; } /* * Re-establish the previous pointers, */ reqinfo = cache->reqinfo; requests = cache->requests; DEBUGMSGTL(("demo_module_10", "continuing delayed request, mode = %d\n", cache->reqinfo->mode)); /* * Set delegated to zero to indicate that the request is no longer * delegated and answer the query. */ requests->delegated = 0; switch (cache->reqinfo->mode) { /* * Registering as an instance means we do not need to deal with * GETNEXT processing, so we do not handle it here at all. * * However, since the instance handler already reset the mode * back to GETNEXT from the GET mode, we need to do the * same thing in both cases. * */ case MODE_GET: // no collection running, start one. if (status == 0) collect_data(); snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) & status, sizeof (status) /* length in bytes */); break; case MODE_SET_RESERVE1: /* * check type */ if (requests->requestvb->type != ASN_INTEGER) { /* * If not an integer, return SNMP error. */ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); /* * Free cache. It is no longer needed. */ netsnmp_free_delegated_cache(cache); return; } break; case MODE_SET_RESERVE2: /* * Store old value for UNDO support in the future. */ memdup((u_char **) & delay_time_cache, (u_char *) & delay_time, sizeof(delay_time)); /* * malloc failed */ if (delay_time_cache == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); netsnmp_free_delegated_cache(cache); return; } /* * Add our temporary information to the request itself. * This is then retrivable later. The free function * passed auto-frees it when the request is later * deleted. */ netsnmp_request_add_list_data(requests, netsnmp_create_data_list (DELAYED_INSTANCE_SET_NAME, delay_time_cache, free)); break; case MODE_SET_ACTION: // get status integer from request // if status == 0, start data collection, else return error if (*(requests->requestvb->val.integer) == 0) { status = *(requests->requestvb->val.integer); collect_data(); } else netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); break; case MODE_SET_UNDO: /* * A failure occurred. Reset to the * previously value by extracting the previosuly * stored information from the request. */ delay_time = *((u_long *) netsnmp_request_get_list_data(requests, DELAYED_INSTANCE_SET_NAME)); break; case MODE_SET_COMMIT: break; case MODE_SET_FREE: /* * The only thing to do here is free the old memdup'ed * value, but it's auto-freed by the datalist recovery, so * we don't have anything to actually do here */ break; } /* * free the information cache */ netsnmp_free_delegated_cache(cache); }
int proxy_got_response(int operation, netsnmp_session * sess, int reqid, netsnmp_pdu *pdu, void *cb_data) { netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) cb_data; netsnmp_request_info *requests, *request; netsnmp_variable_list *vars, *var; struct simple_proxy *sp; oid myname[MAX_OID_LEN]; size_t myname_len = MAX_OID_LEN; cache = netsnmp_handler_check_cache(cache); if (!cache) { DEBUGMSGTL(("proxy", "a proxy request was no longer valid.\n")); return SNMP_ERR_NOERROR; } requests = cache->requests; sp = (struct simple_proxy *) cache->localinfo; if (!sp) { DEBUGMSGTL(("proxy", "a proxy request was no longer valid.\n")); return SNMP_ERR_NOERROR; } switch (operation) { case NETSNMP_CALLBACK_OP_TIMED_OUT: /* * WWWXXX: don't leave requests delayed if operation is * something like TIMEOUT */ DEBUGMSGTL(("proxy", "got timed out... requests = %08p\n", requests)); netsnmp_handler_mark_requests_as_delegated(requests, REQUEST_IS_NOT_DELEGATED); netsnmp_set_request_error(cache->reqinfo, requests, /* XXXWWW: should be index = 0 */ SNMP_ERR_GENERR); netsnmp_free_delegated_cache(cache); return 0; case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: vars = pdu->variables; /* * update the original request varbinds with the results */ for (var = vars, request = requests; request && var; request = request->next, var = var->next_variable) { snmp_set_var_typed_value(request->requestvb, var->type, var->val.string, var->val_len); DEBUGMSGTL(("proxy", "got response... ")); DEBUGMSGOID(("proxy", var->name, var->name_length)); DEBUGMSG(("proxy", "\n")); request->delegated = 0; /* * copy the oid it belongs to */ if (sp->base_len && (var->name_length < sp->base_len || snmp_oid_compare(var->name, sp->base_len, sp->base, sp->base_len) != 0)) { DEBUGMSGTL(("proxy", "out of registered range... ")); DEBUGMSGOID(("proxy", var->name, sp->base_len)); DEBUGMSG(("proxy", " (%d) != ", sp->base_len)); DEBUGMSGOID(("proxy", sp->base, sp->base_len)); DEBUGMSG(("proxy", "\n")); continue; } else if (!sp->base_len && (var->name_length < sp->name_len || snmp_oid_compare(var->name, sp->name_len, sp->name, sp->name_len) != 0)) { DEBUGMSGTL(("proxy", "out of registered base range...\n")); /* * or not if its out of our search range */ continue; } else { if (sp->base_len) { /* * XXX: oid size maxed? */ memcpy(myname, sp->name, sizeof(oid) * sp->name_len); myname_len = sp->name_len + var->name_length - sp->base_len; if (myname_len > MAX_OID_LEN) { snmp_log(LOG_WARNING, "proxy OID return length too long.\n"); netsnmp_set_request_error(cache->reqinfo, requests, SNMP_ERR_GENERR); if (pdu) snmp_free_pdu(pdu); netsnmp_free_delegated_cache(cache); return 1; } if (var->name_length > sp->base_len) memcpy(&myname[sp->name_len], &var->name[sp->base_len], sizeof(oid) * (var->name_length - sp->base_len)); snmp_set_var_objid(request->requestvb, myname, myname_len); } else { snmp_set_var_objid(request->requestvb, var->name, var->name_length); } } } if (request || var) { /* * ack, this is bad. The # of varbinds don't match and * there is no way to fix the problem */ if (pdu) snmp_free_pdu(pdu); snmp_log(LOG_ERR, "response to proxy request illegal. We're screwed.\n"); netsnmp_set_request_error(cache->reqinfo, requests, SNMP_ERR_GENERR); } /* fix bulk_to_next operations */ if (cache->reqinfo->mode == MODE_GETBULK) netsnmp_bulk_to_next_fix_requests(requests); /* * free the response */ if (pdu && 0) snmp_free_pdu(pdu); break; default: DEBUGMSGTL(("proxy", "no response received: op = %d\n", operation)); break; } netsnmp_free_delegated_cache(cache); return 1; }