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