ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.8
Committed: Thu Apr 9 10:08:25 2009 UTC (15 years, 2 months ago) by root
Branch: MAIN
Changes since 1.7: +74 -6 lines
Log Message:
counter64

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