annotate .svn/pristine/29/2916f748eef73ff691bcfc74e7c1ac23960d090b.svn-base @ 1327:287f201c2802 redmine-2.2-integration

Add italic
author Chris Cannam <chris.cannam@soundsoftware.ac.uk>
date Wed, 19 Jun 2013 20:56:22 +0100
parents cbb26bc654de
children
rev   line source
Chris@909 1 # $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $
Chris@909 2 #
Chris@909 3 # NET::BER
Chris@909 4 # Mixes ASN.1/BER convenience methods into several standard classes.
Chris@909 5 # Also provides BER parsing functionality.
Chris@909 6 #
Chris@909 7 #----------------------------------------------------------------------------
Chris@909 8 #
Chris@909 9 # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
Chris@909 10 #
Chris@909 11 # Gmail: garbagecat10
Chris@909 12 #
Chris@909 13 # This program is free software; you can redistribute it and/or modify
Chris@909 14 # it under the terms of the GNU General Public License as published by
Chris@909 15 # the Free Software Foundation; either version 2 of the License, or
Chris@909 16 # (at your option) any later version.
Chris@909 17 #
Chris@909 18 # This program is distributed in the hope that it will be useful,
Chris@909 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@909 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@909 21 # GNU General Public License for more details.
Chris@909 22 #
Chris@909 23 # You should have received a copy of the GNU General Public License
Chris@909 24 # along with this program; if not, write to the Free Software
Chris@909 25 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Chris@909 26 #
Chris@909 27 #---------------------------------------------------------------------------
Chris@909 28 #
Chris@909 29 #
Chris@909 30
Chris@909 31
Chris@909 32
Chris@909 33
Chris@909 34 module Net
Chris@909 35
Chris@909 36 module BER
Chris@909 37
Chris@909 38 class BerError < Exception; end
Chris@909 39
Chris@909 40
Chris@909 41 # This module is for mixing into IO and IO-like objects.
Chris@909 42 module BERParser
Chris@909 43
Chris@909 44 # The order of these follows the class-codes in BER.
Chris@909 45 # Maybe this should have been a hash.
Chris@909 46 TagClasses = [:universal, :application, :context_specific, :private]
Chris@909 47
Chris@909 48 BuiltinSyntax = {
Chris@909 49 :universal => {
Chris@909 50 :primitive => {
Chris@909 51 1 => :boolean,
Chris@909 52 2 => :integer,
Chris@909 53 4 => :string,
Chris@909 54 10 => :integer,
Chris@909 55 },
Chris@909 56 :constructed => {
Chris@909 57 16 => :array,
Chris@909 58 17 => :array
Chris@909 59 }
Chris@909 60 }
Chris@909 61 }
Chris@909 62
Chris@909 63 #
Chris@909 64 # read_ber
Chris@909 65 # TODO: clean this up so it works properly with partial
Chris@909 66 # packets coming from streams that don't block when
Chris@909 67 # we ask for more data (like StringIOs). At it is,
Chris@909 68 # this can throw TypeErrors and other nasties.
Chris@909 69 #
Chris@909 70 def read_ber syntax=nil
Chris@909 71 return nil if (StringIO == self.class) and eof?
Chris@909 72
Chris@909 73 id = getc # don't trash this value, we'll use it later
Chris@909 74 tag = id & 31
Chris@909 75 tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
Chris@909 76 tagclass = TagClasses[ id >> 6 ]
Chris@909 77 encoding = (id & 0x20 != 0) ? :constructed : :primitive
Chris@909 78
Chris@909 79 n = getc
Chris@909 80 lengthlength,contentlength = if n <= 127
Chris@909 81 [1,n]
Chris@909 82 else
Chris@909 83 j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
Chris@909 84 [1 + (n & 127), j]
Chris@909 85 end
Chris@909 86
Chris@909 87 newobj = read contentlength
Chris@909 88
Chris@909 89 objtype = nil
Chris@909 90 [syntax, BuiltinSyntax].each {|syn|
Chris@909 91 if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
Chris@909 92 objtype = ot[tag]
Chris@909 93 break
Chris@909 94 end
Chris@909 95 }
Chris@909 96
Chris@909 97 obj = case objtype
Chris@909 98 when :boolean
Chris@909 99 newobj != "\000"
Chris@909 100 when :string
Chris@909 101 (newobj || "").dup
Chris@909 102 when :integer
Chris@909 103 j = 0
Chris@909 104 newobj.each_byte {|b| j = (j << 8) + b}
Chris@909 105 j
Chris@909 106 when :array
Chris@909 107 seq = []
Chris@909 108 sio = StringIO.new( newobj || "" )
Chris@909 109 # Interpret the subobject, but note how the loop
Chris@909 110 # is built: nil ends the loop, but false (a valid
Chris@909 111 # BER value) does not!
Chris@909 112 while (e = sio.read_ber(syntax)) != nil
Chris@909 113 seq << e
Chris@909 114 end
Chris@909 115 seq
Chris@909 116 else
Chris@909 117 raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
Chris@909 118 end
Chris@909 119
Chris@909 120 # Add the identifier bits into the object if it's a String or an Array.
Chris@909 121 # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
Chris@909 122 obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
Chris@909 123 obj
Chris@909 124
Chris@909 125 end
Chris@909 126
Chris@909 127 end # module BERParser
Chris@909 128 end # module BER
Chris@909 129
Chris@909 130 end # module Net
Chris@909 131
Chris@909 132
Chris@909 133 class IO
Chris@909 134 include Net::BER::BERParser
Chris@909 135 end
Chris@909 136
Chris@909 137 require "stringio"
Chris@909 138 class StringIO
Chris@909 139 include Net::BER::BERParser
Chris@909 140 end
Chris@909 141
Chris@909 142 begin
Chris@909 143 require 'openssl'
Chris@909 144 class OpenSSL::SSL::SSLSocket
Chris@909 145 include Net::BER::BERParser
Chris@909 146 end
Chris@909 147 rescue LoadError
Chris@909 148 # Ignore LoadError.
Chris@909 149 # DON'T ignore NameError, which means the SSLSocket class
Chris@909 150 # is somehow unavailable on this implementation of Ruby's openssl.
Chris@909 151 # This may be WRONG, however, because we don't yet know how Ruby's
Chris@909 152 # openssl behaves on machines with no OpenSSL library. I suppose
Chris@909 153 # it's possible they do not fail to require 'openssl' but do not
Chris@909 154 # create the classes. So this code is provisional.
Chris@909 155 # Also, you might think that OpenSSL::SSL::SSLSocket inherits from
Chris@909 156 # IO so we'd pick it up above. But you'd be wrong.
Chris@909 157 end
Chris@909 158
Chris@909 159 class String
Chris@909 160 def read_ber syntax=nil
Chris@909 161 StringIO.new(self).read_ber(syntax)
Chris@909 162 end
Chris@909 163 end
Chris@909 164
Chris@909 165
Chris@909 166
Chris@909 167 #----------------------------------------------
Chris@909 168
Chris@909 169
Chris@909 170 class FalseClass
Chris@909 171 #
Chris@909 172 # to_ber
Chris@909 173 #
Chris@909 174 def to_ber
Chris@909 175 "\001\001\000"
Chris@909 176 end
Chris@909 177 end
Chris@909 178
Chris@909 179
Chris@909 180 class TrueClass
Chris@909 181 #
Chris@909 182 # to_ber
Chris@909 183 #
Chris@909 184 def to_ber
Chris@909 185 "\001\001\001"
Chris@909 186 end
Chris@909 187 end
Chris@909 188
Chris@909 189
Chris@909 190
Chris@909 191 class Fixnum
Chris@909 192 #
Chris@909 193 # to_ber
Chris@909 194 #
Chris@909 195 def to_ber
Chris@909 196 i = [self].pack('w')
Chris@909 197 [2, i.length].pack("CC") + i
Chris@909 198 end
Chris@909 199
Chris@909 200 #
Chris@909 201 # to_ber_enumerated
Chris@909 202 #
Chris@909 203 def to_ber_enumerated
Chris@909 204 i = [self].pack('w')
Chris@909 205 [10, i.length].pack("CC") + i
Chris@909 206 end
Chris@909 207
Chris@909 208 #
Chris@909 209 # to_ber_length_encoding
Chris@909 210 #
Chris@909 211 def to_ber_length_encoding
Chris@909 212 if self <= 127
Chris@909 213 [self].pack('C')
Chris@909 214 else
Chris@909 215 i = [self].pack('N').sub(/^[\0]+/,"")
Chris@909 216 [0x80 + i.length].pack('C') + i
Chris@909 217 end
Chris@909 218 end
Chris@909 219
Chris@909 220 end # class Fixnum
Chris@909 221
Chris@909 222
Chris@909 223 class Bignum
Chris@909 224
Chris@909 225 def to_ber
Chris@909 226 i = [self].pack('w')
Chris@909 227 i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
Chris@909 228 [2, i.length].pack("CC") + i
Chris@909 229 end
Chris@909 230
Chris@909 231 end
Chris@909 232
Chris@909 233
Chris@909 234
Chris@909 235 class String
Chris@909 236 #
Chris@909 237 # to_ber
Chris@909 238 # A universal octet-string is tag number 4,
Chris@909 239 # but others are possible depending on the context, so we
Chris@909 240 # let the caller give us one.
Chris@909 241 # The preferred way to do this in user code is via to_ber_application_sring
Chris@909 242 # and to_ber_contextspecific.
Chris@909 243 #
Chris@909 244 def to_ber code = 4
Chris@909 245 [code].pack('C') + length.to_ber_length_encoding + self
Chris@909 246 end
Chris@909 247
Chris@909 248 #
Chris@909 249 # to_ber_application_string
Chris@909 250 #
Chris@909 251 def to_ber_application_string code
Chris@909 252 to_ber( 0x40 + code )
Chris@909 253 end
Chris@909 254
Chris@909 255 #
Chris@909 256 # to_ber_contextspecific
Chris@909 257 #
Chris@909 258 def to_ber_contextspecific code
Chris@909 259 to_ber( 0x80 + code )
Chris@909 260 end
Chris@909 261
Chris@909 262 end # class String
Chris@909 263
Chris@909 264
Chris@909 265
Chris@909 266 class Array
Chris@909 267 #
Chris@909 268 # to_ber_appsequence
Chris@909 269 # An application-specific sequence usually gets assigned
Chris@909 270 # a tag that is meaningful to the particular protocol being used.
Chris@909 271 # This is different from the universal sequence, which usually
Chris@909 272 # gets a tag value of 16.
Chris@909 273 # Now here's an interesting thing: We're adding the X.690
Chris@909 274 # "application constructed" code at the top of the tag byte (0x60),
Chris@909 275 # but some clients, notably ldapsearch, send "context-specific
Chris@909 276 # constructed" (0xA0). The latter would appear to violate RFC-1777,
Chris@909 277 # but what do I know? We may need to change this.
Chris@909 278 #
Chris@909 279
Chris@909 280 def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
Chris@909 281 def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
Chris@909 282 def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
Chris@909 283 def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
Chris@909 284 def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
Chris@909 285
Chris@909 286 private
Chris@909 287 def to_ber_seq_internal code
Chris@909 288 s = self.to_s
Chris@909 289 [code].pack('C') + s.length.to_ber_length_encoding + s
Chris@909 290 end
Chris@909 291
Chris@909 292 end # class Array
Chris@909 293
Chris@909 294