--- Net-SNMP-XS/XS.xs 2009/04/09 04:37:05 1.4 +++ Net-SNMP-XS/XS.xs 2009/04/09 10:08:25 1.8 @@ -4,6 +4,8 @@ // C99 required +//#define BENCHMARK + #define ASN_BOOLEAN 0x01 #define ASN_INTEGER32 0x02 #define ASN_OCTET_STRING 0x04 @@ -17,8 +19,6 @@ #define ASN_OPAQUE 0x44 #define ASN_COUNTER64 0x46 -#define BENCHMARK - #define MAX_OID_STRLEN 4096 static SV *msg; @@ -40,11 +40,17 @@ } static void -error (const char *msg) +error (const char *errmsg) { errflag = 1; - printf ("<<<%s>>>\n", msg);//D + dSP; + PUSHMARK (SP); + EXTEND (SP, 2); + PUSHs (msg); + PUSHs (sv_2mortal (newSVpv (errmsg, 0))); + PUTBACK; + call_method ("_error", G_VOID | G_DISCARD); } static int @@ -185,6 +191,52 @@ return newSVuv ((U32)process_integer32 ()); } +#if IVSIZE >= 8 + +static U64TYPE +process_integer64 (void) +{ + U32 length = process_length (); + + if (length <= 0) + { + error ("INTEGER64 length equal to zero"); + return 0; + } + + U8 *data = getn (length, 0); + + if (!data) + return 0; + + if (length > 9 || (length > 8 && data [0])) + { + error ("INTEGER64 length too long"); + return 0; + } + + U64TYPE res = data [0] & 0x80 ? 0xffffffffffffffff : 0; + + while (length--) + res = (res << 8) | *data++; + + return res; +} + +static SV * +process_integer64_sv (void) +{ + return newSViv ((I64TYPE)process_integer64 ()); +} + +static SV * +process_unsigned64_sv (void) +{ + return newSVuv ((U64TYPE)process_integer64 ()); +} + +#endif + static SV * process_octet_string_sv (void) { @@ -199,6 +251,19 @@ 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) { @@ -213,25 +278,84 @@ U8 *end = cur + length; U32 w = getb (); - static char oid[MAX_OID_STRLEN]; - char *buf = oid; + static char oid[MAX_OID_STRLEN]; // must be static + char *app = oid; - if (leading_dot) - *buf++ = '.'; + *app = '.'; app += ! ! leading_dot; + app = write_uv (app, (U8)w / 40); + *app++ = '.'; + app = write_uv (app, (U8)w % 40); - buf += snprintf (buf, oid + sizeof (oid) - buf, "%d.%d", (int)w / 40, (int)w % 40); - - while (cur < end) + // we assume an oid component is never > 64 bytes + while (cur < end && oid + sizeof (oid) - app > 64) { w = getb (); - buf += snprintf (buf, oid + sizeof (oid) - buf, ".%u", (unsigned int)w); + *app++ = '.'; + app = write_uv (app, w); } - return newSVpvn (oid, buf - oid); + return newSVpvn (oid, app - oid); } static AV *av_type; +static SV * +process_sv (int *found) +{ + int type = get8 (); + + *found = type; + + SV *res; + + switch (type) + { + case ASN_OBJECT_IDENTIFIER: + res = process_object_identifier_sv (); + break; + + case ASN_INTEGER32: + res = process_integer32_sv (); + break; + + case ASN_UNSIGNED32: + case ASN_COUNTER32: + case ASN_TIMETICKS: + res = process_unsigned32_sv (); + break; + + case ASN_SEQUENCE: + res = newSVuv (process_length ()); + break; + + case ASN_OCTET_STRING: + case ASN_OPAQUE: + res = process_octet_string_sv (); + break; + + default: + { + if (type > AvFILLp (av_type) || !SvTYPE (AvARRAY (av_type)[type]) == SVt_PVCV) + { + error ("Unknown ASN.1 type"); + return &PL_sv_undef; + } + + dSP; + PUSHMARK (SP); + EXTEND (SP, 2); + PUSHs (msg); + PUSHs (sv_2mortal (newSViv (type))); + PUTBACK; + int count = call_sv (AvARRAY (av_type)[type], G_SCALAR); + SPAGAIN; + res = count ? SvREFCNT_inc (TOPs) : &PL_sv_undef; + } + } + + return errflag ? &PL_sv_undef : res; +} + MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS PROTOTYPES: ENABLE @@ -247,6 +371,10 @@ void set_msg (SV *msg_, SV *buf_) CODE: +{ + if (msg) + croak ("recursive invocation of Net::SNMP::XS parser is not supported"); + errflag = 0; leading_dot = -1; msg = SvREFCNT_inc (msg_); @@ -256,12 +384,13 @@ #ifdef BENCHMARK t1 = tstamp (); #endif +} void clr_msg () CODE: - SvREFCNT_dec (msg); - buf = cur = ""; + SvREFCNT_dec (msg); msg = 0; + buf = cur = (U8 *)""; len = rem = 0; #ifdef BENCHMARK printf ("%f\n", tstamp () - t1);//D @@ -331,6 +460,17 @@ OUTPUT: RETVAL +#if IVSIZE >= 8 + +SV * +_process_counter64 (SV *self, ...) + CODE: + RETVAL = process_unsigned64_sv (); + OUTPUT: + RETVAL + +#endif + SV * _process_object_identifier (SV *self, ...) CODE: @@ -365,73 +505,21 @@ RETVAL SV * -process (SV *self, SV *expected = 0, SV *found = 0) - PPCODE: +process (SV *self, SV *expected = &PL_sv_undef, SV *found = 0) + CODE: { - U8 type = get8 (); + int type; - 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; - } + RETVAL = process_sv (&type); 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 (SvOK (expected) && type != SvIV (expected)) + error ("Expected a different type than found"); } - -#if 0 + OUTPUT: + RETVAL MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU @@ -439,67 +527,42 @@ _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; - } + if (get8 () != ASN_SEQUENCE) + error ("SEQUENCE expected at beginning of VarBindList"); + int seqlen = process_length (); + U8 *end = cur + seqlen; + + HV *list = newHV (); + AV *names = newAV (); + HV *types = newHV (); + + hv_store ((HV *)SvRV (self), "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0); + hv_store ((HV *)SvRV (self), "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0); + hv_store ((HV *)SvRV (self), "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0); - # 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. + while (cur < end && !errflag) + { + // SEQUENCE ObjectName ObjectSyntax + if (get8 () != ASN_SEQUENCE) + error ("SEQUENCE expected at beginning of VarBind"); + process_length (); + + if (get8 () != ASN_OBJECT_IDENTIFIER) + error ("OBJECT IDENTIFIER expected at beginning of VarBind"); + int type, oidlen; + SV *oid = process_object_identifier_sv (); + SV *val = process_sv (&type); + + hv_store_ent (types, oid, newSViv (type), 0); + hv_store_ent (list , oid, val, 0); + av_push (names, oid); + } - return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT); + //return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT); - # Return the var_bind_list hash - $this->{_var_bind_list}; + RETVAL = newRV_inc ((SV *)list); } + OUTPUT: + RETVAL -#endif