rubinius-net-ldap 0.11

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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +5 -0
  4. data/.rubocop_todo.yml +462 -0
  5. data/.travis.yml +19 -0
  6. data/CONTRIBUTING.md +54 -0
  7. data/Contributors.rdoc +24 -0
  8. data/Gemfile +2 -0
  9. data/Hacking.rdoc +63 -0
  10. data/History.rdoc +260 -0
  11. data/License.rdoc +29 -0
  12. data/README.rdoc +65 -0
  13. data/Rakefile +17 -0
  14. data/lib/net-ldap.rb +2 -0
  15. data/lib/net/ber.rb +320 -0
  16. data/lib/net/ber/ber_parser.rb +182 -0
  17. data/lib/net/ber/core_ext.rb +55 -0
  18. data/lib/net/ber/core_ext/array.rb +96 -0
  19. data/lib/net/ber/core_ext/false_class.rb +10 -0
  20. data/lib/net/ber/core_ext/integer.rb +74 -0
  21. data/lib/net/ber/core_ext/string.rb +66 -0
  22. data/lib/net/ber/core_ext/true_class.rb +11 -0
  23. data/lib/net/ldap.rb +1229 -0
  24. data/lib/net/ldap/connection.rb +702 -0
  25. data/lib/net/ldap/dataset.rb +168 -0
  26. data/lib/net/ldap/dn.rb +225 -0
  27. data/lib/net/ldap/entry.rb +193 -0
  28. data/lib/net/ldap/error.rb +38 -0
  29. data/lib/net/ldap/filter.rb +778 -0
  30. data/lib/net/ldap/instrumentation.rb +23 -0
  31. data/lib/net/ldap/password.rb +38 -0
  32. data/lib/net/ldap/pdu.rb +297 -0
  33. data/lib/net/ldap/version.rb +5 -0
  34. data/lib/net/snmp.rb +264 -0
  35. data/rubinius-net-ldap.gemspec +37 -0
  36. data/script/install-openldap +112 -0
  37. data/script/package +7 -0
  38. data/script/release +16 -0
  39. data/test/ber/core_ext/test_array.rb +22 -0
  40. data/test/ber/core_ext/test_string.rb +25 -0
  41. data/test/ber/test_ber.rb +99 -0
  42. data/test/fixtures/cacert.pem +20 -0
  43. data/test/fixtures/openldap/memberof.ldif +33 -0
  44. data/test/fixtures/openldap/retcode.ldif +76 -0
  45. data/test/fixtures/openldap/slapd.conf.ldif +67 -0
  46. data/test/fixtures/seed.ldif +374 -0
  47. data/test/integration/test_add.rb +28 -0
  48. data/test/integration/test_ber.rb +30 -0
  49. data/test/integration/test_bind.rb +34 -0
  50. data/test/integration/test_delete.rb +31 -0
  51. data/test/integration/test_open.rb +88 -0
  52. data/test/integration/test_return_codes.rb +38 -0
  53. data/test/integration/test_search.rb +77 -0
  54. data/test/support/vm/openldap/.gitignore +1 -0
  55. data/test/support/vm/openldap/README.md +32 -0
  56. data/test/support/vm/openldap/Vagrantfile +33 -0
  57. data/test/test_dn.rb +44 -0
  58. data/test/test_entry.rb +65 -0
  59. data/test/test_filter.rb +223 -0
  60. data/test/test_filter_parser.rb +20 -0
  61. data/test/test_helper.rb +66 -0
  62. data/test/test_ldap.rb +60 -0
  63. data/test/test_ldap_connection.rb +404 -0
  64. data/test/test_ldif.rb +104 -0
  65. data/test/test_password.rb +10 -0
  66. data/test/test_rename.rb +77 -0
  67. data/test/test_search.rb +39 -0
  68. data/test/test_snmp.rb +119 -0
  69. data/test/test_ssl_ber.rb +40 -0
  70. data/test/testdata.ldif +101 -0
  71. data/testserver/ldapserver.rb +210 -0
  72. data/testserver/testdata.ldif +101 -0
  73. metadata +204 -0
data/README.rdoc ADDED
@@ -0,0 +1,65 @@
1
+ = Net::LDAP for Ruby {<img src="https://travis-ci.org/ruby-ldap/ruby-net-ldap.png" />}[https://travis-ci.org/ruby-ldap/ruby-net-ldap]
2
+
3
+ == Description
4
+
5
+ Net::LDAP for Ruby (also called net-ldap) implements client access for the
6
+ Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for
7
+ accessing distributed directory services. Net::LDAP is written completely in
8
+ Ruby with no external dependencies. It supports most LDAP client features and a
9
+ subset of server features as well.
10
+
11
+ Net::LDAP has been tested against modern popular LDAP servers including
12
+ OpenLDAP and Active Directory. The current release is mostly compliant with
13
+ earlier versions of the IETF LDAP RFCs (2251–2256, 2829–2830, 3377, and 3771).
14
+ Our roadmap for Net::LDAP 1.0 is to gain full <em>client</em> compliance with
15
+ the most recent LDAP RFCs (4510–4519, plus portions of 4520–4532).
16
+
17
+ == Where
18
+
19
+ * {GitHub}[https://github.com/ruby-ldap/ruby-net-ldap]
20
+ * {ruby-ldap@googlegroups.com}[http://groups.google.com/group/ruby-ldap]
21
+
22
+ == Synopsis
23
+
24
+ See Net::LDAP for documentation and usage samples.
25
+
26
+ == Requirements
27
+
28
+ Net::LDAP requires a Ruby 1.9.3 compatible interpreter or better.
29
+
30
+ == Install
31
+
32
+ Net::LDAP is a pure Ruby library. It does not require any external libraries.
33
+ You can install the RubyGems version of Net::LDAP available from the usual
34
+ sources.
35
+
36
+ gem install net-ldap
37
+
38
+ Simply require either 'net-ldap' or 'net/ldap'.
39
+
40
+ == Develop
41
+
42
+ This task will run the test suite and the
43
+ {RuboCop}[https://github.com/bbatsov/rubocop] static code analyzer.
44
+
45
+ rake rubotest
46
+
47
+ To run the integration tests against an LDAP server:
48
+
49
+ cd test/support/vm/openldap
50
+ vagrant up
51
+ cd ../../../..
52
+ INTEGRATION=openldap bundle exec rake rubotest
53
+
54
+ == Release
55
+
56
+ This section is for gem maintainers to cut a new version of the gem.
57
+
58
+ * Update lib/net/ldap/version.rb to next version number X.X.X following {semver}(http://semver.org/).
59
+ * Update `History.rdoc`. Get latest changes with `git log --oneline vLAST_RELEASE..HEAD | grep Merge`
60
+
61
+ * On the master branch, run `script/release`
62
+
63
+ :include: Contributors.rdoc
64
+
65
+ :include: License.rdoc
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ # vim: syntax=ruby
3
+
4
+ require 'rake/testtask'
5
+ require 'bundler'
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'test'
9
+ t.test_files = FileList['test/**/test_*.rb']
10
+ t.verbose = true
11
+ t.description = 'Run tests, set INTEGRATION=openldap to run integration tests, INTEGRATION_HOST and INTEGRATION_PORT are also supported'
12
+ end
13
+
14
+ desc 'Run tests'
15
+ task :ci => [:test]
16
+
17
+ task :default => [:test]
data/lib/net-ldap.rb ADDED
@@ -0,0 +1,2 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'net/ldap'
data/lib/net/ber.rb ADDED
@@ -0,0 +1,320 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'net/ldap/version'
3
+
4
+ module Net # :nodoc:
5
+ ##
6
+ # == Basic Encoding Rules (BER) Support Module
7
+ #
8
+ # Much of the text below is cribbed from Wikipedia:
9
+ # http://en.wikipedia.org/wiki/Basic_Encoding_Rules
10
+ #
11
+ # The ITU Specification is also worthwhile reading:
12
+ # http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
13
+ #
14
+ # The Basic Encoding Rules were the original rules laid out by the ASN.1
15
+ # standard for encoding abstract information into a concrete data stream.
16
+ # The rules, collectively referred to as a transfer syntax in ASN.1
17
+ # parlance, specify the exact octet sequences which are used to encode a
18
+ # given data item. The syntax defines such elements as: the
19
+ # representations for basic data types, the structure of length
20
+ # information, and the means for defining complex or compound types based
21
+ # on more primitive types. The BER syntax, along with two subsets of BER
22
+ # (the Canonical Encoding Rules and the Distinguished Encoding Rules), are
23
+ # defined by the ITU-T's X.690 standards document, which is part of the
24
+ # ASN.1 document series.
25
+ #
26
+ # == Encoding
27
+ # The BER format specifies a self-describing and self-delimiting format
28
+ # for encoding ASN.1 data structures. Each data element is encoded as a
29
+ # type identifier, a length description, the actual data elements, and
30
+ # where necessary, an end-of-content marker. This format allows a receiver
31
+ # to decode the ASN.1 information from an incomplete stream, without
32
+ # requiring any pre-knowledge of the size, content, or semantic meaning of
33
+ # the data.
34
+ #
35
+ # <Type | Length | Value [| End-of-Content]>
36
+ #
37
+ # == Protocol Data Units (PDU)
38
+ # Protocols are defined with schema represented in BER, such that a PDU
39
+ # consists of cascaded type-length-value encodings.
40
+ #
41
+ # === Type Tags
42
+ # BER type tags are represented as single octets (bytes). The lower five
43
+ # bits of the octet are tag identifier numbers and the upper three bits of
44
+ # the octet are used to distinguish the type as native to ASN.1,
45
+ # application-specific, context-specific, or private. See
46
+ # Net::BER::TAG_CLASS and Net::BER::ENCODING_TYPE for more information.
47
+ #
48
+ # If Class is set to Universal (0b00______), the value is of a type native
49
+ # to ASN.1 (e.g. INTEGER). The Application class (0b01______) is only
50
+ # valid for one specific application. Context_specific (0b10______)
51
+ # depends on the context and private (0b11_______) can be defined in
52
+ # private specifications
53
+ #
54
+ # If the primitive/constructed bit is zero (0b__0_____), it specifies that
55
+ # the value is primitive like an INTEGER. If it is one (0b__1_____), the
56
+ # value is a constructed value that contains type-length-value encoded
57
+ # types like a SET or a SEQUENCE.
58
+ #
59
+ # === Defined Universal (ASN.1 Native) Types
60
+ # There are a number of pre-defined universal (native) types.
61
+ #
62
+ # <table>
63
+ # <tr><th>Name</th><th>Primitive<br />Constructed</th><th>Number</th></tr>
64
+ # <tr><th>EOC (End-of-Content)</th><th>P</th><td>0: 0 (0x0, 0b00000000)</td></tr>
65
+ # <tr><th>BOOLEAN</th><th>P</th><td>1: 1 (0x01, 0b00000001)</td></tr>
66
+ # <tr><th>INTEGER</th><th>P</th><td>2: 2 (0x02, 0b00000010)</td></tr>
67
+ # <tr><th>BIT STRING</th><th>P</th><td>3: 3 (0x03, 0b00000011)</td></tr>
68
+ # <tr><th>BIT STRING</th><th>C</th><td>3: 35 (0x23, 0b00100011)</td></tr>
69
+ # <tr><th>OCTET STRING</th><th>P</th><td>4: 4 (0x04, 0b00000100)</td></tr>
70
+ # <tr><th>OCTET STRING</th><th>C</th><td>4: 36 (0x24, 0b00100100)</td></tr>
71
+ # <tr><th>NULL</th><th>P</th><td>5: 5 (0x05, 0b00000101)</td></tr>
72
+ # <tr><th>OBJECT IDENTIFIER</th><th>P</th><td>6: 6 (0x06, 0b00000110)</td></tr>
73
+ # <tr><th>Object Descriptor</th><th>P</th><td>7: 7 (0x07, 0b00000111)</td></tr>
74
+ # <tr><th>EXTERNAL</th><th>C</th><td>8: 40 (0x28, 0b00101000)</td></tr>
75
+ # <tr><th>REAL (float)</th><th>P</th><td>9: 9 (0x09, 0b00001001)</td></tr>
76
+ # <tr><th>ENUMERATED</th><th>P</th><td>10: 10 (0x0a, 0b00001010)</td></tr>
77
+ # <tr><th>EMBEDDED PDV</th><th>C</th><td>11: 43 (0x2b, 0b00101011)</td></tr>
78
+ # <tr><th>UTF8String</th><th>P</th><td>12: 12 (0x0c, 0b00001100)</td></tr>
79
+ # <tr><th>UTF8String</th><th>C</th><td>12: 44 (0x2c, 0b00101100)</td></tr>
80
+ # <tr><th>RELATIVE-OID</th><th>P</th><td>13: 13 (0x0d, 0b00001101)</td></tr>
81
+ # <tr><th>SEQUENCE and SEQUENCE OF</th><th>C</th><td>16: 48 (0x30, 0b00110000)</td></tr>
82
+ # <tr><th>SET and SET OF</th><th>C</th><td>17: 49 (0x31, 0b00110001)</td></tr>
83
+ # <tr><th>NumericString</th><th>P</th><td>18: 18 (0x12, 0b00010010)</td></tr>
84
+ # <tr><th>NumericString</th><th>C</th><td>18: 50 (0x32, 0b00110010)</td></tr>
85
+ # <tr><th>PrintableString</th><th>P</th><td>19: 19 (0x13, 0b00010011)</td></tr>
86
+ # <tr><th>PrintableString</th><th>C</th><td>19: 51 (0x33, 0b00110011)</td></tr>
87
+ # <tr><th>T61String</th><th>P</th><td>20: 20 (0x14, 0b00010100)</td></tr>
88
+ # <tr><th>T61String</th><th>C</th><td>20: 52 (0x34, 0b00110100)</td></tr>
89
+ # <tr><th>VideotexString</th><th>P</th><td>21: 21 (0x15, 0b00010101)</td></tr>
90
+ # <tr><th>VideotexString</th><th>C</th><td>21: 53 (0x35, 0b00110101)</td></tr>
91
+ # <tr><th>IA5String</th><th>P</th><td>22: 22 (0x16, 0b00010110)</td></tr>
92
+ # <tr><th>IA5String</th><th>C</th><td>22: 54 (0x36, 0b00110110)</td></tr>
93
+ # <tr><th>UTCTime</th><th>P</th><td>23: 23 (0x17, 0b00010111)</td></tr>
94
+ # <tr><th>UTCTime</th><th>C</th><td>23: 55 (0x37, 0b00110111)</td></tr>
95
+ # <tr><th>GeneralizedTime</th><th>P</th><td>24: 24 (0x18, 0b00011000)</td></tr>
96
+ # <tr><th>GeneralizedTime</th><th>C</th><td>24: 56 (0x38, 0b00111000)</td></tr>
97
+ # <tr><th>GraphicString</th><th>P</th><td>25: 25 (0x19, 0b00011001)</td></tr>
98
+ # <tr><th>GraphicString</th><th>C</th><td>25: 57 (0x39, 0b00111001)</td></tr>
99
+ # <tr><th>VisibleString</th><th>P</th><td>26: 26 (0x1a, 0b00011010)</td></tr>
100
+ # <tr><th>VisibleString</th><th>C</th><td>26: 58 (0x3a, 0b00111010)</td></tr>
101
+ # <tr><th>GeneralString</th><th>P</th><td>27: 27 (0x1b, 0b00011011)</td></tr>
102
+ # <tr><th>GeneralString</th><th>C</th><td>27: 59 (0x3b, 0b00111011)</td></tr>
103
+ # <tr><th>UniversalString</th><th>P</th><td>28: 28 (0x1c, 0b00011100)</td></tr>
104
+ # <tr><th>UniversalString</th><th>C</th><td>28: 60 (0x3c, 0b00111100)</td></tr>
105
+ # <tr><th>CHARACTER STRING</th><th>P</th><td>29: 29 (0x1d, 0b00011101)</td></tr>
106
+ # <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
107
+ # <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
108
+ # <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
109
+ # </table>
110
+ module BER
111
+ VERSION = Net::LDAP::VERSION
112
+
113
+ ##
114
+ # Used for BER-encoding the length and content bytes of a Fixnum integer
115
+ # values.
116
+ MAX_FIXNUM_SIZE = 0.size
117
+
118
+ ##
119
+ # BER tag classes are kept in bits seven and eight of the tag type
120
+ # octet.
121
+ #
122
+ # <table>
123
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
124
+ # <tr><th><tt>0b00______</tt></th><td>Universal (ASN.1 Native) Types</td></tr>
125
+ # <tr><th><tt>0b01______</tt></th><td>Application Types</td></tr>
126
+ # <tr><th><tt>0b10______</tt></th><td>Context-Specific Types</td></tr>
127
+ # <tr><th><tt>0b11______</tt></th><td>Private Types</td></tr>
128
+ # </table>
129
+ TAG_CLASS = {
130
+ :universal => 0b00000000, # 0
131
+ :application => 0b01000000, # 64
132
+ :context_specific => 0b10000000, # 128
133
+ :private => 0b11000000, # 192
134
+ }
135
+
136
+ ##
137
+ # BER encoding type is kept in bit 6 of the tag type octet.
138
+ #
139
+ # <table>
140
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
141
+ # <tr><th><tt>0b__0_____</tt></th><td>Primitive</td></tr>
142
+ # <tr><th><tt>0b__1_____</tt></th><td>Constructed</td></tr>
143
+ # </table>
144
+ ENCODING_TYPE = {
145
+ :primitive => 0b00000000, # 0
146
+ :constructed => 0b00100000, # 32
147
+ }
148
+
149
+ ##
150
+ # Accepts a hash of hashes describing a BER syntax and converts it into
151
+ # a byte-keyed object for fast BER conversion lookup. The resulting
152
+ # "compiled" syntax is used by Net::BER::BERParser.
153
+ #
154
+ # This method should be called only by client classes of Net::BER (e.g.,
155
+ # Net::LDAP and Net::SNMP) and not by clients of those classes.
156
+ #
157
+ # The hash-based syntax uses TAG_CLASS keys that contain hashes of
158
+ # ENCODING_TYPE keys that contain tag numbers with object type markers.
159
+ #
160
+ # :<TAG_CLASS> => {
161
+ # :<ENCODING_TYPE> => {
162
+ # <number> => <object-type>
163
+ # },
164
+ # },
165
+ #
166
+ # === Permitted Object Types
167
+ # <tt>:string</tt>:: A string value, represented as BerIdentifiedString.
168
+ # <tt>:integer</tt>:: An integer value, represented with Fixnum.
169
+ # <tt>:oid</tt>:: An Object Identifier value; see X.690 section
170
+ # 8.19. Currently represented with a standard array,
171
+ # but may be better represented as a
172
+ # BerIdentifiedOID object.
173
+ # <tt>:array</tt>:: A sequence, represented as BerIdentifiedArray.
174
+ # <tt>:boolean</tt>:: A boolean value, represented as +true+ or +false+.
175
+ # <tt>:null</tt>:: A null value, represented as BerIdentifiedNull.
176
+ #
177
+ # === Example
178
+ # Net::LDAP defines its ASN.1 BER syntax something like this:
179
+ #
180
+ # class Net::LDAP
181
+ # AsnSyntax = Net::BER.compile_syntax({
182
+ # :application => {
183
+ # :primitive => {
184
+ # 2 => :null,
185
+ # },
186
+ # :constructed => {
187
+ # 0 => :array,
188
+ # # ...
189
+ # },
190
+ # },
191
+ # :context_specific => {
192
+ # :primitive => {
193
+ # 0 => :string,
194
+ # # ...
195
+ # },
196
+ # :constructed => {
197
+ # 0 => :array,
198
+ # # ...
199
+ # },
200
+ # }
201
+ # })
202
+ # end
203
+ #
204
+ # NOTE:: For readability and formatting purposes, Net::LDAP and its
205
+ # siblings actually construct their syntaxes more deliberately,
206
+ # as shown below. Since a hash is passed in the end in any case,
207
+ # the format does not matter.
208
+ #
209
+ # primitive = { 2 => :null }
210
+ # constructed = {
211
+ # 0 => :array,
212
+ # # ...
213
+ # }
214
+ # application = {
215
+ # :primitive => primitive,
216
+ # :constructed => constructed
217
+ # }
218
+ #
219
+ # primitive = {
220
+ # 0 => :string,
221
+ # # ...
222
+ # }
223
+ # constructed = {
224
+ # 0 => :array,
225
+ # # ...
226
+ # }
227
+ # context_specific = {
228
+ # :primitive => primitive,
229
+ # :constructed => constructed
230
+ # }
231
+ # AsnSyntax = Net::BER.compile_syntax(:application => application,
232
+ # :context_specific => context_specific)
233
+ def self.compile_syntax(syntax)
234
+ # TODO 20100327 AZ: Should we be allocating an array of 256 values
235
+ # that will either be +nil+ or an object type symbol, or should we
236
+ # allocate an empty Hash since unknown values return +nil+ anyway?
237
+ out = [ nil ] * 256
238
+ syntax.each do |tag_class_id, encodings|
239
+ tag_class = TAG_CLASS[tag_class_id]
240
+ encodings.each do |encoding_id, classes|
241
+ encoding = ENCODING_TYPE[encoding_id]
242
+ object_class = tag_class + encoding
243
+ classes.each do |number, object_type|
244
+ out[object_class + number] = object_type
245
+ end
246
+ end
247
+ end
248
+ out
249
+ end
250
+ end
251
+ end
252
+
253
+ class Net::BER::BerError < RuntimeError; end
254
+
255
+ ##
256
+ # An Array object with a BER identifier attached.
257
+ class Net::BER::BerIdentifiedArray < Array
258
+ attr_accessor :ber_identifier
259
+
260
+ def initialize(*args)
261
+ super
262
+ end
263
+ end
264
+
265
+ ##
266
+ # A BER object identifier.
267
+ class Net::BER::BerIdentifiedOid
268
+ attr_accessor :ber_identifier
269
+
270
+ def initialize(oid)
271
+ if oid.is_a?(String)
272
+ oid = oid.split(/\./).map {|s| s.to_i }
273
+ end
274
+ @value = oid
275
+ end
276
+
277
+ def to_ber
278
+ to_ber_oid
279
+ end
280
+
281
+ def to_ber_oid
282
+ @value.to_ber_oid
283
+ end
284
+
285
+ def to_s
286
+ @value.join(".")
287
+ end
288
+
289
+ def to_arr
290
+ @value.dup
291
+ end
292
+ end
293
+
294
+ ##
295
+ # A String object with a BER identifier attached.
296
+ class Net::BER::BerIdentifiedString < String
297
+ attr_accessor :ber_identifier
298
+ def initialize args
299
+ super args
300
+ # LDAP uses UTF-8 encoded strings
301
+ self.encode('UTF-8') if self.respond_to?(:encoding) rescue self
302
+ end
303
+ end
304
+
305
+ module Net::BER
306
+ ##
307
+ # A BER null object.
308
+ class BerIdentifiedNull
309
+ attr_accessor :ber_identifier
310
+ def to_ber
311
+ "\005\000"
312
+ end
313
+ end
314
+
315
+ ##
316
+ # The default BerIdentifiedNull object.
317
+ Null = Net::BER::BerIdentifiedNull.new
318
+ end
319
+
320
+ require 'net/ber/core_ext'
@@ -0,0 +1,182 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'stringio'
3
+
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 }
20
+
21
+ primitive = { 10 => :integer }
22
+ context = { :primitive => primitive }
23
+
24
+ # The universal, built-in ASN.1 BER syntax.
25
+ BuiltinSyntax = Net::BER.compile_syntax(:universal => universal,
26
+ :context_specific => context)
27
+
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]
37
+
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
+ neg = !(data.unpack("C").first & 0x80).zero?
45
+ int = 0
46
+
47
+ data.each_byte do |b|
48
+ int = (int << 8) + (neg ? 255 - b : b)
49
+ end
50
+
51
+ if neg
52
+ (int + 1) * -1
53
+ else
54
+ int
55
+ end
56
+ elsif object_type == :oid
57
+ # See X.690 pgh 8.19 for an explanation of this algorithm.
58
+ # This is potentially not good enough. We may need a
59
+ # BerIdentifiedOid as a subclass of BerIdentifiedArray, to
60
+ # get the ber identifier and also a to_s method that produces
61
+ # the familiar dotted notation.
62
+ oid = data.unpack("w*")
63
+ f = oid.shift
64
+ g = if f < 40
65
+ [0, f]
66
+ elsif f < 80
67
+ [1, f - 40]
68
+ else
69
+ # f - 80 can easily be > 80. What a weird optimization.
70
+ [2, f - 80]
71
+ end
72
+ oid.unshift g.last
73
+ oid.unshift g.first
74
+ # Net::BER::BerIdentifiedOid.new(oid)
75
+ oid
76
+ elsif object_type == :array
77
+ seq = Net::BER::BerIdentifiedArray.new
78
+ seq.ber_identifier = id
79
+ sio = StringIO.new(data || "")
80
+ # Interpret the subobject, but note how the loop is built:
81
+ # nil ends the loop, but false (a valid BER value) does not!
82
+ while (e = sio.read_ber(syntax)) != nil
83
+ seq << e
84
+ end
85
+ seq
86
+ elsif object_type == :boolean
87
+ data != "\000"
88
+ elsif object_type == :null
89
+ n = Net::BER::BerIdentifiedNull.new
90
+ n.ber_identifier = id
91
+ n
92
+ else
93
+ raise Net::BER::BerError, "Unsupported object type: id=#{id}"
94
+ end
95
+ end
96
+ private :parse_ber_object
97
+
98
+ ##
99
+ # This is an extract of how our BER object length parsing is done to
100
+ # simplify the primary call. This is defined in X.690 section 8.1.3.
101
+ #
102
+ # The BER length will either be a single byte or up to 126 bytes in
103
+ # length. There is a special case of a BER length indicating that the
104
+ # content-length is undefined and will be identified by the presence of
105
+ # two null values (0x00 0x00).
106
+ #
107
+ # <table>
108
+ # <tr>
109
+ # <th>Range</th>
110
+ # <th>Length</th>
111
+ # </tr>
112
+ # <tr>
113
+ # <th>0x00 -- 0x7f<br />0b00000000 -- 0b01111111</th>
114
+ # <td>0 - 127 bytes</td>
115
+ # </tr>
116
+ # <tr>
117
+ # <th>0x80<br />0b10000000</th>
118
+ # <td>Indeterminate (end-of-content marker required)</td>
119
+ # </tr>
120
+ # <tr>
121
+ # <th>0x81 -- 0xfe<br />0b10000001 -- 0b11111110</th>
122
+ # <td>1 - 126 bytes of length as an integer value</td>
123
+ # </tr>
124
+ # <tr>
125
+ # <th>0xff<br />0b11111111</th>
126
+ # <td>Illegal (reserved for future expansion)</td>
127
+ # </tr>
128
+ # </table>
129
+ #
130
+ #--
131
+ # This has been modified from the version that was previously inside
132
+ # #read_ber to handle both the indeterminate terminator case and the
133
+ # invalid BER length case. Because the "lengthlength" value was not used
134
+ # inside of #read_ber, we no longer return it.
135
+ def read_ber_length
136
+ n = getbyte
137
+
138
+ if n <= 0x7f
139
+ n
140
+ elsif n == 0x80
141
+ -1
142
+ elsif n == 0xff
143
+ raise Net::BER::BerError, "Invalid BER length 0xFF detected."
144
+ else
145
+ v = 0
146
+ read(n & 0x7f).each_byte do |b|
147
+ v = (v << 8) + b
148
+ end
149
+
150
+ v
151
+ end
152
+ end
153
+ private :read_ber_length
154
+
155
+ ##
156
+ # Reads a BER object from the including object. Requires that #getbyte is
157
+ # implemented on the including object and that it returns a Fixnum value.
158
+ # Also requires #read(bytes) to work.
159
+ #
160
+ # Yields the object type `id` and the data `content_length` if a block is
161
+ # given. This is namely to support instrumentation.
162
+ #
163
+ # This does not work with non-blocking I/O.
164
+ def read_ber(syntax = nil)
165
+ # TODO: clean this up so it works properly with partial packets coming
166
+ # from streams that don't block when we ask for more data (like
167
+ # StringIOs). At it is, this can throw TypeErrors and other nasties.
168
+
169
+ id = getbyte or return nil # don't trash this value, we'll use it later
170
+ content_length = read_ber_length
171
+
172
+ yield id, content_length if block_given?
173
+
174
+ if -1 == content_length
175
+ raise Net::BER::BerError, "Indeterminite BER content length not implemented."
176
+ else
177
+ data = read(content_length)
178
+ end
179
+
180
+ parse_ber_object(syntax, id, data)
181
+ end
182
+ end