ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.22
Committed: Fri Apr 19 16:01:03 2019 UTC (5 years, 1 month ago) by root
Branch: MAIN
Changes since 1.21: +0 -8 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 #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 clear_bufobj (void)
72 {
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 }
79
80 SvREFCNT_dec (msg);
81 msg = 0;
82 cur_bufobj = 0;
83 }
84
85 static void
86 switch_bufobj (BUFOBJ neu)
87 {
88 clear_bufobj ();
89
90 msg = newSVsv (neu);
91 cur_bufobj = SvRV (msg);
92 sv_rvweaken (msg);
93
94 errflag = 0;
95 leading_dot = -1;
96
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 }
104
105 /////////////////////////////////////////////////////////////////////////////
106
107 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 static void
121 error (const char *errmsg)
122 {
123 errflag = 1;
124
125 if (!msg)
126 croak ("Net::SNMP::XS fatal error, parser called without parsing context");
127
128 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 }
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 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 #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 if (length > 9 || (length > 8 && data [0]))
294 {
295 error ("INTEGER64 length too long");
296 return 0;
297 }
298
299 U64TYPE res = data [0] & 0x80 ? 0xffffffffffffffff : 0;
300
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 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
336 static char *
337 write_uv (char *buf, U32 u)
338 {
339 // the one-digit case is absolutely predominant
340 if (u < 10)
341 *buf++ = u + '0';
342 else
343 buf += sprintf (buf, "%u", (unsigned int)u);
344
345 return buf;
346 }
347
348 static SV *
349 process_object_identifier_sv (void)
350 {
351 U32 length = process_length ();
352
353 if (length <= 0)
354 {
355 error ("OBJECT IDENTIFIER length equal to zero");
356 return &PL_sv_undef;
357 }
358
359 U8 *end = cur + length;
360 U32 w = getb ();
361
362 static char oid[MAX_OID_STRLEN]; // must be static
363 char *app = oid;
364
365 if (leading_dot < 0)
366 leading_dot = SvTRUE (*hv_fetch ((HV *)SvRV (msg), "_leading_dot", sizeof ("_leading_dot") - 1, 1));
367
368 *app = '.'; app += ! ! leading_dot;
369 app = write_uv (app, (U8)w / 40);
370 *app++ = '.';
371 app = write_uv (app, (U8)w % 40);
372
373 // we assume an oid component is never > 64 bytes
374 while (cur < end && oid + sizeof (oid) - app > 64)
375 {
376 w = getb ();
377 *app++ = '.';
378 app = write_uv (app, w);
379 }
380
381 return newSVpvn (oid, app - oid);
382 }
383
384 static AV *av_type;
385
386 static SV *
387 process_sv (int *found)
388 {
389 int type = get8 ();
390
391 *found = type;
392
393 SV *res;
394
395 switch (type)
396 {
397 case ASN_OBJECT_IDENTIFIER:
398 res = process_object_identifier_sv ();
399 break;
400
401 case ASN_INTEGER32:
402 res = process_integer32_sv ();
403 break;
404
405 case ASN_APPLICATION | ASN_UNSIGNED32:
406 case ASN_APPLICATION | ASN_COUNTER32:
407 case ASN_APPLICATION | ASN_TIMETICKS:
408 res = process_unsigned32_sv ();
409 break;
410
411 case ASN_SEQUENCE | ASN_CONSTRUCTED:
412 res = newSVuv (process_length ());
413 break;
414
415 case ASN_OCTET_STRING:
416 case ASN_APPLICATION | ASN_OPAQUE:
417 res = process_octet_string_sv ();
418 break;
419
420 default:
421 {
422 if (type > AvFILLp (av_type)
423 || AvARRAY (av_type)[type] == 0
424 || AvARRAY (av_type)[type] == &PL_sv_undef)
425 {
426 error ("Unknown ASN.1 type");
427 return &PL_sv_undef;
428 }
429
430 dSP;
431 PUSHMARK (SP);
432 EXTEND (SP, 2);
433 PUSHs (msg);
434 PUSHs (sv_2mortal (newSViv (type)));
435 PUTBACK;
436 int count = call_sv (AvARRAY (av_type)[type], G_SCALAR);
437 SPAGAIN;
438 res = count ? SvREFCNT_inc (TOPs) : &PL_sv_undef;
439 }
440 }
441
442 return errflag ? &PL_sv_undef : res;
443 }
444
445 /////////////////////////////////////////////////////////////////////////////
446
447 #if HAVE_VERSIONSORT
448
449 static int
450 oid_lex_cmp (const void *a_, const void *b_)
451 {
452 const char *a = SvPVX (*(SV **)a_);
453 const char *b = SvPVX (*(SV **)b_);
454
455 a += *a == '.';
456 b += *b == '.';
457
458 return strverscmp (a, b);
459 }
460
461 #endif
462
463 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
464
465 PROTOTYPES: ENABLE
466
467 BOOT:
468 av_type = newAV ();
469
470 void
471 set_type (int type, SV *cv)
472 CODE:
473 cv = x_get_cv (cv);
474 assert (SvTYPE (cv) == SVt_PVCV);
475 av_store (av_type, type, SvREFCNT_inc_NN (cv));
476
477 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
478
479 void
480 _buffer_append (BUFOBJ self, SV *value)
481 ALIAS:
482 _buffer_put = 1
483 PPCODE:
484 {
485 STRLEN vlen;
486 const char *vstr = SvPVbyte (value, vlen);
487
488 if (ix)
489 sv_insert (bufsv, 0, 0, vstr, vlen);
490 else
491 sv_catpvn (bufsv, vstr, vlen);
492
493 buf = SvPVbyte (bufsv, len);
494 cur = buf;
495 rem = len;
496
497 SV *len_sv = *hv_fetch ((HV *)cur_bufobj, "_length", sizeof ("_length") - 1, 1);
498 sv_setiv (len_sv, len);
499
500 // some callers test for defined'ness of the returnvalue. *sigh*
501 XPUSHs (&PL_sv_yes);
502 }
503
504 void
505 _buffer_get (BUFOBJ self, int count = -1)
506 PPCODE:
507 {
508 // grrr.
509 if (count < 0)
510 {
511 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
512 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
513 XPUSHs (sv_2mortal (newSVsv (bufsv)));
514 sv_setpvn (bufsv, "", 0);
515
516 buf = "";
517 cur = buf;
518 rem = 0;
519
520 XSRETURN (1);
521 }
522
523 char *data = getn (count, 0);
524
525 if (data)
526 XPUSHs (sv_2mortal (newSVpvn (data, count)));
527 }
528
529 U32
530 index (BUFOBJ self, int ndx = -1)
531 CODE:
532 {
533 if (ndx >= 0 && ndx < len)
534 {
535 cur = buf + ndx;
536 rem = len - ndx;
537 }
538
539 RETVAL = cur - buf;
540 }
541 OUTPUT: RETVAL
542
543 U32
544 _process_length (BUFOBJ self, ...)
545 ALIAS:
546 _process_sequence = 0
547 CODE:
548 RETVAL = process_length ();
549 OUTPUT: RETVAL
550
551 SV *
552 _process_integer32 (BUFOBJ self, ...)
553 CODE:
554 RETVAL = process_integer32_sv ();
555 OUTPUT: RETVAL
556
557 SV *
558 _process_counter (BUFOBJ self, ...)
559 ALIAS:
560 _process_gauge = 0
561 _process_timeticks = 0
562 CODE:
563 RETVAL = process_unsigned32_sv ();
564 OUTPUT: RETVAL
565
566 #if IVSIZE >= 8
567
568 SV *
569 _process_counter64 (BUFOBJ self, ...)
570 CODE:
571 RETVAL = process_unsigned64_sv ();
572 OUTPUT: RETVAL
573
574 #endif
575
576 SV *
577 _process_object_identifier (BUFOBJ self, ...)
578 CODE:
579 RETVAL = process_object_identifier_sv ();
580 OUTPUT: RETVAL
581
582 SV *
583 _process_octet_string (BUFOBJ self, ...)
584 ALIAS:
585 _process_opaque = 0
586 CODE:
587 RETVAL = process_octet_string_sv ();
588 OUTPUT: RETVAL
589
590 SV *
591 _process_ipaddress (BUFOBJ self, ...)
592 CODE:
593 {
594 U32 length = process_length ();
595 if (length != 4)
596 {
597 error ("IP ADDRESS length not four");
598 XSRETURN_UNDEF;
599 }
600
601 U8 *data = getn (4, "\x00\x00\x00\x00");
602 RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
603 }
604 OUTPUT: RETVAL
605
606 SV *
607 process (BUFOBJ self, SV *expected = &PL_sv_undef, SV *found = 0)
608 CODE:
609 {
610 int type;
611
612 RETVAL = process_sv (&type);
613
614 if (found)
615 sv_setiv (found, type);
616
617 if (SvOK (expected) && type != SvIV (expected))
618 error ("Expected a different type than found");
619 }
620 OUTPUT: RETVAL
621
622 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
623
624 SV *
625 _process_var_bind_list (BUFOBJ self)
626 CODE:
627 {
628 if (get8 () != ASN_SEQUENCE | ASN_CONSTRUCTED)
629 error ("SEQUENCE expected at beginning of VarBindList");
630
631 int seqlen = process_length ();
632 U8 *end = cur + seqlen;
633
634 HV *list = newHV ();
635 AV *names = newAV ();
636 HV *types = newHV ();
637
638 hv_store ((HV *)cur_bufobj, "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0);
639 hv_store ((HV *)cur_bufobj, "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0);
640 hv_store ((HV *)cur_bufobj, "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0);
641
642 while (cur < end && !errflag)
643 {
644 // SEQUENCE ObjectName ObjectSyntax
645 if (get8 () != ASN_SEQUENCE | ASN_CONSTRUCTED)
646 error ("SEQUENCE expected at beginning of VarBind");
647 process_length ();
648
649 if (get8 () != ASN_OBJECT_IDENTIFIER)
650 error ("OBJECT IDENTIFIER expected at beginning of VarBind");
651 int type, oidlen;
652 SV *oid = process_object_identifier_sv ();
653 SV *val = process_sv (&type);
654
655 hv_store_ent (types, oid, newSViv (type), 0);
656 hv_store_ent (list , oid, val, 0);
657 av_push (names, oid);
658 }
659
660 // sigh - great design to do it here
661 SV *pdu_type = *hv_fetch ((HV *)cur_bufobj, "_pdu_type" , sizeof ("_pdu_type" ) - 1, 1);
662
663 if (SvIV (pdu_type) == 0xa8) // REPORT
664 {
665 PUSHMARK (SP);
666 XPUSHs (msg);
667 PUTBACK;
668 call_method ("_report_pdu_error", G_VOID | G_DISCARD);
669 SPAGAIN;
670 XSRETURN_EMPTY;
671 }
672
673 RETVAL = newRV_inc ((SV *)list);
674 }
675 OUTPUT: RETVAL
676
677 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP
678
679 void
680 oid_base_match (SV *base_, SV *oid_)
681 PROTOTYPE: $$
682 ALIAS:
683 oid_context_match = 0
684 PPCODE:
685 {
686 if (!SvOK (base_) || !SvOK (oid_))
687 XSRETURN_NO;
688
689 STRLEN blen, olen;
690 char *base = SvPVbyte (base_, blen);
691 char *oid = SvPVbyte (oid_ , olen);
692
693 blen -= *base == '.'; base += *base == '.';
694 olen -= *base == '.'; oid += *oid == '.';
695
696 if (olen < blen)
697 XSRETURN_NO;
698
699 if (memcmp (base, oid, blen))
700 XSRETURN_NO;
701
702 if (oid [blen] && oid [blen] != '.')
703 XSRETURN_NO;
704
705 XSRETURN_YES;
706 }
707
708 #if HAVE_VERSIONSORT
709
710 void
711 oid_lex_sort (...)
712 PROTOTYPE: @
713 PPCODE:
714 {
715 // make sure SvPVX is valid
716 int i;
717 for (i = items; i--; )
718 {
719 SV *sv = ST (i);
720
721 if (SvTYPE (sv) < SVt_PV || SvTYPE (sv) == SVt_PVAV && SvTYPE (sv) == SVt_PVHV)
722 SvPV_force_nolen (sv);
723 }
724
725 qsort (&ST (0), items, sizeof (SV *), oid_lex_cmp);
726
727 EXTEND (SP, items);
728 // we cheat somewhat by not returning copies here
729 for (i = 0; i < items; ++i)
730 PUSHs (sv_2mortal (SvREFCNT_inc (ST (i))));
731 }
732
733 int
734 _index_cmp (const char *a, const char *b)
735 PROTOTYPE: $$
736 CODE:
737 RETVAL = strverscmp (a, b);
738 OUTPUT: RETVAL
739
740 #endif
741