pry-remote-em 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|