#include "EXTERN.h" #include "perl.h" #include "XSUB.h" // C99 required #define ASN_BOOLEAN 0x01 #define ASN_INTEGER32 0x02 #define ASN_OCTET_STRING 0x04 #define ASN_NULL 0x05 #define ASN_OBJECT_IDENTIFIER 0x06 #define ASN_SEQUENCE 0x30 #define ASN_IPADDRESS 0x40 #define ASN_COUNTER32 0x41 #define ASN_UNSIGNED32 0x42 #define ASN_TIMETICKS 0x43 #define ASN_OPAQUE 0x44 #define ASN_COUNTER64 0x46 #define BENCHMARK #define MAX_OID_STRLEN 4096 static SV *msg; static int errflag, leading_dot; 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 U32 process_integer32 (void) { U32 length = process_length (); if (length <= 0) { error ("INTEGER32 length equal to zero"); return 0; } U8 *data = getn (length, 0); if (!data) return 0; if (length > 5 || (length > 4 && data [0])) { error ("INTEGER32 length too long"); return 0; } U32 res = data [0] & 0x80 ? 0xffffffff : 0; while (length--) res = (res << 8) | *data++; return res; } static SV * process_integer32_sv (void) { return newSViv ((I32)process_integer32 ()); } static SV * process_unsigned32_sv (void) { return newSVuv ((U32)process_integer32 ()); } static SV * process_octet_string_sv (void) { U32 length = process_length (); U8 *data = getn (length, 0); if (!data) { error ("OCTET STRING too long"); return &PL_sv_undef; } return newSVpvn (data, length); } static SV * process_object_identifier_sv (void) { U32 length = process_length (); if (length <= 0) { error ("OBJECT IDENTIFIER length equal to zero"); return &PL_sv_undef; } U8 *end = cur + length; U32 w = getb (); static char oid[MAX_OID_STRLEN]; char *buf = oid; if (leading_dot) *buf++ = '.'; buf += snprintf (buf, oid + sizeof (oid) - buf, "%d.%d", (int)w / 40, (int)w % 40); while (cur < end) { w = getb (); buf += snprintf (buf, oid + sizeof (oid) - buf, ".%u", (unsigned int)w); } return newSVpvn (oid, buf - oid); } 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; leading_dot = -1; 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 SV * _process_integer32 (SV *self, ...) CODE: RETVAL = process_integer32_sv (); OUTPUT: RETVAL SV * _process_counter (SV *self, ...) ALIAS: _process_gauge = 0 _process_timeticks = 0 CODE: RETVAL = process_unsigned32_sv (); OUTPUT: RETVAL SV * _process_object_identifier (SV *self, ...) CODE: RETVAL = process_object_identifier_sv (); OUTPUT: RETVAL SV * _process_octet_string (SV *self, ...) ALIAS: _process_opaque = 0 CODE: RETVAL = process_octet_string_sv (); 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); //TODO: switch based on type, to avoid calling overhead SV *res; switch (type) { case ASN_OBJECT_IDENTIFIER: res = sv_2mortal (process_object_identifier_sv ()); break; case ASN_INTEGER32: res = sv_2mortal (process_integer32_sv ()); break; case ASN_UNSIGNED32: case ASN_COUNTER32: case ASN_TIMETICKS: res = sv_2mortal (process_unsigned32_sv ()); break; case ASN_SEQUENCE: res = sv_2mortal (newSVuv (process_length ())); break; case ASN_OCTET_STRING: case ASN_OPAQUE: res = sv_2mortal (process_octet_string_sv ()); break; default: { 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 (errflag ? &PL_sv_undef : res); } #if 0 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU SV * _process_var_bind_list (SV *self) CODE: { # VarBindList::=SEQUENCE if (!defined($value = $this->process(SEQUENCE))) { return $this->_error; } # Using the length of the VarBindList SEQUENCE, # calculate the end index. my $end = $this->index + $value; $this->{_var_bind_list} = {}; $this->{_var_bind_names} = []; $this->{_var_bind_types} = {}; my ($oid, $type); while ($this->index < $end) { # VarBind::=SEQUENCE if (!defined($this->process(SEQUENCE))) { return $this->_error; } # name::=ObjectName if (!defined($oid = $this->process(OBJECT_IDENTIFIER))) { return $this->_error; } # value::=ObjectSyntax if (!defined($value = $this->process(undef, $type))) { return $this->_error; } # Create a hash consisting of the OBJECT IDENTIFIER as a # key and the ObjectSyntax as the value. If there is a # duplicate OBJECT IDENTIFIER in the VarBindList, we pad # that OBJECT IDENTIFIER with spaces to make a unique # key in the hash. while (exists($this->{_var_bind_list}->{$oid})) { $oid .= ' '; # Pad with spaces } DEBUG_INFO('{ %s => %s: %s }', $oid, asn1_itoa($type), $value); $this->{_var_bind_list}->{$oid} = $value; $this->{_var_bind_types}->{$oid} = $type; # Create an array with the ObjectName OBJECT IDENTIFIERs # so that the order in which the VarBinds where encoded # in the PDU can be retrieved later. push(@{$this->{_var_bind_names}}, $oid); } # Return an error based on the contents of the VarBindList # if we received a Report-PDU. return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT); # Return the var_bind_list hash $this->{_var_bind_list}; } #endif