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 |