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