Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "EXTERN.h"
- #include "perl.h"
- #include "XSUB.h"
- // C99 required
- //#define BENCHMARK
- #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 *errmsg)
- {
- errflag = 1;
- 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
- 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 ());
- }
- #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)
- {
- 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
- set_type (int type, SV *cv)
- CODE:
- av_store (av_type, type, SvREFCNT_inc (x_get_cv (cv)));
- MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
- void
- _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.
- if (count < 0)
- {
- hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
- hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
- XPUSHs (sv_2mortal (newSVsv (bufsv)));
- sv_setpvn (bufsv, "", 0);
- buf = "";
- cur = buf;
- rem = 0;
- XSRETURN (1);
- }
- char *data = getn (count, 0);
- if (data)
- XPUSHs (sv_2mortal (newSVpvn (data, count)));
- }
- U32
- index (BUFOBJ self, int ndx = -1)
- CODE:
- {
- if (ndx >= 0 && ndx < len)
- {
- cur = buf + ndx;
- rem = len - ndx;
- }
- RETVAL = cur - buf;
- }
- OUTPUT:
- RETVAL
- U32
- _process_length (BUFOBJ self, ...)
- ALIAS:
- _process_sequence = 0
- CODE:
- RETVAL = process_length ();
- OUTPUT:
- RETVAL
- SV *
- _process_integer32 (BUFOBJ self, ...)
- CODE:
- RETVAL = process_integer32_sv ();
- OUTPUT:
- RETVAL
- SV *
- _process_counter (BUFOBJ self, ...)
- ALIAS:
- _process_gauge = 0
- _process_timeticks = 0
- CODE:
- RETVAL = process_unsigned32_sv ();
- OUTPUT:
- RETVAL
- #if IVSIZE >= 8
- SV *
- _process_counter64 (BUFOBJ self, ...)
- CODE:
- RETVAL = process_unsigned64_sv ();
- OUTPUT:
- RETVAL
- #endif
- SV *
- _process_object_identifier (BUFOBJ self, ...)
- CODE:
- RETVAL = process_object_identifier_sv ();
- OUTPUT:
- RETVAL
- SV *
- _process_octet_string (BUFOBJ self, ...)
- ALIAS:
- _process_opaque = 0
- CODE:
- RETVAL = process_octet_string_sv ();
- OUTPUT:
- RETVAL
- SV *
- _process_ipaddress (BUFOBJ 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 (BUFOBJ self, SV *expected = &PL_sv_undef, SV *found = 0)
- CODE:
- {
- int type;
- RETVAL = process_sv (&type);
- if (found)
- sv_setiv (found, type);
- 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_var_bind_list (BUFOBJ self)
- CODE:
- {
- 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)
- {
- // 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);
- 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
Advertisement
Add Comment
Please, Sign In to add comment