int handle_snmp_packet(int operation, struct snmp_session *session, int reqid, struct snmp_pdu *pdu, void *magic) { struct agent_snmp_session *asp; int status, allDone, i; struct variable_list *var_ptr, *var_ptr2; if ( magic == NULL ) { asp = init_agent_snmp_session( session, snmp_clone_pdu(pdu) ); status = SNMP_ERR_NOERROR; } else { asp = (struct agent_snmp_session *)magic; status = asp->status; } if (asp->outstanding_requests != NULL) return 1; if ( check_access(pdu) != 0) { /* access control setup is incorrect */ send_easy_trap(SNMP_TRAP_AUTHFAIL, 0); if (asp->pdu->version != SNMP_VERSION_1 && asp->pdu->version != SNMP_VERSION_2c) { asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR; asp->pdu->command = SNMP_MSG_RESPONSE; snmp_increment_statistic(STAT_SNMPOUTPKTS); snmp_send( asp->session, asp->pdu ); free( asp ); return 1; } else { /* drop the request */ free( asp ); return 0; } } switch (pdu->command) { case SNMP_MSG_GET: if ( asp->mode != RESERVE1 ) break; /* Single pass */ snmp_increment_statistic(STAT_SNMPINGETREQUESTS); status = handle_next_pass( asp ); asp->mode = RESERVE2; break; case SNMP_MSG_GETNEXT: if ( asp->mode != RESERVE1 ) break; /* Single pass */ snmp_increment_statistic(STAT_SNMPINGETNEXTS); asp->exact = FALSE; status = handle_next_pass( asp ); asp->mode = RESERVE2; break; case SNMP_MSG_GETBULK: /* * GETBULKS require multiple passes. The first pass handles the * explicitly requested varbinds, and subsequent passes append * to the existing var_op_list. Each pass (after the first) * uses the results of the preceeding pass as the input list * (delimited by the start & end pointers. * Processing is terminated if all entries in a pass are * EndOfMib, or the maximum number of repetitions are made. */ if ( asp->mode == RESERVE1 ) { snmp_increment_statistic(STAT_SNMPINGETREQUESTS); asp->exact = FALSE; /* * Limit max repetitions to something reasonable * XXX: We should figure out what will fit somehow... */ if ( asp->pdu->errindex > 100 ) asp->pdu->errindex = 100; status = handle_next_pass( asp ); /* First pass */ asp->mode = RESERVE2; if ( status != SNMP_ERR_NOERROR ) break; while ( asp->pdu->errstat-- > 0 ) /* Skip non-repeaters */ asp->start = asp->start->next_variable; asp->pdu->errindex--; /* Handled first repetition */ if ( asp->outstanding_requests != NULL ) return 1; } while ( asp->pdu->errindex-- > 0 ) { /* Process repeaters */ /* * Add new variable structures for the * repeating elements, ready for the next pass. * Also check that these are not all EndOfMib */ allDone = TRUE; /* Check for some content */ for ( var_ptr = asp->start; var_ptr != asp->end->next_variable; var_ptr = var_ptr->next_variable ) { /* XXX: we don't know the size of the next OID, so assume the maximum length */ if ( var_ptr->type != SNMP_ENDOFMIBVIEW ) { var_ptr2 = snmp_add_null_var(asp->pdu, var_ptr->name, MAX_OID_LEN); for ( i=var_ptr->name_length ; i<MAX_OID_LEN ; i++) var_ptr2->name[i] = 0; var_ptr2->name_length = var_ptr->name_length; allDone = FALSE; } } if ( allDone ) break; asp->start = asp->end->next_variable; while ( asp->end->next_variable != NULL ) asp->end = asp->end->next_variable; status = handle_next_pass( asp ); if ( status != SNMP_ERR_NOERROR ) break; if ( asp->outstanding_requests != NULL ) return 1; } break; case SNMP_MSG_SET: /* * SETS require 3-4 passes through the var_op_list. The first two * passes verify that all types, lengths, and values are valid * and may reserve resources and the third does the set and a * fourth executes any actions. Then the identical GET RESPONSE * packet is returned. * If either of the first two passes returns an error, another * pass is made so that any reserved resources can be freed. * If the third pass returns an error, another pass is made so that * any changes can be reversed. * If the fourth pass (or any of the error handling passes) * return an error, we'd rather not know about it! */ if ( asp->mode == RESERVE1 ) { snmp_increment_statistic(STAT_SNMPINSETREQUESTS); asp->rw = WRITE; status = handle_next_pass( asp ); if ( status != SNMP_ERR_NOERROR ) asp->mode = FREE; else asp->mode = RESERVE2; if ( asp->outstanding_requests != NULL ) return 1; } if ( asp->mode == RESERVE2 ) { status = handle_next_pass( asp ); if ( status != SNMP_ERR_NOERROR ) asp->mode = FREE; else asp->mode = ACTION; if ( asp->outstanding_requests != NULL ) return 1; } if ( asp->mode == ACTION ) { status = handle_next_pass( asp ); if ( status != SNMP_ERR_NOERROR ) asp->mode = UNDO; else asp->mode = COMMIT; if ( asp->outstanding_requests != NULL ) return 1; } if ( asp->mode == COMMIT ) { status = handle_next_pass( asp ); if ( status != SNMP_ERR_NOERROR ) { status = SNMP_ERR_COMMITFAILED; asp->mode = FINISHED_FAILURE; } else asp->mode = FINISHED_SUCCESS; if ( asp->outstanding_requests != NULL ) return 1; } if ( asp->mode == UNDO ) { if (handle_next_pass( asp ) != SNMP_ERR_NOERROR ) status = SNMP_ERR_UNDOFAILED; asp->mode = FINISHED_FAILURE; break; } if ( asp->mode == FREE ) { (void) handle_next_pass( asp ); break; } break; case SNMP_MSG_RESPONSE: snmp_increment_statistic(STAT_SNMPINGETRESPONSES); free( asp ); return 0; case SNMP_MSG_TRAP: case SNMP_MSG_TRAP2: snmp_increment_statistic(STAT_SNMPINTRAPS); free( asp ); return 0; default: snmp_increment_statistic(STAT_SNMPINASNPARSEERRS); free( asp ); return 0; } if ( asp->outstanding_requests != NULL ) { asp->status = status; asp->next = agent_session_list; agent_session_list = asp; } else { /* * May need to "dumb down" a SET error status for a * v1 query. See RFC2576 - section 4.3 */ if (( asp->pdu->command == SNMP_MSG_SET ) && ( asp->pdu->version == SNMP_VERSION_1 )) { switch ( status ) { case SNMP_ERR_WRONGVALUE: case SNMP_ERR_WRONGENCODING: case SNMP_ERR_WRONGTYPE: case SNMP_ERR_WRONGLENGTH: case SNMP_ERR_INCONSISTENTVALUE: status = SNMP_ERR_BADVALUE; break; case SNMP_ERR_NOACCESS: case SNMP_ERR_NOTWRITABLE: case SNMP_ERR_NOCREATION: case SNMP_ERR_INCONSISTENTNAME: case SNMP_ERR_AUTHORIZATIONERROR: status = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_RESOURCEUNAVAILABLE: case SNMP_ERR_COMMITFAILED: case SNMP_ERR_UNDOFAILED: status = SNMP_ERR_GENERR; break; } } /* * Similarly we may need to "dumb down" v2 exception * types to throw an error for a v1 query. * See RFC2576 - section 4.1.2.3 */ if (( asp->pdu->command != SNMP_MSG_SET ) && ( asp->pdu->version == SNMP_VERSION_1 )) { for ( var_ptr = asp->pdu->variables, i=0 ; var_ptr != NULL ; var_ptr = var_ptr->next_variable, i++ ) { switch ( var_ptr->type ) { case SNMP_NOSUCHOBJECT: case SNMP_NOSUCHINSTANCE: case SNMP_ENDOFMIBVIEW: case ASN_COUNTER64: status = SNMP_ERR_NOSUCHNAME; asp->pdu->errindex=i; break; } } } if ( status == SNMP_ERR_NOERROR ) { snmp_increment_statistic_by( (asp->pdu->command == SNMP_MSG_SET ? STAT_SNMPINTOTALSETVARS : STAT_SNMPINTOTALREQVARS ), count_varbinds( asp->pdu )); } else { /* * Use a copy of the original request * to report failures. */ i = asp->pdu->errindex; snmp_free_pdu( asp->pdu ); asp->pdu = snmp_clone_pdu( pdu ); asp->pdu->errindex = i; } asp->pdu->command = SNMP_MSG_RESPONSE; asp->pdu->errstat = status; snmp_send( asp->session, asp->pdu ); snmp_increment_statistic(STAT_SNMPOUTPKTS); snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES); free( asp ); } return 1; }
int perl_trapd_handler( netsnmp_pdu *pdu, netsnmp_transport *transport, netsnmp_trapd_handler *handler) { trapd_cb_data *cb_data; SV *pcallback; netsnmp_variable_list *vb; netsnmp_oid *o; SV *arg; SV *rarg; SV **tmparray; int i, c = 0; u_char *outbuf; size_t ob_len = 0, oo_len = 0; AV *varbinds; HV *pduinfo; dSP; ENTER; SAVETMPS; if (!pdu || !handler) return 0; /* nuke v1 PDUs */ if (pdu->command == SNMP_MSG_TRAP) pdu = convert_v1pdu_to_v2(pdu); cb_data = handler->handler_data; if (!cb_data || !cb_data->perl_cb) return 0; pcallback = cb_data->perl_cb; /* get PDU related info */ pduinfo = newHV(); #define STOREPDU(n, v) hv_store(pduinfo, n, strlen(n), v, 0) #define STOREPDUi(n, v) STOREPDU(n, newSViv(v)) #define STOREPDUs(n, v) STOREPDU(n, newSVpv(v, 0)) STOREPDUi("version", pdu->version); STOREPDUs("notificationtype", ((pdu->command == SNMP_MSG_INFORM) ? "INFORM":"TRAP")); STOREPDUi("requestid", pdu->reqid); STOREPDUi("messageid", pdu->msgid); STOREPDUi("transactionid", pdu->transid); STOREPDUi("errorstatus", pdu->errstat); STOREPDUi("errorindex", pdu->errindex); if (pdu->version == 3) { STOREPDUi("securitymodel", pdu->securityModel); STOREPDUi("securitylevel", pdu->securityLevel); STOREPDU("contextName", newSVpv(pdu->contextName, pdu->contextNameLen)); STOREPDU("contextEngineID", newSVpv(pdu->contextEngineID, pdu->contextEngineIDLen)); STOREPDU("securityEngineID", newSVpv(pdu->securityEngineID, pdu->securityEngineIDLen)); STOREPDU("securityName", newSVpv(pdu->securityName, pdu->securityNameLen)); } else { STOREPDU("community", newSVpv(pdu->community, pdu->community_len)); } if (transport && transport->f_fmtaddr) { char *tstr = transport->f_fmtaddr(transport, pdu->transport_data, pdu->transport_data_length); STOREPDUs("receivedfrom", tstr); free(tstr); } /* * collect OID objects in a temp array first */ /* get VARBIND related info */ i = count_varbinds(pdu->variables); tmparray = malloc(sizeof(*tmparray) * i); for(vb = pdu->variables; vb; vb = vb->next_variable) { /* get the oid */ o = SNMP_MALLOC_TYPEDEF(netsnmp_oid); o->name = o->namebuf; o->len = vb->name_length; memcpy(o->name, vb->name, vb->name_length * sizeof(oid)); #undef CALL_EXTERNAL_OID_NEW #ifdef CALL_EXTERNAL_OID_NEW PUSHMARK(sp); rarg = sv_2mortal(newSViv((IV) 0)); arg = sv_2mortal(newSVrv(rarg, "netsnmp_oidPtr")); sv_setiv(arg, (IV) o); XPUSHs(rarg); PUTBACK; i = perl_call_pv("NetSNMP::OID::newwithptr", G_SCALAR); SPAGAIN; if (i != 1) { snmp_log(LOG_ERR, "unhandled OID error.\n"); /* ack XXX */ } /* get the value */ tmparray[c++] = POPs; SvREFCNT_inc(tmparray[c-1]); PUTBACK; #else /* build it and bless ourselves */ { HV *hv = newHV(); SV *rv = newRV_noinc((SV *) hv); SV *rvsub = newRV_noinc((SV *) newSViv((UV) o)); SV *sv; rvsub = sv_bless(rvsub, gv_stashpv("netsnmp_oidPtr", 1)); hv_store(hv, "oidptr", 6, rvsub, 0); rv = sv_bless(rv, gv_stashpv("NetSNMP::OID", 1)); tmparray[c++] = rv; } #endif /* build oid ourselves */ } /* * build the varbind lists */ varbinds = newAV(); for(vb = pdu->variables, i = 0; vb; vb = vb->next_variable, i++) { /* push the oid */ AV *vba; vba = newAV(); /* get the value */ outbuf = NULL; ob_len = 0; oo_len = 0; sprint_realloc_by_type(&outbuf, &ob_len, &oo_len, 1, vb, 0, 0, 0); av_push(vba,tmparray[i]); av_push(vba,newSVpvn(outbuf, oo_len)); free(outbuf); av_push(vba,newSViv(vb->type)); av_push(varbinds, (SV *) newRV_noinc((SV *) vba)); } PUSHMARK(sp); /* store the collected information on the stack */ XPUSHs(sv_2mortal(newRV_noinc((SV*) pduinfo))); XPUSHs(sv_2mortal(newRV_noinc((SV*) varbinds))); /* put the stack back in order */ PUTBACK; /* actually call the callback function */ if (SvTYPE(pcallback) == SVt_PVCV) { perl_call_sv(pcallback, G_DISCARD); /* XXX: it discards the results, which isn't right */ } else if (SvROK(pcallback) && SvTYPE(SvRV(pcallback)) == SVt_PVCV) { /* reference to code */ perl_call_sv(SvRV(pcallback), G_DISCARD); } else { snmp_log(LOG_ERR, " tried to call a perl function but failed to understand its type: (ref = %x, svrok: %lu, SVTYPE: %lu)\n", (uintptr_t)pcallback, SvROK(pcallback), SvTYPE(pcallback)); } #ifdef DUMPIT fprintf(stderr, "DUMPDUMPDUMPDUMPDUMPDUMP\n"); sv_dump(pduinfo); fprintf(stderr, "--------------------\n"); sv_dump(varbinds); #endif /* svREFCNT_dec((SV *) pduinfo); */ #ifdef NOT_THIS { SV *vba; while(vba = av_pop(varbinds)) { av_undef((AV *) vba); } } av_undef(varbinds); #endif free(tmparray); /* Not needed because of the G_DISCARD flag (I think) */ /* SPAGAIN; */ /* PUTBACK; */ #ifndef __x86_64__ FREETMPS; /* FIXME: known to cause a segfault on x86-64 */ #endif LEAVE; return NETSNMPTRAPD_HANDLER_OK; }