--- Net-SNMP-XS/XS.xs 2009/04/09 04:49:16 1.5 +++ Net-SNMP-XS/XS.xs 2010/07/29 02:52:07 1.13 @@ -4,6 +4,8 @@ // C99 required +//#define BENCHMARK + #define ASN_BOOLEAN 0x01 #define ASN_INTEGER32 0x02 #define ASN_OCTET_STRING 0x04 @@ -17,15 +19,70 @@ #define ASN_OPAQUE 0x44 #define ASN_COUNTER64 0x46 -#define BENCHMARK - #define MAX_OID_STRLEN 4096 -static SV *msg; +#define HAVE_VERSIONSORT defined (_GNU_SOURCE) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 + +static SV *cur_bufobj; +static SV *msg, *bufsv; static int errflag, leading_dot; static U8 *buf, *cur; static STRLEN len, rem; +typedef SV *BUFOBJ; + +///////////////////////////////////////////////////////////////////////////// + +#if 0 + if (msg) + croak ("recursive invocation of Net::SNMP::XS parser is not supported"); + + +void +clr_msg () + CODE: + SvREFCNT_dec (msg); msg = 0; + buf = cur = (U8 *)""; + len = rem = 0; +#endif + +static void +clear_bufobj (void) +{ + // serialise our state back + if (msg && SvROK (msg)) + { + SV *idx_sv = *hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1); + sv_setiv (idx_sv, cur - buf); + + SvREFCNT_dec (msg); + msg = 0; + cur_bufobj = 0; + } +} + +static void +switch_bufobj (BUFOBJ neu) +{ + clear_bufobj (); + + msg = newSVsv (neu); + cur_bufobj = SvRV (msg); + sv_rvweaken (msg); + + errflag = 0; + leading_dot = -1; + + IV index = SvIV (*hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1)); + bufsv = *hv_fetch ((HV *)cur_bufobj, "_buffer", sizeof ("_buffer") - 1, 1); + + buf = SvPVbyte (bufsv, len); + cur = buf + index; + rem = len - index; +} + +///////////////////////////////////////////////////////////////////////////// + static SV * x_get_cv (SV *cb_sv) { @@ -40,11 +97,20 @@ } static void -error (const char *msg) +error (const char *errmsg) { errflag = 1; - printf ("<<<%s>>>\n", msg);//D + if (!msg) + croak ("Net::SNMP::XS fatal error, parser called without parsing context"); + + 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 +251,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) { @@ -226,13 +338,16 @@ U8 *end = cur + length; U32 w = getb (); - static char oid[MAX_OID_STRLEN]; + static char oid[MAX_OID_STRLEN]; // must be static char *app = oid; + if (leading_dot < 0) + leading_dot = SvTRUE (*hv_fetch ((HV *)SvRV (msg), "_leading_dot", sizeof ("_leading_dot") - 1, 1)); + *app = '.'; app += ! ! leading_dot; - app = write_uv (app, w / 40); + app = write_uv (app, (U8)w / 40); *app++ = '.'; - app = write_uv (app, w % 40); + app = write_uv (app, (U8)w % 40); // we assume an oid component is never > 64 bytes while (cur < end && oid + sizeof (oid) - app > 64) @@ -241,20 +356,86 @@ *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) + return newSVpvn (oid, app - oid); +} + +static AV *av_type; + +static SV * +process_sv (int *found) +{ + int type = get8 (); + + *found = type; + + SV *res; + + switch (type) { - w = getb (); - buf += snprintf (buf, oid + sizeof (oid) - buf, ".%u", (unsigned int)w); + 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; + } } -#endif - return newSVpvn (oid, app - oid); + return errflag ? &PL_sv_undef : res; } -static AV *av_type; +///////////////////////////////////////////////////////////////////////////// + +#if HAVE_VERSIONSORT + +static int +oid_lex_cmp (const void *a_, const void *b_) +{ + const char *a = SvPVX (*(SV **)a_); + const char *b = SvPVX (*(SV **)b_); + + a += *a == '.'; + b += *b == '.'; + + return strverscmp (a, b); +} + +#endif MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS @@ -268,33 +449,39 @@ 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 xxx(...) + CODE: + clear_bufobj (); + +MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message void -clr_msg () - CODE: - SvREFCNT_dec (msg); - buf = cur = ""; - len = rem = 0; -#ifdef BENCHMARK - printf ("%f\n", tstamp () - t1);//D -#endif +_buffer_append (BUFOBJ self, SV *value) + ALIAS: + _buffer_put = 1 + PPCODE: +{ + STRLEN vlen; + const char *vstr = SvPVbyte (value, vlen); -MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message + if (ix) + sv_insert (bufsv, 0, 0, vstr, vlen); + else + sv_catpvn (bufsv, vstr, vlen); + + buf = SvPVbyte (bufsv, len); + cur = buf; + rem = len; + + SV *len_sv = *hv_fetch ((HV *)cur_bufobj, "_length", sizeof ("_length") - 1, 1); + sv_setiv (len_sv, len); + + // some callers test for defined'ness of the returnvalue. *sigh* + XPUSHs (&PL_sv_yes); +} void -_buffer_get (SV *self, int count = -1) +_buffer_get (BUFOBJ self, int count = -1) PPCODE: { // grrr. @@ -302,9 +489,13 @@ { 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); + XPUSHs (sv_2mortal (newSVsv (bufsv))); + sv_setpvn (bufsv, "", 0); + + buf = ""; + cur = buf; + rem = 0; + XSRETURN (1); } @@ -315,7 +506,7 @@ } U32 -index (SV *self, int ndx = -1) +index (BUFOBJ self, int ndx = -1) CODE: { if (ndx >= 0 && ndx < len) @@ -330,7 +521,7 @@ RETVAL U32 -_process_length (SV *self, ...) +_process_length (BUFOBJ self, ...) ALIAS: _process_sequence = 0 CODE: @@ -339,14 +530,14 @@ RETVAL SV * -_process_integer32 (SV *self, ...) +_process_integer32 (BUFOBJ self, ...) CODE: RETVAL = process_integer32_sv (); OUTPUT: RETVAL SV * -_process_counter (SV *self, ...) +_process_counter (BUFOBJ self, ...) ALIAS: _process_gauge = 0 _process_timeticks = 0 @@ -355,15 +546,26 @@ OUTPUT: RETVAL +#if IVSIZE >= 8 + +SV * +_process_counter64 (BUFOBJ self, ...) + CODE: + RETVAL = process_unsigned64_sv (); + OUTPUT: + RETVAL + +#endif + SV * -_process_object_identifier (SV *self, ...) +_process_object_identifier (BUFOBJ self, ...) CODE: RETVAL = process_object_identifier_sv (); OUTPUT: RETVAL SV * -_process_octet_string (SV *self, ...) +_process_octet_string (BUFOBJ self, ...) ALIAS: _process_opaque = 0 CODE: @@ -372,7 +574,7 @@ RETVAL SV * -_process_ipaddress (SV *self, ...) +_process_ipaddress (BUFOBJ self, ...) CODE: { U32 length = process_length (); @@ -389,141 +591,140 @@ RETVAL SV * -process (SV *self, SV *expected = 0, SV *found = 0) - PPCODE: +process (BUFOBJ 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 SV * -_process_var_bind_list (SV *self) +_process_var_bind_list (BUFOBJ 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. + 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 *)cur_bufobj, "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0); + hv_store ((HV *)cur_bufobj, "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0); + hv_store ((HV *)cur_bufobj, "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0); - 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); + // sigh - great design to do it here + SV *pdu_type = *hv_fetch ((HV *)cur_bufobj, "_pdu_type" , sizeof ("_pdu_type" ) - 1, 1); + + if (SvIV (pdu_type) == 0xa8) // REPORT + { + PUSHMARK (SP); + XPUSHs (msg); + PUTBACK; + call_method ("_report_pdu_error", G_VOID | G_DISCARD); + SPAGAIN; + XSRETURN_EMPTY; + } - # Return the var_bind_list hash - $this->{_var_bind_list}; + RETVAL = newRV_inc ((SV *)list); } + OUTPUT: + RETVAL + +MODULE = Net::SNMP::XS PACKAGE = Net::SNMP + +void +oid_base_match (SV *base_, SV *oid_) + PROTOTYPE: $$ + ALIAS: + oid_context_match = 0 + PPCODE: +{ + if (!SvOK (base_) || !SvOK (oid_)) + XSRETURN_NO; + + STRLEN blen, olen; + char *base = SvPVbyte (base_, blen); + char *oid = SvPVbyte (oid_ , olen); + + blen -= *base == '.'; base += *base == '.'; + olen -= *base == '.'; oid += *oid == '.'; + + if (olen < blen) + XSRETURN_NO; + + if (memcmp (base, oid, blen)) + XSRETURN_NO; + + if (oid [blen] && oid [blen] != '.') + XSRETURN_NO; + + XSRETURN_YES; +} + +#if HAVE_VERSIONSORT + +void +oid_lex_sort (...) + PROTOTYPE: @ + PPCODE: +{ + // make sure SvPVX is valid + int i; + for (i = items; i--; ) + { + SV *sv = ST (i); + + if (SvTYPE (sv) < SVt_PV || SvTYPE (sv) == SVt_PVAV && SvTYPE (sv) == SVt_PVHV) + SvPV_force_nolen (sv); + } + + qsort (&ST (0), items, sizeof (SV *), oid_lex_cmp); + + EXTEND (SP, items); + // we cheat somewhat by not returning copies here + for (i = 0; i < items; ++i) + PUSHs (sv_2mortal (SvREFCNT_inc (ST (i)))); +} + +int +_index_cmp (const char *a, const char *b) + PROTOTYPE: $$ + CODE: + RETVAL = strverscmp (a, b); + OUTPUT: + RETVAL #endif