drab 0.1.0

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.
@@ -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