File Coverage

XS.xs
Criterion Covered Total %
statement 7 280 2.5
branch 1 212 0.4
condition n/a
subroutine n/a
pod n/a
total 8 492 1.6


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