File Coverage

XS.xs
Criterion Covered Total %
statement 7 292 2.4
branch 1 216 0.4
condition n/a
subroutine n/a
pod n/a
total 8 508 1.5


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