1 /* -*- c -*- */
2 
3 #include "EXTERN.h"
4 #include "perl.h"
5 #include "XSUB.h"
6 
7 #include "ppport.h"
8 
9 #include <net-snmp/net-snmp-config.h>
10 #include <net-snmp/net-snmp-includes.h>
11 #include <net-snmp/agent/net-snmp-agent-includes.h>
12 
13 #include "perl_snmptrapd.h"
14 
15 #include "const-c.inc"
16 
17 typedef struct trapd_cb_data_s {
18    SV *perl_cb;
19 } trapd_cb_data;
20 
21 typedef struct netsnmp_oid_s {
22     oid                 *name;
23     size_t               len;
24     oid                  namebuf[ MAX_OID_LEN ];
25 } netsnmp_oid;
26 
newSVoid(oid * sname,size_t slen)27 static SV* newSVoid(oid* sname, size_t slen)
28 {
29     /*assert(slen < MAX_OID_LEN);*/
30     netsnmp_oid *o = malloc(sizeof(netsnmp_oid));
31     o->name = o->namebuf;
32     o->len = slen;
33     memcpy(o->name, sname, slen * sizeof(oid));
34 
35 #undef CALL_EXTERNAL_OID_NEW
36 
37 #ifdef CALL_EXTERNAL_OID_NEW
38     {
39       SV *arg;
40       SV *rarg;
41 
42       PUSHMARK(sp);
43 
44       rarg = sv_2mortal(newSViv((IV) 0));
45       arg = sv_2mortal(newSVrv(rarg, "netsnmp_oidPtr"));
46       sv_setiv(arg, (IV) o);
47       XPUSHs(rarg);
48 
49       PUTBACK;
50       i = perl_call_pv("NetSNMP::OID::newwithptr", G_SCALAR);
51       SPAGAIN;
52 
53       if (i != 1) {
54 	snmp_log(LOG_ERR, "unhandled OID error.\n");
55 	/* ack XXX */
56       }
57       /* get the value */
58       {
59 	  SV *rv = POPs;
60 	  SvREFCNT_inc(rv);
61 	  PUTBACK;
62 	  return rv;
63       }
64     }
65 #else /* build it and bless ourselves */
66     {
67       HV *hv = newHV();
68       SV *rv = newRV_noinc((SV *) hv);
69       SV *rvsub = newRV_noinc((SV *) newSViv((UV) o));
70       rvsub = sv_bless(rvsub, gv_stashpv("netsnmp_oidPtr", 1));
71       (void)hv_store(hv, "oidptr", 6,  rvsub, 0);
72       return sv_bless(rv, gv_stashpv("NetSNMP::OID", 1));
73     }
74 #endif /* build oid ourselves */
75 }
76 
perl_trapd_handler(netsnmp_pdu * pdu,netsnmp_transport * transport,netsnmp_trapd_handler * handler)77 int   perl_trapd_handler( netsnmp_pdu           *pdu,
78                           netsnmp_transport     *transport,
79                           netsnmp_trapd_handler *handler)
80 {
81     trapd_cb_data *cb_data;
82     SV *pcallback;
83     netsnmp_variable_list *vb;
84     SV **tmparray;
85     int i, c = 0;
86     u_char *outbuf;
87     size_t ob_len = 0, oo_len = 0;
88     AV *varbinds;
89     HV *pduinfo;
90     int noValuesReturned;
91     int callingCFfailed = 0;
92     int result = NETSNMPTRAPD_HANDLER_OK;
93     netsnmp_pdu * v2pdu = NULL;
94 
95     dSP;
96     ENTER;
97     SAVETMPS;
98 
99     if (!pdu || !handler)
100         return 0;
101 
102     /* nuke v1 PDUs */
103     if (pdu->command == SNMP_MSG_TRAP) {
104         v2pdu = convert_v1pdu_to_v2(pdu);
105         pdu = v2pdu;
106     }
107 
108     cb_data = handler->handler_data;
109     if (!cb_data || !cb_data->perl_cb)
110         return 0;
111 
112     pcallback = cb_data->perl_cb;
113 
114     /* get PDU related info */
115     pduinfo = newHV();
116 #define STOREPDU(n, v) (void)hv_store(pduinfo, n, strlen(n), v, 0)
117 #define STOREPDUi(n, v) STOREPDU(n, newSViv(v))
118 #define STOREPDUs(n, v) STOREPDU(n, newSVpv(v, 0))
119     STOREPDUi("version", pdu->version);
120     STOREPDUs("notificationtype", ((pdu->command == SNMP_MSG_INFORM) ? "INFORM":"TRAP"));
121     STOREPDUi("requestid", pdu->reqid);
122     STOREPDUi("messageid", pdu->msgid);
123     STOREPDUi("transactionid", pdu->transid);
124     STOREPDUi("errorstatus", pdu->errstat);
125     STOREPDUi("errorindex", pdu->errindex);
126     if (pdu->version == 3) {
127         STOREPDUi("securitymodel", pdu->securityModel);
128         STOREPDUi("securitylevel", pdu->securityLevel);
129         STOREPDU("contextName",
130                  newSVpv(pdu->contextName ? pdu->contextName : "", pdu->contextNameLen));
131         STOREPDU("contextEngineID",
132                  newSVpv(pdu->contextEngineID ? (char *) pdu->contextEngineID : "",
133                                     pdu->contextEngineIDLen));
134         STOREPDU("securityEngineID",
135                  newSVpv(pdu->securityEngineID ? (char *) pdu->securityEngineID : "",
136                                     pdu->securityEngineIDLen));
137         STOREPDU("securityName",
138                  newSVpv(pdu->securityName ? (char *) pdu->securityName : "", pdu->securityNameLen));
139     } else {
140         STOREPDU("community",
141                  newSVpv(pdu->community ? (char *) pdu->community : "", pdu->community_len));
142     }
143 
144     if (transport && transport->f_fmtaddr) {
145         char *tstr = transport->f_fmtaddr(transport, pdu->transport_data,
146                                           pdu->transport_data_length);
147         STOREPDUs("receivedfrom", tstr);
148         netsnmp_free(tstr);
149     }
150 
151 
152     /*
153      * collect OID objects in a temp array first
154      */
155     /* get VARBIND related info */
156     i = count_varbinds(pdu->variables);
157     tmparray = malloc(sizeof(*tmparray) * i);
158 
159     for(vb = pdu->variables; vb; vb = vb->next_variable) {
160 
161         /* get the oid */
162 	tmparray[c++] = newSVoid(vb->name, vb->name_length);
163     }
164 
165     /*
166      * build the varbind lists
167      */
168     varbinds = newAV();
169     for(vb = pdu->variables, i = 0; vb; vb = vb->next_variable, i++) {
170         /* push the oid */
171         AV *vba;
172         vba = newAV();
173 
174 
175         /* get the value */
176         outbuf = NULL;
177         ob_len = 0;
178         oo_len = 0;
179 	sprint_realloc_by_type(&outbuf, &ob_len, &oo_len, 1,
180                                vb, 0, 0, 0);
181 
182         av_push(vba,tmparray[i]);
183         av_push(vba,newSVpvn((char *) outbuf, oo_len));
184         netsnmp_free(outbuf);
185         av_push(vba,newSViv(vb->type));
186 	switch (vb->type) {
187 	case ASN_INTEGER:
188 		av_push(vba,newSViv((int32_t)*vb->val.integer));
189 		break;
190 	case ASN_COUNTER:
191 	case ASN_GAUGE:
192 	case ASN_TIMETICKS:
193 	case ASN_UINTEGER: /* from rfc1442 */
194 		av_push(vba,newSVuv((uint32_t)*vb->val.integer));
195 		break;
196 	case ASN_IPADDRESS:
197 	case ASN_OCTET_STR:
198 	case ASN_OPAQUE:
199 	case ASN_NSAP:
200 		av_push(vba,newSVpvn((char*)vb->val.string,vb->val_len));
201 		break;
202 	case ASN_BIT_STR:
203 		av_push(vba,newSVpvn((char*)vb->val.bitstring,vb->val_len));
204 		break;
205 	case ASN_OBJECT_ID:
206 		av_push(vba,newSVoid(vb->val.objid,vb->val_len / sizeof(oid)));
207 		break;
208 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
209 	case ASN_OPAQUE_FLOAT:
210 		av_push(vba,newSVnv(*vb->val.floatVal));
211 		break;
212 	case ASN_OPAQUE_DOUBLE:
213 		av_push(vba,newSVnv(*vb->val.doubleVal));
214 		break;
215 	case ASN_OPAQUE_I64:
216 		{
217 			char buf[I64CHARSZ + 1];
218 			printI64(buf, vb->val.counter64);
219 			av_push(vba,newSVpv(buf,0));
220 		}
221 		break;
222 	case ASN_OPAQUE_COUNTER64:
223 	case ASN_OPAQUE_U64:
224 #endif
225 	case ASN_COUNTER64:
226 		{
227 			char buf[I64CHARSZ + 1];
228 			printU64(buf, vb->val.counter64);
229 			av_push(vba,newSVpv(buf,0));
230 		}
231 		break;
232 	case ASN_NULL:
233 	default:
234 		av_push(vba,newSV(0));
235 		break;
236 	}
237         av_push(varbinds, (SV *) newRV_noinc((SV *) vba));
238     }
239 
240     PUSHMARK(sp);
241 
242     /* store the collected information on the stack */
243     XPUSHs(sv_2mortal(newRV_noinc((SV*) pduinfo)));
244     XPUSHs(sv_2mortal(newRV_noinc((SV*) varbinds)));
245 
246     /* put the stack back in order */
247     PUTBACK;
248 
249     /* actually call the callback function */
250     if (SvTYPE(pcallback) == SVt_PVCV) {
251         noValuesReturned = perl_call_sv(pcallback, G_SCALAR);
252         /* XXX: it discards the results, which isn't right */
253     } else if (SvROK(pcallback) && SvTYPE(SvRV(pcallback)) == SVt_PVCV) {
254         /* reference to code */
255         noValuesReturned = perl_call_sv(SvRV(pcallback), G_SCALAR);
256     } else {
257         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));
258 	callingCFfailed = 1;
259     }
260 
261     if (!callingCFfailed) {
262       SPAGAIN;
263 
264       if ( noValuesReturned == 0 ) {
265         snmp_log(LOG_WARNING, " perl callback function %p did not return a scalar, assuming %d (NETSNMPTRAPD_HANDLER_OK)\n", pcallback, NETSNMPTRAPD_HANDLER_OK);
266       }
267       else {
268 	SV *rv = POPs;
269 
270 	if (SvTYPE(rv) != SVt_IV) {
271 	  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);
272 	}
273 	else {
274 	  int rvi = (IV)SvIVx(rv);
275 
276 	  if ((NETSNMPTRAPD_HANDLER_OK <= rvi) && (rvi <= NETSNMPTRAPD_HANDLER_FINISH)) {
277 	    snmp_log(LOG_DEBUG, " perl callback function %p returns %d\n", pcallback, rvi);
278 	    result = rvi;
279 	  }
280 	  else {
281 	    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);
282 	  }
283 	}
284       }
285 
286       PUTBACK;
287     }
288 
289 #ifdef DUMPIT
290     fprintf(stderr, "DUMPDUMPDUMPDUMPDUMPDUMP\n");
291     sv_dump(pduinfo);
292     fprintf(stderr, "--------------------\n");
293     sv_dump(varbinds);
294 #endif
295 
296     /* svREFCNT_dec((SV *) pduinfo); */
297 #ifdef NOT_THIS
298     {
299         SV *vba;
300         while(vba = av_pop(varbinds)) {
301             av_undef((AV *) vba);
302         }
303     }
304     av_undef(varbinds);
305 #endif
306     free(tmparray);
307 
308       if (v2pdu) {
309               snmp_free_pdu(v2pdu);
310       }
311 
312     FREETMPS;
313     LEAVE;
314     return result;
315 }
316 
317 MODULE = NetSNMP::TrapReceiver		PACKAGE = NetSNMP::TrapReceiver
318 
319 INCLUDE: const-xs.inc
320 
321 MODULE = NetSNMP::TrapReceiver PACKAGE = NetSNMP::TrapReceiver PREFIX=trapd_
322 int
323 trapd_register(regoid, perlcallback)
324 	char *regoid;
325         SV   *perlcallback;
326     PREINIT:
327 	oid myoid[MAX_OID_LEN];
328 	size_t myoid_len = MAX_OID_LEN;
329         trapd_cb_data *cb_data;
330         netsnmp_trapd_handler *handler = NULL;
331     CODE:
332         {
333             if (!regoid || !perlcallback) {
334                 RETVAL = 0;
335                 return;
336             }
337             if (strcmp(regoid,"all") == 0) {
338                 handler =
339                     netsnmp_add_global_traphandler(NETSNMPTRAPD_POST_HANDLER,
340                                                    perl_trapd_handler);
341             } else if (strcmp(regoid,"default") == 0) {
342                 handler =
343                     netsnmp_add_default_traphandler(perl_trapd_handler);
344             } else if (!snmp_parse_oid(regoid, myoid, &myoid_len)) {
345                 snmp_log(LOG_ERR,
346                          "Failed to parse oid for perl registration: %s\n",
347                          regoid);
348                 RETVAL = 0;
349                 return;
350             } else {
351                 handler =
352                     netsnmp_add_traphandler(perl_trapd_handler,
353                                             myoid, myoid_len);
354             }
355 
356             if (handler) {
357                 cb_data = malloc(sizeof(trapd_cb_data));
358                 cb_data->perl_cb = newSVsv(perlcallback);
359                 handler->handler_data = cb_data;
360                 handler->authtypes = (1 << VACM_VIEW_EXECUTE);
361                 RETVAL = 1;
362             } else {
363                 RETVAL = 0;
364             }
365         }
366     OUTPUT:
367         RETVAL
368