BER.pm
上传用户:shbosideng
上传日期:2013-05-04
资源大小:1555k
文件大小:27k
源码类别:

SNMP编程

开发平台:

C/C++

  1. ### -*- mode: Perl -*-
  2. ######################################################################
  3. ### BER (Basic Encoding Rules) encoding and decoding.
  4. ######################################################################
  5. ### Copyright (c) 1995-2002, Simon Leinen.
  6. ###
  7. ### This program is free software; you can redistribute it under the
  8. ### "Artistic License" included in this distribution (file "Artistic").
  9. ######################################################################
  10. ### This module implements encoding and decoding of ASN.1-based data
  11. ### structures using the Basic Encoding Rules (BER).  Only the subset
  12. ### necessary for SNMP is implemented.
  13. ######################################################################
  14. ### Created by:  Simon Leinen  <simon@switch.ch>
  15. ###
  16. ### Contributions and fixes by:
  17. ###
  18. ### Andrzej Tobola <san@iem.pw.edu.pl>:  Added long String decode
  19. ### Tobias Oetiker <oetiker@ee.ethz.ch>:  Added 5 Byte Integer decode ...
  20. ### Dave Rand <dlr@Bungi.com>:  Added SysUpTime decode
  21. ### Philippe Simonet <sip00@vg.swissptt.ch>:  Support larger subids
  22. ### Yufang HU <yhu@casc.com>:  Support even larger subids
  23. ### Mike Mitchell <Mike.Mitchell@sas.com>: New generalized encode_int()
  24. ### Mike Diehn <mdiehn@mindspring.net>: encode_ip_address()
  25. ### Rik Hoorelbeke <rik.hoorelbeke@pandora.be>: encode_oid() fix
  26. ### Brett T Warden <wardenb@eluminant.com>: pretty UInteger32
  27. ### Bert Driehuis <driehuis@playbeing.org>: Handle SNMPv2 exception codes
  28. ### Jakob Ilves (/IlvJa) <jakob.ilves@oracle.com>: PDU decoding
  29. ### Jan Kasprzak <kas@informatics.muni.cz>: Fix for PDU syntax check
  30. ### Milen Pavlov <milen@batmbg.com>: Recognize variant length for ints
  31. ######################################################################
  32. package BER;
  33. require 5.002;
  34. use strict;
  35. use vars qw(@ISA @EXPORT $VERSION $pretty_print_timeticks
  36.     %pretty_printer %default_printer $errmsg);
  37. use Exporter;
  38. $VERSION = '1.05';
  39. @ISA = qw(Exporter);
  40. @EXPORT = qw(context_flag constructor_flag
  41.      encode_int encode_int_0 encode_null encode_oid
  42.      encode_sequence encode_tagged_sequence
  43.      encode_string encode_ip_address encode_timeticks
  44.      encode_uinteger32 encode_counter32 encode_counter64
  45.      encode_gauge32 
  46.      decode_sequence decode_by_template
  47.      pretty_print pretty_print_timeticks
  48.      hex_string hex_string_of_type
  49.      encoded_oid_prefix_p errmsg
  50.      register_pretty_printer unregister_pretty_printer);
  51. ### Variables
  52. ## Bind this to zero if you want to avoid that TimeTicks are converted
  53. ## into "human readable" strings containing days, hours, minutes and
  54. ## seconds.
  55. ##
  56. ## If the variable is zero, pretty_print will simply return an
  57. ## unsigned integer representing hundredths of seconds.
  58. ##
  59. $pretty_print_timeticks = 1;
  60. ### Prototypes
  61. sub encode_header ($$);
  62. sub encode_int_0 ();
  63. sub encode_int ($);
  64. sub encode_oid (@);
  65. sub encode_null ();
  66. sub encode_sequence (@);
  67. sub encode_tagged_sequence ($@);
  68. sub encode_string ($);
  69. sub encode_ip_address ($);
  70. sub encode_timeticks ($);
  71. sub pretty_print ($);
  72. sub pretty_using_decoder ($$);
  73. sub pretty_string ($);
  74. sub pretty_intlike ($);
  75. sub pretty_unsignedlike ($);
  76. sub pretty_oid ($);
  77. sub pretty_uptime ($);
  78. sub pretty_uptime_value ($);
  79. sub pretty_ip_address ($);
  80. sub pretty_generic_sequence ($);
  81. sub register_pretty_printer ($);
  82. sub unregister_pretty_printer ($);
  83. sub hex_string ($);
  84. sub hex_string_of_type ($$);
  85. sub decode_oid ($);
  86. sub decode_by_template;
  87. sub decode_by_template_2;
  88. sub decode_sequence ($);
  89. sub decode_int ($);
  90. sub decode_intlike ($);
  91. sub decode_unsignedlike ($);
  92. sub decode_intlike_s ($$);
  93. sub decode_string ($);
  94. sub decode_length ($@);
  95. sub encoded_oid_prefix_p ($$);
  96. sub decode_subid ($$$);
  97. sub decode_generic_tlv ($);
  98. sub error (@);
  99. sub template_error ($$$);
  100. sub version () { $VERSION; }
  101. ### Flags for different types of tags
  102. sub universal_flag { 0x00 }
  103. sub application_flag { 0x40 }
  104. sub context_flag { 0x80 }
  105. sub private_flag { 0xc0 }
  106. sub primitive_flag { 0x00 }
  107. sub constructor_flag { 0x20 }
  108. ### Universal tags
  109. sub boolean_tag { 0x01 }
  110. sub int_tag { 0x02 }
  111. sub bit_string_tag { 0x03 }
  112. sub octet_string_tag { 0x04 }
  113. sub null_tag { 0x05 }
  114. sub object_id_tag { 0x06 }
  115. sub sequence_tag { 0x10 }
  116. sub set_tag { 0x11 }
  117. sub uptime_tag { 0x43 }
  118. ### Flag for length octet announcing multi-byte length field
  119. sub long_length { 0x80 }
  120. ### SNMP specific tags
  121. sub snmp_ip_address_tag { 0x00 | application_flag () }
  122. sub snmp_counter32_tag { 0x01 | application_flag () }
  123. sub snmp_gauge32_tag { 0x02 | application_flag () }
  124. sub snmp_timeticks_tag { 0x03 | application_flag () }
  125. sub snmp_opaque_tag { 0x04 | application_flag () }
  126. sub snmp_nsap_address_tag { 0x05 | application_flag () }
  127. sub snmp_counter64_tag { 0x06 | application_flag () }
  128. sub snmp_uinteger32_tag { 0x07 | application_flag () }
  129. ## Error codes (SNMPv2 and later)
  130. ##
  131. sub snmp_nosuchobject { context_flag () | 0x00 }
  132. sub snmp_nosuchinstance { context_flag () | 0x01 }
  133. sub snmp_endofmibview { context_flag () | 0x02 }
  134. ### pretty-printer initialization code.  Create a hash with
  135. ### the most common types of pretty-printer routines.
  136. BEGIN {
  137.     $default_printer{int_tag()}             = &pretty_intlike;
  138.     $default_printer{snmp_counter32_tag()}  = &pretty_unsignedlike;
  139.     $default_printer{snmp_gauge32_tag()}    = &pretty_unsignedlike;
  140.     $default_printer{snmp_counter64_tag()}  = &pretty_unsignedlike;
  141.     $default_printer{snmp_uinteger32_tag()} = &pretty_unsignedlike;
  142.     $default_printer{octet_string_tag()}    = &pretty_string;
  143.     $default_printer{object_id_tag()}       = &pretty_oid;
  144.     $default_printer{snmp_ip_address_tag()} = &pretty_ip_address;
  145.     %pretty_printer = %default_printer;
  146. }
  147. #### Encoding
  148. sub encode_header ($$) {
  149.     my ($type,$length) = @_;
  150.     return pack ("C C", $type, $length) if $length < 128;
  151.     return pack ("C C C", $type, long_length | 1, $length) if $length < 256;
  152.     return pack ("C C n", $type, long_length | 2, $length) if $length < 65536;
  153.     return error ("Cannot encode length $length yet");
  154. }
  155. sub encode_int_0 () {
  156.     return pack ("C C C", 2, 1, 0);
  157. }
  158. sub encode_int ($) {
  159.     return encode_intlike ($_[0], int_tag);
  160. }
  161. sub encode_uinteger32 ($) {
  162.     return encode_intlike ($_[0], snmp_uinteger32_tag);
  163. }
  164. sub encode_counter32 ($) {
  165.     return encode_intlike ($_[0], snmp_counter32_tag);
  166. }
  167. sub encode_counter64 ($) {
  168.     return encode_intlike ($_[0], snmp_counter64_tag);
  169. }
  170. sub encode_gauge32 ($) {
  171.     return encode_intlike ($_[0], snmp_gauge32_tag);
  172. }
  173. sub encode_intlike ($$) {
  174.     my ($int, $tag)=@_;
  175.     my ($sign, $val, @vals);
  176.     $sign = ($int >= 0) ? 0 : 0xff;
  177.     if (ref $int && $int->isa ("Math::BigInt")) {
  178. for(;;) {
  179.     $val = $int->copy()->bmod (256);
  180.     unshift(@vals, $val);
  181.     return encode_header ($tag, $#vals + 1).pack ("C*", @vals)
  182. if ($int >= -128 && $int < 128);
  183.     $int->bsub ($sign)->bdiv (256);
  184. }
  185.     } else {
  186. for(;;) {
  187.     $val = $int & 0xff;
  188.     unshift(@vals, $val);
  189.     return encode_header ($tag, $#vals + 1).pack ("C*", @vals)
  190. if ($int >= -128 && $int < 128);
  191.     $int -= $sign, $int = int($int / 256);
  192. }
  193.     }
  194. }
  195. sub encode_oid (@) {
  196.     my @oid = @_;
  197.     my ($result,$subid);
  198.     $result = '';
  199.     ## Ignore leading empty sub-ID.  The favourite reason for
  200.     ## those to occur is that people cut&paste numeric OIDs from
  201.     ## CMU/UCD SNMP including the leading dot.
  202.     shift @oid if $oid[0] eq '';
  203.     return error ("Object ID too short: ", join('.',@oid))
  204. if $#oid < 1;
  205.     ## The first two subids in an Object ID are encoded as a single
  206.     ## byte in BER, according to a funny convention.  This poses
  207.     ## restrictions on the ranges of those subids.  In the past, I
  208.     ## didn't check for those.  But since so many people try to use
  209.     ## OIDs in CMU/UCD SNMP's format and leave out the mib-2 or
  210.     ## enterprises prefix, I introduced this check to catch those
  211.     ## errors.
  212.     ##
  213.     return error ("first subid too big in Object ID ", join('.',@oid))
  214. if $oid[0] > 2;
  215.     $result = shift (@oid) * 40;
  216.     $result += shift @oid;
  217.     return error ("second subid too big in Object ID ", join('.',@oid))
  218. if $result > 255;
  219.     $result = pack ("C", $result);
  220.     foreach $subid (@oid) {
  221. if ( ($subid>=0) && ($subid<128) ){ #7 bits long subid 
  222.     $result .= pack ("C", $subid);
  223. } elsif ( ($subid>=128) && ($subid<16384) ){ #14 bits long subid
  224.     $result .= pack ("CC", 0x80 | $subid >> 7, $subid & 0x7f);
  225. elsif ( ($subid>=16384) && ($subid<2097152) ) {#21 bits long subid
  226.     $result .= pack ("CCC",
  227.      0x80 | (($subid>>14) & 0x7f), 
  228.      0x80 | (($subid>>7) & 0x7f),
  229.      $subid & 0x7f); 
  230. } elsif ( ($subid>=2097152) && ($subid<268435456) ){ #28 bits long subid
  231.     $result .= pack ("CCCC", 
  232.      0x80 | (($subid>>21) & 0x7f),
  233.      0x80 | (($subid>>14) & 0x7f),
  234.      0x80 | (($subid>>7) & 0x7f),
  235.      $subid & 0x7f);
  236. } elsif ( ($subid>=268435456) && ($subid<4294967296) ){ #32 bits long subid
  237.     $result .= pack ("CCCCC", 
  238.      0x80 | (($subid>>28) & 0x0f), #mask the bits beyond 32 
  239.      0x80 | (($subid>>21) & 0x7f),
  240.      0x80 | (($subid>>14) & 0x7f),
  241.      0x80 | (($subid>>7) & 0x7f),
  242.      $subid & 0x7f);
  243. } else {
  244.     return error ("Cannot encode subid $subid");
  245. }
  246.     }
  247.     encode_header (object_id_tag, length $result).$result;
  248. }
  249. sub encode_null () { encode_header (null_tag, 0); }
  250. sub encode_sequence (@) { encode_tagged_sequence (sequence_tag, @_); }
  251. sub encode_tagged_sequence ($@) {
  252.     my ($tag,$result);
  253.     $tag = shift @_;
  254.     $result = join '',@_;
  255.     return encode_header ($tag | constructor_flag, length $result).$result;
  256. }
  257. sub encode_string ($) {
  258.     my ($string)=@_;
  259.     return encode_header (octet_string_tag, length $string).$string;
  260. }
  261. sub encode_ip_address ($) {
  262.     my ($addr)=@_;
  263.     my @octets;
  264.     if (length $addr == 4) {
  265.       ## Four bytes... let's suppose that this is a binary IP address
  266.       ## in network byte order.
  267.       return encode_header (snmp_ip_address_tag, length $addr).$addr;
  268.     } elsif (@octets = ($addr =~ /^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$/)) {
  269.       return encode_ip_address (pack ("CCCC", @octets));
  270.     } else {
  271.       return error ("IP address must be four bytes long or a dotted-quad");
  272.     }
  273. }
  274. sub encode_timeticks ($) {
  275.   my ($tt) = @_;
  276.   return encode_intlike ($tt, snmp_timeticks_tag);
  277. }
  278. #### Decoding
  279. sub pretty_print ($) {
  280.     my ($packet) = @_;
  281.     return undef unless defined $packet;
  282.     my $result = ord (substr ($packet, 0, 1));
  283.     if (exists ($pretty_printer{$result})) {
  284. my $c_ref = $pretty_printer{$result};
  285. return &$c_ref ($packet);
  286.     }
  287.     return ($pretty_print_timeticks
  288.     ? pretty_uptime ($packet)
  289.     : pretty_unsignedlike ($packet))
  290. if $result == uptime_tag;
  291.     return "(null)" if $result == null_tag;
  292.     return error ("Exception code: noSuchObject") if $result == snmp_nosuchobject;
  293.     return error ("Exception code: noSuchInstance") if $result == snmp_nosuchinstance;
  294.     return error ("Exception code: endOfMibView") if $result == snmp_endofmibview;
  295.     # IlvJa
  296.     # pretty print sequences and their contents.
  297.     my $ctx_cons_flags = context_flag | constructor_flag;
  298.     if($result == (&constructor_flag | &sequence_tag) # sequence
  299. || $result == (0 | $ctx_cons_flags) #get_request
  300. || $result == (1 | $ctx_cons_flags) #getnext_request
  301. || $result == (2 | $ctx_cons_flags) #get_response
  302. || $result == (3 | $ctx_cons_flags) #set_request
  303. || $result == (4 | $ctx_cons_flags) #trap_request
  304. || $result == (5 | $ctx_cons_flags) #getbulk_request
  305. || $result == (6 | $ctx_cons_flags) #inform_request
  306. || $result == (7 | $ctx_cons_flags) #trap2_request
  307. )
  308.     {
  309. my $pretty_result = pretty_generic_sequence($packet);
  310. $pretty_result =~ s/^/    /gm; #Indent.
  311. my $seq_type_desc =
  312. {
  313.     (constructor_flag | sequence_tag) => "Sequence",
  314.     (0 | $ctx_cons_flags)             => "GetRequest",
  315.     (1 | $ctx_cons_flags)             => "GetNextRequest",
  316.     (2 | $ctx_cons_flags)             => "GetResponse",
  317.     (3 | $ctx_cons_flags)             => "SetRequest",
  318.     (4 | $ctx_cons_flags)             => "TrapRequest",
  319.     (5 | $ctx_cons_flags)             => "GetbulkRequest",
  320.     (6 | $ctx_cons_flags)             => "InformRequest",
  321.     (7 | $ctx_cons_flags)             => "Trap2Request",
  322. }->{($result)};
  323. return $seq_type_desc . "{n" . $pretty_result . "n}";
  324.     }
  325.     return sprintf ("#<unprintable BER type 0x%x>", $result);
  326. }
  327. sub pretty_using_decoder ($$) {
  328.     my ($decoder, $packet) = @_;
  329.     my ($decoded,$rest);
  330.     ($decoded,$rest) = &$decoder ($packet);
  331.     return error ("Junk after object") unless $rest eq '';
  332.     return $decoded;
  333. }
  334. sub pretty_string ($) {
  335.     pretty_using_decoder (&decode_string, $_[0]);
  336. }
  337. sub pretty_intlike ($) {
  338.     my $decoded = pretty_using_decoder (&decode_intlike, $_[0]);
  339.     $decoded;
  340. }
  341. sub pretty_unsignedlike ($) {
  342.     return pretty_using_decoder (&decode_unsignedlike, $_[0]);
  343. }
  344. sub pretty_oid ($) {
  345.     my ($oid) = shift;
  346.     my ($result,$subid,$next);
  347.     my (@oid);
  348.     $result = ord (substr ($oid, 0, 1));
  349.     return error ("Object ID expected") unless $result == object_id_tag;
  350.     ($result, $oid) = decode_length ($oid, 1);
  351.     return error ("inconsistent length in OID") unless $result == length $oid;
  352.     @oid = ();
  353.     $subid = ord (substr ($oid, 0, 1));
  354.     push @oid, int ($subid / 40);
  355.     push @oid, $subid % 40;
  356.     $oid = substr ($oid, 1);
  357.     while ($oid ne '') {
  358. $subid = ord (substr ($oid, 0, 1));
  359. if ($subid < 128) {
  360.     $oid = substr ($oid, 1);
  361.     push @oid, $subid;
  362. } else {
  363.     $next = $subid;
  364.     $subid = 0;
  365.     while ($next >= 128) {
  366. $subid = ($subid << 7) + ($next & 0x7f);
  367. $oid = substr ($oid, 1);
  368. $next = ord (substr ($oid, 0, 1));
  369.     }
  370.     $subid = ($subid << 7) + $next;
  371.     $oid = substr ($oid, 1);
  372.     push @oid, $subid;
  373. }
  374.     }
  375.     join ('.', @oid);
  376. }
  377. sub pretty_uptime ($) {
  378.     my ($packet,$uptime);
  379.     ($uptime,$packet) = &decode_unsignedlike (@_);
  380.     pretty_uptime_value ($uptime);
  381. }
  382. sub pretty_uptime_value ($) {
  383.     my ($uptime) = @_;
  384.     my ($seconds,$minutes,$hours,$days,$result);
  385.     ## We divide the uptime by hundred since we're not interested in
  386.     ## sub-second precision.
  387.     $uptime = int ($uptime / 100);
  388.     $days = int ($uptime / (60 * 60 * 24));
  389.     $uptime %= (60 * 60 * 24);
  390.     $hours = int ($uptime / (60 * 60));
  391.     $uptime %= (60 * 60);
  392.     $minutes = int ($uptime / 60);
  393.     $seconds = $uptime % 60;
  394.     if ($days == 0){
  395. $result = sprintf ("%d:%02d:%02d", $hours, $minutes, $seconds);
  396.     } elsif ($days == 1) {
  397. $result = sprintf ("%d day, %d:%02d:%02d", 
  398.    $days, $hours, $minutes, $seconds);
  399.     } else {
  400. $result = sprintf ("%d days, %d:%02d:%02d", 
  401.    $days, $hours, $minutes, $seconds);
  402.     }
  403.     return $result;
  404. }
  405. sub pretty_ip_address ($) {
  406.     my $pdu = shift;
  407.     my ($length, $rest);
  408.     return error ("IP Address tag (".snmp_ip_address_tag.") expected")
  409. unless ord (substr ($pdu, 0, 1)) == snmp_ip_address_tag;
  410.     ($length,$pdu) = decode_length ($pdu, 1);
  411.     return error ("Length of IP address should be four")
  412. unless $length == 4;
  413.     sprintf "%d.%d.%d.%d", unpack ("CCCC", $pdu);
  414. }
  415. # IlvJa
  416. # Returns a string with the pretty prints of all
  417. # the elements in the sequence.
  418. sub pretty_generic_sequence ($) {
  419.     my ($pdu) = shift;
  420.     my $rest;
  421.     my $type = ord substr ($pdu, 0 ,1);
  422.     my $flags = context_flag | constructor_flag;
  423.     
  424.     return error (sprintf ("Tag 0x%x is not a valid sequence tag",$type))
  425. unless ($type == (&constructor_flag | &sequence_tag) # sequence
  426. || $type == (0 | $flags) #get_request
  427. || $type == (1 | $flags) #getnext_request
  428. || $type == (2 | $flags) #get_response
  429. || $type == (3 | $flags) #set_request
  430. || $type == (4 | $flags) #trap_request
  431. || $type == (5 | $flags) #getbulk_request
  432. || $type == (6 | $flags) #inform_request
  433. || $type == (7 | $flags) #trap2_request
  434. );
  435.     
  436.     my $curelem;
  437.     my $pretty_result; # Holds the pretty printed sequence.
  438.     my $pretty_elem;   # Holds the pretty printed current elem.
  439.     my $first_elem = 'true';
  440.     
  441.     # Cut away the first Tag and Length from $packet and then
  442.     # init $rest with that.
  443.     (undef, $rest) = decode_length ($pdu, 1);
  444.     while($rest)
  445.     {
  446. ($curelem,$rest) = decode_generic_tlv($rest);
  447. $pretty_elem = pretty_print($curelem);
  448. $pretty_result .= "n" if not $first_elem;
  449. $pretty_result .= $pretty_elem;
  450. # The rest of the iterations are not related to the
  451. # first element of the sequence so..
  452. $first_elem = '' if $first_elem;
  453.     }
  454.     return $pretty_result;
  455. }    
  456. sub hex_string ($) {
  457.     &hex_string_of_type ($_[0], octet_string_tag);
  458. }
  459. sub hex_string_of_type ($$) {
  460.     my ($pdu, $wanted_type) = @_;
  461.     my ($length);
  462.     return error ("BER tag ".$wanted_type." expected")
  463. unless ord (substr ($pdu, 0, 1)) == $wanted_type;
  464.     ($length,$pdu) = decode_length ($pdu, 1);
  465.     hex_string_aux ($pdu);
  466. }
  467. sub hex_string_aux ($) {
  468.     my ($binary_string) = @_;
  469.     my ($c, $result);
  470.     $result = '';
  471.     for $c (unpack "C*", $binary_string) {
  472. $result .= sprintf "%02x", $c;
  473.     }
  474.     $result;
  475. }
  476. sub decode_oid ($) {
  477.     my ($pdu) = @_;
  478.     my ($result,$pdu_rest);
  479.     my (@result);
  480.     $result = ord (substr ($pdu, 0, 1));
  481.     return error ("Object ID expected") unless $result == object_id_tag;
  482.     ($result, $pdu_rest) = decode_length ($pdu, 1);
  483.     return error ("Short PDU")
  484. if $result > length $pdu_rest;
  485.     @result = (substr ($pdu, 0, $result + (length ($pdu) - length ($pdu_rest))),
  486.        substr ($pdu_rest, $result));
  487.     @result;
  488. }
  489. # IlvJa
  490. # This takes a PDU and returns a two element list consisting of
  491. # the first element found in the PDU (whatever it is) and the
  492. # rest of the PDU
  493. sub decode_generic_tlv ($) {
  494.     my ($pdu) = @_;
  495.     my (@result);
  496.     my ($elemlength,$pdu_rest) = decode_length ($pdu, 1);
  497.     @result = (# Extract the first element.
  498.        substr ($pdu, 0, $elemlength + (length ($pdu)
  499.        - length ($pdu_rest)
  500.        )
  501.        ),
  502.        #Extract the rest of the PDU.
  503.        substr ($pdu_rest, $elemlength)
  504.        );
  505.     @result;
  506. }
  507. sub decode_by_template {
  508.     my ($pdu) = shift;
  509.     local ($_) = shift;
  510.     return decode_by_template_2 ($pdu, $_, 0, 0, @_);
  511. }
  512. my $template_debug = 0;
  513. sub decode_by_template_2 {
  514.     my ($pdu, $template, $pdu_index, $template_index);
  515.     local ($_);
  516.     $pdu = shift;
  517.     $template = $_ = shift;
  518.     $pdu_index = shift;
  519.     $template_index = shift;
  520.     my (@results);
  521.     my ($length,$expected,$read,$rest);
  522.     return undef unless defined $pdu;
  523.     while (0 < length ($_)) {
  524. if (substr ($_, 0, 1) eq '%') {
  525.     print STDERR "template $_ ", length $pdu," bytes remainingn"
  526. if $template_debug;
  527.     $_ = substr ($_,1);
  528.     ++$template_index;
  529.     if (($expected) = /^(d*|*){(.*)/) {
  530. ## %{
  531. $template_index += length ($expected) + 1;
  532. print STDERR "%{n" if $template_debug;
  533. $_ = $2;
  534. $expected = shift | constructor_flag if ($expected eq '*');
  535. $expected = sequence_tag | constructor_flag
  536.     if $expected eq '';
  537. return template_error ("Unexpected end of PDU",
  538.        $template, $template_index)
  539.     if !defined $pdu or $pdu eq '';
  540. return template_error ("Expected sequence tag $expected, got ".
  541.        ord (substr ($pdu, 0, 1)),
  542.       $template,
  543.       $template_index)
  544.     unless (ord (substr ($pdu, 0, 1)) == $expected);
  545. (($length,$pdu) = decode_length ($pdu, 1))
  546.     || return template_error ("cannot read length",
  547.       $template, $template_index);
  548. return template_error ("Expected length $length, got ".length $pdu ,
  549.       $template, $template_index)
  550.   unless length $pdu == $length;
  551.     } elsif (($expected,$rest) = /^(*|)s(.*)/) {
  552. ## %s
  553. $template_index += length ($expected) + 1;
  554. ($expected = shift) if $expected eq '*';
  555. (($read,$pdu) = decode_string ($pdu))
  556.     || return template_error ("cannot read string",
  557.       $template, $template_index);
  558. print STDERR "%s => $readn" if $template_debug;
  559. if ($expected eq '') {
  560.     push @results, $read;
  561. } else {
  562.     return template_error ("Expected $expected, read $read",
  563.    $template, $template_index)
  564. unless $expected eq $read;
  565. }
  566. $_ = $rest;
  567.     } elsif (($rest) = /^A(.*)/) {
  568. ## %A
  569. $template_index += 1;
  570. {
  571.     my ($tag, $length, $value);
  572.     $tag = ord (substr ($pdu, 0, 1));
  573.     return error ("Expected IP address, got tag ".$tag)
  574. unless $tag == snmp_ip_address_tag;
  575.     ($length, $pdu) = decode_length ($pdu, 1);
  576.     return error ("Inconsistent length of InetAddress encoding")
  577. if $length > length $pdu;
  578.     return template_error ("IP address must be four bytes long",
  579.    $template, $template_index)
  580. unless $length == 4;
  581.     $read = substr ($pdu, 0, $length);
  582.     $pdu = substr ($pdu, $length);
  583. }
  584. print STDERR "%A => $readn" if $template_debug;
  585. push @results, $read;
  586. $_ = $rest;
  587.     } elsif (/^O(.*)/) {
  588. ## %O
  589. $template_index += 1;
  590. $_ = $1;
  591. (($read,$pdu) = decode_oid ($pdu))
  592.   || return template_error ("cannot read OID",
  593.     $template, $template_index);
  594. print STDERR "%O => ".pretty_oid ($read)."n"
  595.     if $template_debug;
  596. push @results, $read;
  597.     } elsif (($expected,$rest) = /^(d*|*|)i(.*)/) {
  598. ## %i
  599. $template_index += length ($expected) + 1;
  600. print STDERR "%in" if $template_debug;
  601. $_ = $rest;
  602. (($read,$pdu) = decode_int ($pdu))
  603.   || return template_error ("cannot read int",
  604.     $template, $template_index);
  605. if ($expected eq '') {
  606.     push @results, $read;
  607. } else {
  608.     $expected = int (shift) if $expected eq '*';
  609.     return template_error (sprintf ("Expected %d (0x%x), got %d (0x%x)",
  610.     $expected, $expected, $read, $read),
  611.    $template, $template_index)
  612. unless ($expected == $read)
  613. }
  614.     } elsif (($rest) = /^u(.*)/) {
  615. ## %u
  616. $template_index += 1;
  617. print STDERR "%un" if $template_debug;
  618. $_ = $rest;
  619. (($read,$pdu) = decode_unsignedlike ($pdu))
  620.   || return template_error ("cannot read uptime",
  621.     $template, $template_index);
  622. push @results, $read;
  623.     } elsif (/^@(.*)/) {
  624. ## %@
  625. $template_index += 1;
  626. print STDERR "%@n" if $template_debug;
  627. $_ = $1;
  628. push @results, $pdu;
  629. $pdu = '';
  630.     } else {
  631. return template_error ("Unknown decoding directive in template: $_",
  632.        $template, $template_index);
  633.     }
  634. } else {
  635.     if (substr ($_, 0, 1) ne substr ($pdu, 0, 1)) {
  636. return template_error ("Expected ".substr ($_, 0, 1).", got ".substr ($pdu, 0, 1),
  637.        $template, $template_index);
  638.     }
  639.     $_ = substr ($_,1);
  640.     $pdu = substr ($pdu,1);
  641. }
  642.     }
  643.     return template_error ("PDU too long", $template, $template_index)
  644.       if length ($pdu) > 0;
  645.     return template_error ("PDU too short", $template, $template_index)
  646.       if length ($_) > 0;
  647.     @results;
  648. }
  649. sub decode_sequence ($) {
  650.     my ($pdu) = @_;
  651.     my ($result);
  652.     my (@result);
  653.     $result = ord (substr ($pdu, 0, 1));
  654.     return error ("Sequence expected")
  655. unless $result == (sequence_tag | constructor_flag);
  656.     ($result, $pdu) = decode_length ($pdu, 1);
  657.     return error ("Short PDU")
  658. if $result > length $pdu;
  659.     @result = (substr ($pdu, 0, $result), substr ($pdu, $result));
  660.     @result;
  661. }
  662. sub decode_int ($) {
  663.     my ($pdu) = @_;
  664.     my $tag = ord (substr ($pdu, 0, 1));
  665.     return error ("Integer expected, found tag ".$tag)
  666. unless $tag == int_tag;
  667.     decode_intlike ($pdu);
  668. }
  669. sub decode_intlike ($) {
  670.     decode_intlike_s ($_[0], 1);
  671. }
  672. sub decode_unsignedlike ($) {
  673.     decode_intlike_s ($_[0], 0);
  674. }
  675. my $have_math_bigint_p = 0;
  676. sub decode_intlike_s ($$) {
  677.     my ($pdu, $signedp) = @_;
  678.     my ($length,$result);
  679.     ($length,$pdu) = decode_length ($pdu, 1);
  680.     my $ptr = 0;
  681.     $result = unpack ($signedp ? "c" : "C", substr ($pdu, $ptr++, 1));
  682.     if ($length > 5 || ($length == 5 && $result > 0)) {
  683. require 'Math/BigInt.pm' unless $have_math_bigint_p++;
  684. $result = new Math::BigInt ($result);
  685.     }
  686.     while (--$length > 0) {
  687. $result *= 256;
  688. $result += unpack ("C", substr ($pdu, $ptr++, 1));
  689.     }
  690.     ($result, substr ($pdu, $ptr));
  691. }
  692. sub decode_string ($) {
  693.     my ($pdu) = shift;
  694.     my ($result);
  695.     $result = ord (substr ($pdu, 0, 1));
  696.     return error ("Expected octet string, got tag ".$result)
  697. unless $result == octet_string_tag;
  698.     ($result, $pdu) = decode_length ($pdu, 1);
  699.     return error ("Short PDU")
  700. if $result > length $pdu;
  701.     return (substr ($pdu, 0, $result), substr ($pdu, $result));
  702. }
  703. sub decode_length ($@) {
  704.     my ($pdu) = shift;
  705.     my $index = shift || 0;
  706.     my ($result);
  707.     my (@result);
  708.     $result = ord (substr ($pdu, $index, 1));
  709.     if ($result & long_length) {
  710. if ($result == (long_length | 1)) {
  711.     @result = (ord (substr ($pdu, $index+1, 1)), substr ($pdu, $index+2));
  712. } elsif ($result == (long_length | 2)) {
  713.     @result = ((ord (substr ($pdu, $index+1, 1)) << 8)
  714.        + ord (substr ($pdu, $index+2, 1)), substr ($pdu, $index+3));
  715. } else {
  716.     return error ("Unsupported length");
  717. }
  718.     } else {
  719. @result = ($result, substr ($pdu, $index+1));
  720.     }
  721.     @result;
  722. }
  723. # This takes a hashref that specifies functions to call when
  724. # the specified value type is being printed.  It returns the
  725. # number of functions that were registered.
  726. sub register_pretty_printer($)
  727. {
  728.     my ($h_ref) = shift;
  729.     my ($type, $val, $cnt);
  730.     $cnt = 0;
  731.     while(($type, $val) = each %$h_ref) {
  732. if (ref $val eq "CODE") {
  733.     $pretty_printer{$type} = $val;
  734.     $cnt++;
  735. }
  736.     }
  737.     return($cnt);
  738. }
  739. # This takes a hashref that specifies functions to call when
  740. # the specified value type is being printed.  It removes the
  741. # functions from the list for the types specified.
  742. # It returns the number of functions that were unregistered.
  743. sub unregister_pretty_printer($)
  744. {
  745.     my ($h_ref) = shift;
  746.     my ($type, $val, $cnt);
  747.     $cnt = 0;
  748.     while(($type, $val) = each %$h_ref) {
  749. if ((exists ($pretty_printer{$type}))
  750.     && ($pretty_printer{$type} == $val)) {
  751.     if (exists($default_printer{$type})) {
  752. $pretty_printer{$type} = $default_printer{$type};
  753.     } else {
  754. delete $pretty_printer{$type};
  755.     }
  756.     $cnt++;
  757. }
  758.     }
  759.     return($cnt);
  760. }
  761. #### OID prefix check
  762. ### encoded_oid_prefix_p OID1 OID2
  763. ###
  764. ### OID1 and OID2 should be BER-encoded OIDs.
  765. ### The function returns non-zero iff OID1 is a prefix of OID2.
  766. ### This can be used in the termination condition of a loop that walks
  767. ### a table using GetNext or GetBulk.
  768. ###
  769. sub encoded_oid_prefix_p ($$) {
  770.     my ($oid1, $oid2) = @_;
  771.     my ($i1, $i2);
  772.     my ($l1, $l2);
  773.     my ($subid1, $subid2);
  774.     return error ("OID tag expected") unless ord (substr ($oid1, 0, 1)) == object_id_tag;
  775.     return error ("OID tag expected") unless ord (substr ($oid2, 0, 1)) == object_id_tag;
  776.     ($l1,$oid1) = decode_length ($oid1, 1);
  777.     ($l2,$oid2) = decode_length ($oid2, 1);
  778.     for ($i1 = 0, $i2 = 0;
  779.  $i1 < $l1 && $i2 < $l2;
  780.  ++$i1, ++$i2) {
  781. ($subid1,$i1) = &decode_subid ($oid1, $i1, $l1);
  782. ($subid2,$i2) = &decode_subid ($oid2, $i2, $l2);
  783. return 0 unless $subid1 == $subid2;
  784.     }
  785.     return $i2 if $i1 == $l1;
  786.     return 0;
  787. }
  788. ### decode_subid OID INDEX
  789. ###
  790. ### Decodes a subid field from a BER-encoded object ID.
  791. ### Returns two values: the field, and the index of the last byte that
  792. ### was actually decoded.
  793. ###
  794. sub decode_subid ($$$) {
  795.     my ($oid, $i, $l) = @_;
  796.     my $subid = 0;
  797.     my $next;
  798.     while (($next = ord (substr ($oid, $i, 1))) >= 128) {
  799. $subid = ($subid << 7) + ($next & 0x7f);
  800. ++$i;
  801. return error ("decoding object ID: short field")
  802.     unless $i < $l;
  803.     }
  804.     return (($subid << 7) + $next, $i);
  805. }
  806. sub error (@) {
  807.   $errmsg = join ("",@_);
  808.   return undef;
  809. }
  810. sub template_error ($$$) {
  811.   my ($errmsg, $template, $index) = @_;
  812.   return error ($errmsg."n  ".$template."n  ".(' ' x $index)."^");
  813. }
  814. 1;