ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.10
Committed: Sat Apr 11 06:24:18 2009 UTC (15 years, 2 months ago) by root
Branch: MAIN
Changes since 1.9: +85 -0 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 *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 *app = '.'; app += ! ! leading_dot;
290 app = write_uv (app, (U8)w / 40);
291 *app++ = '.';
292 app = write_uv (app, (U8)w % 40);
293
294 // we assume an oid component is never > 64 bytes
295 while (cur < end && oid + sizeof (oid) - app > 64)
296 {
297 w = getb ();
298 *app++ = '.';
299 app = write_uv (app, w);
300 }
301
302 return newSVpvn (oid, app - oid);
303 }
304
305 static AV *av_type;
306
307 static SV *
308 process_sv (int *found)
309 {
310 int type = get8 ();
311
312 *found = type;
313
314 SV *res;
315
316 switch (type)
317 {
318 case ASN_OBJECT_IDENTIFIER:
319 res = process_object_identifier_sv ();
320 break;
321
322 case ASN_INTEGER32:
323 res = process_integer32_sv ();
324 break;
325
326 case ASN_UNSIGNED32:
327 case ASN_COUNTER32:
328 case ASN_TIMETICKS:
329 res = process_unsigned32_sv ();
330 break;
331
332 case ASN_SEQUENCE:
333 res = newSVuv (process_length ());
334 break;
335
336 case ASN_OCTET_STRING:
337 case ASN_OPAQUE:
338 res = process_octet_string_sv ();
339 break;
340
341 default:
342 {
343 if (type > AvFILLp (av_type) || !SvTYPE (AvARRAY (av_type)[type]) == SVt_PVCV)
344 {
345 error ("Unknown ASN.1 type");
346 return &PL_sv_undef;
347 }
348
349 dSP;
350 PUSHMARK (SP);
351 EXTEND (SP, 2);
352 PUSHs (msg);
353 PUSHs (sv_2mortal (newSViv (type)));
354 PUTBACK;
355 int count = call_sv (AvARRAY (av_type)[type], G_SCALAR);
356 SPAGAIN;
357 res = count ? SvREFCNT_inc (TOPs) : &PL_sv_undef;
358 }
359 }
360
361 return errflag ? &PL_sv_undef : res;
362 }
363
364 /////////////////////////////////////////////////////////////////////////////
365
366 #if HAVE_VERSIONSORT
367
368 static int
369 oid_lex_cmp (const void *a_, const void *b_)
370 {
371 const char *a = SvPVX (*(SV **)a_);
372 const char *b = SvPVX (*(SV **)b_);
373
374 a += *a == '.';
375 b += *b == '.';
376
377 return strverscmp (a, b);
378 }
379
380 #endif
381
382 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
383
384 PROTOTYPES: ENABLE
385
386 BOOT:
387 av_type = newAV ();
388
389 void
390 set_type (int type, SV *cv)
391 CODE:
392 av_store (av_type, type, SvREFCNT_inc (x_get_cv (cv)));
393
394 void
395 set_msg (SV *msg_, SV *buf_)
396 CODE:
397 {
398 if (msg)
399 croak ("recursive invocation of Net::SNMP::XS parser is not supported");
400
401 errflag = 0;
402 leading_dot = -1;
403 msg = SvREFCNT_inc (msg_);
404 buf = SvPVbyte (buf_, len);
405 cur = buf;
406 rem = len;
407 #ifdef BENCHMARK
408 t1 = tstamp ();
409 #endif
410 }
411
412 void
413 clr_msg ()
414 CODE:
415 SvREFCNT_dec (msg); msg = 0;
416 buf = cur = (U8 *)"";
417 len = rem = 0;
418 #ifdef BENCHMARK
419 printf ("%f\n", tstamp () - t1);//D
420 #endif
421
422 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
423
424 void
425 _buffer_get (SV *self, int count = -1)
426 PPCODE:
427 {
428 // grrr.
429 if (count < 0)
430 {
431 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
432 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
433 SV **svp = hv_fetch ((HV *)SvRV (self), "_buffer", 7, 1);
434 XPUSHs (sv_2mortal (newSVsv (*svp)));
435 sv_setpvn (*svp, "", 0);
436 XSRETURN (1);
437 }
438
439 char *data = getn (count, 0);
440
441 if (data)
442 XPUSHs (sv_2mortal (newSVpvn (data, count)));
443 }
444
445 U32
446 index (SV *self, int ndx = -1)
447 CODE:
448 {
449 if (ndx >= 0 && ndx < len)
450 {
451 cur = buf + ndx;
452 rem = len - ndx;
453 }
454
455 RETVAL = cur - buf;
456 }
457 OUTPUT:
458 RETVAL
459
460 U32
461 _process_length (SV *self, ...)
462 ALIAS:
463 _process_sequence = 0
464 CODE:
465 RETVAL = process_length ();
466 OUTPUT:
467 RETVAL
468
469 SV *
470 _process_integer32 (SV *self, ...)
471 CODE:
472 RETVAL = process_integer32_sv ();
473 OUTPUT:
474 RETVAL
475
476 SV *
477 _process_counter (SV *self, ...)
478 ALIAS:
479 _process_gauge = 0
480 _process_timeticks = 0
481 CODE:
482 RETVAL = process_unsigned32_sv ();
483 OUTPUT:
484 RETVAL
485
486 #if IVSIZE >= 8
487
488 SV *
489 _process_counter64 (SV *self, ...)
490 CODE:
491 RETVAL = process_unsigned64_sv ();
492 OUTPUT:
493 RETVAL
494
495 #endif
496
497 SV *
498 _process_object_identifier (SV *self, ...)
499 CODE:
500 RETVAL = process_object_identifier_sv ();
501 OUTPUT:
502 RETVAL
503
504 SV *
505 _process_octet_string (SV *self, ...)
506 ALIAS:
507 _process_opaque = 0
508 CODE:
509 RETVAL = process_octet_string_sv ();
510 OUTPUT:
511 RETVAL
512
513 SV *
514 _process_ipaddress (SV *self, ...)
515 CODE:
516 {
517 U32 length = process_length ();
518 if (length != 4)
519 {
520 error ("IP ADDRESS length not four");
521 XSRETURN_UNDEF;
522 }
523
524 U8 *data = getn (4, "\x00\x00\x00\x00");
525 RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
526 }
527 OUTPUT:
528 RETVAL
529
530 SV *
531 process (SV *self, SV *expected = &PL_sv_undef, SV *found = 0)
532 CODE:
533 {
534 int type;
535
536 RETVAL = process_sv (&type);
537
538 if (found)
539 sv_setiv (found, type);
540
541 if (SvOK (expected) && type != SvIV (expected))
542 error ("Expected a different type than found");
543 }
544 OUTPUT:
545 RETVAL
546
547 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
548
549 SV *
550 _process_var_bind_list (SV *self)
551 CODE:
552 {
553 if (get8 () != ASN_SEQUENCE)
554 error ("SEQUENCE expected at beginning of VarBindList");
555 int seqlen = process_length ();
556 U8 *end = cur + seqlen;
557
558 HV *list = newHV ();
559 AV *names = newAV ();
560 HV *types = newHV ();
561
562 hv_store ((HV *)SvRV (self), "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0);
563 hv_store ((HV *)SvRV (self), "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0);
564 hv_store ((HV *)SvRV (self), "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0);
565
566 while (cur < end && !errflag)
567 {
568 // SEQUENCE ObjectName ObjectSyntax
569 if (get8 () != ASN_SEQUENCE)
570 error ("SEQUENCE expected at beginning of VarBind");
571 process_length ();
572
573 if (get8 () != ASN_OBJECT_IDENTIFIER)
574 error ("OBJECT IDENTIFIER expected at beginning of VarBind");
575 int type, oidlen;
576 SV *oid = process_object_identifier_sv ();
577 SV *val = process_sv (&type);
578
579 hv_store_ent (types, oid, newSViv (type), 0);
580 hv_store_ent (list , oid, val, 0);
581 av_push (names, oid);
582 }
583
584 //return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT);
585
586 RETVAL = newRV_inc ((SV *)list);
587 }
588 OUTPUT:
589 RETVAL
590
591 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP
592
593 void
594 oid_base_match (SV *base_, SV *oid_)
595 PROTOTYPE: $$
596 ALIAS:
597 oid_context_match = 0
598 PPCODE:
599 {
600 if (!SvOK (base_) || !SvOK (oid_))
601 XSRETURN_NO;
602
603 STRLEN blen, olen;
604 char *base = SvPV (base_, blen);
605 char *oid = SvPV (oid_ , olen);
606
607 blen -= *base == '.'; base += *base == '.';
608 olen -= *base == '.'; oid += *oid == '.';
609
610 if (olen < blen)
611 XSRETURN_NO;
612
613 if (memcmp (base, oid, blen))
614 XSRETURN_NO;
615
616 if (oid [blen] && oid [blen] != '.')
617 XSRETURN_NO;
618
619 XSRETURN_YES;
620 }
621
622 #if HAVE_VERSIONSORT
623
624 void
625 oid_lex_sort (...)
626 PROTOTYPE: @
627 PPCODE:
628 {
629 // make sure SvPVX is valid
630 int i;
631 for (i = items; i--; )
632 {
633 SV *sv = ST (i);
634
635 if (SvTYPE (sv) < SVt_PV || SvTYPE (sv) == SVt_PVAV && SvTYPE (sv) == SVt_PVHV)
636 SvPV_force_nolen (sv);
637 }
638
639 qsort (&ST (0), items, sizeof (SV *), oid_lex_cmp);
640
641 EXTEND (SP, items);
642 // we cheat somewhat by not returning copies here
643 for (i = 0; i < items; ++i)
644 PUSHs (sv_2mortal (SvREFCNT_inc (ST (i))));
645 }
646
647 int
648 _index_cmp (const char *a, const char *b)
649 PROTOTYPE: $$
650 CODE:
651 RETVAL = strverscmp (a, b);
652 OUTPUT:
653 RETVAL
654
655 #endif
656