ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.18
Committed: Fri Apr 19 10:11:03 2019 UTC (5 years, 1 month ago) by root
Branch: MAIN
Changes since 1.17: +114 -34 lines
Log Message:
*** empty log message ***

File Contents

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