ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.4
Committed: Thu Apr 9 04:37:05 2009 UTC (15 years, 2 months ago) by root
Branch: MAIN
Changes since 1.3: +234 -103 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 static SV *
203 process_object_identifier_sv (void)
204 {
205 U32 length = process_length ();
206
207 if (length <= 0)
208 {
209 error ("OBJECT IDENTIFIER length equal to zero");
210 return &PL_sv_undef;
211 }
212
213 U8 *end = cur + length;
214 U32 w = getb ();
215
216 static char oid[MAX_OID_STRLEN];
217 char *buf = oid;
218
219 if (leading_dot)
220 *buf++ = '.';
221
222 buf += snprintf (buf, oid + sizeof (oid) - buf, "%d.%d", (int)w / 40, (int)w % 40);
223
224 while (cur < end)
225 {
226 w = getb ();
227 buf += snprintf (buf, oid + sizeof (oid) - buf, ".%u", (unsigned int)w);
228 }
229
230 return newSVpvn (oid, buf - oid);
231 }
232
233 static AV *av_type;
234
235 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
236
237 PROTOTYPES: ENABLE
238
239 BOOT:
240 av_type = newAV ();
241
242 void
243 set_type (int type, SV *cv)
244 CODE:
245 av_store (av_type, type, SvREFCNT_inc (x_get_cv (cv)));
246
247 void
248 set_msg (SV *msg_, SV *buf_)
249 CODE:
250 errflag = 0;
251 leading_dot = -1;
252 msg = SvREFCNT_inc (msg_);
253 buf = SvPVbyte (buf_, len);
254 cur = buf;
255 rem = len;
256 #ifdef BENCHMARK
257 t1 = tstamp ();
258 #endif
259
260 void
261 clr_msg ()
262 CODE:
263 SvREFCNT_dec (msg);
264 buf = cur = "";
265 len = rem = 0;
266 #ifdef BENCHMARK
267 printf ("%f\n", tstamp () - t1);//D
268 #endif
269
270 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
271
272 void
273 _buffer_get (SV *self, int count = -1)
274 PPCODE:
275 {
276 // grrr.
277 if (count < 0)
278 {
279 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
280 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
281 SV **svp = hv_fetch ((HV *)SvRV (self), "_buffer", 7, 1);
282 XPUSHs (sv_2mortal (newSVsv (*svp)));
283 sv_setpvn (*svp, "", 0);
284 XSRETURN (1);
285 }
286
287 char *data = getn (count, 0);
288
289 if (data)
290 XPUSHs (sv_2mortal (newSVpvn (data, count)));
291 }
292
293 U32
294 index (SV *self, int ndx = -1)
295 CODE:
296 {
297 if (ndx >= 0 && ndx < len)
298 {
299 cur = buf + ndx;
300 rem = len - ndx;
301 }
302
303 RETVAL = cur - buf;
304 }
305 OUTPUT:
306 RETVAL
307
308 U32
309 _process_length (SV *self, ...)
310 ALIAS:
311 _process_sequence = 0
312 CODE:
313 RETVAL = process_length ();
314 OUTPUT:
315 RETVAL
316
317 SV *
318 _process_integer32 (SV *self, ...)
319 CODE:
320 RETVAL = process_integer32_sv ();
321 OUTPUT:
322 RETVAL
323
324 SV *
325 _process_counter (SV *self, ...)
326 ALIAS:
327 _process_gauge = 0
328 _process_timeticks = 0
329 CODE:
330 RETVAL = process_unsigned32_sv ();
331 OUTPUT:
332 RETVAL
333
334 SV *
335 _process_object_identifier (SV *self, ...)
336 CODE:
337 RETVAL = process_object_identifier_sv ();
338 OUTPUT:
339 RETVAL
340
341 SV *
342 _process_octet_string (SV *self, ...)
343 ALIAS:
344 _process_opaque = 0
345 CODE:
346 RETVAL = process_octet_string_sv ();
347 OUTPUT:
348 RETVAL
349
350 SV *
351 _process_ipaddress (SV *self, ...)
352 CODE:
353 {
354 U32 length = process_length ();
355 if (length != 4)
356 {
357 error ("IP ADDRESS length not four");
358 XSRETURN_UNDEF;
359 }
360
361 U8 *data = getn (4, "\x00\x00\x00\x00");
362 RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
363 }
364 OUTPUT:
365 RETVAL
366
367 SV *
368 process (SV *self, SV *expected = 0, SV *found = 0)
369 PPCODE:
370 {
371 U8 type = get8 ();
372
373 if (expected && SvOK (expected) && type != SvIV (expected))
374 {
375 error ("Expected a different type than found");
376 XSRETURN_UNDEF;
377 }
378
379 if (type > AvFILLp (av_type) || !SvTYPE (AvARRAY (av_type)[type]) == SVt_PVCV)
380 {
381 sv_dump (AvARRAY (av_type)[type]);//D
382 error ("Unknown ASN.1 type");
383 XSRETURN_UNDEF;
384 }
385
386 if (found)
387 sv_setiv (found, type);
388
389 //TODO: switch based on type, to avoid calling overhead
390 SV *res;
391
392 switch (type)
393 {
394 case ASN_OBJECT_IDENTIFIER:
395 res = sv_2mortal (process_object_identifier_sv ());
396 break;
397
398 case ASN_INTEGER32:
399 res = sv_2mortal (process_integer32_sv ());
400 break;
401
402 case ASN_UNSIGNED32:
403 case ASN_COUNTER32:
404 case ASN_TIMETICKS:
405 res = sv_2mortal (process_unsigned32_sv ());
406 break;
407
408 case ASN_SEQUENCE:
409 res = sv_2mortal (newSVuv (process_length ()));
410 break;
411
412 case ASN_OCTET_STRING:
413 case ASN_OPAQUE:
414 res = sv_2mortal (process_octet_string_sv ());
415 break;
416
417 default:
418 {
419 dSP;
420 PUSHMARK (SP);
421 EXTEND (SP, 2);
422 PUSHs (self);
423 PUSHs (expected);
424 PUTBACK;
425 int count = call_sv (AvARRAY (av_type)[type], G_SCALAR);
426 SPAGAIN;
427 res = count ? TOPs : &PL_sv_undef;
428 }
429 }
430
431 XPUSHs (errflag ? &PL_sv_undef : res);
432 }
433
434 #if 0
435
436 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
437
438 SV *
439 _process_var_bind_list (SV *self)
440 CODE:
441 {
442 # VarBindList::=SEQUENCE
443 if (!defined($value = $this->process(SEQUENCE))) {
444 return $this->_error;
445 }
446
447 # Using the length of the VarBindList SEQUENCE,
448 # calculate the end index.
449
450 my $end = $this->index + $value;
451
452 $this->{_var_bind_list} = {};
453 $this->{_var_bind_names} = [];
454 $this->{_var_bind_types} = {};
455
456 my ($oid, $type);
457
458 while ($this->index < $end) {
459
460 # VarBind::=SEQUENCE
461 if (!defined($this->process(SEQUENCE))) {
462 return $this->_error;
463 }
464 # name::=ObjectName
465 if (!defined($oid = $this->process(OBJECT_IDENTIFIER))) {
466 return $this->_error;
467 }
468 # value::=ObjectSyntax
469 if (!defined($value = $this->process(undef, $type))) {
470 return $this->_error;
471 }
472
473 # Create a hash consisting of the OBJECT IDENTIFIER as a
474 # key and the ObjectSyntax as the value. If there is a
475 # duplicate OBJECT IDENTIFIER in the VarBindList, we pad
476 # that OBJECT IDENTIFIER with spaces to make a unique
477 # key in the hash.
478
479 while (exists($this->{_var_bind_list}->{$oid})) {
480 $oid .= ' '; # Pad with spaces
481 }
482
483 DEBUG_INFO('{ %s => %s: %s }', $oid, asn1_itoa($type), $value);
484 $this->{_var_bind_list}->{$oid} = $value;
485 $this->{_var_bind_types}->{$oid} = $type;
486
487 # Create an array with the ObjectName OBJECT IDENTIFIERs
488 # so that the order in which the VarBinds where encoded
489 # in the PDU can be retrieved later.
490
491 push(@{$this->{_var_bind_names}}, $oid);
492
493 }
494
495 # Return an error based on the contents of the VarBindList
496 # if we received a Report-PDU.
497
498 return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT);
499
500 # Return the var_bind_list hash
501 $this->{_var_bind_list};
502 }
503
504 #endif
505