pry-remote-em 0.6.3 → 0.7.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.
- data/README.md +86 -5
- data/bin/pry-remote-em +2 -2
- data/lib/pry-remote-em.rb +8 -6
- data/lib/pry-remote-em/broker.rb +189 -0
- data/lib/pry-remote-em/client.rb +154 -86
- data/lib/pry-remote-em/client/broker.rb +36 -0
- data/lib/pry-remote-em/client/generic.rb +73 -0
- data/lib/pry-remote-em/client/proxy.rb +32 -0
- data/lib/pry-remote-em/proto.rb +167 -0
- data/lib/pry-remote-em/server.rb +152 -74
- data/lib/pry-remote-em/server/shell_cmd.rb +2 -2
- data/lib/pry-remote-em/version.rb +1 -1
- metadata +8 -20
- data/lib/pry-remote-em/json-proto.rb +0 -31
@@ -0,0 +1,36 @@
|
|
1
|
+
require "pry-remote-em/client/generic"
|
2
|
+
|
3
|
+
module PryRemoteEm
|
4
|
+
module Client
|
5
|
+
module Broker
|
6
|
+
include Client::Generic
|
7
|
+
include EM::Deferrable
|
8
|
+
|
9
|
+
def log
|
10
|
+
return opts[:logger] if opts[:logger]
|
11
|
+
@log ||= Logger.new(STDERR)
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive_banner(name, version, scheme)
|
15
|
+
if super(name, version, scheme)
|
16
|
+
@opts[:tls] ? start_tls : succeed(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ssl_handshake_completed
|
21
|
+
succeed(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def unbind
|
25
|
+
log.info("[pry-remote-em broker-client] broker connection unbound starting a new one")
|
26
|
+
# Give the existing broker a little time to release the port. Even if the
|
27
|
+
# restart here fails the next time a server tries to register, a new client
|
28
|
+
# will be created; when that fails Broker#restart will be called again.
|
29
|
+
EM::Timer.new(rand(0.9)) do
|
30
|
+
PryRemoteEm::Broker.restart
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # module::Broker
|
35
|
+
end # module::Client
|
36
|
+
end # module::PryRemoteEm
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "pry-remote-em/proto"
|
2
|
+
|
3
|
+
module PryRemoteEm
|
4
|
+
module Client
|
5
|
+
module Generic
|
6
|
+
include EM::Deferrable
|
7
|
+
include Proto
|
8
|
+
|
9
|
+
def initialize(opt = {})
|
10
|
+
@opts = opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def opts
|
14
|
+
@opts ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def log
|
18
|
+
@log ||= Class.new do
|
19
|
+
def print(str); $stderr.puts(str) end
|
20
|
+
alias :info :print
|
21
|
+
alias :warn :print
|
22
|
+
alias :error :print
|
23
|
+
alias :debug :print
|
24
|
+
end.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_tls
|
28
|
+
return if @tls_started
|
29
|
+
@tls_started = true
|
30
|
+
log.info("[pry-remote-em] negotiating TLS")
|
31
|
+
super(opts[:tls].is_a?(Hash) ? opts[:tls] : {})
|
32
|
+
end
|
33
|
+
|
34
|
+
def connection_completed
|
35
|
+
if get_peername
|
36
|
+
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
37
|
+
log.info("[pry-remote-em] client connected to pryem://#{ip}:#{port}/")
|
38
|
+
else
|
39
|
+
# TODO use the args used to create this connection
|
40
|
+
log.info("[pry-remote-em] client connected")
|
41
|
+
end
|
42
|
+
@nego_timer = EM::Timer.new(PryRemoteEm::NEGOTIMER) do
|
43
|
+
fail("[pry-remote-em] server didn't finish negotiation within #{PryRemoteEm::NEGOTIMER} seconds; terminating")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def receive_banner(name, version, scheme)
|
48
|
+
log.info("[pry-remote-em] remote is #{name} #{version} #{scheme}")
|
49
|
+
client_ver = Gem::Version.new(PryRemoteEm::VERSION)
|
50
|
+
server_req = Gem::Requirement.new("~>#{version}")
|
51
|
+
server_ver = Gem::Version.new(version)
|
52
|
+
client_req = Gem::Requirement.new("~>#{PryRemoteEm::VERSION}")
|
53
|
+
unless server_req.satisfied_by?(client_ver) || client_req.satisfied_by?(server_ver)
|
54
|
+
fail("[pry-remote-em] incompatible version #{PryRemoteEm::VERSION}")
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
if scheme.nil? || scheme != (reqscheme = opts[:tls] ? 'pryems' : 'pryem')
|
58
|
+
if scheme == 'pryems' && defined?(::OpenSSL)
|
59
|
+
opts[:tls] = true
|
60
|
+
else
|
61
|
+
fail("[pry-remote-em] server doesn't support required scheme #{reqscheme.dump}")
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@negotiated = true
|
66
|
+
@nego_timer.cancel
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end # module::Generic
|
72
|
+
end # module::Client
|
73
|
+
end # module::PryRemoteEm
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "pry-remote-em/client/generic"
|
2
|
+
module PryRemoteEm
|
3
|
+
module Client
|
4
|
+
module Proxy
|
5
|
+
|
6
|
+
def initialize(client, opts = {})
|
7
|
+
@opts = opts
|
8
|
+
@client = client
|
9
|
+
end
|
10
|
+
|
11
|
+
def connection_completed
|
12
|
+
if get_peername
|
13
|
+
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
14
|
+
log.info("[pry-remote-em] proxy connected to pryem://#{ip}:#{port}/")
|
15
|
+
else
|
16
|
+
log.info("[pry-remote-em] proxy connected")
|
17
|
+
end
|
18
|
+
@client.proxy_incoming_to(self)
|
19
|
+
proxy_incoming_to(@client)
|
20
|
+
end
|
21
|
+
|
22
|
+
def log
|
23
|
+
return @opts[:logger] if @opts[:logger]
|
24
|
+
@log ||= Logger.new(STDERR)
|
25
|
+
end
|
26
|
+
|
27
|
+
def unbind
|
28
|
+
@client && @client.close_connection(true)
|
29
|
+
end
|
30
|
+
end # module::Proxy
|
31
|
+
end # module::Client
|
32
|
+
end # module::PryRemoteEm
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'json'
|
2
|
+
require "zlib"
|
3
|
+
|
4
|
+
module PryRemoteEm
|
5
|
+
module Proto
|
6
|
+
PREAMBLE = 'PRYEM'
|
7
|
+
SEPERATOR = ' '
|
8
|
+
PREAMBLE_LEN = PREAMBLE.length
|
9
|
+
SEPERATOR_LEN = SEPERATOR.length
|
10
|
+
|
11
|
+
def send_json(d)
|
12
|
+
send_data(JSON.dump(d.is_a?(String) ? {:d => d} : d))
|
13
|
+
end
|
14
|
+
|
15
|
+
def send_data(data)
|
16
|
+
crc = Zlib::crc32(data).to_s
|
17
|
+
msg = PREAMBLE + (data.length + crc.length + SEPERATOR_LEN).to_s + SEPERATOR + crc + SEPERATOR + data
|
18
|
+
super(msg)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Each frame is a string consisting of 4 parts
|
22
|
+
# 1. preamble (PRYEM)
|
23
|
+
# 2. length in characters of crc, a seperator, and body
|
24
|
+
# 3. CRC
|
25
|
+
# 4. JSON encoded body
|
26
|
+
# It is possible and likely that receive_data will be given more than one frame at a time, or
|
27
|
+
# an incomplete frame.
|
28
|
+
# @example "PRYEM42 3900082256 {\"g\":\"PryRemoteEm 0.7.0 pryem\"}PRYEM22 1794245389 {\"a\":false}"
|
29
|
+
def receive_data(d)
|
30
|
+
return unless d && d.length > 0
|
31
|
+
@buffer ||= "" # inlieu of a post_init
|
32
|
+
@buffer << d
|
33
|
+
while @buffer && !@buffer.empty?
|
34
|
+
return unless @buffer.length >= PREAMBLE_LEN &&
|
35
|
+
(len_ends = @buffer.index(SEPERATOR)) &&
|
36
|
+
(crc_ends = @buffer.index(SEPERATOR, len_ends))
|
37
|
+
if (preamble = @buffer[0...PREAMBLE_LEN]) != PREAMBLE
|
38
|
+
raise "message is not in proper format; expected #{PREAMBLE.inspect} not #{preamble.inspect}"
|
39
|
+
end
|
40
|
+
length = @buffer[PREAMBLE_LEN ... len_ends].to_i
|
41
|
+
return if len_ends + length > @buffer.length
|
42
|
+
crc_start = len_ends + SEPERATOR_LEN
|
43
|
+
crc, data = @buffer[crc_start ... crc_start + length].split(SEPERATOR, 2)
|
44
|
+
crc = crc.to_i
|
45
|
+
@buffer = @buffer[crc_start + length .. -1]
|
46
|
+
if (dcrc = Zlib::crc32(data)) == crc
|
47
|
+
receive_json(JSON.load(data))
|
48
|
+
else
|
49
|
+
warn("data crc #{dcrc} doesn't match crc #{crc.inspect}; discarding #{data.inspect}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
@buffer
|
53
|
+
end
|
54
|
+
|
55
|
+
def receive_json(j)
|
56
|
+
if j['p']
|
57
|
+
receive_prompt(j['p'])
|
58
|
+
elsif j['d']
|
59
|
+
receive_raw(j['d'])
|
60
|
+
elsif j['m']
|
61
|
+
receive_msg(j['m'])
|
62
|
+
elsif j['mb']
|
63
|
+
receive_msg_bcast(j['mb'])
|
64
|
+
elsif j['s']
|
65
|
+
receive_shell_cmd(j['s'])
|
66
|
+
elsif j.include?('sc')
|
67
|
+
receive_shell_result(j['sc'])
|
68
|
+
elsif j['g']
|
69
|
+
receive_banner(*j['g'].split(" ", 3))
|
70
|
+
elsif j['c']
|
71
|
+
receive_completion(j['c'])
|
72
|
+
elsif j.include?('a')
|
73
|
+
receive_auth(*Array(j['a']))
|
74
|
+
elsif j['sd']
|
75
|
+
receive_shell_data(j['sd'])
|
76
|
+
elsif j['ssc']
|
77
|
+
receive_shell_sig(:term)
|
78
|
+
elsif j['hb']
|
79
|
+
receive_heartbeat(j['hb'])
|
80
|
+
elsif j['rs']
|
81
|
+
receive_register_server(*Array(j['rs']))
|
82
|
+
elsif j['urs']
|
83
|
+
receive_unregister_server(j['urs'])
|
84
|
+
elsif j.include?('sl')
|
85
|
+
j['sl'] ? receive_server_list(j['sl']) : receive_server_list
|
86
|
+
elsif j['tls']
|
87
|
+
receive_start_tls
|
88
|
+
elsif j['pc']
|
89
|
+
receive_proxy_connection(j['pc'])
|
90
|
+
else
|
91
|
+
receive_unknown(j)
|
92
|
+
end
|
93
|
+
j
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def receive_prompt(p); end
|
98
|
+
def receive_banner(name, version, scheme); end
|
99
|
+
def receive_auth(a, b = nil); end
|
100
|
+
def receive_msg(m); end
|
101
|
+
def receive_msg_bcast(mb); end
|
102
|
+
def receive_shell_cmd(c); end
|
103
|
+
def receive_shell_result(c); end
|
104
|
+
def receive_completion(c); end
|
105
|
+
def receive_raw(r); end
|
106
|
+
def receive_shell_sig(sym); end
|
107
|
+
def receive_shell_data(d); end
|
108
|
+
def receive_unknown(j); end
|
109
|
+
|
110
|
+
def receive_start_tls; end
|
111
|
+
|
112
|
+
def receive_register_server(url, name); end
|
113
|
+
def receive_unregister_server(url); end
|
114
|
+
def receive_server_list(list = nil); end
|
115
|
+
|
116
|
+
def receive_proxy_connection(url); end
|
117
|
+
|
118
|
+
def send_banner(g)
|
119
|
+
send_json({:g => g})
|
120
|
+
end
|
121
|
+
def send_auth(a)
|
122
|
+
send_json({:a => a})
|
123
|
+
end
|
124
|
+
def send_prompt(p)
|
125
|
+
send_json({:p => p})
|
126
|
+
end
|
127
|
+
def send_msg_bcast(m)
|
128
|
+
send_json({:mb => m})
|
129
|
+
end
|
130
|
+
def send_msg(m)
|
131
|
+
send_json({:m => m})
|
132
|
+
end
|
133
|
+
def send_shell_cmd(c)
|
134
|
+
send_json({:s => c})
|
135
|
+
end
|
136
|
+
def send_shell_result(r)
|
137
|
+
send_json({:sc => r})
|
138
|
+
end
|
139
|
+
def send_completion(word)
|
140
|
+
send_json({:c => word})
|
141
|
+
end
|
142
|
+
def send_raw(l)
|
143
|
+
send_json(l)
|
144
|
+
end
|
145
|
+
|
146
|
+
def send_start_tls
|
147
|
+
send_json({:tls => true})
|
148
|
+
end
|
149
|
+
|
150
|
+
def send_register_server(url, name)
|
151
|
+
send_json({:rs => [url, name]})
|
152
|
+
end
|
153
|
+
def send_unregister_server(url)
|
154
|
+
send_json({:urs => url})
|
155
|
+
end
|
156
|
+
def send_heatbeat(url)
|
157
|
+
send_json({:hb => url})
|
158
|
+
end
|
159
|
+
def send_server_list(list = nil)
|
160
|
+
send_json({:sl => list})
|
161
|
+
end
|
162
|
+
|
163
|
+
def send_proxy_connection(url)
|
164
|
+
send_json({:pc => url})
|
165
|
+
end
|
166
|
+
end # module::Proto
|
167
|
+
end # module::PryRemoteEm
|
data/lib/pry-remote-em/server.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'pry'
|
2
2
|
require 'logger'
|
3
3
|
require 'pry-remote-em'
|
4
|
+
require 'pry-remote-em/broker'
|
4
5
|
require 'pry-remote-em/server/shell_cmd'
|
6
|
+
|
5
7
|
# How it works with Pry
|
6
8
|
#
|
7
9
|
# When PryRemoteEm.run is called it registers with EventMachine for a given ip
|
@@ -26,14 +28,41 @@ require 'pry-remote-em/server/shell_cmd'
|
|
26
28
|
# Pry just interacts with PryRemoteEm as if it were any other blocking Readline
|
27
29
|
# object. The important bit is making sure that it is started in a new Fiber that
|
28
30
|
# can be paused and resumed as needed. PryRemoteEm#readline pauses it and
|
29
|
-
# PryRemoteEm#
|
31
|
+
# PryRemoteEm#receive_raw resumes it.
|
30
32
|
#
|
31
33
|
# Reference:
|
32
34
|
# http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/
|
33
|
-
|
34
35
|
module PryRemoteEm
|
36
|
+
class << self
|
37
|
+
# Local PryRemoteEm EM signatures and server name indexed by url. Each
|
38
|
+
# signature can be used with high level EM methods like EM.stop_server or
|
39
|
+
# EM.get_sockname. If a server has been stopped EM.get_sockname will return
|
40
|
+
# nil for that server's signature.
|
41
|
+
def servers
|
42
|
+
@servers ||= {}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Safely stop one or more PryRemoteEm servers and remove them from the list
|
46
|
+
# of servers.
|
47
|
+
# @param [String] id url or name
|
48
|
+
def stop_server(id)
|
49
|
+
if servers[id]
|
50
|
+
EM.stop_server(servers[id][0]) if EM.get_sockname(servers[id][0])
|
51
|
+
Broker.unregister(id)
|
52
|
+
servers.delete(id)
|
53
|
+
return Array(id)
|
54
|
+
end
|
55
|
+
servers.select{ |i, (sig, name)| name == id }.map do |i, (sig, name)|
|
56
|
+
EM.stop_server(sig) if EM.get_sockname(sig)
|
57
|
+
Broker.unregister(i)
|
58
|
+
servers.delete(i)
|
59
|
+
i
|
60
|
+
end
|
61
|
+
end # stop_server(id)
|
62
|
+
end
|
63
|
+
|
35
64
|
module Server
|
36
|
-
include
|
65
|
+
include Proto
|
37
66
|
|
38
67
|
class << self
|
39
68
|
# Start a pry-remote-em server
|
@@ -46,11 +75,12 @@ module PryRemoteEm
|
|
46
75
|
# @option opts [Proc, Object] :auth require user authentication - see README
|
47
76
|
# @option opts [Boolean] :allow_shell_cmds
|
48
77
|
def run(obj, host = DEFHOST, port = DEFPORT, opts = {:tls => false})
|
49
|
-
tries = :auto
|
78
|
+
tries = [port, opts[:port_fail]].include?(:auto) ? 100 : 1
|
79
|
+
port = DEFPORT if :auto == port
|
50
80
|
# TODO raise a useful exception not RuntimeError
|
51
81
|
raise "root permission required for port below 1024 (#{port})" if port < 1024 && Process.euid != 0
|
52
82
|
begin
|
53
|
-
EM.start_server(host, port, PryRemoteEm::Server, obj, opts) do |pre|
|
83
|
+
server = EM.start_server(host, port, PryRemoteEm::Server, obj, opts) do |pre|
|
54
84
|
Fiber.new {
|
55
85
|
begin
|
56
86
|
yield pre if block_given?
|
@@ -62,16 +92,29 @@ module PryRemoteEm
|
|
62
92
|
end
|
63
93
|
rescue => e
|
64
94
|
# EM 1.0.0.beta4's message tells us the port is in use; 0.12.10 just says, 'no acceptor'
|
65
|
-
if (e.message.include?('port is in use') || e.message.include?('no acceptor')) && tries
|
95
|
+
if (e.message.include?('port is in use') || e.message.include?('no acceptor')) && tries > 1
|
66
96
|
tries -= 1
|
67
97
|
port += 1
|
68
98
|
retry
|
69
99
|
end
|
70
|
-
raise e
|
100
|
+
raise "can't bind to #{host}:#{port} - #{e}"
|
101
|
+
end
|
102
|
+
url = "#{opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
|
103
|
+
begin
|
104
|
+
name = obj.send(:eval, 'self')
|
105
|
+
rescue
|
106
|
+
name = "#{obj}"
|
107
|
+
end
|
108
|
+
name = Pry.view_clip(name)
|
109
|
+
PryRemoteEm.servers[url] = [server, name]
|
110
|
+
(opts[:logger] || ::Logger.new(STDERR)).info("[pry-remote-em] listening for connections on #{url}")
|
111
|
+
Broker.run(opts[:broker_host] || ENV['PRYEMBROKER'] || DEF_BROKERHOST, opts[:broker_port] || ENV['PRYEMBROKERPORT'] || DEF_BROKERPORT, opts)
|
112
|
+
EM::Timer.new(rand(0.9)) { Broker.register(url, name) }
|
113
|
+
rereg = EM::PeriodicTimer.new(15) do
|
114
|
+
EM.get_sockname(server) ? Broker.register(url, name) : nil #rereg.cancel
|
71
115
|
end
|
72
|
-
|
73
|
-
|
74
|
-
end # run(obj, host = DEFHOST, port = DEFPORT)
|
116
|
+
url
|
117
|
+
end
|
75
118
|
|
76
119
|
# The list of pry-remote-em connections for a given object, or the list of all pry-remote-em
|
77
120
|
# connections for this process.
|
@@ -127,14 +170,21 @@ module PryRemoteEm
|
|
127
170
|
end
|
128
171
|
end
|
129
172
|
|
173
|
+
def url
|
174
|
+
port, host = Socket.unpack_sockaddr_in(get_sockname)
|
175
|
+
"#{@opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
|
176
|
+
end
|
177
|
+
|
130
178
|
def post_init
|
131
179
|
@lines = []
|
132
180
|
Pry.config.pager, @old_pager = false, Pry.config.pager
|
133
181
|
@auth_required = @auth
|
134
182
|
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
135
183
|
@log.info("[pry-remote-em] received client connection from #{ip}:#{port}")
|
136
|
-
|
137
|
-
@opts[:tls] ?
|
184
|
+
# TODO include first level prompt in banner
|
185
|
+
send_banner("PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}")
|
186
|
+
@log.info("#{url} PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}")
|
187
|
+
@opts[:tls] ? start_tls : (@auth_required && send_auth(false))
|
138
188
|
PryRemoteEm::Server.register(@obj, self)
|
139
189
|
end
|
140
190
|
|
@@ -145,7 +195,7 @@ module PryRemoteEm
|
|
145
195
|
|
146
196
|
def ssl_handshake_completed
|
147
197
|
@log.info("[pry-remote-em] TLS connection established (#{peer_ip}:#{peer_port})")
|
148
|
-
|
198
|
+
send_auth(false) if @auth_required
|
149
199
|
end
|
150
200
|
|
151
201
|
def peer_ip
|
@@ -162,74 +212,94 @@ module PryRemoteEm
|
|
162
212
|
@peer_port
|
163
213
|
end
|
164
214
|
|
165
|
-
def
|
166
|
-
return
|
215
|
+
def receive_raw(d)
|
216
|
+
return if require_auth
|
217
|
+
return send_last_prompt if d.nil? || d.empty?
|
218
|
+
@lines.push(*d.split("\n"))
|
219
|
+
if @waiting
|
220
|
+
f, @waiting = @waiting, nil
|
221
|
+
f.resume(@lines.shift)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# tab completion request
|
226
|
+
def receive_completion(c)
|
227
|
+
return if require_auth
|
228
|
+
send_completion(@compl_proc.call(c))
|
229
|
+
end
|
167
230
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
@user = j['a'][0]
|
184
|
-
auth_ok(j['a'][0], peer_ip)
|
185
|
-
authenticated!
|
186
|
-
else
|
187
|
-
auth_fail(j['a'][0], peer_ip)
|
188
|
-
if @auth_tries <= 0
|
189
|
-
msg = "max authentication attempts reached"
|
190
|
-
send_data({:a => msg})
|
191
|
-
@log.debug("[pry-remote-em] #{msg} (#{peer_ip}:#{peer_port})")
|
192
|
-
return close_connection_after_writing
|
193
|
-
end
|
194
|
-
@auth_tries -= 1
|
231
|
+
def receive_auth(user, pass)
|
232
|
+
return send_auth(true) if !@auth || !@auth_required
|
233
|
+
return send_auth('auth data must include a user and pass') if user.nil? || pass.nil?
|
234
|
+
auth_attempt(user, peer_ip)
|
235
|
+
unless (@auth_required = !@auth.call(user, pass))
|
236
|
+
@user = user
|
237
|
+
auth_ok(user, peer_ip)
|
238
|
+
authenticated!
|
239
|
+
else
|
240
|
+
auth_fail(user, peer_ip)
|
241
|
+
if @auth_tries <= 0
|
242
|
+
msg = "max authentication attempts reached"
|
243
|
+
send_auth(msg)
|
244
|
+
@log.debug("[pry-remote-em] #{msg} (#{peer_ip}:#{peer_port})")
|
245
|
+
return close_connection_after_writing
|
195
246
|
end
|
196
|
-
|
247
|
+
@auth_tries -= 1
|
248
|
+
end
|
249
|
+
return send_auth(!@auth_required)
|
250
|
+
end
|
197
251
|
|
198
|
-
|
199
|
-
|
200
|
-
|
252
|
+
def receive_msg(m)
|
253
|
+
return if require_auth
|
254
|
+
peers.each { |peer| peer.send_message(m, @user) }
|
255
|
+
send_last_prompt
|
256
|
+
end
|
201
257
|
|
202
|
-
|
203
|
-
|
204
|
-
|
258
|
+
def receive_msg_bcast(mb)
|
259
|
+
return if require_auth
|
260
|
+
peers(:all).each { |peer| peer.send_bmessage(mb, @user) }
|
261
|
+
send_last_prompt
|
262
|
+
end
|
205
263
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
264
|
+
def receive_shell_cmd(cmd)
|
265
|
+
return if require_auth
|
266
|
+
unless @allow_shell_cmds
|
267
|
+
puts "\033[1mshell commands are not allowed by this server\033[0m"
|
268
|
+
@log.error("refused to execute shell command '#{cmd}' for #{@user} (#{peer_ip}:#{peer_port})")
|
269
|
+
send_shell_result(-1)
|
270
|
+
send_last_prompt
|
271
|
+
else
|
272
|
+
@log.warn("executing shell command '#{cmd}' for #{@user} (#{peer_ip}:#{peer_port})")
|
273
|
+
@shell_cmd = EM.popen3(cmd, ShellCmd, self)
|
274
|
+
end
|
275
|
+
end
|
217
276
|
|
218
|
-
|
219
|
-
|
277
|
+
def receive_shell_data(d)
|
278
|
+
return if require_auth
|
279
|
+
@shell_cmd.send_data(d)
|
280
|
+
end
|
220
281
|
|
221
|
-
|
222
|
-
|
282
|
+
def receive_shell_sig(type)
|
283
|
+
return if require_auth
|
284
|
+
type == :term && @shell_cmd.close_connection
|
285
|
+
end
|
223
286
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
287
|
+
def receive_unknown(j)
|
288
|
+
return if require_auth
|
289
|
+
warn "received unexpected data: #{j.inspect}"
|
290
|
+
send_error("received unexpected data: #{j.inspect}")
|
291
|
+
send_last_prompt
|
292
|
+
end
|
228
293
|
|
294
|
+
def require_auth
|
295
|
+
return false if !@auth_required
|
296
|
+
send_auth(false)
|
297
|
+
true
|
298
|
+
end
|
229
299
|
|
230
300
|
def authenticated!
|
231
301
|
while (aa = @after_auth.shift)
|
232
|
-
|
302
|
+
aa.call
|
233
303
|
end
|
234
304
|
end
|
235
305
|
|
@@ -246,19 +316,24 @@ module PryRemoteEm
|
|
246
316
|
end
|
247
317
|
|
248
318
|
def send_last_prompt
|
249
|
-
@auth_required ?
|
319
|
+
@auth_required ? (after_auth { send_prompt(@last_prompt) }) : send_prompt(@last_prompt)
|
320
|
+
end
|
321
|
+
|
322
|
+
def after_auth(&blk)
|
323
|
+
# TODO perhaps replace with #auth_ok
|
324
|
+
@after_auth.push(blk)
|
250
325
|
end
|
251
326
|
|
252
327
|
# Sends a chat message to the client.
|
253
328
|
def send_message(msg, from = nil)
|
254
329
|
msg = "#{msg} (@#{from})" unless from.nil?
|
255
|
-
@auth_required ?
|
330
|
+
@auth_required ? (after_auth {send_msg(msg)}) : send_msg(msg)
|
256
331
|
end
|
257
332
|
#
|
258
333
|
# Sends a chat message to the client.
|
259
334
|
def send_bmessage(msg, from = nil)
|
260
335
|
msg = "#{msg} (@#{from})" unless from.nil?
|
261
|
-
@auth_required ?
|
336
|
+
@auth_required ? (after_auth {send_msg_bcast(msg)}) : send_msg_bcast(msg)
|
262
337
|
end
|
263
338
|
|
264
339
|
# Callbacks for events on the server
|
@@ -299,19 +374,22 @@ module PryRemoteEm
|
|
299
374
|
end
|
300
375
|
end # auth_fail(*args, &blk)
|
301
376
|
|
377
|
+
def send_error(msg)
|
378
|
+
puts "\033[31m#{msg}\033[0m"
|
379
|
+
end
|
302
380
|
|
303
381
|
# Methods that make Server compatible with Pry
|
304
382
|
|
305
383
|
def readline(prompt)
|
306
384
|
@last_prompt = prompt
|
307
|
-
@auth_required ?
|
385
|
+
@auth_required ? (after_auth { send_prompt(prompt) }) : send_prompt(prompt)
|
308
386
|
return @lines.shift unless @lines.empty?
|
309
387
|
@waiting = Fiber.current
|
310
388
|
return Fiber.yield
|
311
389
|
end
|
312
390
|
|
313
391
|
def print(val)
|
314
|
-
@auth_required ?
|
392
|
+
@auth_required ? (after_auth { send_raw(val) }) : send_raw(val)
|
315
393
|
end
|
316
394
|
alias :write :print
|
317
395
|
|