--- Net-SNMP-XS/XS.xs 2009/04/08 10:30:46 1.1 +++ Net-SNMP-XS/XS.xs 2009/04/09 04:49:16 1.5 @@ -4,34 +4,41 @@ // 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 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; +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) { @@ -136,19 +143,143 @@ 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 char * +write_uv (char *buf, U32 u) +{ + // the one-digit case is absolutely predominant + if (u < 10) + *buf++ = u + '0'; + else + buf += sprintf (buf, "%u", (unsigned int)u); + + return buf; +} + +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 *app = oid; + + *app = '.'; app += ! ! leading_dot; + app = write_uv (app, w / 40); + *app++ = '.'; + app = write_uv (app, w % 40); + + // we assume an oid component is never > 64 bytes + while (cur < end && oid + sizeof (oid) - app > 64) + { + w = getb (); + *app++ = '.'; + app = write_uv (app, w); + } +#if 0 + 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); + } +#endif + + return newSVpvn (oid, app - 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; - msg = SvREFCNT_inc (msg_); - buf = SvPVbyte (buf_, len); - cur = buf; - rem = len; - #ifdef BENCHMARK - t1 = tstamp (); - #endif + 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 () @@ -156,9 +287,9 @@ SvREFCNT_dec (msg); buf = cur = ""; len = rem = 0; - #ifdef BENCHMARK +#ifdef BENCHMARK printf ("%f\n", tstamp () - t1);//D - #endif +#endif MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message @@ -201,73 +332,33 @@ U32 _process_length (SV *self, ...) ALIAS: - _process_sequence = 1 + _process_sequence = 0 CODE: RETVAL = process_length (); OUTPUT: RETVAL -I32 +SV * _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 = process_integer32_sv (); + OUTPUT: + RETVAL - RETVAL = res; -} +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: -{ - 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); - } -} + RETVAL = process_object_identifier_sv (); OUTPUT: RETVAL @@ -276,18 +367,7 @@ 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); -} + RETVAL = process_octet_string_sv (); OUTPUT: RETVAL @@ -308,3 +388,142 @@ 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 +