ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.5
Committed: Thu Apr 9 04:49:16 2009 UTC (15 years, 2 months ago) by root
Branch: MAIN
Changes since 1.4: +28 -4 lines
Log Message:
*** empty log message ***

File Contents

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