net-ldap 0.1.1 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of net-ldap might be problematic. Click here for more details.

Files changed (58) hide show
  1. data/.autotest +11 -0
  2. data/.gemtest +0 -0
  3. data/.rspec +2 -0
  4. data/Contributors.rdoc +21 -0
  5. data/Hacking.rdoc +68 -0
  6. data/{History.txt → History.rdoc} +65 -1
  7. data/License.rdoc +29 -0
  8. data/Manifest.txt +25 -14
  9. data/README.rdoc +52 -0
  10. data/Rakefile +52 -96
  11. data/autotest/discover.rb +1 -0
  12. data/lib/net-ldap.rb +1 -0
  13. data/lib/net/ber.rb +302 -81
  14. data/lib/net/ber/ber_parser.rb +153 -97
  15. data/lib/net/ber/core_ext.rb +62 -0
  16. data/lib/net/ber/core_ext/array.rb +82 -0
  17. data/lib/net/ber/core_ext/bignum.rb +22 -0
  18. data/lib/net/ber/core_ext/false_class.rb +10 -0
  19. data/lib/net/ber/core_ext/fixnum.rb +66 -0
  20. data/lib/net/ber/core_ext/string.rb +48 -0
  21. data/lib/net/ber/core_ext/true_class.rb +12 -0
  22. data/lib/net/ldap.rb +1455 -1475
  23. data/lib/net/ldap/dataset.rb +134 -79
  24. data/lib/net/ldap/dn.rb +225 -0
  25. data/lib/net/ldap/entry.rb +168 -249
  26. data/lib/net/ldap/filter.rb +654 -387
  27. data/lib/net/ldap/password.rb +31 -0
  28. data/lib/net/ldap/pdu.rb +232 -233
  29. data/lib/net/snmp.rb +4 -31
  30. data/net-ldap.gemspec +59 -0
  31. data/spec/integration/ssl_ber_spec.rb +3 -0
  32. data/spec/spec_helper.rb +2 -2
  33. data/spec/unit/ber/ber_spec.rb +82 -6
  34. data/spec/unit/ber/core_ext/string_spec.rb +51 -0
  35. data/spec/unit/ldap/dn_spec.rb +80 -0
  36. data/spec/unit/ldap/entry_spec.rb +51 -0
  37. data/spec/unit/ldap/filter_spec.rb +84 -0
  38. data/spec/unit/ldap_spec.rb +48 -0
  39. data/test/test_entry.rb +54 -2
  40. data/test/test_filter.rb +93 -54
  41. data/test/test_ldap_connection.rb +24 -0
  42. data/test/test_ldif.rb +31 -23
  43. data/test/test_rename.rb +77 -0
  44. data/test/test_snmp.rb +34 -33
  45. metadata +88 -52
  46. data/COPYING +0 -272
  47. data/LICENSE +0 -56
  48. data/README.txt +0 -68
  49. data/lib/net/ldap/core_ext/all.rb +0 -43
  50. data/lib/net/ldap/core_ext/array.rb +0 -42
  51. data/lib/net/ldap/core_ext/bignum.rb +0 -25
  52. data/lib/net/ldap/core_ext/false_class.rb +0 -11
  53. data/lib/net/ldap/core_ext/fixnum.rb +0 -74
  54. data/lib/net/ldap/core_ext/string.rb +0 -40
  55. data/lib/net/ldap/core_ext/true_class.rb +0 -11
  56. data/lib/net/ldap/psw.rb +0 -57
  57. data/lib/net/ldif.rb +0 -34
  58. data/test/test_ber.rb +0 -78
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
@@ -1 +1,2 @@
1
+ # -*- ruby encoding: utf-8 -*-
1
2
  require 'net/ldap'
@@ -1,95 +1,316 @@
1
- # NET::BER
2
- # Mixes ASN.1/BER convenience methods into several standard classes.
3
- # Also provides BER parsing functionality.
4
- #
5
- #----------------------------------------------------------------------------
6
- #
7
- # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
8
- #
9
- # Gmail: garbagecat10
10
- #
11
- # This program is free software; you can redistribute it and/or modify
12
- # it under the terms of the GNU General Public License as published by
13
- # the Free Software Foundation; either version 2 of the License, or
14
- # (at your option) any later version.
15
- #
16
- # This program is distributed in the hope that it will be useful,
17
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- # GNU General Public License for more details.
20
- #
21
- # You should have received a copy of the GNU General Public License
22
- # along with this program; if not, write to the Free Software
23
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
- #
25
- #---------------------------------------------------------------------------
26
-
27
- module Net
1
+ # -*- ruby encoding: utf-8 -*-
2
+ module Net # :nodoc:
3
+ ##
4
+ # == Basic Encoding Rules (BER) Support Module
5
+ #
6
+ # Much of the text below is cribbed from Wikipedia:
7
+ # http://en.wikipedia.org/wiki/Basic_Encoding_Rules
8
+ #
9
+ # The ITU Specification is also worthwhile reading:
10
+ # http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
11
+ #
12
+ # The Basic Encoding Rules were the original rules laid out by the ASN.1
13
+ # standard for encoding abstract information into a concrete data stream.
14
+ # The rules, collectively referred to as a transfer syntax in ASN.1
15
+ # parlance, specify the exact octet sequences which are used to encode a
16
+ # given data item. The syntax defines such elements as: the
17
+ # representations for basic data types, the structure of length
18
+ # information, and the means for defining complex or compound types based
19
+ # on more primitive types. The BER syntax, along with two subsets of BER
20
+ # (the Canonical Encoding Rules and the Distinguished Encoding Rules), are
21
+ # defined by the ITU-T's X.690 standards document, which is part of the
22
+ # ASN.1 document series.
23
+ #
24
+ # == Encoding
25
+ # The BER format specifies a self-describing and self-delimiting format
26
+ # for encoding ASN.1 data structures. Each data element is encoded as a
27
+ # type identifier, a length description, the actual data elements, and
28
+ # where necessary, an end-of-content marker. This format allows a receiver
29
+ # to decode the ASN.1 information from an incomplete stream, without
30
+ # requiring any pre-knowledge of the size, content, or semantic meaning of
31
+ # the data.
32
+ #
33
+ # <Type | Length | Value [| End-of-Content]>
34
+ #
35
+ # == Protocol Data Units (PDU)
36
+ # Protocols are defined with schema represented in BER, such that a PDU
37
+ # consists of cascaded type-length-value encodings.
38
+ #
39
+ # === Type Tags
40
+ # BER type tags are represented as single octets (bytes). The lower five
41
+ # bits of the octet are tag identifier numbers and the upper three bits of
42
+ # the octet are used to distinguish the type as native to ASN.1,
43
+ # application-specific, context-specific, or private. See
44
+ # Net::BER::TAG_CLASS and Net::BER::ENCODING_TYPE for more information.
45
+ #
46
+ # If Class is set to Universal (0b00______), the value is of a type native
47
+ # to ASN.1 (e.g. INTEGER). The Application class (0b01______) is only
48
+ # valid for one specific application. Context_specific (0b10______)
49
+ # depends on the context and private (0b11_______) can be defined in
50
+ # private specifications
51
+ #
52
+ # If the primitive/constructed bit is zero (0b__0_____), it specifies that
53
+ # the value is primitive like an INTEGER. If it is one (0b__1_____), the
54
+ # value is a constructed value that contains type-length-value encoded
55
+ # types like a SET or a SEQUENCE.
56
+ #
57
+ # === Defined Universal (ASN.1 Native) Types
58
+ # There are a number of pre-defined universal (native) types.
59
+ #
60
+ # <table>
61
+ # <tr><th>Name</th><th>Primitive<br />Constructed</th><th>Number</th></tr>
62
+ # <tr><th>EOC (End-of-Content)</th><th>P</th><td>0: 0 (0x0, 0b00000000)</td></tr>
63
+ # <tr><th>BOOLEAN</th><th>P</th><td>1: 1 (0x01, 0b00000001)</td></tr>
64
+ # <tr><th>INTEGER</th><th>P</th><td>2: 2 (0x02, 0b00000010)</td></tr>
65
+ # <tr><th>BIT STRING</th><th>P</th><td>3: 3 (0x03, 0b00000011)</td></tr>
66
+ # <tr><th>BIT STRING</th><th>C</th><td>3: 35 (0x23, 0b00100011)</td></tr>
67
+ # <tr><th>OCTET STRING</th><th>P</th><td>4: 4 (0x04, 0b00000100)</td></tr>
68
+ # <tr><th>OCTET STRING</th><th>C</th><td>4: 36 (0x24, 0b00100100)</td></tr>
69
+ # <tr><th>NULL</th><th>P</th><td>5: 5 (0x05, 0b00000101)</td></tr>
70
+ # <tr><th>OBJECT IDENTIFIER</th><th>P</th><td>6: 6 (0x06, 0b00000110)</td></tr>
71
+ # <tr><th>Object Descriptor</th><th>P</th><td>7: 7 (0x07, 0b00000111)</td></tr>
72
+ # <tr><th>EXTERNAL</th><th>C</th><td>8: 40 (0x28, 0b00101000)</td></tr>
73
+ # <tr><th>REAL (float)</th><th>P</th><td>9: 9 (0x09, 0b00001001)</td></tr>
74
+ # <tr><th>ENUMERATED</th><th>P</th><td>10: 10 (0x0a, 0b00001010)</td></tr>
75
+ # <tr><th>EMBEDDED PDV</th><th>C</th><td>11: 43 (0x2b, 0b00101011)</td></tr>
76
+ # <tr><th>UTF8String</th><th>P</th><td>12: 12 (0x0c, 0b00001100)</td></tr>
77
+ # <tr><th>UTF8String</th><th>C</th><td>12: 44 (0x2c, 0b00101100)</td></tr>
78
+ # <tr><th>RELATIVE-OID</th><th>P</th><td>13: 13 (0x0d, 0b00001101)</td></tr>
79
+ # <tr><th>SEQUENCE and SEQUENCE OF</th><th>C</th><td>16: 48 (0x30, 0b00110000)</td></tr>
80
+ # <tr><th>SET and SET OF</th><th>C</th><td>17: 49 (0x31, 0b00110001)</td></tr>
81
+ # <tr><th>NumericString</th><th>P</th><td>18: 18 (0x12, 0b00010010)</td></tr>
82
+ # <tr><th>NumericString</th><th>C</th><td>18: 50 (0x32, 0b00110010)</td></tr>
83
+ # <tr><th>PrintableString</th><th>P</th><td>19: 19 (0x13, 0b00010011)</td></tr>
84
+ # <tr><th>PrintableString</th><th>C</th><td>19: 51 (0x33, 0b00110011)</td></tr>
85
+ # <tr><th>T61String</th><th>P</th><td>20: 20 (0x14, 0b00010100)</td></tr>
86
+ # <tr><th>T61String</th><th>C</th><td>20: 52 (0x34, 0b00110100)</td></tr>
87
+ # <tr><th>VideotexString</th><th>P</th><td>21: 21 (0x15, 0b00010101)</td></tr>
88
+ # <tr><th>VideotexString</th><th>C</th><td>21: 53 (0x35, 0b00110101)</td></tr>
89
+ # <tr><th>IA5String</th><th>P</th><td>22: 22 (0x16, 0b00010110)</td></tr>
90
+ # <tr><th>IA5String</th><th>C</th><td>22: 54 (0x36, 0b00110110)</td></tr>
91
+ # <tr><th>UTCTime</th><th>P</th><td>23: 23 (0x17, 0b00010111)</td></tr>
92
+ # <tr><th>UTCTime</th><th>C</th><td>23: 55 (0x37, 0b00110111)</td></tr>
93
+ # <tr><th>GeneralizedTime</th><th>P</th><td>24: 24 (0x18, 0b00011000)</td></tr>
94
+ # <tr><th>GeneralizedTime</th><th>C</th><td>24: 56 (0x38, 0b00111000)</td></tr>
95
+ # <tr><th>GraphicString</th><th>P</th><td>25: 25 (0x19, 0b00011001)</td></tr>
96
+ # <tr><th>GraphicString</th><th>C</th><td>25: 57 (0x39, 0b00111001)</td></tr>
97
+ # <tr><th>VisibleString</th><th>P</th><td>26: 26 (0x1a, 0b00011010)</td></tr>
98
+ # <tr><th>VisibleString</th><th>C</th><td>26: 58 (0x3a, 0b00111010)</td></tr>
99
+ # <tr><th>GeneralString</th><th>P</th><td>27: 27 (0x1b, 0b00011011)</td></tr>
100
+ # <tr><th>GeneralString</th><th>C</th><td>27: 59 (0x3b, 0b00111011)</td></tr>
101
+ # <tr><th>UniversalString</th><th>P</th><td>28: 28 (0x1c, 0b00011100)</td></tr>
102
+ # <tr><th>UniversalString</th><th>C</th><td>28: 60 (0x3c, 0b00111100)</td></tr>
103
+ # <tr><th>CHARACTER STRING</th><th>P</th><td>29: 29 (0x1d, 0b00011101)</td></tr>
104
+ # <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
105
+ # <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
106
+ # <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
107
+ # </table>
28
108
  module BER
29
- VERSION = '0.1.0'
30
-
31
- #--
32
- # This condenses our nicely self-documenting ASN hashes down
33
- # to an array for fast lookups.
34
- # Scoped to be called as a module method, but not intended for
35
- # user code to call.
36
- #
37
- def self.compile_syntax(syn)
38
- out = [nil] * 256
39
- syn.each do |tclass, tclasses|
40
- tagclass = {:universal=>0, :application=>64, :context_specific=>128, :private=>192} [tclass]
41
- tclasses.each do |codingtype,codings|
42
- encoding = {:primitive=>0, :constructed=>32} [codingtype]
43
- codings.each {|tag, objtype| out[tagclass + encoding + tag] = objtype }
109
+ VERSION = '0.2'
110
+
111
+ ##
112
+ # Used for BER-encoding the length and content bytes of a Fixnum integer
113
+ # values.
114
+ MAX_FIXNUM_SIZE = 0.size
115
+
116
+ ##
117
+ # BER tag classes are kept in bits seven and eight of the tag type
118
+ # octet.
119
+ #
120
+ # <table>
121
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
122
+ # <tr><th><tt>0b00______</tt></th><td>Universal (ASN.1 Native) Types</td></tr>
123
+ # <tr><th><tt>0b01______</tt></th><td>Application Types</td></tr>
124
+ # <tr><th><tt>0b10______</tt></th><td>Context-Specific Types</td></tr>
125
+ # <tr><th><tt>0b11______</tt></th><td>Private Types</td></tr>
126
+ # </table>
127
+ TAG_CLASS = {
128
+ :universal => 0b00000000, # 0
129
+ :application => 0b01000000, # 64
130
+ :context_specific => 0b10000000, # 128
131
+ :private => 0b11000000, # 192
132
+ }
133
+
134
+ ##
135
+ # BER encoding type is kept in bit 6 of the tag type octet.
136
+ #
137
+ # <table>
138
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
139
+ # <tr><th><tt>0b__0_____</tt></th><td>Primitive</td></tr>
140
+ # <tr><th><tt>0b__1_____</tt></th><td>Constructed</td></tr>
141
+ # </table>
142
+ ENCODING_TYPE = {
143
+ :primitive => 0b00000000, # 0
144
+ :constructed => 0b00100000, # 32
145
+ }
146
+
147
+ ##
148
+ # Accepts a hash of hashes describing a BER syntax and converts it into
149
+ # a byte-keyed object for fast BER conversion lookup. The resulting
150
+ # "compiled" syntax is used by Net::BER::BERParser.
151
+ #
152
+ # This method should be called only by client classes of Net::BER (e.g.,
153
+ # Net::LDAP and Net::SNMP) and not by clients of those classes.
154
+ #
155
+ # The hash-based syntax uses TAG_CLASS keys that contain hashes of
156
+ # ENCODING_TYPE keys that contain tag numbers with object type markers.
157
+ #
158
+ # :<TAG_CLASS> => {
159
+ # :<ENCODING_TYPE> => {
160
+ # <number> => <object-type>
161
+ # },
162
+ # },
163
+ #
164
+ # === Permitted Object Types
165
+ # <tt>:string</tt>:: A string value, represented as BerIdentifiedString.
166
+ # <tt>:integer</tt>:: An integer value, represented with Fixnum.
167
+ # <tt>:oid</tt>:: An Object Identifier value; see X.690 section
168
+ # 8.19. Currently represented with a standard array,
169
+ # but may be better represented as a
170
+ # BerIdentifiedOID object.
171
+ # <tt>:array</tt>:: A sequence, represented as BerIdentifiedArray.
172
+ # <tt>:boolean</tt>:: A boolean value, represented as +true+ or +false+.
173
+ # <tt>:null</tt>:: A null value, represented as BerIdentifiedNull.
174
+ #
175
+ # === Example
176
+ # Net::LDAP defines its ASN.1 BER syntax something like this:
177
+ #
178
+ # class Net::LDAP
179
+ # AsnSyntax = Net::BER.compile_syntax({
180
+ # :application => {
181
+ # :primitive => {
182
+ # 2 => :null,
183
+ # },
184
+ # :constructed => {
185
+ # 0 => :array,
186
+ # # ...
187
+ # },
188
+ # },
189
+ # :context_specific => {
190
+ # :primitive => {
191
+ # 0 => :string,
192
+ # # ...
193
+ # },
194
+ # :constructed => {
195
+ # 0 => :array,
196
+ # # ...
197
+ # },
198
+ # }
199
+ # })
200
+ # end
201
+ #
202
+ # NOTE:: For readability and formatting purposes, Net::LDAP and its
203
+ # siblings actually construct their syntaxes more deliberately,
204
+ # as shown below. Since a hash is passed in the end in any case,
205
+ # the format does not matter.
206
+ #
207
+ # primitive = { 2 => :null }
208
+ # constructed = {
209
+ # 0 => :array,
210
+ # # ...
211
+ # }
212
+ # application = {
213
+ # :primitive => primitive,
214
+ # :constructed => constructed
215
+ # }
216
+ #
217
+ # primitive = {
218
+ # 0 => :string,
219
+ # # ...
220
+ # }
221
+ # constructed = {
222
+ # 0 => :array,
223
+ # # ...
224
+ # }
225
+ # context_specific = {
226
+ # :primitive => primitive,
227
+ # :constructed => constructed
228
+ # }
229
+ # AsnSyntax = Net::BER.compile_syntax(:application => application,
230
+ # :context_specific => context_specific)
231
+ def self.compile_syntax(syntax)
232
+ # TODO 20100327 AZ: Should we be allocating an array of 256 values
233
+ # that will either be +nil+ or an object type symbol, or should we
234
+ # allocate an empty Hash since unknown values return +nil+ anyway?
235
+ out = [ nil ] * 256
236
+ syntax.each do |tag_class_id, encodings|
237
+ tag_class = TAG_CLASS[tag_class_id]
238
+ encodings.each do |encoding_id, classes|
239
+ encoding = ENCODING_TYPE[encoding_id]
240
+ object_class = tag_class + encoding
241
+ classes.each do |number, object_type|
242
+ out[object_class + number] = object_type
243
+ end
44
244
  end
45
245
  end
46
246
  out
47
247
  end
248
+ end
249
+ end
48
250
 
49
- def to_ber
50
- # Provisional implementation.
51
- # We ASSUME that our incoming value is an array, and we
52
- # use the Array#to_ber_oid method defined below.
53
- # We probably should obsolete that method, actually, in
54
- # and move the code here.
55
- # WE ARE NOT CURRENTLY ENCODING THE BER-IDENTIFIER.
56
- # This implementation currently hardcodes 6, the universal OID tag.
57
- ary = @value.dup
58
- first = ary.shift
59
- raise Net::BER::BerError.new(" invalid OID" ) unless [0,1,2].include?(first)
60
- first = first * 40 + ary.shift
61
- ary.unshift first
62
- oid = ary.pack("w*")
63
- [6, oid.length].pack("CC") + oid
64
- end
251
+ class Net::BER::BerError < RuntimeError; end
252
+
253
+ ##
254
+ # An Array object with a BER identifier attached.
255
+ class Net::BER::BerIdentifiedArray < Array
256
+ attr_accessor :ber_identifier
257
+
258
+ def initialize(*args)
259
+ super
65
260
  end
66
261
  end
67
262
 
68
- module Net
69
- module BER
70
- class BerError < StandardError; end
71
-
72
- class BerIdentifiedString < String
73
- attr_accessor :ber_identifier
74
- def initialize args
75
- super args
76
- end
77
- end
78
-
79
- class BerIdentifiedArray < Array
80
- attr_accessor :ber_identifier
81
- def initialize(*args)
82
- super
83
- end
263
+ ##
264
+ # A BER object identifier.
265
+ class Net::BER::BerIdentifiedOid
266
+ attr_accessor :ber_identifier
267
+
268
+ def initialize(oid)
269
+ if oid.is_a?(String)
270
+ oid = oid.split(/\./).map {|s| s.to_i }
84
271
  end
85
-
86
- class BerIdentifiedNull
87
- attr_accessor :ber_identifier
88
- def to_ber
89
- "\005\000"
90
- end
272
+ @value = oid
273
+ end
274
+
275
+ def to_ber
276
+ to_ber_oid
277
+ end
278
+
279
+ def to_ber_oid
280
+ @value.to_ber_oid
281
+ end
282
+
283
+ def to_s
284
+ @value.join(".")
285
+ end
286
+
287
+ def to_arr
288
+ @value.dup
289
+ end
290
+ end
291
+
292
+ ##
293
+ # A String object with a BER identifier attached.
294
+ class Net::BER::BerIdentifiedString < String
295
+ attr_accessor :ber_identifier
296
+ def initialize args
297
+ super args
298
+ end
299
+ end
300
+
301
+ module Net::BER
302
+ ##
303
+ # A BER null object.
304
+ class BerIdentifiedNull
305
+ attr_accessor :ber_identifier
306
+ def to_ber
307
+ "\005\000"
91
308
  end
92
309
  end
310
+
311
+ ##
312
+ # The default BerIdentifiedNull object.
313
+ Null = Net::BER::BerIdentifiedNull.new
93
314
  end
94
315
 
95
- require 'net/ber/ber_parser'
316
+ require 'net/ber/core_ext'
@@ -1,112 +1,168 @@
1
+ # -*- ruby encoding: utf-8 -*-
1
2
  require 'stringio'
2
3
 
3
- module Net
4
- module BER
5
- module BERParser
6
- VERSION = '0.1.0'
4
+ # Implements Basic Encoding Rules parsing to be mixed into types as needed.
5
+ module Net::BER::BERParser
6
+ primitive = {
7
+ 1 => :boolean,
8
+ 2 => :integer,
9
+ 4 => :string,
10
+ 5 => :null,
11
+ 6 => :oid,
12
+ 10 => :integer,
13
+ 13 => :string # (relative OID)
14
+ }
15
+ constructed = {
16
+ 16 => :array,
17
+ 17 => :array
18
+ }
19
+ universal = { :primitive => primitive, :constructed => constructed }
7
20
 
8
- # The order of these follows the class-codes in BER.
9
- # Maybe this should have been a hash.
10
- TagClasses = [:universal, :application, :context_specific, :private]
21
+ primitive = { 10 => :integer }
22
+ context = { :primitive => primitive }
11
23
 
12
- BuiltinSyntax = Net::BER.compile_syntax( {
13
- :universal => {
14
- :primitive => {
15
- 1 => :boolean,
16
- 2 => :integer,
17
- 4 => :string,
18
- 5 => :null,
19
- 6 => :oid,
20
- 10 => :integer,
21
- 13 => :string # (relative OID)
22
- },
23
- :constructed => {
24
- 16 => :array,
25
- 17 => :array
26
- }
27
- },
28
- :context_specific => {
29
- :primitive => {
30
- 10 => :integer
31
- }
32
- }
33
- })
24
+ # The universal, built-in ASN.1 BER syntax.
25
+ BuiltinSyntax = Net::BER.compile_syntax(:universal => universal,
26
+ :context_specific => context)
34
27
 
35
- def read_ber syntax=nil
36
- # TODO: clean this up so it works properly with partial
37
- # packets coming from streams that don't block when
38
- # we ask for more data (like StringIOs). At it is,
39
- # this can throw TypeErrors and other nasties.
40
-
41
- id = getbyte or return nil # don't trash this value, we'll use it later
28
+ ##
29
+ # This is an extract of our BER object parsing to simplify our
30
+ # understanding of how we parse basic BER object types.
31
+ def parse_ber_object(syntax, id, data)
32
+ # Find the object type from either the provided syntax lookup table or
33
+ # the built-in syntax lookup table.
34
+ #
35
+ # This exceptionally clever bit of code is verrrry slow.
36
+ object_type = (syntax && syntax[id]) || BuiltinSyntax[id]
42
37
 
43
- n = getbyte
44
- lengthlength,contentlength = if n <= 127
45
- [1,n]
46
- else
47
- # Replaced the inject because it profiles hot.
48
- # j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
49
- j = 0
50
- read( n & 127 ).each_byte {|n1| j = (j << 8) + n1}
51
- [1 + (n & 127), j]
52
- end
53
-
54
- newobj = read contentlength
55
-
56
- # This exceptionally clever and clear bit of code is verrrry slow.
57
- objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
58
-
59
- # == is expensive so sort this if/else so the common cases are at the top.
60
- obj = if objtype == :string
61
- #(newobj || "").dup
62
- s = BerIdentifiedString.new( newobj || "" )
63
- s.ber_identifier = id
64
- s
65
- elsif objtype == :integer
66
- j = 0
67
- newobj.each_byte {|b| j = (j << 8) + b}
68
- j
69
- elsif objtype == :oid
70
- # cf X.690 pgh 8.19 for an explanation of this algorithm.
71
- # Potentially not good enough. We may need a BerIdentifiedOid
72
- # as a subclass of BerIdentifiedArray, to get the ber identifier
73
- # and also a to_s method that produces the familiar dotted notation.
74
- oid = newobj.unpack("w*")
75
- f = oid.shift
76
- g = if f < 40
38
+ # == is expensive so sort this so the common cases are at the top.
39
+ if object_type == :string
40
+ s = Net::BER::BerIdentifiedString.new(data || "")
41
+ s.ber_identifier = id
42
+ s
43
+ elsif object_type == :integer
44
+ j = 0
45
+ data.each_byte { |b| j = (j << 8) + b }
46
+ j
47
+ elsif object_type == :oid
48
+ # See X.690 pgh 8.19 for an explanation of this algorithm.
49
+ # This is potentially not good enough. We may need a
50
+ # BerIdentifiedOid as a subclass of BerIdentifiedArray, to
51
+ # get the ber identifier and also a to_s method that produces
52
+ # the familiar dotted notation.
53
+ oid = data.unpack("w*")
54
+ f = oid.shift
55
+ g = if f < 40
77
56
  [0, f]
78
57
  elsif f < 80
79
- [1, f-40]
58
+ [1, f - 40]
80
59
  else
81
- [2, f-80] # f-80 can easily be > 80. What a weird optimization.
82
- end
83
- oid.unshift g.last
84
- oid.unshift g.first
85
- oid
86
- elsif objtype == :array
87
- #seq = []
88
- seq = BerIdentifiedArray.new
89
- seq.ber_identifier = id
90
- sio = StringIO.new( newobj || "" )
91
- # Interpret the subobject, but note how the loop
92
- # is built: nil ends the loop, but false (a valid
93
- # BER value) does not!
94
- while (e = sio.read_ber(syntax)) != nil
95
- seq << e
60
+ # f - 80 can easily be > 80. What a weird optimization.
61
+ [2, f - 80]
96
62
  end
97
- seq
98
- elsif objtype == :boolean
99
- newobj != "\000"
100
- elsif objtype == :null
101
- n = BerIdentifiedNull.new
102
- n.ber_identifier = id
103
- n
104
- else
105
- raise BerError.new( "unsupported object type: id=#{id}" )
106
- end
63
+ oid.unshift g.last
64
+ oid.unshift g.first
65
+ # Net::BER::BerIdentifiedOid.new(oid)
66
+ oid
67
+ elsif object_type == :array
68
+ seq = Net::BER::BerIdentifiedArray.new
69
+ seq.ber_identifier = id
70
+ sio = StringIO.new(data || "")
71
+ # Interpret the subobject, but note how the loop is built:
72
+ # nil ends the loop, but false (a valid BER value) does not!
73
+ while (e = sio.read_ber(syntax)) != nil
74
+ seq << e
75
+ end
76
+ seq
77
+ elsif object_type == :boolean
78
+ data != "\000"
79
+ elsif object_type == :null
80
+ n = Net::BER::BerIdentifiedNull.new
81
+ n.ber_identifier = id
82
+ n
83
+ else
84
+ raise Net::BER::BerError, "Unsupported object type: id=#{id}"
85
+ end
86
+ end
87
+ private :parse_ber_object
88
+
89
+ ##
90
+ # This is an extract of how our BER object length parsing is done to
91
+ # simplify the primary call. This is defined in X.690 section 8.1.3.
92
+ #
93
+ # The BER length will either be a single byte or up to 126 bytes in
94
+ # length. There is a special case of a BER length indicating that the
95
+ # content-length is undefined and will be identified by the presence of
96
+ # two null values (0x00 0x00).
97
+ #
98
+ # <table>
99
+ # <tr>
100
+ # <th>Range</th>
101
+ # <th>Length</th>
102
+ # </tr>
103
+ # <tr>
104
+ # <th>0x00 -- 0x7f<br />0b00000000 -- 0b01111111</th>
105
+ # <td>0 - 127 bytes</td>
106
+ # </tr>
107
+ # <tr>
108
+ # <th>0x80<br />0b10000000</th>
109
+ # <td>Indeterminate (end-of-content marker required)</td>
110
+ # </tr>
111
+ # <tr>
112
+ # <th>0x81 -- 0xfe<br />0b10000001 -- 0b11111110</th>
113
+ # <td>1 - 126 bytes of length as an integer value</td>
114
+ # </tr>
115
+ # <tr>
116
+ # <th>0xff<br />0b11111111</th>
117
+ # <td>Illegal (reserved for future expansion)</td>
118
+ # </tr>
119
+ # </table>
120
+ #
121
+ #--
122
+ # This has been modified from the version that was previously inside
123
+ # #read_ber to handle both the indeterminate terminator case and the
124
+ # invalid BER length case. Because the "lengthlength" value was not used
125
+ # inside of #read_ber, we no longer return it.
126
+ def read_ber_length
127
+ n = getbyte
107
128
 
108
- obj
129
+ if n <= 0x7f
130
+ n
131
+ elsif n == 0x80
132
+ -1
133
+ elsif n == 0xff
134
+ raise Net::BER::BerError, "Invalid BER length 0xFF detected."
135
+ else
136
+ v = 0
137
+ read(n & 0x7f).each_byte do |b|
138
+ v = (v << 8) + b
109
139
  end
140
+
141
+ v
110
142
  end
111
143
  end
144
+ private :read_ber_length
145
+
146
+ ##
147
+ # Reads a BER object from the including object. Requires that #getbyte is
148
+ # implemented on the including object and that it returns a Fixnum value.
149
+ # Also requires #read(bytes) to work.
150
+ #
151
+ # This does not work with non-blocking I/O.
152
+ def read_ber(syntax = nil)
153
+ # TODO: clean this up so it works properly with partial packets coming
154
+ # from streams that don't block when we ask for more data (like
155
+ # StringIOs). At it is, this can throw TypeErrors and other nasties.
156
+
157
+ id = getbyte or return nil # don't trash this value, we'll use it later
158
+ content_length = read_ber_length
159
+
160
+ if -1 == content_length
161
+ raise Net::BER::BerError, "Indeterminite BER content length not implemented."
162
+ else
163
+ data = read(content_length)
164
+ end
165
+
166
+ parse_ber_object(syntax, id, data)
167
+ end
112
168
  end