ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.11
Committed: Sun Apr 12 00:48:39 2009 UTC (15 years, 2 months ago) by root
Branch: MAIN
CVS Tags: rel-0_03
Changes since 1.10: +3 -0 lines
Log Message:
0.03

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