ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.24
Committed: Tue May 7 21:42:36 2019 UTC (5 years ago) by root
Branch: MAIN
CVS Tags: rel-1_34, HEAD
Changes since 1.23: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #include "EXTERN.h"
2     #include "perl.h"
3     #include "XSUB.h"
4    
5     // C99 required
6    
7 root 1.8 //#define BENCHMARK
8 root 1.7
9 root 1.18 enum {
10 root 1.20 // ASN_TAG
11 root 1.18 ASN_BOOLEAN = 0x01,
12     ASN_INTEGER32 = 0x02,
13 root 1.19 ASN_BIT_STRING = 0x03,
14 root 1.18 ASN_OCTET_STRING = 0x04,
15     ASN_NULL = 0x05,
16     ASN_OBJECT_IDENTIFIER = 0x06,
17 root 1.20 ASN_SEQUENCE = 0x10,
18 root 1.19
19     ASN_TAG_BER = 0x1f,
20     ASN_TAG_MASK = 0x1f,
21    
22 root 1.20 // primitive/constructed
23 root 1.19 ASN_CONSTRUCTED = 0x20,
24    
25 root 1.20 // ASN_CLASS
26 root 1.19 ASN_UNIVERSAL = 0x00,
27     ASN_APPLICATION = 0x40,
28     ASN_CONTEXT = 0x80,
29     ASN_PRIVATE = 0xc0,
30    
31     ASN_CLASS_MASK = 0xc0,
32     ASN_CLASS_SHIFT = 6,
33    
34 root 1.20 // ASN_APPLICATION
35     ASN_IPADDRESS = 0x00,
36     ASN_COUNTER32 = 0x01,
37     ASN_UNSIGNED32 = 0x02,
38     ASN_TIMETICKS = 0x03,
39     ASN_OPAQUE = 0x04,
40     ASN_COUNTER64 = 0x06,
41     };
42    
43 root 1.4 #define MAX_OID_STRLEN 4096
44    
45 root 1.10 #define HAVE_VERSIONSORT defined (_GNU_SOURCE) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
46    
47 root 1.12 static SV *cur_bufobj;
48 root 1.13 static SV *msg, *bufsv;
49 root 1.4 static int errflag, leading_dot;
50 root 1.1 static U8 *buf, *cur;
51     static STRLEN len, rem;
52    
53 root 1.12 typedef SV *BUFOBJ;
54    
55     /////////////////////////////////////////////////////////////////////////////
56    
57     #if 0
58     if (msg)
59     croak ("recursive invocation of Net::SNMP::XS parser is not supported");
60    
61    
62     void
63     clr_msg ()
64     CODE:
65     SvREFCNT_dec (msg); msg = 0;
66     buf = cur = (U8 *)"";
67     len = rem = 0;
68     #endif
69    
70     static void
71 root 1.13 clear_bufobj (void)
72 root 1.12 {
73     // serialise our state back
74     if (msg && SvROK (msg))
75     {
76     SV *idx_sv = *hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1);
77     sv_setiv (idx_sv, cur - buf);
78 root 1.15 }
79 root 1.13
80 root 1.15 SvREFCNT_dec (msg);
81     msg = 0;
82     cur_bufobj = 0;
83 root 1.13 }
84    
85     static void
86     switch_bufobj (BUFOBJ neu)
87     {
88     clear_bufobj ();
89 root 1.12
90     msg = newSVsv (neu);
91     cur_bufobj = SvRV (msg);
92     sv_rvweaken (msg);
93    
94     errflag = 0;
95     leading_dot = -1;
96 root 1.13
97     IV index = SvIV (*hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1));
98     bufsv = *hv_fetch ((HV *)cur_bufobj, "_buffer", sizeof ("_buffer") - 1, 1);
99    
100     buf = SvPVbyte (bufsv, len);
101     cur = buf + index;
102     rem = len - index;
103 root 1.12 }
104    
105     /////////////////////////////////////////////////////////////////////////////
106    
107 root 1.3 static SV *
108     x_get_cv (SV *cb_sv)
109     {
110     HV *st;
111     GV *gvp;
112     CV *cv = sv_2cv (cb_sv, &st, &gvp, 0);
113    
114     if (!cv)
115     croak ("CODE reference expected");
116    
117     return (SV *)cv;
118     }
119    
120 root 1.1 static void
121 root 1.8 error (const char *errmsg)
122 root 1.1 {
123     errflag = 1;
124    
125 root 1.9 if (!msg)
126     croak ("Net::SNMP::XS fatal error, parser called without parsing context");
127    
128 root 1.8 dSP;
129     PUSHMARK (SP);
130     EXTEND (SP, 2);
131     PUSHs (msg);
132     PUSHs (sv_2mortal (newSVpv (errmsg, 0)));
133     PUTBACK;
134     call_method ("_error", G_VOID | G_DISCARD);
135 root 1.1 }
136    
137     static int
138     need (int count)
139     {
140     if (count < 0 || (int)rem < count)
141     {
142     error ("Unexpected end of message buffer");
143     return 0;
144     }
145    
146     return 1;
147     }
148    
149     static U8 *
150     getn (int count, const U8 *errres)
151     {
152     if (!need (count))
153     return (U8 *)errres;
154    
155     U8 *res = cur;
156    
157     cur += count;
158     rem -= count;
159    
160     return res;
161     }
162    
163     static U8
164     get8 (void)
165     {
166     if (rem <= 0)
167     {
168     error ("Unexpected end of message buffer");
169     return 0;
170     }
171    
172     rem--;
173     return *cur++;
174     }
175    
176     static U32
177     getb (void)
178     {
179     U32 res = 0;
180    
181     for (;;)
182     {
183     U8 c = get8 ();
184     res = (res << 7) | (c & 0x7f);
185    
186     if (!(c & 0x80))
187     return res;
188     }
189     }
190    
191     #ifdef BENCHMARK
192     static double t1;
193    
194     static double
195     tstamp (void)
196     {
197     struct timeval tv;
198     gettimeofday (&tv, 0);
199     return tv.tv_sec + tv.tv_usec * 0.000001;
200     }
201     #endif
202    
203     static U32
204     process_length (void)
205     {
206     U32 res = get8 ();
207    
208     if (res & 0x80)
209     {
210     int cnt = res & 0x7f;
211     res = 0;
212    
213     switch (cnt)
214     {
215     case 0:
216     error ("Indefinite ASN.1 lengths not supported");
217     return 0;
218    
219     default:
220     error ("ASN.1 length too long");
221     return 0;
222    
223     case 4: res = (res << 8) | get8 ();
224     case 3: res = (res << 8) | get8 ();
225     case 2: res = (res << 8) | get8 ();
226     case 1: res = (res << 8) | get8 ();
227     }
228     }
229    
230     return res;
231     }
232    
233 root 1.4 static U32
234     process_integer32 (void)
235     {
236     U32 length = process_length ();
237    
238     if (length <= 0)
239     {
240     error ("INTEGER32 length equal to zero");
241     return 0;
242     }
243    
244     U8 *data = getn (length, 0);
245    
246     if (!data)
247     return 0;
248    
249     if (length > 5 || (length > 4 && data [0]))
250     {
251     error ("INTEGER32 length too long");
252     return 0;
253     }
254    
255     U32 res = data [0] & 0x80 ? 0xffffffff : 0;
256    
257     while (length--)
258     res = (res << 8) | *data++;
259    
260     return res;
261     }
262    
263     static SV *
264     process_integer32_sv (void)
265     {
266     return newSViv ((I32)process_integer32 ());
267     }
268    
269     static SV *
270     process_unsigned32_sv (void)
271     {
272     return newSVuv ((U32)process_integer32 ());
273     }
274    
275 root 1.8 #if IVSIZE >= 8
276    
277     static U64TYPE
278     process_integer64 (void)
279     {
280     U32 length = process_length ();
281    
282     if (length <= 0)
283     {
284     error ("INTEGER64 length equal to zero");
285     return 0;
286     }
287    
288     U8 *data = getn (length, 0);
289    
290     if (!data)
291     return 0;
292    
293 root 1.23 if (length > 8 + !data [0])
294 root 1.8 {
295     error ("INTEGER64 length too long");
296     return 0;
297     }
298    
299 root 1.23 U64TYPE res = data [0] & 0x80 ? -1 : 0;
300 root 1.8
301     while (length--)
302     res = (res << 8) | *data++;
303    
304     return res;
305     }
306    
307     static SV *
308     process_integer64_sv (void)
309     {
310     return newSViv ((I64TYPE)process_integer64 ());
311     }
312    
313     static SV *
314     process_unsigned64_sv (void)
315     {
316     return newSVuv ((U64TYPE)process_integer64 ());
317     }
318    
319     #endif
320    
321 root 1.4 static SV *
322     process_octet_string_sv (void)
323     {
324     U32 length = process_length ();
325    
326     U8 *data = getn (length, 0);
327     if (!data)
328     {
329     error ("OCTET STRING too long");
330     return &PL_sv_undef;
331     }
332    
333     return newSVpvn (data, length);
334     }
335 root 1.5
336     static char *
337     write_uv (char *buf, U32 u)
338     {
339 root 1.23 // the one-digit case is absolutely predominant, so this pays off (hopefully)
340 root 1.5 if (u < 10)
341     *buf++ = u + '0';
342     else
343 root 1.23 {
344     // this *could* be done much faster using branchless fixed-point arithmetics
345     char *beg = buf;
346    
347     do
348     {
349     *buf++ = u % 10 + '0';
350     u /= 10;
351     }
352     while (u);
353    
354     // reverse digits
355     char *ptr = buf;
356     while (--ptr > beg)
357     {
358     char c = *ptr;
359     *ptr = *beg;
360     *beg = c;
361     ++beg;
362     }
363     }
364 root 1.5
365     return buf;
366     }
367    
368 root 1.7 static SV *
369     process_object_identifier_sv (void)
370 root 1.4 {
371     U32 length = process_length ();
372    
373     if (length <= 0)
374     {
375     error ("OBJECT IDENTIFIER length equal to zero");
376 root 1.8 return &PL_sv_undef;
377 root 1.4 }
378    
379     U8 *end = cur + length;
380     U32 w = getb ();
381    
382 root 1.6 static char oid[MAX_OID_STRLEN]; // must be static
383 root 1.5 char *app = oid;
384 root 1.4
385 root 1.11 if (leading_dot < 0)
386     leading_dot = SvTRUE (*hv_fetch ((HV *)SvRV (msg), "_leading_dot", sizeof ("_leading_dot") - 1, 1));
387    
388 root 1.5 *app = '.'; app += ! ! leading_dot;
389 root 1.23
390     {
391     UV w1, w2;
392    
393     if (w < 2 * 40)
394     (w1 = w / 40), (w2 = w % 40);
395     else
396     (w1 = 2), (w2 = w - 2 * 40);
397    
398     app = write_uv (app, w1);
399     *app++ = '.';
400     app = write_uv (app, w2);
401     }
402 root 1.4
403 root 1.5 // we assume an oid component is never > 64 bytes
404     while (cur < end && oid + sizeof (oid) - app > 64)
405     {
406     w = getb ();
407     *app++ = '.';
408     app = write_uv (app, w);
409     }
410 root 1.4
411 root 1.7 return newSVpvn (oid, app - oid);
412 root 1.6 }
413    
414     static AV *av_type;
415    
416     static SV *
417     process_sv (int *found)
418     {
419     int type = get8 ();
420    
421     *found = type;
422    
423     SV *res;
424    
425     switch (type)
426 root 1.4 {
427 root 1.6 case ASN_OBJECT_IDENTIFIER:
428     res = process_object_identifier_sv ();
429     break;
430    
431     case ASN_INTEGER32:
432     res = process_integer32_sv ();
433     break;
434    
435 root 1.20 case ASN_APPLICATION | ASN_UNSIGNED32:
436     case ASN_APPLICATION | ASN_COUNTER32:
437     case ASN_APPLICATION | ASN_TIMETICKS:
438 root 1.6 res = process_unsigned32_sv ();
439     break;
440    
441 root 1.20 case ASN_SEQUENCE | ASN_CONSTRUCTED:
442 root 1.6 res = newSVuv (process_length ());
443     break;
444    
445     case ASN_OCTET_STRING:
446 root 1.20 case ASN_APPLICATION | ASN_OPAQUE:
447 root 1.6 res = process_octet_string_sv ();
448     break;
449    
450     default:
451     {
452 root 1.16 if (type > AvFILLp (av_type)
453     || AvARRAY (av_type)[type] == 0
454     || AvARRAY (av_type)[type] == &PL_sv_undef)
455 root 1.6 {
456     error ("Unknown ASN.1 type");
457     return &PL_sv_undef;
458     }
459    
460     dSP;
461     PUSHMARK (SP);
462     EXTEND (SP, 2);
463     PUSHs (msg);
464     PUSHs (sv_2mortal (newSViv (type)));
465     PUTBACK;
466     int count = call_sv (AvARRAY (av_type)[type], G_SCALAR);
467     SPAGAIN;
468     res = count ? SvREFCNT_inc (TOPs) : &PL_sv_undef;
469     }
470 root 1.4 }
471    
472 root 1.6 return errflag ? &PL_sv_undef : res;
473 root 1.4 }
474    
475 root 1.10 /////////////////////////////////////////////////////////////////////////////
476    
477     #if HAVE_VERSIONSORT
478    
479     static int
480     oid_lex_cmp (const void *a_, const void *b_)
481     {
482     const char *a = SvPVX (*(SV **)a_);
483     const char *b = SvPVX (*(SV **)b_);
484    
485     a += *a == '.';
486     b += *b == '.';
487    
488     return strverscmp (a, b);
489     }
490    
491     #endif
492    
493 root 1.1 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
494    
495 root 1.3 PROTOTYPES: ENABLE
496    
497     BOOT:
498 root 1.21 av_type = newAV ();
499 root 1.3
500     void
501     set_type (int type, SV *cv)
502     CODE:
503 root 1.16 cv = x_get_cv (cv);
504     assert (SvTYPE (cv) == SVt_PVCV);
505     av_store (av_type, type, SvREFCNT_inc_NN (cv));
506 root 1.3
507 root 1.13 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
508    
509 root 1.1 void
510 root 1.13 _buffer_append (BUFOBJ self, SV *value)
511     ALIAS:
512     _buffer_put = 1
513     PPCODE:
514     {
515     STRLEN vlen;
516     const char *vstr = SvPVbyte (value, vlen);
517    
518     if (ix)
519     sv_insert (bufsv, 0, 0, vstr, vlen);
520     else
521     sv_catpvn (bufsv, vstr, vlen);
522    
523     buf = SvPVbyte (bufsv, len);
524     cur = buf;
525     rem = len;
526 root 1.1
527 root 1.13 SV *len_sv = *hv_fetch ((HV *)cur_bufobj, "_length", sizeof ("_length") - 1, 1);
528     sv_setiv (len_sv, len);
529 root 1.1
530 root 1.13 // some callers test for defined'ness of the returnvalue. *sigh*
531     XPUSHs (&PL_sv_yes);
532     }
533 root 1.1
534     void
535 root 1.12 _buffer_get (BUFOBJ self, int count = -1)
536 root 1.1 PPCODE:
537     {
538     // grrr.
539     if (count < 0)
540     {
541     hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
542     hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
543 root 1.13 XPUSHs (sv_2mortal (newSVsv (bufsv)));
544     sv_setpvn (bufsv, "", 0);
545    
546     buf = "";
547     cur = buf;
548     rem = 0;
549    
550 root 1.1 XSRETURN (1);
551     }
552    
553     char *data = getn (count, 0);
554    
555     if (data)
556     XPUSHs (sv_2mortal (newSVpvn (data, count)));
557     }
558    
559     U32
560 root 1.12 index (BUFOBJ self, int ndx = -1)
561 root 1.1 CODE:
562     {
563     if (ndx >= 0 && ndx < len)
564     {
565     cur = buf + ndx;
566     rem = len - ndx;
567     }
568    
569     RETVAL = cur - buf;
570     }
571 root 1.18 OUTPUT: RETVAL
572 root 1.1
573     U32
574 root 1.12 _process_length (BUFOBJ self, ...)
575 root 1.1 ALIAS:
576 root 1.3 _process_sequence = 0
577 root 1.1 CODE:
578     RETVAL = process_length ();
579 root 1.18 OUTPUT: RETVAL
580 root 1.1
581 root 1.4 SV *
582 root 1.12 _process_integer32 (BUFOBJ self, ...)
583 root 1.4 CODE:
584     RETVAL = process_integer32_sv ();
585 root 1.18 OUTPUT: RETVAL
586 root 1.4
587     SV *
588 root 1.12 _process_counter (BUFOBJ self, ...)
589 root 1.1 ALIAS:
590 root 1.4 _process_gauge = 0
591     _process_timeticks = 0
592 root 1.1 CODE:
593 root 1.4 RETVAL = process_unsigned32_sv ();
594 root 1.18 OUTPUT: RETVAL
595 root 1.1
596 root 1.8 #if IVSIZE >= 8
597    
598     SV *
599 root 1.12 _process_counter64 (BUFOBJ self, ...)
600 root 1.8 CODE:
601     RETVAL = process_unsigned64_sv ();
602 root 1.18 OUTPUT: RETVAL
603 root 1.8
604     #endif
605    
606 root 1.1 SV *
607 root 1.12 _process_object_identifier (BUFOBJ self, ...)
608 root 1.1 CODE:
609 root 1.4 RETVAL = process_object_identifier_sv ();
610 root 1.18 OUTPUT: RETVAL
611 root 1.1
612     SV *
613 root 1.12 _process_octet_string (BUFOBJ self, ...)
614 root 1.1 ALIAS:
615     _process_opaque = 0
616     CODE:
617 root 1.4 RETVAL = process_octet_string_sv ();
618 root 1.18 OUTPUT: RETVAL
619 root 1.1
620     SV *
621 root 1.12 _process_ipaddress (BUFOBJ self, ...)
622 root 1.1 CODE:
623     {
624 root 1.17 U32 length = process_length ();
625 root 1.1 if (length != 4)
626     {
627     error ("IP ADDRESS length not four");
628     XSRETURN_UNDEF;
629     }
630    
631     U8 *data = getn (4, "\x00\x00\x00\x00");
632     RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
633     }
634 root 1.18 OUTPUT: RETVAL
635 root 1.1
636 root 1.3 SV *
637 root 1.12 process (BUFOBJ self, SV *expected = &PL_sv_undef, SV *found = 0)
638 root 1.6 CODE:
639 root 1.3 {
640 root 1.17 int type;
641 root 1.3
642 root 1.6 RETVAL = process_sv (&type);
643 root 1.3
644     if (found)
645     sv_setiv (found, type);
646    
647 root 1.6 if (SvOK (expected) && type != SvIV (expected))
648     error ("Expected a different type than found");
649 root 1.4 }
650 root 1.18 OUTPUT: RETVAL
651 root 1.4
652     MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
653    
654     SV *
655 root 1.12 _process_var_bind_list (BUFOBJ self)
656 root 1.4 CODE:
657     {
658 root 1.24 if (get8 () != (ASN_SEQUENCE | ASN_CONSTRUCTED))
659 root 1.6 error ("SEQUENCE expected at beginning of VarBindList");
660 root 1.20
661 root 1.6 int seqlen = process_length ();
662     U8 *end = cur + seqlen;
663    
664     HV *list = newHV ();
665     AV *names = newAV ();
666     HV *types = newHV ();
667    
668 root 1.12 hv_store ((HV *)cur_bufobj, "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0);
669     hv_store ((HV *)cur_bufobj, "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0);
670     hv_store ((HV *)cur_bufobj, "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0);
671 root 1.4
672 root 1.6 while (cur < end && !errflag)
673     {
674     // SEQUENCE ObjectName ObjectSyntax
675 root 1.24 if (get8 () != (ASN_SEQUENCE | ASN_CONSTRUCTED))
676 root 1.6 error ("SEQUENCE expected at beginning of VarBind");
677     process_length ();
678    
679     if (get8 () != ASN_OBJECT_IDENTIFIER)
680     error ("OBJECT IDENTIFIER expected at beginning of VarBind");
681     int type, oidlen;
682 root 1.7 SV *oid = process_object_identifier_sv ();
683 root 1.6 SV *val = process_sv (&type);
684    
685 root 1.7 hv_store_ent (types, oid, newSViv (type), 0);
686     hv_store_ent (list , oid, val, 0);
687     av_push (names, oid);
688 root 1.6 }
689 root 1.4
690 root 1.12 // sigh - great design to do it here
691     SV *pdu_type = *hv_fetch ((HV *)cur_bufobj, "_pdu_type" , sizeof ("_pdu_type" ) - 1, 1);
692    
693     if (SvIV (pdu_type) == 0xa8) // REPORT
694     {
695     PUSHMARK (SP);
696     XPUSHs (msg);
697     PUTBACK;
698     call_method ("_report_pdu_error", G_VOID | G_DISCARD);
699     SPAGAIN;
700     XSRETURN_EMPTY;
701     }
702 root 1.4
703 root 1.6 RETVAL = newRV_inc ((SV *)list);
704 root 1.4 }
705 root 1.18 OUTPUT: RETVAL
706 root 1.3
707 root 1.10 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP
708    
709     void
710     oid_base_match (SV *base_, SV *oid_)
711     PROTOTYPE: $$
712     ALIAS:
713     oid_context_match = 0
714     PPCODE:
715     {
716     if (!SvOK (base_) || !SvOK (oid_))
717     XSRETURN_NO;
718    
719     STRLEN blen, olen;
720 root 1.13 char *base = SvPVbyte (base_, blen);
721     char *oid = SvPVbyte (oid_ , olen);
722 root 1.10
723     blen -= *base == '.'; base += *base == '.';
724     olen -= *base == '.'; oid += *oid == '.';
725    
726     if (olen < blen)
727     XSRETURN_NO;
728    
729     if (memcmp (base, oid, blen))
730     XSRETURN_NO;
731    
732     if (oid [blen] && oid [blen] != '.')
733     XSRETURN_NO;
734    
735     XSRETURN_YES;
736     }
737    
738     #if HAVE_VERSIONSORT
739    
740     void
741     oid_lex_sort (...)
742     PROTOTYPE: @
743     PPCODE:
744     {
745     // make sure SvPVX is valid
746     int i;
747     for (i = items; i--; )
748     {
749     SV *sv = ST (i);
750    
751     if (SvTYPE (sv) < SVt_PV || SvTYPE (sv) == SVt_PVAV && SvTYPE (sv) == SVt_PVHV)
752     SvPV_force_nolen (sv);
753     }
754    
755     qsort (&ST (0), items, sizeof (SV *), oid_lex_cmp);
756    
757     EXTEND (SP, items);
758     // we cheat somewhat by not returning copies here
759     for (i = 0; i < items; ++i)
760     PUSHs (sv_2mortal (SvREFCNT_inc (ST (i))));
761     }
762    
763     int
764     _index_cmp (const char *a, const char *b)
765     PROTOTYPE: $$
766     CODE:
767     RETVAL = strverscmp (a, b);
768 root 1.18 OUTPUT: RETVAL
769 root 1.10
770     #endif
771 root 1.3