drab 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5481db4287eb6cb52247f7168320c885184a05d6
4
+ data.tar.gz: 62553e86a2b6f0dbfeb3bc313d189f26fb31d393
5
+ SHA512:
6
+ metadata.gz: f981513e4fa4ce1afc0c2f1cd61a2abaffbbb0a7b51b336615c9fd160a28518dde8ca9528f02210ecb39f3e9a339227ad1a33c9dbb9f36d19b05afb1bf674200
7
+ data.tar.gz: 65854246a51b043cb837978eda03babcdb53882013a1d845795988c5e664342cbf665bd48236ab27c8fe11f0de90531f825ab1c5dc5f3142e58183221c68e0ef
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ test/
3
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in drab.gemspec
4
+ gemspec
5
+
6
+ platform :jruby do
7
+ gem 'rb-readline'
8
+ end
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ drab (0.1.0)
5
+ marshal-structure (~> 2.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.3)
11
+ marshal-structure (2.0)
12
+ rake (10.5.0)
13
+ rspec (3.6.0)
14
+ rspec-core (~> 3.6.0)
15
+ rspec-expectations (~> 3.6.0)
16
+ rspec-mocks (~> 3.6.0)
17
+ rspec-core (3.6.0)
18
+ rspec-support (~> 3.6.0)
19
+ rspec-expectations (3.6.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.6.0)
22
+ rspec-mocks (3.6.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.6.0)
25
+ rspec-support (3.6.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.13)
32
+ drab!
33
+ rake (~> 10.0)
34
+ rb-readline
35
+ rspec (~> 3.0)
36
+
37
+ BUNDLED WITH
38
+ 1.15.4
data/LICENSE ADDED
@@ -0,0 +1,59 @@
1
+ secure_compare in lib/drab/drab.rb is sourced from rails/rails and licensed
2
+ under the MIT license.
3
+
4
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
5
+ You can redistribute it and/or modify it under either the terms of the
6
+ 2-clause BSDL (see the file BSDL), or the conditions below:
7
+
8
+ 1. You may make and give away verbatim copies of the source form of the
9
+ software without restriction, provided that you duplicate all of the
10
+ original copyright notices and associated disclaimers.
11
+
12
+ 2. You may modify your copy of the software in any way, provided that
13
+ you do at least ONE of the following:
14
+
15
+ a) place your modifications in the Public Domain or otherwise
16
+ make them Freely Available, such as by posting said
17
+ modifications to Usenet or an equivalent medium, or by allowing
18
+ the author to include your modifications in the software.
19
+
20
+ b) use the modified software only within your corporation or
21
+ organization.
22
+
23
+ c) give non-standard binaries non-standard names, with
24
+ instructions on where to get the original software distribution.
25
+
26
+ d) make other distribution arrangements with the author.
27
+
28
+ 3. You may distribute the software in object code or binary form,
29
+ provided that you do at least ONE of the following:
30
+
31
+ a) distribute the binaries and library files of the software,
32
+ together with instructions (in the manual page or equivalent)
33
+ on where to get the original distribution.
34
+
35
+ b) accompany the distribution with the machine-readable source of
36
+ the software.
37
+
38
+ c) give non-standard binaries non-standard names, with
39
+ instructions on where to get the original software distribution.
40
+
41
+ d) make other distribution arrangements with the author.
42
+
43
+ 4. You may modify and include the part of the software into any other
44
+ software (possibly commercial). But some files in the distribution
45
+ are not written by the author, so that they are not under these terms.
46
+
47
+ For the list of those files and their copying conditions, see the
48
+ file LEGAL.
49
+
50
+ 5. The scripts and library files supplied as input to or produced as
51
+ output from the software do not automatically fall under the
52
+ copyright of the software, but belong to whomever generated them,
53
+ and may be sold commercially, and may be aggregated with this
54
+ software.
55
+
56
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
57
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59
+ PURPOSE.
@@ -0,0 +1,2 @@
1
+ # drab
2
+ hardened (incompatible) druby
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "drab"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'drab/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "drab"
8
+ spec.version = DRab::VERSION
9
+ spec.authors = ["Masatoshi SEKI", "Jeff Dileo"]
10
+ spec.email = ["jtdileo@gmail.com"]
11
+ spec.summary = "Make DRb great again!"
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/chaosdata/drab"
14
+ spec.licenses = ['Ruby']
15
+
16
+ spec.required_ruby_version = '>= 1.9.3'
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against " \
24
+ "public gem pushes."
25
+ end
26
+
27
+ #spec.files = `git ls-files lib *.md LICENSE`.split("\n")
28
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
29
+ f.match(%r{^(test|spec|features)/})
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ spec.add_dependency "marshal-structure", "~> 2.0"
36
+
37
+ spec.add_development_dependency "bundler", "~> 1.13"
38
+ spec.add_development_dependency "rake", "~> 10.0"
39
+ spec.add_development_dependency "rspec", "~> 3.0"
40
+ end
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: false
2
+ require 'drab/drab'
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: false
2
+ # Copyright (c) 2000,2002,2003 Masatoshi SEKI
3
+ #
4
+ # acl.rb is copyrighted free software by Masatoshi SEKI.
5
+ # You can redistribute it and/or modify it under the same terms as Ruby.
6
+
7
+ require 'ipaddr'
8
+
9
+ ##
10
+ # Simple Access Control Lists.
11
+ #
12
+ # Access control lists are composed of "allow" and "deny" halves to control
13
+ # access. Use "all" or "*" to match any address. To match a specific address
14
+ # use any address or address mask that IPAddr can understand.
15
+ #
16
+ # Example:
17
+ #
18
+ # list = %w[
19
+ # deny all
20
+ # allow 192.168.1.1
21
+ # allow ::ffff:192.168.1.2
22
+ # allow 192.168.1.3
23
+ # ]
24
+ #
25
+ # # From Socket#peeraddr, see also ACL#allow_socket?
26
+ # addr = ["AF_INET", 10, "lc630", "192.168.1.3"]
27
+ #
28
+ # acl = ACL.new
29
+ # p acl.allow_addr?(addr) # => true
30
+ #
31
+ # acl = ACL.new(list, ACL::DENY_ALLOW)
32
+ # p acl.allow_addr?(addr) # => true
33
+
34
+ class ACL
35
+
36
+ ##
37
+ # The current version of ACL
38
+
39
+ VERSION=["2.0.0"]
40
+
41
+ ##
42
+ # An entry in an ACL
43
+
44
+ class ACLEntry
45
+
46
+ ##
47
+ # Creates a new entry using +str+.
48
+ #
49
+ # +str+ may be "*" or "all" to match any address, an IP address string
50
+ # to match a specific address, an IP address mask per IPAddr, or one
51
+ # containing "*" to match part of an IPv4 address.
52
+
53
+ def initialize(str)
54
+ if str == '*' or str == 'all'
55
+ @pat = [:all]
56
+ elsif str.include?('*')
57
+ @pat = [:name, dot_pat(str)]
58
+ else
59
+ begin
60
+ @pat = [:ip, IPAddr.new(str)]
61
+ rescue ArgumentError
62
+ @pat = [:name, dot_pat(str)]
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ ##
70
+ # Creates a regular expression to match IPv4 addresses
71
+
72
+ def dot_pat_str(str)
73
+ list = str.split('.').collect { |s|
74
+ (s == '*') ? '.+' : s
75
+ }
76
+ list.join("\\.")
77
+ end
78
+
79
+ private
80
+
81
+ ##
82
+ # Creates a Regexp to match an address.
83
+
84
+ def dot_pat(str)
85
+ /\A#{dot_pat_str(str)}\z/
86
+ end
87
+
88
+ public
89
+
90
+ ##
91
+ # Matches +addr+ against this entry.
92
+
93
+ def match(addr)
94
+ case @pat[0]
95
+ when :all
96
+ true
97
+ when :ip
98
+ begin
99
+ ipaddr = IPAddr.new(addr[3])
100
+ ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4?
101
+ rescue ArgumentError
102
+ return false
103
+ end
104
+ (@pat[1].include?(ipaddr)) ? true : false
105
+ when :name
106
+ (@pat[1] =~ addr[2]) ? true : false
107
+ else
108
+ false
109
+ end
110
+ end
111
+ end
112
+
113
+ ##
114
+ # A list of ACLEntry objects. Used to implement the allow and deny halves
115
+ # of an ACL
116
+
117
+ class ACLList
118
+
119
+ ##
120
+ # Creates an empty ACLList
121
+
122
+ def initialize
123
+ @list = []
124
+ end
125
+
126
+ public
127
+
128
+ ##
129
+ # Matches +addr+ against each ACLEntry in this list.
130
+
131
+ def match(addr)
132
+ @list.each do |e|
133
+ return true if e.match(addr)
134
+ end
135
+ false
136
+ end
137
+
138
+ public
139
+
140
+ ##
141
+ # Adds +str+ as an ACLEntry in this list
142
+
143
+ def add(str)
144
+ @list.push(ACLEntry.new(str))
145
+ end
146
+
147
+ end
148
+
149
+ ##
150
+ # Default to deny
151
+
152
+ DENY_ALLOW = 0
153
+
154
+ ##
155
+ # Default to allow
156
+
157
+ ALLOW_DENY = 1
158
+
159
+ ##
160
+ # Creates a new ACL from +list+ with an evaluation +order+ of DENY_ALLOW or
161
+ # ALLOW_DENY.
162
+ #
163
+ # An ACL +list+ is an Array of "allow" or "deny" and an address or address
164
+ # mask or "all" or "*" to match any address:
165
+ #
166
+ # %w[
167
+ # deny all
168
+ # allow 192.0.2.2
169
+ # allow 192.0.2.128/26
170
+ # ]
171
+
172
+ def initialize(list=nil, order = DENY_ALLOW)
173
+ @order = order
174
+ @deny = ACLList.new
175
+ @allow = ACLList.new
176
+ install_list(list) if list
177
+ end
178
+
179
+ public
180
+
181
+ ##
182
+ # Allow connections from Socket +soc+?
183
+
184
+ def allow_socket?(soc)
185
+ allow_addr?(soc.peeraddr)
186
+ end
187
+
188
+ public
189
+
190
+ ##
191
+ # Allow connections from addrinfo +addr+? It must be formatted like
192
+ # Socket#peeraddr:
193
+ #
194
+ # ["AF_INET", 10, "lc630", "192.0.2.1"]
195
+
196
+ def allow_addr?(addr)
197
+ case @order
198
+ when DENY_ALLOW
199
+ return true if @allow.match(addr)
200
+ return false if @deny.match(addr)
201
+ return true
202
+ when ALLOW_DENY
203
+ return false if @deny.match(addr)
204
+ return true if @allow.match(addr)
205
+ return false
206
+ else
207
+ false
208
+ end
209
+ end
210
+
211
+ public
212
+
213
+ ##
214
+ # Adds +list+ of ACL entries to this ACL.
215
+
216
+ def install_list(list)
217
+ i = 0
218
+ while i < list.size
219
+ permission, domain = list.slice(i,2)
220
+ case permission.downcase
221
+ when 'allow'
222
+ @allow.add(domain)
223
+ when 'deny'
224
+ @deny.add(domain)
225
+ else
226
+ raise "Invalid ACL entry #{list}"
227
+ end
228
+ i += 2
229
+ end
230
+ end
231
+
232
+ end
@@ -0,0 +1,1499 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # = ./drab.rb
4
+ #
5
+ # Restricted Distributed Ruby: _drab_ version 0.1.0
6
+ #
7
+ # Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or
8
+ # modify it under the same terms as Ruby.
9
+ #
10
+ # Author:: Masatoshi SEKI, Jeff Dileo
11
+ #
12
+
13
+ require 'socket'
14
+ require 'thread'
15
+ require 'io/wait'
16
+ require 'drab/eq'
17
+ require 'json'
18
+ require 'marshal/structure'
19
+ require 'set'
20
+
21
+ module DRab
22
+
23
+ class DRabError < RuntimeError; end
24
+ class DRabConnError < DRabError; end
25
+
26
+ class DRabIdConv
27
+ @@ids = {} # totally a memory leak, but not much that can be done
28
+ # unless there's a way to remove recycled objects' ids
29
+
30
+
31
+ def to_obj(ref)
32
+ if @@ids.include?(ref)
33
+ #ObjectSpace._id2ref(ref)
34
+ @@ids[ref]
35
+ else
36
+ STDERR.puts "attempted to load an unshared object by object id"
37
+ raise Exception.new("attempted to load an unshared object by object id")
38
+ end
39
+ end
40
+
41
+ def to_id(obj)
42
+ ret = obj.nil? ? nil : obj.__id__
43
+ #@@id_list.add(ret)
44
+ @@ids[ret] = obj
45
+ ret
46
+ end
47
+ end
48
+
49
+ module DRabUndumped
50
+ def _dump(dummy)
51
+ raise TypeError, "can't/won't dump"
52
+ end
53
+ end
54
+
55
+ class DRabServerNotFound < DRabError; end
56
+ class DRabBadURI < DRabError; end
57
+ class DRabBadScheme < DRabError; end
58
+
59
+ class DRabUnknownError < DRabError
60
+
61
+ def initialize(unknown)
62
+ @unknown = unknown
63
+ super(unknown.name)
64
+ end
65
+
66
+ attr_reader :unknown
67
+
68
+ #def self._load(s) # :nodoc:
69
+ # Marshal::load(s)
70
+ #end
71
+
72
+ #def _dump(lv) # :nodoc:
73
+ # Marshal::dump(@unknown)
74
+ #end
75
+ end
76
+
77
+ class DRabRemoteError < DRabError
78
+
79
+ def initialize(error)
80
+ @reason = error.class.to_s
81
+ super("#{error.message} (#{error.class})")
82
+ set_backtrace(error.backtrace)
83
+ end
84
+
85
+ attr_reader :reason
86
+ end
87
+
88
+ class DRabUnknown
89
+
90
+ def initialize(err, buf)
91
+ case err.to_s
92
+ when /uninitialized constant (\S+)/
93
+ @name = $1
94
+ when /undefined class\/module (\S+)/
95
+ @name = $1
96
+ else
97
+ @name = nil
98
+ end
99
+ @buf = buf
100
+ end
101
+
102
+ attr_reader :name
103
+
104
+ attr_reader :buf
105
+
106
+ #def self._load(s) # :nodoc:
107
+ # begin
108
+ # Marshal::load(s)
109
+ # rescue NameError, ArgumentError
110
+ # DRabUnknown.new($!, s)
111
+ # end
112
+ #end
113
+
114
+ #def _dump(lv) # :nodoc:
115
+ # @buf
116
+ #end
117
+
118
+ #def reload
119
+ # self.class._load(@buf)
120
+ #end
121
+
122
+ def exception
123
+ DRabUnknownError.new(self)
124
+ end
125
+ end
126
+
127
+ class DRabArray
128
+ class Token
129
+ def initialize(sz)
130
+ @size = sz
131
+ end
132
+
133
+ def size
134
+ @size
135
+ end
136
+
137
+ def self._load(s)
138
+ sz = JSON::load(s)
139
+ if sz.is_a?(Integer)
140
+ self.new(sz)
141
+ else
142
+ raise Exception.new("got non-integer JSON when parsing DRabArray")
143
+ end
144
+ end
145
+
146
+ def _dump(lv)
147
+ JSON::dump(@size)
148
+ end
149
+ end
150
+
151
+ def initialize(ary)
152
+ @ary = ary.collect { |obj|
153
+ if obj.kind_of? DRabUndumped
154
+ DRabObject.new(obj)
155
+ else
156
+ begin
157
+ obj
158
+ rescue
159
+ DRabObject.new(obj)
160
+ end
161
+ end
162
+ }
163
+ end
164
+
165
+ def get
166
+ @ary
167
+ end
168
+
169
+ def self._load(s)
170
+ raise Exception.new("not marshallable")
171
+ end
172
+
173
+ def self._dump(lv)
174
+ raise Exception.new("not marshallable")
175
+ end
176
+ end
177
+
178
+
179
+ class DRabMessage
180
+ def initialize(config)
181
+ @load_limit = config[:load_limit]
182
+ @argc_limit = config[:argc_limit]
183
+ end
184
+
185
+ def dump(obj, error=false, as_json = false)
186
+ if as_json
187
+ str = JSON::dump(obj)
188
+ return [str.size].pack('N') + str
189
+ end
190
+
191
+ if obj.is_a?(DRabUndumped)
192
+ obj = make_proxy(obj, error)
193
+ elsif obj.is_a?(Proc)
194
+ obj = make_proxy(obj, error)
195
+ elsif obj.is_a?(DRabArray)
196
+ p = dump(DRabArray::Token.new(obj.get.size), error)
197
+ obj.get.each { |o| p += dump(o) }
198
+ return p
199
+ end
200
+
201
+ begin
202
+ str = Marshal::dump(obj)
203
+ rescue
204
+ #str = Marshal::dump(make_proxy(obj, error))
205
+ STDERR.puts "failed to marshal value of type " + obj.class.to_s + ", not exposing a proxy"
206
+ str = Marshal::dump(nil)
207
+ end
208
+ [str.size].pack('N') + str
209
+ end
210
+
211
+ def load(soc, as_json = false)
212
+ begin
213
+ sz = soc.read(4)
214
+ rescue
215
+ raise(DRabConnError, $!.message, $!.backtrace)
216
+ end
217
+ raise(DRabConnError, 'connection closed') if sz.nil?
218
+ raise(DRabConnError, 'premature header') if sz.size < 4
219
+ sz = sz.unpack('N')[0]
220
+ raise(DRabConnError, "too large packet #{sz}") if @load_limit < sz
221
+ begin
222
+ str = soc.read(sz)
223
+ rescue
224
+ raise(DRabConnError, $!.message, $!.backtrace)
225
+ end
226
+ raise(DRabConnError, 'connection closed') if str.nil?
227
+ raise(DRabConnError, 'premature marshal format(can\'t read)') if str.size < sz
228
+
229
+ if not as_json
230
+ begin
231
+ typ, structure = parse_marshal(str)
232
+ rescue Exception => e
233
+ end
234
+ end
235
+
236
+ DRab.mutex.synchronize do
237
+ begin
238
+ save = Thread.current[:drab_untaint]
239
+ Thread.current[:drab_untaint] = []
240
+ if as_json
241
+ JSON::load(str)
242
+ else
243
+ if typ.between?(Types::Bln, Types::Arr)
244
+ Marshal::load(str)
245
+ elsif typ === Types::Err
246
+ Exception.new(s.to_s)
247
+ else
248
+ STDERR.puts "got weird object: " + str.inspect + "\nwith structure: " + structure.inspect
249
+ nil
250
+ end
251
+ end
252
+ rescue NameError, ArgumentError => e
253
+ puts e
254
+ puts e.backtrace
255
+ DRabUnknown.new($!, str)
256
+ ensure
257
+ Thread.current[:drab_untaint].each do |x|
258
+ x.untaint
259
+ end
260
+ Thread.current[:drab_untaint] = save
261
+ end
262
+ end
263
+ end
264
+
265
+ def send_request(stream, ref, msg_id, arg, b)
266
+ ary = []
267
+ ary.push(dump(ref.__drabref, false, true))
268
+ ary.push(dump(msg_id.id2name, false, true))
269
+ ary.push(dump(arg.length, false, true))
270
+ arg.each do |e|
271
+ ary.push(dump(e))
272
+ end
273
+ #ary.push(dump(b))
274
+
275
+ send_secret(stream)
276
+ stream.write(ary.join(''))
277
+ rescue
278
+ raise(DRabConnError, $!.message, $!.backtrace)
279
+ end
280
+
281
+ def match_structures(template, target)
282
+ begin
283
+ if not (template.is_a?(Array) and target.is_a?(Array))
284
+ return false
285
+ end
286
+
287
+ if template.size != target.size
288
+ return false
289
+ end
290
+
291
+ template.size.times do |n|
292
+ if template[n].is_a?(Array)
293
+ return match_structures(template[n], target[n])
294
+ end
295
+
296
+ if template[n] === target[n]
297
+ return true
298
+ end
299
+
300
+ if template[n].is_a?(String) and target[n].is_a?(String)
301
+ if template[n] === "*"
302
+ return true
303
+ elsif template[n].start_with?("*")
304
+ return target[n].end_with?(template[n][1..-1])
305
+ elsif template[n].end_with?("*")
306
+ return target[n].start_with?(template[n][0..-2])
307
+ end
308
+ end
309
+ end
310
+ rescue Exception => e
311
+ STDERR.puts e
312
+ STDERR.puts e.backtrace
313
+ end
314
+ return false
315
+ end
316
+
317
+ module Types
318
+ Ukn = 0
319
+ Bln = 1
320
+ Nil = 2
321
+ Sym = 3
322
+ Num = 4
323
+ DRb = 5
324
+ Str = 6
325
+ Arr = 7
326
+ Err = 8
327
+ end
328
+
329
+
330
+ def parse_marshal(str)
331
+ begin
332
+ # i don't know why, but if this runs in the current thread,
333
+ # the __recursive_key__ state in PP, as invoked by pry, gets mangled
334
+ msthread = Thread.new { Thread.current[:structure] = Marshal::Structure::load(str) }
335
+ msthread.join
336
+ parsed_str = msthread[:structure]
337
+ if parsed_str === :true or parsed_str === :false
338
+ return Types::Bln
339
+ elsif parsed_str === :nil
340
+ return Types::Nil
341
+ elsif parsed_str === [:symbol, 0, "write"] # :write
342
+ return Types::Sym
343
+ elsif parsed_str === [:symbol, 0, "readline"] # :readline
344
+ return Types::Sym
345
+ elsif parsed_str.size === 2 and parsed_str[0] === :fixnum and parsed_str[1].is_a?(Integer)
346
+ return Types::Num
347
+ elsif match_structures([ # json DRab::DRabObject
348
+ :user_defined,
349
+ 0,
350
+ [:symbol, 0, "DRab::DRabObject"],
351
+ "*"
352
+ ], parsed_str)
353
+ return [Types::DRb, parsed_str]
354
+ elsif match_structures([ # json DRab::DRabObject
355
+ :instance_variables,
356
+ [
357
+ :user_defined,
358
+ 0,
359
+ [:symbol, 0, "DRab::DRabObject"],
360
+ "*"
361
+ ],
362
+ 1,
363
+ [:symbol, 1, "E"],
364
+ :true
365
+ ], parsed_str)
366
+ return [Types::DRb, parsed_str]
367
+ elsif match_structures([ # string
368
+ :string,
369
+ 0,
370
+ "*"
371
+ ], parsed_str)
372
+ return Types::Str
373
+ elsif match_structures([ # string
374
+ :instance_variables,
375
+ [:string, 0, "*"],
376
+ 1,
377
+ [:symbol, 0, "E"],
378
+ :false
379
+ ], parsed_str)
380
+ return Types::Str
381
+ elsif match_structures([ # json DRab::DRabArray::Token
382
+ :instance_variables,
383
+ [
384
+ :user_defined,
385
+ 0,
386
+ [:symbol, 0, "DRab::DRabArray::Token"],
387
+ "*"
388
+ ],
389
+ 1,
390
+ [:symbol, 1, "E"],
391
+ :true
392
+ ], parsed_str)
393
+ return [Types::Arr, parsed_str]
394
+ elsif match_structures([
395
+ [:object, 0, [:symbol, 0, "*Error"]]
396
+ ], parsed_str[0..2])
397
+ return [Types::Err, parsed_str]
398
+ else
399
+ return [Types::Ukn, parsed_str]
400
+ end
401
+ rescue Exception => e
402
+ STDERR.puts e
403
+ STDERR.puts e.backtrace
404
+ end
405
+ return [Types::Ukn, parsed_str]
406
+ end
407
+
408
+ BLACKLISTED_BASIC_OBJECT_METHODS = Set.new([
409
+ "send",
410
+ "__send__",
411
+ "method",
412
+ "methods",
413
+ "inspect",
414
+ "private_methods",
415
+ "protected_methods",
416
+ "public_method",
417
+ "public_methods",
418
+ "public_send",
419
+ "singleton_class",
420
+ "singleton_method",
421
+ "singleton_methods",
422
+ "taint",
423
+ "trust",
424
+ "untaint",
425
+ "untrust",
426
+ "instance_eval",
427
+ "instance_exec",
428
+ "method_missing",
429
+ "singleton_method_added",
430
+ "singleton_method_removed",
431
+ "instance_variables",
432
+ "instance_variable_get",
433
+ "instance_variable_set",
434
+ "instance_variable_defined?",
435
+ "remove_instance_variable",
436
+ ])
437
+
438
+ BLACKLISTED_MODULE_METHODS = Set.new([
439
+ "used_modules",
440
+ "alias_method",
441
+ "append_features",
442
+ "attr",
443
+ "attr_reader",
444
+ "attr_accessor",
445
+ "attr_writer",
446
+ "define_method",
447
+ "extend_object",
448
+ "extended",
449
+ "include",
450
+ "included",
451
+ "included_modules",
452
+ "instance_method",
453
+ "instance_methods",
454
+ "module_eval",
455
+ "module_exec",
456
+ "prepend",
457
+ "private_class_method",
458
+ "private_constant",
459
+ "private_instance_methods",
460
+ "protected_instance_methods",
461
+ "public_class_method",
462
+ "public_instance_method",
463
+ "public_instance_methods",
464
+ "remove_class_variable",
465
+ "constants",
466
+ "nesting",
467
+ "ancestors",
468
+ "autoload",
469
+ "class_eval",
470
+ "class_exec",
471
+ "class_variable_get",
472
+ "class_variable_set",
473
+ "class_variables",
474
+ "const_get",
475
+ "const_set",
476
+ "const_missing",
477
+ "deprecate_constants",
478
+ "method_added",
479
+ "method_removed",
480
+ "method_undefined",
481
+ "method_function",
482
+ "prepend_features",
483
+ "prepended",
484
+ "private",
485
+ "refine",
486
+ "remove_const",
487
+ "remove_method",
488
+ "undef_method",
489
+ "using",
490
+ ])
491
+
492
+ BLACKLISTED_REPL_METHODS = Set.new([
493
+ "pry",
494
+ "binding",
495
+ "__binding__",
496
+ "remote_pry",
497
+ "remote_pray",
498
+ "pry_remote",
499
+ "pray_remote"
500
+ ])
501
+
502
+ def kill_instance_methods(obj, msg)
503
+ if [ "object_id", "__id__" ].include?(msg)
504
+ return
505
+ end
506
+
507
+ if obj.is_a?(DRabUndumped)
508
+ if obj.class.class_variable_get(:@@drab_whitelist).include?("to_s")
509
+ if msg === "respond_to?"
510
+ return
511
+ end
512
+ end
513
+ if obj.class.class_variable_get(:@@drab_whitelist).include?(msg)
514
+ return
515
+ else
516
+ puts "unwhitelisted method call attempted: #{msg} on" + obj.class.to_s
517
+ raise Exception.new("unwhitelisted method call attempted")
518
+ end
519
+ end
520
+
521
+ if BLACKLISTED_BASIC_OBJECT_METHODS.include?(msg)
522
+ puts "dangerous BasicObject method called: #{msg} on " + obj.class.to_s
523
+ raise Exception.new("dangerous BasicObject method called")
524
+ end
525
+
526
+ if BLACKLISTED_MODULE_METHODS.include?(msg)
527
+ puts "dangerous Module method called: #{msg} on " + obj.class.to_s
528
+ raise Exception.new("dangerous Module method called")
529
+ end
530
+
531
+ if BLACKLISTED_REPL_METHODS.include?(msg)
532
+ puts "dangerous repl method called: #{msg} on " + obj.class.to_s
533
+ raise Exception.new("dangerous repl method called")
534
+ end
535
+
536
+ end
537
+
538
+ # jtd: adding symkey auth here
539
+
540
+ def send_secret(stream)
541
+ if DRab.current_server.secret != nil
542
+ shared_secret = DRab.current_server.secret
543
+ stream.write([shared_secret.size].pack('N') + shared_secret)
544
+ end
545
+ end
546
+
547
+ # taken from rails/ActiveSupport/MessageVerifier/secure_compare
548
+ def secure_compare(a, b)
549
+ return false unless a.bytesize == b.bytesize
550
+
551
+ l = a.unpack "C#{a.bytesize}"
552
+
553
+ res = 0
554
+ b.each_byte { |byte| res |= byte ^ l.shift }
555
+ res == 0
556
+ end
557
+
558
+ def recv_secret(stream)
559
+ if DRab.current_server.secret != nil
560
+ shared_secret = DRab.current_server.secret
561
+ received_secret = nil
562
+
563
+ begin
564
+ sz = stream.read(4)
565
+ rescue
566
+ raise(DRabConnError, $!.message, $!.backtrace)
567
+ end
568
+ raise(DRabConnError, 'connection closed') if sz.nil?
569
+ raise(DRabConnError, 'secret size wrong size') if sz.size != 4
570
+ sz = sz.unpack('N')[0]
571
+ raise(DRabConnError, 'secret size wrong') if sz != shared_secret.size
572
+ begin
573
+ received_secret = stream.read(sz)
574
+ rescue
575
+ raise(DRabConnError, $!.message, $!.backtrace)
576
+ end
577
+ raise(DRabConnError, 'connection closed') if received_secret.nil?
578
+ raise(DRabConnError, 'premature secret (can\'t read)') if received_secret.size < sz
579
+
580
+ if received_secret.length != shared_secret.length
581
+ stream.close
582
+ elsif !secure_compare(shared_secret, received_secret)
583
+ stream.close
584
+ end
585
+ end
586
+ end
587
+ # /jtd
588
+
589
+ def recv_request(stream)
590
+ recv_secret(stream)
591
+ ref = load(stream, true)
592
+ if not ref.is_a? Integer and not ref.is_a? NilClass
593
+ raise Exception.new "non-numeric ref id"
594
+ end
595
+ ro = DRab.to_obj(ref)
596
+
597
+ msg = load(stream, true)
598
+ if not msg.is_a? String
599
+ raise Exception.new "non-string msg name"
600
+ end
601
+
602
+ #note: obviously, blacklisting a few things is not enough, we also need
603
+ # to make sure that arbitrary objects can't just be wrapped by id
604
+ # or deserialized, and maybe do to limit the sorts of
605
+ # objects that can be passed to only primative or "DRab-aware" types.
606
+ kill_instance_methods(ro, msg)
607
+
608
+ argc = load(stream, true)
609
+ if not argc.is_a? Integer
610
+ raise Exception.new "non-numeric argc"
611
+ end
612
+ raise(DRabConnError, "too many arguments") if @argc_limit < argc
613
+ argv = Array.new(argc, nil)
614
+ argc.times do |n|
615
+ argv[n] = load(stream)
616
+ end
617
+ block = nil
618
+
619
+ return ro, msg, argv, block
620
+ end
621
+
622
+ def send_reply(stream, succ, result)
623
+ send_secret(stream)
624
+ stream.write(dump(succ) + dump(result, !succ))
625
+ rescue
626
+ raise(DRabConnError, $!.message, $!.backtrace)
627
+ end
628
+
629
+ def recv_reply(stream)
630
+ recv_secret(stream)
631
+ succ = load(stream)
632
+ result = load(stream)
633
+ if result.is_a?(DRabArray::Token)
634
+ r = result.size.times.collect {|| load(stream) }
635
+ [succ, r]
636
+ else
637
+ [succ, result]
638
+ end
639
+ end
640
+
641
+ private
642
+ def make_proxy(obj, error=false)
643
+ if error
644
+ DRabRemoteError.new(obj)
645
+ else
646
+ DRabObject.new(obj)
647
+ end
648
+ end
649
+ end
650
+
651
+ module DRabProtocol
652
+
653
+ def add_protocol(prot)
654
+ @protocol.push(prot)
655
+ end
656
+ module_function :add_protocol
657
+
658
+ def open(uri, config, first=true)
659
+ @protocol.each do |prot|
660
+ begin
661
+ return prot.open(uri, config)
662
+ rescue DRabBadScheme
663
+ rescue DRabConnError
664
+ raise($!)
665
+ rescue
666
+ raise(DRabConnError, "#{uri} - #{$!.inspect}")
667
+ end
668
+ end
669
+ if first && (config[:auto_load] != false)
670
+ auto_load(uri)
671
+ return open(uri, config, false)
672
+ end
673
+ raise DRabBadURI, 'can\'t parse uri:' + uri
674
+ end
675
+ module_function :open
676
+
677
+ def open_server(uri, config, first=true)
678
+ @protocol.each do |prot|
679
+ begin
680
+ return prot.open_server(uri, config)
681
+ rescue DRabBadScheme
682
+ end
683
+ end
684
+ if first && (config[:auto_load] != false)
685
+ auto_load(uri)
686
+ return open_server(uri, config, false)
687
+ end
688
+ raise DRabBadURI, 'can\'t parse uri:' + uri
689
+ end
690
+ module_function :open_server
691
+
692
+ def uri_option(uri, config, first=true)
693
+ @protocol.each do |prot|
694
+ begin
695
+ uri, opt = prot.uri_option(uri, config)
696
+ # opt = nil if opt == ''
697
+ return uri, opt
698
+ rescue DRabBadScheme
699
+ end
700
+ end
701
+ if first && (config[:auto_load] != false)
702
+ auto_load(uri)
703
+ return uri_option(uri, config, false)
704
+ end
705
+ raise DRabBadURI, 'can\'t parse uri:' + uri
706
+ end
707
+ module_function :uri_option
708
+
709
+ def auto_load(uri) # :nodoc:
710
+ if uri =~ /^drab([a-z0-9]+):/
711
+ require("drab/#{$1}") rescue nil
712
+ end
713
+ end
714
+ module_function :auto_load
715
+ end
716
+
717
+ class DRabTCPSocket
718
+ private
719
+ def self.parse_uri(uri)
720
+ if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
721
+ host = $1
722
+ port = $2.to_i
723
+ option = $4
724
+ [host, port, option]
725
+ else
726
+ raise(DRabBadScheme, uri) unless uri =~ /^druby:/
727
+ raise(DRabBadURI, 'can\'t parse uri:' + uri)
728
+ end
729
+ end
730
+
731
+ public
732
+
733
+ def self.open(uri, config)
734
+ host, port, = parse_uri(uri)
735
+ host.untaint
736
+ port.untaint
737
+ soc = TCPSocket.open(host, port)
738
+ self.new(uri, soc, config)
739
+ end
740
+
741
+ def self.getservername
742
+ host = Socket::gethostname
743
+ begin
744
+ Socket::gethostbyname(host)[0]
745
+ rescue
746
+ 'localhost'
747
+ end
748
+ end
749
+
750
+ def self.open_server_inaddr_any(host, port)
751
+ infos = Socket::getaddrinfo(host, nil,
752
+ Socket::AF_UNSPEC,
753
+ Socket::SOCK_STREAM,
754
+ 0,
755
+ Socket::AI_PASSIVE)
756
+ families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten]
757
+ return TCPServer.open('0.0.0.0', port) if families.has_key?('AF_INET')
758
+ return TCPServer.open('::', port) if families.has_key?('AF_INET6')
759
+ return TCPServer.open(port)
760
+ end
761
+
762
+ def self.open_server(uri, config)
763
+ uri = 'druby://:0' unless uri
764
+ host, port, _ = parse_uri(uri)
765
+ config = {:tcp_original_host => host}.update(config)
766
+ if host.size == 0
767
+ host = getservername
768
+ soc = open_server_inaddr_any(host, port)
769
+ else
770
+ soc = TCPServer.open(host, port)
771
+ end
772
+ port = soc.addr[1] if port == 0
773
+ config[:tcp_port] = port
774
+ uri = "druby://#{host}:#{port}"
775
+ self.new(uri, soc, config)
776
+ end
777
+
778
+ def self.uri_option(uri, config)
779
+ host, port, option = parse_uri(uri)
780
+ return "druby://#{host}:#{port}", option
781
+ end
782
+
783
+ def initialize(uri, soc, config={})
784
+ @uri = uri
785
+ @socket = soc
786
+ @config = config
787
+ @acl = config[:tcp_acl]
788
+ @msg = DRabMessage.new(config)
789
+ set_sockopt(@socket)
790
+ @shutdown_pipe_r, @shutdown_pipe_w = IO.pipe
791
+ end
792
+
793
+ attr_reader :uri
794
+
795
+ def peeraddr
796
+ @socket.peeraddr
797
+ end
798
+
799
+ def stream; @socket; end
800
+
801
+ def send_request(ref, msg_id, arg, b)
802
+ @msg.send_request(stream, ref, msg_id, arg, b)
803
+ end
804
+
805
+ def recv_request
806
+ @msg.recv_request(stream)
807
+ end
808
+
809
+ def send_reply(succ, result)
810
+ @msg.send_reply(stream, succ, result)
811
+ end
812
+
813
+ def recv_reply
814
+ @msg.recv_reply(stream)
815
+ end
816
+
817
+ public
818
+
819
+ def close
820
+ if @socket
821
+ @socket.close
822
+ @socket = nil
823
+ end
824
+ close_shutdown_pipe
825
+ end
826
+
827
+ def close_shutdown_pipe
828
+ if @shutdown_pipe_r && !@shutdown_pipe_r.closed?
829
+ @shutdown_pipe_r.close
830
+ @shutdown_pipe_r = nil
831
+ end
832
+ if @shutdown_pipe_w && !@shutdown_pipe_w.closed?
833
+ @shutdown_pipe_w.close
834
+ @shutdown_pipe_w = nil
835
+ end
836
+ end
837
+ private :close_shutdown_pipe
838
+
839
+ def accept
840
+ while true
841
+ s = accept_or_shutdown
842
+ return nil unless s
843
+ break if (@acl ? @acl.allow_socket?(s) : true)
844
+ s.close
845
+ end
846
+ if @config[:tcp_original_host].to_s.size == 0
847
+ uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}"
848
+ else
849
+ uri = @uri
850
+ end
851
+
852
+ self.class.new(uri, s, @config)
853
+ end
854
+
855
+ def accept_or_shutdown
856
+ readables, = IO.select([@socket, @shutdown_pipe_r])
857
+ if readables.include? @shutdown_pipe_r
858
+ return nil
859
+ end
860
+ @socket.accept
861
+ end
862
+ private :accept_or_shutdown
863
+
864
+ def shutdown
865
+ @shutdown_pipe_w.close if @shutdown_pipe_w && !@shutdown_pipe_w.closed?
866
+ end
867
+
868
+ def alive?
869
+ return false unless @socket
870
+ if IO.select([@socket], nil, nil, 0)
871
+ #if @socket.to_io.wait_readable(0)
872
+ close
873
+ return false
874
+ end
875
+ true
876
+ end
877
+
878
+ def set_sockopt(soc)
879
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
880
+ end
881
+ end
882
+
883
+ module DRabProtocol
884
+ @protocol = [DRabTCPSocket] # default
885
+ end
886
+
887
+ # class DRabURIOption # :nodoc: I don't understand the purpose of this class... # <-- which is why it shouldn't exist
888
+ # def initialize(option)
889
+ # @option = option.to_s
890
+ # end
891
+ # attr_reader :option
892
+ # def to_s; @option; end
893
+ #
894
+ # def ==(other)
895
+ # return false unless DRabURIOption === other
896
+ # @option == other.option
897
+ # end
898
+ #
899
+ # def hash
900
+ # @option.hash
901
+ # end
902
+ #
903
+ # alias eql? ==
904
+ # end
905
+
906
+ class DRabObject
907
+ def self._load(s)
908
+ #uri, ref = Marshal::load(s)
909
+ uri, ref = JSON::load(s)
910
+
911
+ if DRab.here?(uri)
912
+ obj = DRab.to_obj(ref)
913
+ if ((! obj.tainted?) && Thread.current[:drab_untaint])
914
+ Thread.current[:drab_untaint].push(obj)
915
+ end
916
+ return obj
917
+ end
918
+
919
+ self.new_with(uri, ref)
920
+ end
921
+
922
+ def self.new_with(uri, ref)
923
+ it = self.allocate
924
+ it.instance_variable_set(:@uri, uri)
925
+ it.instance_variable_set(:@ref, ref)
926
+ it
927
+ end
928
+
929
+ def self.new_with_uri(uri)
930
+ self.new(nil, uri)
931
+ end
932
+
933
+ def _dump(lv)
934
+ #Marshal::dump([@uri, @ref])
935
+ JSON::dump([@uri, @ref])
936
+ end
937
+
938
+ def initialize(obj, uri=nil)
939
+ @uri = nil
940
+ @ref = nil
941
+ if obj.nil?
942
+ return if uri.nil?
943
+ @uri, option = DRabProtocol.uri_option(uri, DRab.config)
944
+ #@ref = DRabURIOption.new(option) unless option.nil?
945
+ @ref = option.to_s unless option.nil?
946
+ else
947
+ @uri = uri ? uri : (DRab.uri rescue nil)
948
+ @ref = obj ? DRab.to_id(obj) : nil
949
+ end
950
+ end
951
+
952
+ def __draburi
953
+ @uri
954
+ end
955
+
956
+ def __drabref
957
+ @ref
958
+ end
959
+
960
+ undef :to_s
961
+ undef :to_a if respond_to?(:to_a)
962
+
963
+ def respond_to?(msg_id, priv=false)
964
+ case msg_id
965
+ when :_dump
966
+ true
967
+ when :marshal_dump
968
+ false
969
+ else
970
+ method_missing(:respond_to?, msg_id, priv)
971
+ end
972
+ end
973
+
974
+ def method_missing(msg_id, *a, &b)
975
+ if DRab.here?(@uri)
976
+ obj = DRab.to_obj(@ref)
977
+ DRab.current_server.check_insecure_method(obj, msg_id)
978
+ return obj.__send__(msg_id, *a, &b)
979
+ end
980
+
981
+ succ, result = self.class.with_friend(@uri) do
982
+ DRabConn.open(@uri) do |conn|
983
+ suc, res = conn.send_message(self, msg_id, a, b)
984
+ end
985
+ end
986
+
987
+ if succ
988
+ return result
989
+ elsif DRabUnknown === result
990
+ raise result
991
+ else
992
+ bt = self.class.prepare_backtrace(@uri, result)
993
+ result.set_backtrace(bt + caller)
994
+ raise result
995
+ end
996
+ end
997
+
998
+ def self.with_friend(uri)
999
+ friend = DRab.fetch_server(uri)
1000
+ return yield() unless friend
1001
+
1002
+ save = Thread.current['DRab']
1003
+ Thread.current['DRab'] = { 'server' => friend }
1004
+ return yield
1005
+ ensure
1006
+ Thread.current['DRab'] = save if friend
1007
+ end
1008
+
1009
+ def self.prepare_backtrace(uri, result)
1010
+ prefix = "(#{uri}) "
1011
+ bt = []
1012
+ result.backtrace.each do |x|
1013
+ break if /`__send__'$/ =~ x
1014
+ if /^\(druby:\/\// =~ x
1015
+ bt.push(x)
1016
+ else
1017
+ bt.push(prefix + x)
1018
+ end
1019
+ end
1020
+ bt
1021
+ end
1022
+
1023
+ def pretty_print(q)
1024
+ q.pp_object(self)
1025
+ end
1026
+
1027
+ def pretty_print_cycle(q)
1028
+ q.object_address_group(self) {
1029
+ q.breakable
1030
+ q.text '...'
1031
+ }
1032
+ end
1033
+ end
1034
+
1035
+ class DRabConn
1036
+ POOL_SIZE = 16
1037
+ #@mutex = Thread::Mutex.new
1038
+ @mutex = Mutex.new
1039
+ @pool = []
1040
+
1041
+ def self.open(remote_uri)
1042
+ begin
1043
+ conn = nil
1044
+
1045
+ @mutex.synchronize do
1046
+ #FIXME
1047
+ new_pool = []
1048
+ @pool.each do |c|
1049
+ if conn.nil? and c.uri == remote_uri
1050
+ conn = c if c.alive?
1051
+ else
1052
+ new_pool.push c
1053
+ end
1054
+ end
1055
+ @pool = new_pool
1056
+ end
1057
+
1058
+ conn = self.new(remote_uri) unless conn
1059
+ succ, result = yield(conn)
1060
+ return succ, result
1061
+
1062
+ ensure
1063
+ if conn
1064
+ if succ
1065
+ @mutex.synchronize do
1066
+ @pool.unshift(conn)
1067
+ @pool.pop.close while @pool.size > POOL_SIZE
1068
+ end
1069
+ else
1070
+ conn.close
1071
+ end
1072
+ end
1073
+ end
1074
+ end
1075
+
1076
+ def initialize(remote_uri)
1077
+ @uri = remote_uri
1078
+ @protocol = DRabProtocol.open(remote_uri, DRab.config)
1079
+ end
1080
+ attr_reader :uri
1081
+
1082
+ def send_message(ref, msg_id, arg, block)
1083
+ @protocol.send_request(ref, msg_id, arg, block)
1084
+ @protocol.recv_reply
1085
+ end
1086
+
1087
+ def close
1088
+ @protocol.close
1089
+ @protocol = nil
1090
+ end
1091
+
1092
+ def alive?
1093
+ return false unless @protocol
1094
+ @protocol.alive?
1095
+ end
1096
+ end
1097
+
1098
+ class DRabServer
1099
+ @@acl = nil
1100
+ @@idconv = DRabIdConv.new
1101
+ @@secondary_server = nil
1102
+ @@argc_limit = 256
1103
+ @@load_limit = 256 * 102400
1104
+ @@verbose = false
1105
+ @@safe_level = 0
1106
+
1107
+ def self.default_argc_limit(argc)
1108
+ @@argc_limit = argc
1109
+ end
1110
+
1111
+ def self.default_load_limit(sz)
1112
+ @@load_limit = sz
1113
+ end
1114
+
1115
+ def self.default_acl(acl)
1116
+ @@acl = acl
1117
+ end
1118
+
1119
+ def self.default_safe_level(level)
1120
+ @@safe_level = level
1121
+ end
1122
+
1123
+ def self.verbose=(on)
1124
+ @@verbose = on
1125
+ end
1126
+
1127
+ def self.verbose
1128
+ @@verbose
1129
+ end
1130
+
1131
+ def self.make_config(hash={})
1132
+ default_config = {
1133
+ :verbose => @@verbose,
1134
+ :tcp_acl => @@acl,
1135
+ :load_limit => @@load_limit,
1136
+ :argc_limit => @@argc_limit,
1137
+ :safe_level => @@safe_level,
1138
+ :secret => nil
1139
+ }
1140
+ default_config.update(hash)
1141
+ end
1142
+
1143
+ def initialize(uri=nil, front=nil, config_or_acl=nil)
1144
+ if Hash === config_or_acl
1145
+ config = config_or_acl.dup
1146
+ else
1147
+ acl = config_or_acl || @@acl
1148
+ config = {
1149
+ :tcp_acl => acl
1150
+ }
1151
+ end
1152
+
1153
+ @config = self.class.make_config(config)
1154
+
1155
+ @protocol = DRabProtocol.open_server(uri, @config)
1156
+ @uri = @protocol.uri
1157
+ @exported_uri = [@uri]
1158
+ @secret = @config[:secret]
1159
+
1160
+ @front = front
1161
+ @idconv = @@idconv
1162
+ @safe_level = @config[:safe_level]
1163
+
1164
+ @grp = ThreadGroup.new
1165
+ @thread = run
1166
+
1167
+ DRab.regist_server(self)
1168
+ end
1169
+
1170
+ attr_reader :uri
1171
+
1172
+ attr_reader :secret
1173
+
1174
+ attr_reader :thread
1175
+
1176
+ attr_reader :front
1177
+
1178
+ attr_reader :config
1179
+
1180
+ attr_reader :safe_level
1181
+
1182
+ def verbose=(v); @config[:verbose]=v; end
1183
+
1184
+ def verbose; @config[:verbose]; end
1185
+
1186
+ def alive?
1187
+ @thread.alive?
1188
+ end
1189
+
1190
+ def here?(uri)
1191
+ @exported_uri.include?(uri)
1192
+ end
1193
+
1194
+ def stop_service
1195
+ DRab.remove_server(self)
1196
+ if Thread.current['DRab'] && Thread.current['DRab']['server'] == self
1197
+ Thread.current['DRab']['stop_service'] = true
1198
+ else
1199
+ if @protocol.respond_to? :shutdown
1200
+ @protocol.shutdown
1201
+ else
1202
+ [@thread, *@grp.list].each {|thread| thread.kill}
1203
+ end
1204
+ @thread.join
1205
+ end
1206
+ end
1207
+
1208
+ def to_obj(ref)
1209
+ return front if ref.nil?
1210
+ #return front[ref.to_s] if DRabURIOption === ref
1211
+ return front[ref] if ref.is_a? String
1212
+ @idconv.to_obj(ref)
1213
+ end
1214
+
1215
+ def to_id(obj)
1216
+ return nil if obj.__id__ == front.__id__
1217
+ @idconv.to_id(obj)
1218
+ end
1219
+
1220
+
1221
+ private
1222
+
1223
+ def run
1224
+ Thread.start do
1225
+ begin
1226
+ while main_loop
1227
+ end
1228
+ ensure
1229
+ @protocol.close if @protocol
1230
+ end
1231
+ end
1232
+ end
1233
+
1234
+ # List of insecure methods. # it's a list of "insecure method" (singular)
1235
+ #
1236
+ # These methods are not callable via dRuby. # you say that, but :send was allowed. that and a bunch of other dangerous stuff.
1237
+ INSECURE_METHOD = [
1238
+ :__send__
1239
+ ]
1240
+
1241
+ def insecure_method?(msg_id)
1242
+ INSECURE_METHOD.include?(msg_id)
1243
+ end
1244
+
1245
+ def any_to_s(obj)
1246
+ obj.to_s + ":#{obj.class}"
1247
+ rescue
1248
+ sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
1249
+ end
1250
+
1251
+ def check_insecure_method(obj, msg_id)
1252
+ return true if Proc === obj && msg_id == :__drab_yield
1253
+ raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
1254
+ raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
1255
+
1256
+ if obj.private_methods.include?(msg_id)
1257
+ desc = any_to_s(obj)
1258
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
1259
+ elsif obj.protected_methods.include?(msg_id)
1260
+ desc = any_to_s(obj)
1261
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
1262
+ else
1263
+ true
1264
+ end
1265
+ end
1266
+ public :check_insecure_method
1267
+
1268
+ class InvokeMethod
1269
+ def initialize(drab_server, client)
1270
+ @drab_server = drab_server
1271
+ @safe_level = drab_server.safe_level
1272
+ @client = client
1273
+ end
1274
+
1275
+ def perform
1276
+ @result = nil
1277
+ @succ = false
1278
+ setup_message
1279
+
1280
+ if $SAFE < @safe_level
1281
+ info = Thread.current['DRab']
1282
+ @result = Thread.new {
1283
+ Thread.current['DRab'] = info
1284
+ $SAFE = @safe_level
1285
+ perform_without_block
1286
+ }.value
1287
+ else
1288
+ @result = perform_without_block
1289
+ end
1290
+ @succ = true
1291
+ if @result.class == Array
1292
+ @result = DRabArray.new(@result)
1293
+ end
1294
+ return @succ, @result
1295
+ rescue StandardError, ScriptError, Interrupt
1296
+ @result = $!
1297
+ return @succ, @result
1298
+ end
1299
+
1300
+ private
1301
+ def init_with_client
1302
+ obj, msg, argv, block = @client.recv_request
1303
+ @obj = obj
1304
+ @msg_id = msg.intern
1305
+ @argv = argv
1306
+ @block = block
1307
+ end
1308
+
1309
+ def check_insecure_method
1310
+ @drab_server.check_insecure_method(@obj, @msg_id)
1311
+ end
1312
+
1313
+ def setup_message
1314
+ init_with_client
1315
+ check_insecure_method
1316
+ end
1317
+
1318
+ def perform_without_block
1319
+ #STDOUT.puts @obj.class.to_s + "::" + @msg_id.to_s
1320
+ if Proc === @obj && @msg_id == :__drab_yield
1321
+ if @argv.size == 1
1322
+ ary = @argv
1323
+ else
1324
+ ary = [@argv]
1325
+ end
1326
+ ary.collect(&@obj)[0]
1327
+ elsif Proc === @obj and @msg_id != :call
1328
+ STDERR.puts "spooky proc call of " + @msg_id.to_s
1329
+ raise Exception.new "spooky proc call of " + @msg_id.to_s
1330
+ else
1331
+ if @obj.class.method_defined?(@msg_id)
1332
+ @obj.__send__(@msg_id, *@argv)
1333
+ else
1334
+ STDERR.puts "spooky call of " + @msg_id.to_s + " on " + @obj.class.to_s
1335
+ raise Exception.new "spooky call of " + @msg_id.to_s + " on " + @obj.class.to_s
1336
+ end
1337
+ end
1338
+ end
1339
+ end
1340
+
1341
+ require 'drab/invokemethod'
1342
+ class InvokeMethod
1343
+ include InvokeMethod18Mixin
1344
+ end
1345
+
1346
+ def error_print(exception)
1347
+ exception.backtrace.inject(true) do |first, x|
1348
+ if first
1349
+ STDERR.puts "#{x}: #{exception} (#{exception.class})"
1350
+ else
1351
+ STDERR.puts "\tfrom #{x}"
1352
+ end
1353
+ false
1354
+ end
1355
+ end
1356
+
1357
+ def main_loop
1358
+ client0 = @protocol.accept
1359
+ return nil if !client0
1360
+ Thread.start(client0) do |client|
1361
+ @grp.add Thread.current
1362
+ Thread.current['DRab'] = { 'client' => client ,
1363
+ 'server' => self }
1364
+ DRab.mutex.synchronize do
1365
+ client_uri = client.uri
1366
+ @exported_uri << client_uri unless @exported_uri.include?(client_uri)
1367
+ end
1368
+ loop do
1369
+ begin
1370
+ succ = false
1371
+ invoke_method = InvokeMethod.new(self, client)
1372
+ succ, result = invoke_method.perform
1373
+ error_print(result) if !succ && verbose
1374
+ client.send_reply(succ, result)
1375
+ rescue Exception => e
1376
+ error_print(e) if verbose
1377
+ ensure
1378
+ client.close unless succ
1379
+ if Thread.current['DRab']['stop_service']
1380
+ Thread.new { stop_service }
1381
+ end
1382
+ break unless succ
1383
+ end
1384
+ end
1385
+ end
1386
+ end
1387
+ end
1388
+
1389
+ @primary_server = nil
1390
+
1391
+ def start_service(uri=nil, front=nil, config=nil)
1392
+ if config == nil
1393
+ config = {}
1394
+ end
1395
+ if uri.include? "?secret="
1396
+ uri, secret = uri.split("?secret=")
1397
+ config[:secret] = secret
1398
+ end
1399
+
1400
+ @primary_server = DRabServer.new(uri, front, config)
1401
+ end
1402
+ module_function :start_service
1403
+
1404
+ attr_accessor :primary_server
1405
+ module_function :primary_server=, :primary_server
1406
+
1407
+ def current_server
1408
+ drab = Thread.current['DRab']
1409
+ server = (drab && drab['server']) ? drab['server'] : @primary_server
1410
+ raise DRabServerNotFound unless server
1411
+ return server
1412
+ end
1413
+ module_function :current_server
1414
+
1415
+ def stop_service
1416
+ @primary_server.stop_service if @primary_server
1417
+ @primary_server = nil
1418
+ end
1419
+ module_function :stop_service
1420
+
1421
+ def uri
1422
+ drab = Thread.current['DRab']
1423
+ client = (drab && drab['client'])
1424
+ if client
1425
+ uri = client.uri
1426
+ return uri if uri
1427
+ end
1428
+ current_server.uri
1429
+ end
1430
+ module_function :uri
1431
+
1432
+ def here?(uri)
1433
+ current_server.here?(uri) rescue false
1434
+ # (current_server.uri rescue nil) == uri
1435
+ end
1436
+ module_function :here?
1437
+
1438
+ def config
1439
+ current_server.config
1440
+ rescue
1441
+ DRabServer.make_config
1442
+ end
1443
+ module_function :config
1444
+
1445
+ def front
1446
+ current_server.front
1447
+ end
1448
+ module_function :front
1449
+
1450
+ def to_obj(ref)
1451
+ current_server.to_obj(ref)
1452
+ end
1453
+
1454
+ def to_id(obj)
1455
+ current_server.to_id(obj)
1456
+ end
1457
+ module_function :to_id
1458
+ module_function :to_obj
1459
+
1460
+ def thread
1461
+ @primary_server ? @primary_server.thread : nil
1462
+ end
1463
+ module_function :thread
1464
+
1465
+ def install_acl(acl)
1466
+ DRabServer.default_acl(acl)
1467
+ end
1468
+ module_function :install_acl
1469
+
1470
+ #@mutex = Thread::Mutex.new
1471
+ @mutex = Mutex.new
1472
+ def mutex
1473
+ @mutex
1474
+ end
1475
+ module_function :mutex
1476
+
1477
+ @server = {}
1478
+ def regist_server(server)
1479
+ @server[server.uri] = server
1480
+ mutex.synchronize do
1481
+ @primary_server = server unless @primary_server
1482
+ end
1483
+ end
1484
+ module_function :regist_server
1485
+
1486
+ def remove_server(server)
1487
+ @server.delete(server.uri)
1488
+ end
1489
+ module_function :remove_server
1490
+
1491
+ def fetch_server(uri)
1492
+ @server[uri]
1493
+ end
1494
+ module_function :fetch_server
1495
+ end
1496
+
1497
+ DRabObject = DRab::DRabObject
1498
+ DRabUndumped = DRab::DRabUndumped
1499
+ DRabIdConv = DRab::DRabIdConv