--- Net-SNMP-XS/XS.xs 2009/04/08 10:39:32 1.2 +++ Net-SNMP-XS/XS.xs 2010/07/29 02:52:07 1.13 @@ -4,40 +4,113 @@ // 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; +#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 MAX_OID_STRLEN 4096 + +#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) +{ + 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) +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 @@ -136,34 +209,279 @@ return res; } -MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS +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 ()); +} + +#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 ()); +} -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 +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]; // 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, (U8)w / 40); + *app++ = '.'; + app = write_uv (app, (U8)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); + } + + 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; +} + +///////////////////////////////////////////////////////////////////////////// + +#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 + +PROTOTYPES: ENABLE + +BOOT: + av_type = newAV (); + void -clr_msg () +set_type (int type, SV *cv) CODE: - SvREFCNT_dec (msg); - buf = cur = ""; - len = rem = 0; -#ifdef BENCHMARK - printf ("%f\n", tstamp () - t1);//D -#endif + av_store (av_type, type, SvREFCNT_inc (x_get_cv (cv))); + +void xxx(...) + CODE: + clear_bufobj (); MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message void -_buffer_get (SV *self, int count = -1) +_buffer_append (BUFOBJ self, SV *value) + ALIAS: + _buffer_put = 1 + PPCODE: +{ + STRLEN vlen; + const char *vstr = SvPVbyte (value, vlen); + + 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 (BUFOBJ self, int count = -1) PPCODE: { // grrr. @@ -171,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); } @@ -184,7 +506,7 @@ } U32 -index (SV *self, int ndx = -1) +index (BUFOBJ self, int ndx = -1) CODE: { if (ndx >= 0 && ndx < len) @@ -199,112 +521,210 @@ RETVAL U32 -_process_length (SV *self, ...) +_process_length (BUFOBJ self, ...) ALIAS: - _process_sequence = 1 + _process_sequence = 0 CODE: RETVAL = process_length (); OUTPUT: RETVAL -I32 -_process_integer32 (SV *self, ...) - ALIAS: - _process_counter = 0 - _process_gauge = 0 +SV * +_process_integer32 (BUFOBJ self, ...) CODE: -{ - U32 length = process_length (); - - if (length <= 0) - { - error ("INTEGER32 length equal to zero"); - XSRETURN_UNDEF; - } + RETVAL = process_integer32_sv (); + OUTPUT: + RETVAL - U8 *data = getn (length, 0); +SV * +_process_counter (BUFOBJ self, ...) + ALIAS: + _process_gauge = 0 + _process_timeticks = 0 + CODE: + RETVAL = process_unsigned32_sv (); + OUTPUT: + RETVAL - if (!data) - XSRETURN_UNDEF; +#if IVSIZE >= 8 - if (length > 5 || (length > 4 && data [0])) - { - error ("INTEGER32 length too long"); - XSRETURN_UNDEF; - } +SV * +_process_counter64 (BUFOBJ self, ...) + CODE: + RETVAL = process_unsigned64_sv (); + OUTPUT: + RETVAL - U32 res = data [0] & 0x80 ? 0xffffffff : 0; +#endif - while (length--) - res = (res << 8) | *data++; +SV * +_process_object_identifier (BUFOBJ self, ...) + CODE: + RETVAL = process_object_identifier_sv (); + OUTPUT: + RETVAL - RETVAL = res; -} +SV * +_process_octet_string (BUFOBJ self, ...) + ALIAS: + _process_opaque = 0 + CODE: + RETVAL = process_octet_string_sv (); OUTPUT: RETVAL SV * -_process_object_identifier (SV *self, ...) +_process_ipaddress (BUFOBJ self, ...) CODE: { U32 length = process_length (); - - if (length <= 0) + if (length != 4) { - error ("OBJECT IDENTIFIER length equal to zero"); + error ("IP ADDRESS length not four"); 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); - } + 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_octet_string (SV *self, ...) - ALIAS: - _process_opaque = 0 +process (BUFOBJ self, SV *expected = &PL_sv_undef, SV *found = 0) CODE: { - U32 length = process_length (); + int type; - U8 *data = getn (length, 0); - if (!data) - { - error ("OCTET STRING too long"); - XSRETURN_UNDEF; - } + RETVAL = process_sv (&type); + + if (found) + sv_setiv (found, type); - RETVAL = newSVpvn (data, length); + if (SvOK (expected) && type != SvIV (expected)) + error ("Expected a different type than found"); } OUTPUT: RETVAL +MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU + SV * -_process_ipaddress (SV *self, ...) - CODE: +_process_var_bind_list (BUFOBJ self) + CODE: { - U32 length = process_length (); - if (length != 4) + 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); + + while (cur < end && !errflag) { - error ("IP ADDRESS length not four"); - XSRETURN_UNDEF; + // 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); } + + // sigh - great design to do it here + SV *pdu_type = *hv_fetch ((HV *)cur_bufobj, "_pdu_type" , sizeof ("_pdu_type" ) - 1, 1); - U8 *data = getn (4, "\x00\x00\x00\x00"); - RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]); + if (SvIV (pdu_type) == 0xa8) // REPORT + { + PUSHMARK (SP); + XPUSHs (msg); + PUTBACK; + call_method ("_report_pdu_error", G_VOID | G_DISCARD); + SPAGAIN; + XSRETURN_EMPTY; + } + + 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 +