ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.12
Committed: Wed May 5 20:46:09 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
CVS Tags: rel-1_0
Changes since 1.11: +71 -35 lines
Log Message:
1.0

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