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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +8 -0
- data/Gemfile.lock.bak +38 -0
- data/LICENSE +59 -0
- data/README.md +2 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/drab.gemspec +40 -0
- data/lib/drab.rb +2 -0
- data/lib/drab/acl.rb +232 -0
- data/lib/drab/drab.rb +1499 -0
- data/lib/drab/eq.rb +15 -0
- data/lib/drab/extserv.rb +44 -0
- data/lib/drab/extservm.rb +94 -0
- data/lib/drab/gw.rb +165 -0
- data/lib/drab/invokemethod.rb +35 -0
- data/lib/drab/observer.rb +26 -0
- data/lib/drab/ssl.rb +346 -0
- data/lib/drab/timeridconv.rb +97 -0
- data/lib/drab/unix.rb +118 -0
- data/lib/drab/version.rb +3 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock.bak
ADDED
@@ -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.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -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
|
data/bin/setup
ADDED
data/drab.gemspec
ADDED
@@ -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
|
data/lib/drab.rb
ADDED
data/lib/drab/acl.rb
ADDED
@@ -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
|
data/lib/drab/drab.rb
ADDED
@@ -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
|