ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.9
Committed: Sat Apr 11 04:22:49 2009 UTC (15 years, 2 months ago) by root
Branch: MAIN
CVS Tags: rel-0_02
Changes since 1.8: +3 -0 lines
Log Message:
0.02

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