netsnmp_variable_list * register_index(netsnmp_variable_list * varbind, int flags, netsnmp_session * ss) { netsnmp_variable_list *rv = NULL; struct snmp_index *new_index, *idxptr, *idxptr2; struct snmp_index *prev_oid_ptr, *prev_idx_ptr; int res, res2, i; DEBUGMSGTL(("register_index", "register ")); DEBUGMSGVAR(("register_index", varbind)); DEBUGMSG(("register_index", "for session %8p\n", ss)); #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(TESTING) if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE) == SUB_AGENT) { return (agentx_register_index(ss, varbind, flags)); } #endif /* * Look for the requested OID entry */ prev_oid_ptr = NULL; prev_idx_ptr = NULL; res = 1; res2 = 1; for (idxptr = snmp_index_head; idxptr != NULL; prev_oid_ptr = idxptr, idxptr = idxptr->next_oid) { if ((res = snmp_oid_compare(varbind->name, varbind->name_length, idxptr->varbind->name, idxptr->varbind->name_length)) <= 0) break; } /* * Found the OID - now look at the registered indices */ if (res == 0 && idxptr) { if (varbind->type != idxptr->varbind->type) return NULL; /* wrong type */ /* * If we've been asked for an arbitrary new value, * then find the end of the list. * If we've been asked for any arbitrary value, * then look for an unused entry, and use that. * If there aren't any, continue as for new. * Otherwise, locate the given value in the (sorted) * list of already allocated values */ if (flags & ALLOCATE_ANY_INDEX) { for (idxptr2 = idxptr; idxptr2 != NULL; prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) { if (flags == ALLOCATE_ANY_INDEX && !(idxptr2->allocated)) { if ((rv = snmp_clone_varbind(idxptr2->varbind)) != NULL) { idxptr2->session = ss; idxptr2->allocated = 1; } return rv; } } } else { for (idxptr2 = idxptr; idxptr2 != NULL; prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) { switch (varbind->type) { case ASN_INTEGER: res2 = (*varbind->val.integer - *idxptr2->varbind->val.integer); break; case ASN_OCTET_STR: i = SNMP_MIN(varbind->val_len, idxptr2->varbind->val_len); res2 = memcmp(varbind->val.string, idxptr2->varbind->val.string, i); break; case ASN_OBJECT_ID: res2 = snmp_oid_compare(varbind->val.objid, varbind->val_len / sizeof(oid), idxptr2->varbind->val.objid, idxptr2->varbind->val_len / sizeof(oid)); break; default: return NULL; /* wrong type */ } if (res2 <= 0) break; } if (res2 == 0) { if (idxptr2->allocated) { /* * No good: the index is in use. */ return NULL; } else { /* * Okay, it's unallocated, we can just claim ownership * here. */ if ((rv = snmp_clone_varbind(idxptr2->varbind)) != NULL) { idxptr2->session = ss; idxptr2->allocated = 1; } return rv; } } } } /* * OK - we've now located where the new entry needs to * be fitted into the index registry tree * To recap: * 'prev_oid_ptr' points to the head of the OID index * list prior to this one. If this is null, then * it means that this is the first OID in the list. * 'idxptr' points either to the head of this OID list, * or the next OID (if this is a new OID request) * These can be distinguished by the value of 'res'. * * 'prev_idx_ptr' points to the index entry that sorts * immediately prior to the requested value (if any). * If an arbitrary value is required, then this will * point to the last allocated index. * If this pointer is null, then either this is a new * OID request, or the requested value is the first * in the list. * 'idxptr2' points to the next sorted index (if any) * but is not actually needed any more. * * Clear? Good! * I hope you've been paying attention. * There'll be a test later :-) */ /* * We proceed by creating the new entry * (by copying the entry provided) */ new_index = (struct snmp_index *) calloc(1, sizeof(struct snmp_index)); if (new_index == NULL) return NULL; if (NULL == snmp_varlist_add_variable(&new_index->varbind, varbind->name, varbind->name_length, varbind->type, varbind->val.string, varbind->val_len)) { /* * if (snmp_clone_var( varbind, new_index->varbind ) != 0 ) */ free(new_index); return NULL; } new_index->session = ss; new_index->allocated = 1; if (varbind->type == ASN_OCTET_STR && flags == ALLOCATE_THIS_INDEX) new_index->varbind->val.string[new_index->varbind->val_len] = 0; /* * If we've been given a value, then we can use that, but * otherwise, we need to create a new value for this entry. * Note that ANY_INDEX and NEW_INDEX are both covered by this * test (since NEW_INDEX & ANY_INDEX = ANY_INDEX, remember?) */ if (flags & ALLOCATE_ANY_INDEX) { if (prev_idx_ptr) { if (snmp_clone_var(prev_idx_ptr->varbind, new_index->varbind) != 0) { free(new_index); return NULL; } } else new_index->varbind->val.string = new_index->varbind->buf; switch (varbind->type) { case ASN_INTEGER: if (prev_idx_ptr) { (*new_index->varbind->val.integer)++; } else *(new_index->varbind->val.integer) = 1; new_index->varbind->val_len = sizeof(long); break; case ASN_OCTET_STR: if (prev_idx_ptr) { i = new_index->varbind->val_len - 1; while (new_index->varbind->buf[i] == 'z') { new_index->varbind->buf[i] = 'a'; i--; if (i < 0) { i = new_index->varbind->val_len; new_index->varbind->buf[i] = 'a'; new_index->varbind->buf[i + 1] = 0; } } new_index->varbind->buf[i]++; } else strcpy((char *) new_index->varbind->buf, "aaaa"); new_index->varbind->val_len = strlen((char *) new_index->varbind->buf); break; case ASN_OBJECT_ID: if (prev_idx_ptr) { i = prev_idx_ptr->varbind->val_len / sizeof(oid) - 1; while (new_index->varbind->val.objid[i] == 255) { new_index->varbind->val.objid[i] = 1; i--; if (i == 0 && new_index->varbind->val.objid[0] == 2) { new_index->varbind->val.objid[0] = 1; i = new_index->varbind->val_len / sizeof(oid); new_index->varbind->val.objid[i] = 0; new_index->varbind->val_len += sizeof(oid); } } new_index->varbind->val.objid[i]++; } else { /* * If the requested OID name is small enough, * * append another OID (1) and use this as the * * default starting value for new indexes. */ if ((varbind->name_length + 1) * sizeof(oid) <= 40) { for (i = 0; i < (int) varbind->name_length; i++) new_index->varbind->val.objid[i] = varbind->name[i]; new_index->varbind->val.objid[varbind->name_length] = 1; new_index->varbind->val_len = (varbind->name_length + 1) * sizeof(oid); } else { /* * Otherwise use '.1.1.1.1...' */ i = 40 / sizeof(oid); if (i > 4) i = 4; new_index->varbind->val_len = i * (sizeof(oid)); for (i--; i >= 0; i--) new_index->varbind->val.objid[i] = 1; } } break; default: snmp_free_var(new_index->varbind); free(new_index); return NULL; /* Index type not supported */ } } /* * Try to duplicate the new varbind for return. */ if ((rv = snmp_clone_varbind(new_index->varbind)) == NULL) { snmp_free_var(new_index->varbind); free(new_index); return NULL; } /* * Right - we've set up the new entry. * All that remains is to link it into the tree. * There are a number of possible cases here, * so watch carefully. */ if (prev_idx_ptr) { new_index->next_idx = prev_idx_ptr->next_idx; new_index->next_oid = prev_idx_ptr->next_oid; prev_idx_ptr->next_idx = new_index; } else { if (res == 0 && idxptr) { new_index->next_idx = idxptr; new_index->next_oid = idxptr->next_oid; } else { new_index->next_idx = NULL; new_index->next_oid = idxptr; } if (prev_oid_ptr) { while (prev_oid_ptr) { prev_oid_ptr->next_oid = new_index; prev_oid_ptr = prev_oid_ptr->next_idx; } } else snmp_index_head = new_index; } return rv; }
/* * 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; }