ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.17
Committed: Thu Dec 29 22:35:23 2016 UTC (7 years, 5 months ago) by root
Branch: MAIN
CVS Tags: rel-1_33
Changes since 1.16: +2 -2 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 #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 clear_bufobj (void)
51 {
52 // serialise our state back
53 if (msg && SvROK (msg))
54 {
55 SV *idx_sv = *hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1);
56 sv_setiv (idx_sv, cur - buf);
57 }
58
59 SvREFCNT_dec (msg);
60 msg = 0;
61 cur_bufobj = 0;
62 }
63
64 static void
65 switch_bufobj (BUFOBJ neu)
66 {
67 clear_bufobj ();
68
69 msg = newSVsv (neu);
70 cur_bufobj = SvRV (msg);
71 sv_rvweaken (msg);
72
73 errflag = 0;
74 leading_dot = -1;
75
76 IV index = SvIV (*hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1));
77 bufsv = *hv_fetch ((HV *)cur_bufobj, "_buffer", sizeof ("_buffer") - 1, 1);
78
79 buf = SvPVbyte (bufsv, len);
80 cur = buf + index;
81 rem = len - index;
82 }
83
84 /////////////////////////////////////////////////////////////////////////////
85
86 static SV *
87 x_get_cv (SV *cb_sv)
88 {
89 HV *st;
90 GV *gvp;
91 CV *cv = sv_2cv (cb_sv, &st, &gvp, 0);
92
93 if (!cv)
94 croak ("CODE reference expected");
95
96 return (SV *)cv;
97 }
98
99 static void
100 error (const char *errmsg)
101 {
102 errflag = 1;
103
104 if (!msg)
105 croak ("Net::SNMP::XS fatal error, parser called without parsing context");
106
107 dSP;
108 PUSHMARK (SP);
109 EXTEND (SP, 2);
110 PUSHs (msg);
111 PUSHs (sv_2mortal (newSVpv (errmsg, 0)));
112 PUTBACK;
113 call_method ("_error", G_VOID | G_DISCARD);
114 }
115
116 static int
117 need (int count)
118 {
119 if (count < 0 || (int)rem < count)
120 {
121 error ("Unexpected end of message buffer");
122 return 0;
123 }
124
125 return 1;
126 }
127
128 static U8 *
129 getn (int count, const U8 *errres)
130 {
131 if (!need (count))
132 return (U8 *)errres;
133
134 U8 *res = cur;
135
136 cur += count;
137 rem -= count;
138
139 return res;
140 }
141
142 static U8
143 get8 (void)
144 {
145 if (rem <= 0)
146 {
147 error ("Unexpected end of message buffer");
148 return 0;
149 }
150
151 rem--;
152 return *cur++;
153 }
154
155 static U32
156 getb (void)
157 {
158 U32 res = 0;
159
160 for (;;)
161 {
162 U8 c = get8 ();
163 res = (res << 7) | (c & 0x7f);
164
165 if (!(c & 0x80))
166 return res;
167 }
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 process_length (void)
184 {
185 U32 res = get8 ();
186
187 if (res & 0x80)
188 {
189 int cnt = res & 0x7f;
190 res = 0;
191
192 switch (cnt)
193 {
194 case 0:
195 error ("Indefinite ASN.1 lengths not supported");
196 return 0;
197
198 default:
199 error ("ASN.1 length too long");
200 return 0;
201
202 case 4: res = (res << 8) | get8 ();
203 case 3: res = (res << 8) | get8 ();
204 case 2: res = (res << 8) | get8 ();
205 case 1: res = (res << 8) | get8 ();
206 }
207 }
208
209 return res;
210 }
211
212 static U32
213 process_integer32 (void)
214 {
215 U32 length = process_length ();
216
217 if (length <= 0)
218 {
219 error ("INTEGER32 length equal to zero");
220 return 0;
221 }
222
223 U8 *data = getn (length, 0);
224
225 if (!data)
226 return 0;
227
228 if (length > 5 || (length > 4 && data [0]))
229 {
230 error ("INTEGER32 length too long");
231 return 0;
232 }
233
234 U32 res = data [0] & 0x80 ? 0xffffffff : 0;
235
236 while (length--)
237 res = (res << 8) | *data++;
238
239 return res;
240 }
241
242 static SV *
243 process_integer32_sv (void)
244 {
245 return newSViv ((I32)process_integer32 ());
246 }
247
248 static SV *
249 process_unsigned32_sv (void)
250 {
251 return newSVuv ((U32)process_integer32 ());
252 }
253
254 #if IVSIZE >= 8
255
256 static U64TYPE
257 process_integer64 (void)
258 {
259 U32 length = process_length ();
260
261 if (length <= 0)
262 {
263 error ("INTEGER64 length equal to zero");
264 return 0;
265 }
266
267 U8 *data = getn (length, 0);
268
269 if (!data)
270 return 0;
271
272 if (length > 9 || (length > 8 && data [0]))
273 {
274 error ("INTEGER64 length too long");
275 return 0;
276 }
277
278 U64TYPE res = data [0] & 0x80 ? 0xffffffffffffffff : 0;
279
280 while (length--)
281 res = (res << 8) | *data++;
282
283 return res;
284 }
285
286 static SV *
287 process_integer64_sv (void)
288 {
289 return newSViv ((I64TYPE)process_integer64 ());
290 }
291
292 static SV *
293 process_unsigned64_sv (void)
294 {
295 return newSVuv ((U64TYPE)process_integer64 ());
296 }
297
298 #endif
299
300 static SV *
301 process_octet_string_sv (void)
302 {
303 U32 length = process_length ();
304
305 U8 *data = getn (length, 0);
306 if (!data)
307 {
308 error ("OCTET STRING too long");
309 return &PL_sv_undef;
310 }
311
312 return newSVpvn (data, length);
313 }
314
315 static char *
316 write_uv (char *buf, U32 u)
317 {
318 // the one-digit case is absolutely predominant
319 if (u < 10)
320 *buf++ = u + '0';
321 else
322 buf += sprintf (buf, "%u", (unsigned int)u);
323
324 return buf;
325 }
326
327 static SV *
328 process_object_identifier_sv (void)
329 {
330 U32 length = process_length ();
331
332 if (length <= 0)
333 {
334 error ("OBJECT IDENTIFIER length equal to zero");
335 return &PL_sv_undef;
336 }
337
338 U8 *end = cur + length;
339 U32 w = getb ();
340
341 static char oid[MAX_OID_STRLEN]; // must be static
342 char *app = oid;
343
344 if (leading_dot < 0)
345 leading_dot = SvTRUE (*hv_fetch ((HV *)SvRV (msg), "_leading_dot", sizeof ("_leading_dot") - 1, 1));
346
347 *app = '.'; app += ! ! leading_dot;
348 app = write_uv (app, (U8)w / 40);
349 *app++ = '.';
350 app = write_uv (app, (U8)w % 40);
351
352 // we assume an oid component is never > 64 bytes
353 while (cur < end && oid + sizeof (oid) - app > 64)
354 {
355 w = getb ();
356 *app++ = '.';
357 app = write_uv (app, w);
358 }
359
360 return newSVpvn (oid, app - oid);
361 }
362
363 static AV *av_type;
364
365 static SV *
366 process_sv (int *found)
367 {
368 int type = get8 ();
369
370 *found = type;
371
372 SV *res;
373
374 switch (type)
375 {
376 case ASN_OBJECT_IDENTIFIER:
377 res = process_object_identifier_sv ();
378 break;
379
380 case ASN_INTEGER32:
381 res = process_integer32_sv ();
382 break;
383
384 case ASN_UNSIGNED32:
385 case ASN_COUNTER32:
386 case ASN_TIMETICKS:
387 res = process_unsigned32_sv ();
388 break;
389
390 case ASN_SEQUENCE:
391 res = newSVuv (process_length ());
392 break;
393
394 case ASN_OCTET_STRING:
395 case ASN_OPAQUE:
396 res = process_octet_string_sv ();
397 break;
398
399 default:
400 {
401 if (type > AvFILLp (av_type)
402 || AvARRAY (av_type)[type] == 0
403 || AvARRAY (av_type)[type] == &PL_sv_undef)
404 {
405 error ("Unknown ASN.1 type");
406 return &PL_sv_undef;
407 }
408
409 dSP;
410 PUSHMARK (SP);
411 EXTEND (SP, 2);
412 PUSHs (msg);
413 PUSHs (sv_2mortal (newSViv (type)));
414 PUTBACK;
415 int count = call_sv (AvARRAY (av_type)[type], G_SCALAR);
416 SPAGAIN;
417 res = count ? SvREFCNT_inc (TOPs) : &PL_sv_undef;
418 }
419 }
420
421 return errflag ? &PL_sv_undef : res;
422 }
423
424 /////////////////////////////////////////////////////////////////////////////
425
426 #if HAVE_VERSIONSORT
427
428 static int
429 oid_lex_cmp (const void *a_, const void *b_)
430 {
431 const char *a = SvPVX (*(SV **)a_);
432 const char *b = SvPVX (*(SV **)b_);
433
434 a += *a == '.';
435 b += *b == '.';
436
437 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 av_type = newAV ();
448
449 void
450 set_type (int type, SV *cv)
451 CODE:
452 cv = x_get_cv (cv);
453 assert (SvTYPE (cv) == SVt_PVCV);
454 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 const char *vstr = SvPVbyte (value, vlen);
466
467 if (ix)
468 sv_insert (bufsv, 0, 0, vstr, vlen);
469 else
470 sv_catpvn (bufsv, vstr, vlen);
471
472 buf = SvPVbyte (bufsv, len);
473 cur = buf;
474 rem = len;
475
476 SV *len_sv = *hv_fetch ((HV *)cur_bufobj, "_length", sizeof ("_length") - 1, 1);
477 sv_setiv (len_sv, len);
478
479 // some callers test for defined'ness of the returnvalue. *sigh*
480 XPUSHs (&PL_sv_yes);
481 }
482
483 void
484 _buffer_get (BUFOBJ self, int count = -1)
485 PPCODE:
486 {
487 // grrr.
488 if (count < 0)
489 {
490 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
491 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
492 XPUSHs (sv_2mortal (newSVsv (bufsv)));
493 sv_setpvn (bufsv, "", 0);
494
495 buf = "";
496 cur = buf;
497 rem = 0;
498
499 XSRETURN (1);
500 }
501
502 char *data = getn (count, 0);
503
504 if (data)
505 XPUSHs (sv_2mortal (newSVpvn (data, count)));
506 }
507
508 U32
509 index (BUFOBJ self, int ndx = -1)
510 CODE:
511 {
512 if (ndx >= 0 && ndx < len)
513 {
514 cur = buf + ndx;
515 rem = len - ndx;
516 }
517
518 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 RETVAL = process_length ();
529 OUTPUT:
530 RETVAL
531
532 SV *
533 _process_integer32 (BUFOBJ self, ...)
534 CODE:
535 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 RETVAL = process_unsigned32_sv ();
546 OUTPUT:
547 RETVAL
548
549 #if IVSIZE >= 8
550
551 SV *
552 _process_counter64 (BUFOBJ self, ...)
553 CODE:
554 RETVAL = process_unsigned64_sv ();
555 OUTPUT:
556 RETVAL
557
558 #endif
559
560 SV *
561 _process_object_identifier (BUFOBJ self, ...)
562 CODE:
563 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 RETVAL = process_octet_string_sv ();
573 OUTPUT:
574 RETVAL
575
576 SV *
577 _process_ipaddress (BUFOBJ self, ...)
578 CODE:
579 {
580 U32 length = process_length ();
581 if (length != 4)
582 {
583 error ("IP ADDRESS length not four");
584 XSRETURN_UNDEF;
585 }
586
587 U8 *data = getn (4, "\x00\x00\x00\x00");
588 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 RETVAL = process_sv (&type);
600
601 if (found)
602 sv_setiv (found, type);
603
604 if (SvOK (expected) && type != SvIV (expected))
605 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 if (get8 () != ASN_SEQUENCE)
617 error ("SEQUENCE expected at beginning of VarBindList");
618 int seqlen = process_length ();
619 U8 *end = cur + seqlen;
620
621 HV *list = newHV ();
622 AV *names = newAV ();
623 HV *types = newHV ();
624
625 hv_store ((HV *)cur_bufobj, "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0);
626 hv_store ((HV *)cur_bufobj, "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0);
627 hv_store ((HV *)cur_bufobj, "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0);
628
629 while (cur < end && !errflag)
630 {
631 // SEQUENCE ObjectName ObjectSyntax
632 if (get8 () != ASN_SEQUENCE)
633 error ("SEQUENCE expected at beginning of VarBind");
634 process_length ();
635
636 if (get8 () != ASN_OBJECT_IDENTIFIER)
637 error ("OBJECT IDENTIFIER expected at beginning of VarBind");
638 int type, oidlen;
639 SV *oid = process_object_identifier_sv ();
640 SV *val = process_sv (&type);
641
642 hv_store_ent (types, oid, newSViv (type), 0);
643 hv_store_ent (list , oid, val, 0);
644 av_push (names, oid);
645 }
646
647 // sigh - great design to do it here
648 SV *pdu_type = *hv_fetch ((HV *)cur_bufobj, "_pdu_type" , sizeof ("_pdu_type" ) - 1, 1);
649
650 if (SvIV (pdu_type) == 0xa8) // REPORT
651 {
652 PUSHMARK (SP);
653 XPUSHs (msg);
654 PUTBACK;
655 call_method ("_report_pdu_error", G_VOID | G_DISCARD);
656 SPAGAIN;
657 XSRETURN_EMPTY;
658 }
659
660 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 if (!SvOK (base_) || !SvOK (oid_))
675 XSRETURN_NO;
676
677 STRLEN blen, olen;
678 char *base = SvPVbyte (base_, blen);
679 char *oid = SvPVbyte (oid_ , olen);
680
681 blen -= *base == '.'; base += *base == '.';
682 olen -= *base == '.'; oid += *oid == '.';
683
684 if (olen < blen)
685 XSRETURN_NO;
686
687 if (memcmp (base, oid, blen))
688 XSRETURN_NO;
689
690 if (oid [blen] && oid [blen] != '.')
691 XSRETURN_NO;
692
693 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 for (i = items; i--; )
706 {
707 SV *sv = ST (i);
708
709 if (SvTYPE (sv) < SVt_PV || SvTYPE (sv) == SVt_PVAV && SvTYPE (sv) == SVt_PVHV)
710 SvPV_force_nolen (sv);
711 }
712
713 qsort (&ST (0), items, sizeof (SV *), oid_lex_cmp);
714
715 EXTEND (SP, items);
716 // we cheat somewhat by not returning copies here
717 for (i = 0; i < items; ++i)
718 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 RETVAL = strverscmp (a, b);
726 OUTPUT:
727 RETVAL
728
729 #endif
730