| /* |
| * AgentX Administrative request handling |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| |
| #include <sys/types.h> |
| #ifdef HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #if HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include "agentx/protocol.h" |
| #include "agentx/client.h" |
| |
| #include <net-snmp/agent/agent_index.h> |
| #include <net-snmp/agent/agent_trap.h> |
| #include <net-snmp/agent/agent_callbacks.h> |
| #include "mibII/sysORTable.h" |
| #include "master.h" |
| |
| extern struct timeval starttime; |
| |
| |
| |
| netsnmp_session * |
| find_agentx_session(netsnmp_session * session, int sessid) |
| { |
| netsnmp_session *sp; |
| for (sp = session->subsession; sp != NULL; sp = sp->next) { |
| if (sp->sessid == sessid) |
| return sp; |
| } |
| return NULL; |
| } |
| |
| |
| int |
| open_agentx_session(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| struct timeval now; |
| |
| DEBUGMSGTL(("agentx/master", "open %08p\n", session)); |
| sp = (netsnmp_session *) malloc(sizeof(netsnmp_session)); |
| if (sp == NULL) { |
| session->s_snmp_errno = AGENTX_ERR_OPEN_FAILED; |
| return -1; |
| } |
| |
| memcpy(sp, session, sizeof(netsnmp_session)); |
| sp->sessid = snmp_get_next_sessid(); |
| sp->version = pdu->version; |
| sp->timeout = pdu->time; |
| |
| /* |
| * Be careful with fields: if these aren't zeroed, they will get free()d |
| * more than once when the session is closed -- once in the main session, |
| * and once in each subsession. Basically, if it's not being used for |
| * some AgentX-specific purpose, it ought to be zeroed here. |
| */ |
| |
| sp->community = NULL; |
| sp->peername = NULL; |
| sp->contextEngineID = NULL; |
| sp->contextName = NULL; |
| sp->securityEngineID = NULL; |
| sp->securityPrivProto = NULL; |
| |
| /* |
| * This next bit utilises unused SNMPv3 fields |
| * to store the subagent OID and description. |
| * This really ought to use AgentX-specific fields, |
| * but it hardly seems worth it for a one-off use. |
| * |
| * But I'm willing to be persuaded otherwise.... */ |
| sp->securityAuthProto = snmp_duplicate_objid(pdu->variables->name, |
| pdu->variables-> |
| name_length); |
| sp->securityAuthProtoLen = pdu->variables->name_length; |
| sp->securityName = strdup((char *) pdu->variables->val.string); |
| gettimeofday(&now, NULL); |
| sp->engineTime = calculate_time_diff(&now, &starttime); |
| |
| sp->subsession = session; /* link back to head */ |
| sp->flags |= SNMP_FLAGS_SUBSESSION; |
| sp->flags &= ~AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER; |
| sp->flags |= (pdu->flags & AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER); |
| sp->next = session->subsession; |
| session->subsession = sp; |
| DEBUGMSGTL(("agentx/master", "opened %08p = %d with flags = %02x\n", |
| sp, sp->sessid, sp->flags & AGENTX_MSG_FLAGS_MASK)); |
| |
| return sp->sessid; |
| } |
| |
| int |
| close_agentx_session(netsnmp_session * session, int sessid) |
| { |
| netsnmp_session *sp, **prevNext; |
| |
| if (!session) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| DEBUGMSGTL(("agentx/master", "close %08p, %d\n", session, sessid)); |
| if (sessid == -1) { |
| /* |
| * The following is necessary to avoid locking up the agent when |
| * a sugagent dies during a set request. We must clean up the |
| * requests, so that the delegated request will be completed and |
| * further requests can be processed |
| */ |
| netsnmp_remove_delegated_requests_for_session(session); |
| if (session->subsession != NULL) { |
| netsnmp_session *subsession = session->subsession; |
| for(; subsession; subsession = subsession->next) { |
| netsnmp_remove_delegated_requests_for_session(subsession); |
| } |
| } |
| |
| unregister_mibs_by_session(session); |
| unregister_index_by_session(session); |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_REQ_UNREG_SYSOR_SESS, |
| (void*)session); |
| SNMP_FREE(session->myvoid); |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| prevNext = &(session->subsession); |
| |
| for (sp = session->subsession; sp != NULL; sp = sp->next) { |
| |
| if (sp->sessid == sessid) { |
| unregister_mibs_by_session(sp); |
| unregister_index_by_session(sp); |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_REQ_UNREG_SYSOR_SESS, |
| (void*)sp); |
| |
| *prevNext = sp->next; |
| |
| if (sp->securityAuthProto != NULL) { |
| free(sp->securityAuthProto); |
| } |
| if (sp->securityName != NULL) { |
| free(sp->securityName); |
| } |
| free(sp); |
| sp = NULL; |
| |
| DEBUGMSGTL(("agentx/master", "closed %08p, %d okay\n", |
| session, sessid)); |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| prevNext = &(sp->next); |
| } |
| |
| DEBUGMSGTL(("agentx/master", "sessid %d not found\n", sessid)); |
| return AGENTX_ERR_NOT_OPEN; |
| } |
| |
| int |
| register_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| char buf[128]; |
| oid ubound = 0; |
| u_long flags = 0; |
| netsnmp_handler_registration *reg; |
| int rc = 0; |
| int cacheid; |
| |
| DEBUGMSGTL(("agentx/master", "in register_agentx_list\n")); |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| sprintf(buf, "AgentX subagent %ld, session %8p, subsession %8p", |
| sp->sessid, session, sp); |
| /* |
| * * TODO: registration timeout |
| * * registration context |
| */ |
| if (pdu->range_subid) { |
| ubound = pdu->variables->val.objid[pdu->range_subid - 1]; |
| } |
| |
| if (pdu->flags & AGENTX_MSG_FLAG_INSTANCE_REGISTER) { |
| flags = FULLY_QUALIFIED_INSTANCE; |
| } |
| |
| reg = netsnmp_create_handler_registration(buf, agentx_master_handler, pdu->variables->name, pdu->variables->name_length, HANDLER_CAN_RWRITE | HANDLER_CAN_GETBULK); /* fake it */ |
| if (!session->myvoid) { |
| session->myvoid = malloc(sizeof(cacheid)); |
| cacheid = netsnmp_allocate_globalcacheid(); |
| *((int *) session->myvoid) = cacheid; |
| } else { |
| cacheid = *((int *) session->myvoid); |
| } |
| |
| reg->handler->myvoid = session; |
| reg->global_cacheid = cacheid; |
| if (NULL != pdu->community) |
| reg->contextName = strdup(pdu->community); |
| |
| /* |
| * register mib. Note that for failure cases, the registration info |
| * (reg) will be freed, and thus is no longer a valid pointer. |
| */ |
| switch (netsnmp_register_mib(buf, NULL, 0, 1, |
| pdu->variables->name, |
| pdu->variables->name_length, |
| pdu->priority, pdu->range_subid, ubound, |
| sp, (char *) pdu->community, pdu->time, |
| flags, reg, 1)) { |
| |
| case MIB_REGISTERED_OK: |
| DEBUGMSGTL(("agentx/master", "registered ok\n")); |
| return AGENTX_ERR_NOERROR; |
| |
| case MIB_DUPLICATE_REGISTRATION: |
| DEBUGMSGTL(("agentx/master", "duplicate registration\n")); |
| rc = AGENTX_ERR_DUPLICATE_REGISTRATION; |
| break; |
| |
| case MIB_REGISTRATION_FAILED: |
| default: |
| rc = AGENTX_ERR_REQUEST_DENIED; |
| DEBUGMSGTL(("agentx/master", "failed registration\n")); |
| } |
| return rc; |
| } |
| |
| int |
| unregister_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| int rc = 0; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) { |
| return AGENTX_ERR_NOT_OPEN; |
| } |
| |
| if (pdu->range_subid != 0) { |
| oid ubound = |
| pdu->variables->val.objid[pdu->range_subid - 1]; |
| rc = netsnmp_unregister_mib_table_row(pdu->variables->name, |
| pdu->variables->name_length, |
| pdu->priority, |
| pdu->range_subid, ubound, |
| (char *) pdu->community); |
| } else { |
| rc = unregister_mib_context(pdu->variables->name, |
| pdu->variables->name_length, |
| pdu->priority, 0, 0, |
| (char *) pdu->community); |
| } |
| |
| switch (rc) { |
| case MIB_UNREGISTERED_OK: |
| return AGENTX_ERR_NOERROR; |
| case MIB_NO_SUCH_REGISTRATION: |
| return AGENTX_ERR_UNKNOWN_REGISTRATION; |
| case MIB_UNREGISTRATION_FAILED: |
| default: |
| return AGENTX_ERR_REQUEST_DENIED; |
| } |
| } |
| |
| int |
| allocate_idx_list(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| netsnmp_variable_list *vp, *vp2, *next, *res; |
| int flags = 0; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| if (pdu->flags & AGENTX_MSG_FLAG_ANY_INSTANCE) |
| flags |= ALLOCATE_ANY_INDEX; |
| if (pdu->flags & AGENTX_MSG_FLAG_NEW_INSTANCE) |
| flags |= ALLOCATE_NEW_INDEX; |
| |
| /* |
| * XXX - what about errors? |
| * |
| * If any allocations fail, then we need to |
| * *fully* release the earlier ones. |
| * (i.e. remove them completely from the index registry, |
| * not simply mark them as available for re-use) |
| * |
| * For now - assume they all succeed. |
| */ |
| for (vp = pdu->variables; vp != NULL; vp = next) { |
| next = vp->next_variable; |
| res = register_index(vp, flags, session); |
| if (res == NULL) { |
| /* |
| * If any allocations fail, we need to *fully* release |
| * all previous ones (i.e. remove them completely |
| * from the index registry) |
| */ |
| for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) { |
| remove_index(vp2, session); |
| } |
| return AGENTX_ERR_INDEX_NONE_AVAILABLE; /* XXX */ |
| } else { |
| (void) snmp_clone_var(res, vp); |
| free(res); |
| } |
| vp->next_variable = next; |
| } |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| int |
| release_idx_list(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| netsnmp_variable_list *vp, *vp2, *rv = NULL; |
| int res; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) { |
| res = unregister_index(vp, TRUE, session); |
| /* |
| * If any releases fail, |
| * we need to reinstate all previous ones. |
| */ |
| if (res != SNMP_ERR_NOERROR) { |
| for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) { |
| rv = register_index(vp2, ALLOCATE_THIS_INDEX, session); |
| free(rv); |
| } |
| return AGENTX_ERR_INDEX_NOT_ALLOCATED; /* Probably */ |
| } |
| } |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| int |
| add_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| struct sysORTable parms; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| parms.OR_oid = pdu->variables->name; |
| parms.OR_oidlen = pdu->variables->name_length; |
| parms.OR_descr = netsnmp_strdup_and_null(pdu->variables->val.string, |
| pdu->variables->val_len); |
| parms.OR_sess = sp; |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_REQ_REG_SYSOR, (void*)&parms); |
| free(parms.OR_descr); |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| int |
| remove_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| struct sysORTable parms; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| parms.OR_oid = pdu->variables->name; |
| parms.OR_oidlen = pdu->variables->name_length; |
| parms.OR_sess = sp; |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_REQ_UNREG_SYSOR, (void*)&parms); |
| /* |
| * no easy way to get an error code... |
| * if (rc < 0) |
| * return AGENTX_ERR_UNKNOWN_AGENTCAPS; |
| */ |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| int |
| agentx_notify(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| netsnmp_variable_list *var; |
| extern oid sysuptime_oid[], snmptrap_oid[]; |
| extern size_t sysuptime_oid_len, snmptrap_oid_len; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| |
| var = pdu->variables; |
| if (!var) |
| return AGENTX_ERR_PROCESSING_ERROR; |
| |
| if (snmp_oid_compare(var->name, var->name_length, |
| sysuptime_oid, sysuptime_oid_len) == 0) { |
| var = var->next_variable; |
| } |
| |
| if (!var || snmp_oid_compare(var->name, var->name_length, |
| snmptrap_oid, snmptrap_oid_len) != 0) |
| return AGENTX_ERR_PROCESSING_ERROR; |
| |
| /* |
| * If sysUptime isn't the first varbind, don't worry. |
| * send_trap_vars() will add it if necessary. |
| * |
| * Note that if this behaviour is altered, it will |
| * be necessary to add sysUptime here, |
| * as this is valid AgentX syntax. |
| */ |
| |
| send_trap_vars(-1, -1, pdu->variables); |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| |
| int |
| agentx_ping_response(netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| netsnmp_session *sp; |
| |
| sp = find_agentx_session(session, pdu->sessid); |
| if (sp == NULL) |
| return AGENTX_ERR_NOT_OPEN; |
| else |
| return AGENTX_ERR_NOERROR; |
| } |
| |
| int |
| handle_master_agentx_packet(int operation, |
| netsnmp_session * session, |
| int reqid, netsnmp_pdu *pdu, void *magic) |
| { |
| netsnmp_agent_session *asp; |
| struct timeval now; |
| |
| if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) { |
| DEBUGMSGTL(("agentx/master", |
| "transport disconnect on session %08p\n", session)); |
| /* |
| * Shut this session down gracefully. |
| */ |
| close_agentx_session(session, -1); |
| return 1; |
| } else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) { |
| DEBUGMSGTL(("agentx/master", "unexpected callback op %d\n", |
| operation)); |
| return 1; |
| } |
| |
| /* |
| * Okay, it's a NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE op. |
| */ |
| |
| if (magic) { |
| asp = (netsnmp_agent_session *) magic; |
| } else { |
| asp = init_agent_snmp_session(session, pdu); |
| } |
| |
| DEBUGMSGTL(("agentx/master", "handle pdu (req=0x%x,trans=0x%x,sess=0x%x)\n", |
| pdu->reqid,pdu->transid, pdu->sessid)); |
| |
| switch (pdu->command) { |
| case AGENTX_MSG_OPEN: |
| asp->pdu->sessid = open_agentx_session(session, pdu); |
| if (asp->pdu->sessid == -1) |
| asp->status = session->s_snmp_errno; |
| break; |
| |
| case AGENTX_MSG_CLOSE: |
| asp->status = close_agentx_session(session, pdu->sessid); |
| break; |
| |
| case AGENTX_MSG_REGISTER: |
| asp->status = register_agentx_list(session, pdu); |
| break; |
| |
| case AGENTX_MSG_UNREGISTER: |
| asp->status = unregister_agentx_list(session, pdu); |
| break; |
| |
| case AGENTX_MSG_INDEX_ALLOCATE: |
| asp->status = allocate_idx_list(session, asp->pdu); |
| if (asp->status != AGENTX_ERR_NOERROR) { |
| snmp_free_pdu(asp->pdu); |
| asp->pdu = snmp_clone_pdu(pdu); |
| } |
| break; |
| |
| case AGENTX_MSG_INDEX_DEALLOCATE: |
| asp->status = release_idx_list(session, pdu); |
| break; |
| |
| case AGENTX_MSG_ADD_AGENT_CAPS: |
| asp->status = add_agent_caps_list(session, pdu); |
| break; |
| |
| case AGENTX_MSG_REMOVE_AGENT_CAPS: |
| asp->status = remove_agent_caps_list(session, pdu); |
| break; |
| |
| case AGENTX_MSG_NOTIFY: |
| asp->status = agentx_notify(session, pdu); |
| break; |
| |
| case AGENTX_MSG_PING: |
| asp->status = agentx_ping_response(session, pdu); |
| break; |
| |
| /* |
| * TODO: Other admin packets |
| */ |
| |
| case AGENTX_MSG_GET: |
| case AGENTX_MSG_GETNEXT: |
| case AGENTX_MSG_GETBULK: |
| case AGENTX_MSG_TESTSET: |
| case AGENTX_MSG_COMMITSET: |
| case AGENTX_MSG_UNDOSET: |
| case AGENTX_MSG_CLEANUPSET: |
| case AGENTX_MSG_RESPONSE: |
| /* |
| * Shouldn't be handled here |
| */ |
| break; |
| |
| default: |
| asp->status = AGENTX_ERR_PARSE_FAILED; |
| break; |
| } |
| |
| gettimeofday(&now, NULL); |
| asp->pdu->time = calculate_time_diff(&now, &starttime); |
| asp->pdu->command = AGENTX_MSG_RESPONSE; |
| asp->pdu->errstat = asp->status; |
| DEBUGMSGTL(("agentx/master", "send response, stat %d (req=0x%x,trans=" |
| "0x%x,sess=0x%x)\n", |
| asp->status, pdu->reqid,pdu->transid, pdu->sessid)); |
| if (!snmp_send(asp->session, asp->pdu)) { |
| char *eb = NULL; |
| int pe, pse; |
| snmp_error(asp->session, &pe, &pse, &eb); |
| snmp_free_pdu(asp->pdu); |
| DEBUGMSGTL(("agentx/master", "FAILED %d %d %s\n", pe, pse, eb)); |
| free(eb); |
| } |
| asp->pdu = NULL; |
| free_agent_snmp_session(asp); |
| |
| return 1; |
| } |