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

# 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     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 root 1.3 static AV *av_type;
234    
235 root 1.1 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
236    
237 root 1.3 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 root 1.1 void
248     set_msg (SV *msg_, SV *buf_)
249     CODE:
250 root 1.4 errflag = 0;
251     leading_dot = -1;
252     msg = SvREFCNT_inc (msg_);
253     buf = SvPVbyte (buf_, len);
254     cur = buf;
255     rem = len;
256 root 1.2 #ifdef BENCHMARK
257 root 1.4 t1 = tstamp ();
258 root 1.2 #endif
259 root 1.1
260     void
261     clr_msg ()
262     CODE:
263     SvREFCNT_dec (msg);
264     buf = cur = "";
265     len = rem = 0;
266 root 1.2 #ifdef BENCHMARK
267 root 1.1 printf ("%f\n", tstamp () - t1);//D
268 root 1.2 #endif
269 root 1.1
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 root 1.3 _process_sequence = 0
312 root 1.1 CODE:
313     RETVAL = process_length ();
314     OUTPUT:
315     RETVAL
316    
317 root 1.4 SV *
318 root 1.1 _process_integer32 (SV *self, ...)
319 root 1.4 CODE:
320     RETVAL = process_integer32_sv ();
321     OUTPUT:
322     RETVAL
323    
324     SV *
325     _process_counter (SV *self, ...)
326 root 1.1 ALIAS:
327 root 1.4 _process_gauge = 0
328     _process_timeticks = 0
329 root 1.1 CODE:
330 root 1.4 RETVAL = process_unsigned32_sv ();
331 root 1.1 OUTPUT:
332     RETVAL
333    
334     SV *
335     _process_object_identifier (SV *self, ...)
336     CODE:
337 root 1.4 RETVAL = process_object_identifier_sv ();
338 root 1.1 OUTPUT:
339     RETVAL
340    
341     SV *
342     _process_octet_string (SV *self, ...)
343     ALIAS:
344     _process_opaque = 0
345     CODE:
346 root 1.4 RETVAL = process_octet_string_sv ();
347 root 1.1 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 root 1.3 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 root 1.4 //TODO: switch based on type, to avoid calling overhead
390 root 1.3 SV *res;
391    
392 root 1.4 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 root 1.3 }
494 root 1.4
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 root 1.3
504 root 1.4 #endif
505 root 1.3