/* -*- c -*- */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include #include #include #include "perl_snmptrapd.h" #include "const-c.inc" typedef struct trapd_cb_data_s { SV *perl_cb; } trapd_cb_data; typedef struct netsnmp_oid_s { oid *name; size_t len; oid namebuf[ MAX_OID_LEN ]; } netsnmp_oid; static SV* newSVoid(oid* sname, size_t slen) { /*assert(slen < MAX_OID_LEN);*/ netsnmp_oid *o = malloc(sizeof(netsnmp_oid)); o->name = o->namebuf; o->len = slen; memcpy(o->name, sname, slen * sizeof(oid)); #undef CALL_EXTERNAL_OID_NEW #ifdef CALL_EXTERNAL_OID_NEW { SV *arg; SV *rarg; 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 */ { SV *rv = POPs; SvREFCNT_inc(rv); PUTBACK; return rv; } } #else /* build it and bless ourselves */ { HV *hv = newHV(); SV *rv = newRV_noinc((SV *) hv); SV *rvsub = newRV_noinc((SV *) newSViv((UV) o)); rvsub = sv_bless(rvsub, gv_stashpv("netsnmp_oidPtr", 1)); (void)hv_store(hv, "oidptr", 6, rvsub, 0); return sv_bless(rv, gv_stashpv("NetSNMP::OID", 1)); } #endif /* build oid ourselves */ } 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; SV **tmparray; int i, c = 0; u_char *outbuf; size_t ob_len = 0, oo_len = 0; AV *varbinds; HV *pduinfo; int noValuesReturned; int callingCFfailed = 0; int result = NETSNMPTRAPD_HANDLER_OK; netsnmp_pdu * v2pdu = NULL; dSP; ENTER; SAVETMPS; if (!pdu || !handler) return 0; /* nuke v1 PDUs */ if (pdu->command == SNMP_MSG_TRAP) { v2pdu = convert_v1pdu_to_v2(pdu); pdu = v2pdu; } 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) (void)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->contextName : "", pdu->contextNameLen)); STOREPDU("contextEngineID", newSVpv(pdu->contextEngineID ? (char *) pdu->contextEngineID : "", pdu->contextEngineIDLen)); STOREPDU("securityEngineID", newSVpv(pdu->securityEngineID ? (char *) pdu->securityEngineID : "", pdu->securityEngineIDLen)); STOREPDU("securityName", newSVpv(pdu->securityName ? (char *) pdu->securityName : "", pdu->securityNameLen)); } else { STOREPDU("community", newSVpv(pdu->community ? (char *) 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); netsnmp_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 */ tmparray[c++] = newSVoid(vb->name, vb->name_length); } /* * 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((char *) outbuf, oo_len)); netsnmp_free(outbuf); av_push(vba,newSViv(vb->type)); switch (vb->type) { case ASN_INTEGER: av_push(vba,newSViv((int32_t)*vb->val.integer)); break; case ASN_COUNTER: case ASN_GAUGE: case ASN_TIMETICKS: case ASN_UINTEGER: /* from rfc1442 */ av_push(vba,newSVuv((uint32_t)*vb->val.integer)); break; case ASN_IPADDRESS: case ASN_OCTET_STR: case ASN_OPAQUE: case ASN_NSAP: av_push(vba,newSVpvn((char*)vb->val.string,vb->val_len)); break; case ASN_BIT_STR: av_push(vba,newSVpvn((char*)vb->val.bitstring,vb->val_len)); break; case ASN_OBJECT_ID: av_push(vba,newSVoid(vb->val.objid,vb->val_len / sizeof(oid))); break; #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES case ASN_OPAQUE_FLOAT: av_push(vba,newSVnv(*vb->val.floatVal)); break; case ASN_OPAQUE_DOUBLE: av_push(vba,newSVnv(*vb->val.doubleVal)); break; case ASN_OPAQUE_I64: { char buf[I64CHARSZ + 1]; printI64(buf, vb->val.counter64); av_push(vba,newSVpv(buf,0)); } break; case ASN_OPAQUE_COUNTER64: case ASN_OPAQUE_U64: #endif case ASN_COUNTER64: { char buf[I64CHARSZ + 1]; printU64(buf, vb->val.counter64); av_push(vba,newSVpv(buf,0)); } break; case ASN_NULL: default: av_push(vba,newSV(0)); break; } 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) { noValuesReturned = perl_call_sv(pcallback, G_SCALAR); /* XXX: it discards the results, which isn't right */ } else if (SvROK(pcallback) && SvTYPE(SvRV(pcallback)) == SVt_PVCV) { /* reference to code */ noValuesReturned = perl_call_sv(SvRV(pcallback), G_SCALAR); } else { snmp_log(LOG_ERR, " tried to call a perl function but failed to understand its type: (ref = %p, svrok: %lu, SVTYPE: %lu)\n", pcallback, (unsigned long)SvROK(pcallback), (unsigned long)SvTYPE(pcallback)); callingCFfailed = 1; } if (!callingCFfailed) { SPAGAIN; if ( noValuesReturned == 0 ) { snmp_log(LOG_WARNING, " perl callback function %p did not return a scalar, assuming %d (NETSNMPTRAPD_HANDLER_OK)\n", pcallback, NETSNMPTRAPD_HANDLER_OK); } else { SV *rv = POPs; if (SvTYPE(rv) != SVt_IV) { snmp_log(LOG_WARNING, " perl callback function %p returned a scalar of type %lu instead of an integer, assuming %d (NETSNMPTRAPD_HANDLER_OK)\n", pcallback, (unsigned long)SvTYPE(rv), NETSNMPTRAPD_HANDLER_OK); } else { int rvi = (IV)SvIVx(rv); if ((NETSNMPTRAPD_HANDLER_OK <= rvi) && (rvi <= NETSNMPTRAPD_HANDLER_FINISH)) { snmp_log(LOG_DEBUG, " perl callback function %p returns %d\n", pcallback, rvi); result = rvi; } else { snmp_log(LOG_WARNING, " perl callback function %p returned an invalid scalar integer value (%d), assuming %d (NETSNMPTRAPD_HANDLER_OK)\n", pcallback, rvi, NETSNMPTRAPD_HANDLER_OK); } } } PUTBACK; } #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); if (v2pdu) { snmp_free_pdu(v2pdu); } FREETMPS; LEAVE; return result; } MODULE = NetSNMP::TrapReceiver PACKAGE = NetSNMP::TrapReceiver INCLUDE: const-xs.inc MODULE = NetSNMP::TrapReceiver PACKAGE = NetSNMP::TrapReceiver PREFIX=trapd_ int trapd_register(regoid, perlcallback) char *regoid; SV *perlcallback; PREINIT: oid myoid[MAX_OID_LEN]; size_t myoid_len = MAX_OID_LEN; trapd_cb_data *cb_data; netsnmp_trapd_handler *handler = NULL; CODE: { if (!regoid || !perlcallback) { RETVAL = 0; return; } if (strcmp(regoid,"all") == 0) { handler = netsnmp_add_global_traphandler(NETSNMPTRAPD_POST_HANDLER, perl_trapd_handler); } else if (strcmp(regoid,"default") == 0) { handler = netsnmp_add_default_traphandler(perl_trapd_handler); } else if (!snmp_parse_oid(regoid, myoid, &myoid_len)) { snmp_log(LOG_ERR, "Failed to parse oid for perl registration: %s\n", regoid); RETVAL = 0; return; } else { handler = netsnmp_add_traphandler(perl_trapd_handler, myoid, myoid_len); } if (handler) { cb_data = malloc(sizeof(trapd_cb_data)); cb_data->perl_cb = newSVsv(perlcallback); handler->handler_data = cb_data; handler->authtypes = (1 << VACM_VIEW_EXECUTE); RETVAL = 1; } else { RETVAL = 0; } } OUTPUT: RETVAL