/perl/vendor/lib/Convert/ASN1/IO.pm

https://github.com/dwimperl/perl-5.12.3.0 · Perl · 261 lines · 195 code · 54 blank · 12 comment · 33 complexity · 5b0cb754c9e343ba30e5ecf4a7e41e78 MD5 · raw file

  1. # Copyright (c) 2000-2005 Graham Barr <gbarr@pobox.com>. All rights reserved.
  2. # This program is free software; you can redistribute it and/or
  3. # modify it under the same terms as Perl itself.
  4. package Convert::ASN1;
  5. use strict;
  6. use Socket;
  7. BEGIN {
  8. local $SIG{__DIE__};
  9. eval { require bytes } and 'bytes'->import
  10. }
  11. sub asn_recv { # $socket, $buffer, $flags
  12. my $peer;
  13. my $buf;
  14. my $n = 128;
  15. my $pos = 0;
  16. my $depth = 0;
  17. my $len = 0;
  18. my($tmp,$tb,$lb);
  19. MORE:
  20. for(
  21. $peer = recv($_[0],$buf,$n,MSG_PEEK);
  22. defined $peer;
  23. $peer = recv($_[0],$buf,$n<<=1,MSG_PEEK)
  24. ) {
  25. if ($depth) { # Are we searching of "\0\0"
  26. unless (2+$pos <= length $buf) {
  27. next MORE if $n == length $buf;
  28. last MORE;
  29. }
  30. if(substr($buf,$pos,2) eq "\0\0") {
  31. unless (--$depth) {
  32. $len = $pos + 2;
  33. last MORE;
  34. }
  35. }
  36. }
  37. # If we can decode a tag and length we can detemine the length
  38. ($tb,$tmp) = asn_decode_tag(substr($buf,$pos));
  39. unless ($tb || $pos+$tb < length $buf) {
  40. next MORE if $n == length $buf;
  41. last MORE;
  42. }
  43. if (ord(substr($buf,$pos+$tb,1)) == 0x80) {
  44. # indefinite length, grrr!
  45. $depth++;
  46. $pos += $tb + 1;
  47. redo MORE;
  48. }
  49. ($lb,$len) = asn_decode_length(substr($buf,$pos+$tb));
  50. if ($lb) {
  51. if ($depth) {
  52. $pos += $tb + $lb + $len;
  53. redo MORE;
  54. }
  55. else {
  56. $len += $tb + $lb + $pos;
  57. last MORE;
  58. }
  59. }
  60. }
  61. if (defined $peer) {
  62. if ($len > length $buf) {
  63. # Check we can read the whole element
  64. goto error
  65. unless defined($peer = recv($_[0],$buf,$len,MSG_PEEK));
  66. if ($len > length $buf) {
  67. # Cannot get whole element
  68. $_[1]='';
  69. return $peer;
  70. }
  71. }
  72. elsif ($len == 0) {
  73. $_[1] = '';
  74. return $peer;
  75. }
  76. if ($_[2] & MSG_PEEK) {
  77. $_[1] = substr($buf,0,$len);
  78. }
  79. elsif (!defined($peer = recv($_[0],$_[1],$len,0))) {
  80. goto error;
  81. }
  82. return $peer;
  83. }
  84. error:
  85. $_[1] = undef;
  86. }
  87. sub asn_read { # $fh, $buffer, $offset
  88. # We need to read one packet, and exactly only one packet.
  89. # So we have to read the first few bytes one at a time, until
  90. # we have enough to decode a tag and a length. We then know
  91. # how many more bytes to read
  92. if ($_[2]) {
  93. if ($_[2] > length $_[1]) {
  94. require Carp;
  95. Carp::carp("Offset beyond end of buffer");
  96. return;
  97. }
  98. substr($_[1],$_[2]) = '';
  99. }
  100. else {
  101. $_[1] = '';
  102. }
  103. my $pos = 0;
  104. my $need = 0;
  105. my $depth = 0;
  106. my $ch;
  107. my $n;
  108. my $e;
  109. while(1) {
  110. $need = ($pos + ($depth * 2)) || 2;
  111. while(($n = $need - length $_[1]) > 0) {
  112. $e = sysread($_[0],$_[1],$n,length $_[1]) or
  113. goto READ_ERR;
  114. }
  115. my $tch = ord(substr($_[1],$pos++,1));
  116. # Tag may be multi-byte
  117. if(($tch & 0x1f) == 0x1f) {
  118. my $ch;
  119. do {
  120. $need++;
  121. while(($n = $need - length $_[1]) > 0) {
  122. $e = sysread($_[0],$_[1],$n,length $_[1]) or
  123. goto READ_ERR;
  124. }
  125. $ch = ord(substr($_[1],$pos++,1));
  126. } while($ch & 0x80);
  127. }
  128. $need = $pos + 1;
  129. while(($n = $need - length $_[1]) > 0) {
  130. $e = sysread($_[0],$_[1],$n,length $_[1]) or
  131. goto READ_ERR;
  132. }
  133. my $len = ord(substr($_[1],$pos++,1));
  134. if($len & 0x80) {
  135. unless ($len &= 0x7f) {
  136. $depth++;
  137. next;
  138. }
  139. $need = $pos + $len;
  140. while(($n = $need - length $_[1]) > 0) {
  141. $e = sysread($_[0],$_[1],$n,length $_[1]) or
  142. goto READ_ERR;
  143. }
  144. $pos += $len + unpack("N", "\0" x (4 - $len) . substr($_[1],$pos,$len));
  145. }
  146. elsif (!$len && !$tch) {
  147. die "Bad ASN PDU" unless $depth;
  148. unless (--$depth) {
  149. last;
  150. }
  151. }
  152. else {
  153. $pos += $len;
  154. }
  155. last unless $depth;
  156. }
  157. while(($n = $pos - length $_[1]) > 0) {
  158. $e = sysread($_[0],$_[1],$n,length $_[1]) or
  159. goto READ_ERR;
  160. }
  161. return length $_[1];
  162. READ_ERR:
  163. $@ = defined($e) ? "Unexpected EOF" : "I/O Error $!"; # . CORE::unpack("H*",$_[1]);
  164. return undef;
  165. }
  166. sub asn_send { # $sock, $buffer, $flags, $to
  167. @_ == 4
  168. ? send($_[0],$_[1],$_[2],$_[3])
  169. : send($_[0],$_[1],$_[2]);
  170. }
  171. sub asn_write { # $sock, $buffer
  172. syswrite($_[0],$_[1], length $_[1]);
  173. }
  174. sub asn_get { # $fh
  175. my $fh = ref($_[0]) ? $_[0] : \($_[0]);
  176. my $href = \%{*$fh};
  177. $href->{'asn_buffer'} = '' unless exists $href->{'asn_buffer'};
  178. my $need = delete $href->{'asn_need'} || 0;
  179. while(1) {
  180. next if $need;
  181. my($tb,$tag) = asn_decode_tag($href->{'asn_buffer'}) or next;
  182. my($lb,$len) = asn_decode_length(substr($href->{'asn_buffer'},$tb,8)) or next;
  183. $need = $tb + $lb + $len;
  184. }
  185. continue {
  186. if ($need && $need <= length $href->{'asn_buffer'}) {
  187. my $ret = substr($href->{'asn_buffer'},0,$need);
  188. substr($href->{'asn_buffer'},0,$need) = '';
  189. return $ret;
  190. }
  191. my $get = $need > 1024 ? $need : 1024;
  192. sysread($_[0], $href->{'asn_buffer'}, $get, length $href->{'asn_buffer'})
  193. or return undef;
  194. }
  195. }
  196. sub asn_ready { # $fh
  197. my $fh = ref($_[0]) ? $_[0] : \($_[0]);
  198. my $href = \%{*$fh};
  199. return 0 unless exists $href->{'asn_buffer'};
  200. return $href->{'asn_need'} <= length $href->{'asn_buffer'}
  201. if exists $href->{'asn_need'};
  202. my($tb,$tag) = asn_decode_tag($href->{'asn_buffer'}) or return 0;
  203. my($lb,$len) = asn_decode_length(substr($href->{'asn_buffer'},$tb,8)) or return 0;
  204. $href->{'asn_need'} = $tb + $lb + $len;
  205. $href->{'asn_need'} <= length $href->{'asn_buffer'};
  206. }
  207. 1;