#include "EXTERN.h" #include "perl.h" #include "XSUB.h" // C99 required #define ASN_BOOLEAN 0x01 #define ASN_INTEGER 0x02 #define ASN_BIT_STR 0x03 #define ASN_OCTET_STR 0x04 #define ASN_NULL 0x05 #define ASN_OBJECT_ID 0x06 #define ASN_SEQUENCE 0x10 #define ASN_SET 0x11 #define ASN_UNIVERSAL 0x00 #define ASN_APPLICATION 0x40 #define ASN_CONTEXT 0x80 #define ASN_PRIVATE 0xc0 #define ASN_PRIMITIVE 0x00 #define ASN_CONSTRUCTOR 0x20 #define ASN_LONG_LEN 0x80 #define ASN_EXTENSION_ID 0x1f #define ASN_BIT8 0x80 #define BENCHMARK static SV *msg; static int errflag; static U8 *buf, *cur; static STRLEN len, rem; static SV * x_get_cv (SV *cb_sv) { HV *st; GV *gvp; CV *cv = sv_2cv (cb_sv, &st, &gvp, 0); if (!cv) croak ("CODE reference expected"); return (SV *)cv; } static void error (const char *msg) { errflag = 1; printf ("<<<%s>>>\n", msg);//D } static int need (int count) { if (count < 0 || (int)rem < count) { error ("Unexpected end of message buffer"); return 0; } return 1; } static U8 * getn (int count, const U8 *errres) { if (!need (count)) return (U8 *)errres; U8 *res = cur; cur += count; rem -= count; return res; } static U8 get8 (void) { if (rem <= 0) { error ("Unexpected end of message buffer"); return 0; } rem--; return *cur++; } static U32 getb (void) { U32 res = 0; for (;;) { U8 c = get8 (); res = (res << 7) | (c & 0x7f); if (!(c & 0x80)) return res; } } #ifdef BENCHMARK static double t1; static double tstamp (void) { struct timeval tv; gettimeofday (&tv, 0); return tv.tv_sec + tv.tv_usec * 0.000001; } #endif static U32 process_length (void) { U32 res = get8 (); if (res & 0x80) { int cnt = res & 0x7f; res = 0; switch (cnt) { case 0: error ("Indefinite ASN.1 lengths not supported"); return 0; default: error ("ASN.1 length too long"); return 0; case 4: res = (res << 8) | get8 (); case 3: res = (res << 8) | get8 (); case 2: res = (res << 8) | get8 (); case 1: res = (res << 8) | get8 (); } } return res; } static AV *av_type; MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS PROTOTYPES: ENABLE BOOT: av_type = newAV (); void set_type (int type, SV *cv) CODE: av_store (av_type, type, SvREFCNT_inc (x_get_cv (cv))); void set_msg (SV *msg_, SV *buf_) CODE: errflag = 0; msg = SvREFCNT_inc (msg_); buf = SvPVbyte (buf_, len); cur = buf; rem = len; #ifdef BENCHMARK t1 = tstamp (); #endif void clr_msg () CODE: SvREFCNT_dec (msg); buf = cur = ""; len = rem = 0; #ifdef BENCHMARK printf ("%f\n", tstamp () - t1);//D #endif MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message void _buffer_get (SV *self, int count = -1) PPCODE: { // grrr. if (count < 0) { hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD); hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD); SV **svp = hv_fetch ((HV *)SvRV (self), "_buffer", 7, 1); XPUSHs (sv_2mortal (newSVsv (*svp))); sv_setpvn (*svp, "", 0); XSRETURN (1); } char *data = getn (count, 0); if (data) XPUSHs (sv_2mortal (newSVpvn (data, count))); } U32 index (SV *self, int ndx = -1) CODE: { if (ndx >= 0 && ndx < len) { cur = buf + ndx; rem = len - ndx; } RETVAL = cur - buf; } OUTPUT: RETVAL U32 _process_length (SV *self, ...) ALIAS: _process_sequence = 0 CODE: RETVAL = process_length (); OUTPUT: RETVAL I32 _process_integer32 (SV *self, ...) ALIAS: _process_counter = 0 _process_gauge = 0 CODE: { U32 length = process_length (); if (length <= 0) { error ("INTEGER32 length equal to zero"); XSRETURN_UNDEF; } U8 *data = getn (length, 0); if (!data) XSRETURN_UNDEF; if (length > 5 || (length > 4 && data [0])) { error ("INTEGER32 length too long"); XSRETURN_UNDEF; } U32 res = data [0] & 0x80 ? 0xffffffff : 0; while (length--) res = (res << 8) | *data++; RETVAL = res; } OUTPUT: RETVAL SV * _process_object_identifier (SV *self, ...) CODE: { U32 length = process_length (); if (length <= 0) { error ("OBJECT IDENTIFIER length equal to zero"); XSRETURN_UNDEF; } U8 *end = cur + length; U32 w = getb (); //TODO: leading_dots RETVAL = newSVpvf (".%d.%d", (int)w / 40, (int)w % 40); while (cur < end) { w = getb (); sv_catpvf (RETVAL, ".%u", (unsigned int)w); } } OUTPUT: RETVAL SV * _process_octet_string (SV *self, ...) ALIAS: _process_opaque = 0 CODE: { U32 length = process_length (); U8 *data = getn (length, 0); if (!data) { error ("OCTET STRING too long"); XSRETURN_UNDEF; } RETVAL = newSVpvn (data, length); } OUTPUT: RETVAL SV * _process_ipaddress (SV *self, ...) CODE: { U32 length = process_length (); if (length != 4) { error ("IP ADDRESS length not four"); XSRETURN_UNDEF; } U8 *data = getn (4, "\x00\x00\x00\x00"); RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]); } OUTPUT: RETVAL SV * process (SV *self, SV *expected = 0, SV *found = 0) PPCODE: { U8 type = get8 (); if (expected && SvOK (expected) && type != SvIV (expected)) { error ("Expected a different type than found"); XSRETURN_UNDEF; } if (type > AvFILLp (av_type) || !SvTYPE (AvARRAY (av_type)[type]) == SVt_PVCV) { sv_dump (AvARRAY (av_type)[type]);//D error ("Unknown ASN.1 type"); XSRETURN_UNDEF; } if (found) sv_setiv (found, type); SV *res; { dSP; PUSHMARK (SP); EXTEND (SP, 2); PUSHs (self); PUSHs (expected); PUTBACK; int count = call_sv (AvARRAY (av_type)[type], G_SCALAR); SPAGAIN; res = count ? TOPs : &PL_sv_undef; } XPUSHs (res); }