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

# 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.6 //#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.6 static char *
216     process_object_identifier (int *len)
217 root 1.4 {
218     U32 length = process_length ();
219    
220     if (length <= 0)
221     {
222     error ("OBJECT IDENTIFIER length equal to zero");
223 root 1.6 return "";
224 root 1.4 }
225    
226     U8 *end = cur + length;
227     U32 w = getb ();
228    
229 root 1.6 static char oid[MAX_OID_STRLEN]; // must be static
230 root 1.5 char *app = oid;
231 root 1.4
232 root 1.5 *app = '.'; app += ! ! leading_dot;
233 root 1.6 app = write_uv (app, (U8)w / 40);
234 root 1.5 *app++ = '.';
235 root 1.6 app = write_uv (app, (U8)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 root 1.4
245 root 1.6 *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 root 1.4 {
271 root 1.6 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 root 1.4 }
313    
314 root 1.6 return errflag ? &PL_sv_undef : res;
315 root 1.4 }
316    
317 root 1.1 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
318    
319 root 1.3 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 root 1.1 void
330     set_msg (SV *msg_, SV *buf_)
331     CODE:
332 root 1.4 errflag = 0;
333     leading_dot = -1;
334     msg = SvREFCNT_inc (msg_);
335     buf = SvPVbyte (buf_, len);
336     cur = buf;
337     rem = len;
338 root 1.2 #ifdef BENCHMARK
339 root 1.4 t1 = tstamp ();
340 root 1.2 #endif
341 root 1.1
342     void
343     clr_msg ()
344     CODE:
345     SvREFCNT_dec (msg);
346     buf = cur = "";
347     len = rem = 0;
348 root 1.2 #ifdef BENCHMARK
349 root 1.1 printf ("%f\n", tstamp () - t1);//D
350 root 1.2 #endif
351 root 1.1
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 root 1.3 _process_sequence = 0
394 root 1.1 CODE:
395     RETVAL = process_length ();
396     OUTPUT:
397     RETVAL
398    
399 root 1.4 SV *
400 root 1.1 _process_integer32 (SV *self, ...)
401 root 1.4 CODE:
402     RETVAL = process_integer32_sv ();
403     OUTPUT:
404     RETVAL
405    
406     SV *
407     _process_counter (SV *self, ...)
408 root 1.1 ALIAS:
409 root 1.4 _process_gauge = 0
410     _process_timeticks = 0
411 root 1.1 CODE:
412 root 1.4 RETVAL = process_unsigned32_sv ();
413 root 1.1 OUTPUT:
414     RETVAL
415    
416     SV *
417     _process_object_identifier (SV *self, ...)
418     CODE:
419 root 1.4 RETVAL = process_object_identifier_sv ();
420 root 1.1 OUTPUT:
421     RETVAL
422    
423     SV *
424     _process_octet_string (SV *self, ...)
425     ALIAS:
426     _process_opaque = 0
427     CODE:
428 root 1.4 RETVAL = process_octet_string_sv ();
429 root 1.1 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 root 1.3 SV *
450 root 1.6 process (SV *self, SV *expected = &PL_sv_undef, SV *found = 0)
451     CODE:
452 root 1.3 {
453 root 1.6 int type;
454 root 1.3
455 root 1.6 RETVAL = process_sv (&type);
456 root 1.3
457     if (found)
458     sv_setiv (found, type);
459    
460 root 1.6 if (SvOK (expected) && type != SvIV (expected))
461     error ("Expected a different type than found");
462 root 1.4 }
463 root 1.6 OUTPUT:
464     RETVAL
465 root 1.4
466     MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
467    
468     SV *
469     _process_var_bind_list (SV *self)
470     CODE:
471     {
472 root 1.6 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 root 1.4
485 root 1.6 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 root 1.4
503 root 1.6 //return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT);
504 root 1.4
505 root 1.6 RETVAL = newRV_inc ((SV *)list);
506 root 1.4 }
507 root 1.6 OUTPUT:
508     RETVAL
509 root 1.3
510