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