ruby-dbus 0.18.1 → 0.20.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 +4 -4
- data/NEWS.md +26 -0
- data/VERSION +1 -1
- data/examples/simple/get_id.rb +4 -2
- data/lib/dbus/auth.rb +305 -218
- data/lib/dbus/bus.rb +57 -15
- data/lib/dbus/introspect.rb +2 -1
- data/lib/dbus/marshall.rb +1 -1
- data/lib/dbus/message_queue.rb +19 -12
- data/lib/dbus/object.rb +31 -3
- data/lib/dbus/object_manager.rb +58 -0
- data/lib/dbus.rb +1 -0
- data/spec/auth_spec.rb +225 -0
- data/spec/node_spec.rb +46 -0
- data/spec/object_manager_spec.rb +33 -0
- data/spec/object_spec.rb +10 -0
- data/spec/proxy_object_interface_spec.rb +35 -0
- data/spec/server_robustness_spec.rb +18 -6
- data/spec/service_newapi.rb +34 -0
- data/spec/service_spec.rb +18 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/thread_safety_spec.rb +55 -12
- data/spec/tools/dbus-limited-session.conf +15 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d52907f5f6be32813466ae280cb99ff605d4195ca84396e97a90a7568fb579ec
|
4
|
+
data.tar.gz: 01bf87805f19878641a424f5c9ba21fcff637dcb6d4bbcdc230dda3713df8c80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8009bcca0c66ddb5d5e9ca76a2fbc06b0408437ac3f5135d1378362f58520cbcb20c6d625d242e7c19ca1f679b4ab7b4e584e5f9d6c376eea90f57372f7f27aa
|
7
|
+
data.tar.gz: 0d8388654ae2ad5aeca7cf0eb6cec2fb3eb463ac43b85eb7a83bfbd32a011d65749b1a1195fcc4ae0f8c6a5591e49c0176a09c442c67a166ce2432bb30d704f7
|
data/NEWS.md
CHANGED
@@ -2,6 +2,32 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## Ruby D-Bus 0.20.0 - 2023-03-21
|
6
|
+
|
7
|
+
Features:
|
8
|
+
* For EXTERNAL authentication, try also without the user id, to work with
|
9
|
+
containers ([#126][]).
|
10
|
+
* Thread safety, as long as the non-main threads only send signals.
|
11
|
+
|
12
|
+
[#126]: https://github.com/mvidner/ruby-dbus/issues/126
|
13
|
+
|
14
|
+
## Ruby D-Bus 0.19.0 - 2023-01-18
|
15
|
+
|
16
|
+
API:
|
17
|
+
* Added a ObjectManager mix-in to implement the service-side
|
18
|
+
[ObjectManager][objmgr] interface.
|
19
|
+
|
20
|
+
Bug fixes:
|
21
|
+
* dbus_attr_accessor and friends validate the signature ([#120][]).
|
22
|
+
* Declare the Introspectable interface in exported objects([#99][]).
|
23
|
+
* Do reply with an error when calling a nonexisting object
|
24
|
+
with an existing path prefix([#121][]).
|
25
|
+
|
26
|
+
[#120]: https://github.com/mvidner/ruby-dbus/issues/120
|
27
|
+
[#99]: https://github.com/mvidner/ruby-dbus/issues/99
|
28
|
+
[#121]: https://github.com/mvidner/ruby-dbus/issues/121
|
29
|
+
[objmgr]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
30
|
+
|
5
31
|
## Ruby D-Bus 0.18.1 - 2022-07-13
|
6
32
|
|
7
33
|
No changes since 0.18.0.beta8.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.20.0
|
data/examples/simple/get_id.rb
CHANGED
@@ -6,7 +6,9 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
6
6
|
|
7
7
|
require "dbus"
|
8
8
|
|
9
|
-
|
9
|
+
busname = ARGV.fetch(0, "system")
|
10
|
+
bus = busname == "session" ? DBus::SessionBus.instance : DBus::SystemBus.instance
|
11
|
+
|
10
12
|
driver_svc = bus["org.freedesktop.DBus"]
|
11
13
|
# p driver_svc
|
12
14
|
driver_obj = driver_svc["/"]
|
@@ -15,4 +17,4 @@ driver_ifc = driver_obj["org.freedesktop.DBus"]
|
|
15
17
|
# p driver_ifc
|
16
18
|
|
17
19
|
bus_id = driver_ifc.GetId
|
18
|
-
puts "The
|
20
|
+
puts "The #{busname} bus id is #{bus_id}"
|
data/lib/dbus/auth.rb
CHANGED
@@ -12,261 +12,348 @@ require "rbconfig"
|
|
12
12
|
|
13
13
|
module DBus
|
14
14
|
# Exception raised when authentication fails somehow.
|
15
|
-
class AuthenticationFailed <
|
15
|
+
class AuthenticationFailed < StandardError
|
16
16
|
end
|
17
17
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
# The Authentication Protocol.
|
19
|
+
# https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
module Authentication
|
23
|
+
# Base class of authentication mechanisms
|
24
|
+
class Mechanism
|
25
|
+
# @!method call(challenge)
|
26
|
+
# @abstract
|
27
|
+
# Replies to server *challenge*, or sends an initial response if the challenge is `nil`.
|
28
|
+
# @param challenge [String,nil]
|
29
|
+
# @return [Array(Symbol,String)] pair [action, response], where
|
30
|
+
# - [:MechContinue, response] caller should send "DATA response" and go to :WaitingForData
|
31
|
+
# - [:MechOk, response] caller should send "DATA response" and go to :WaitingForOk
|
32
|
+
# - [:MechError, message] caller should send "ERROR message" and go to :WaitingForData
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
# Uppercase mechanism name, as sent to the server
|
35
|
+
# @return [String]
|
36
|
+
def name
|
37
|
+
self.class.to_s.upcase.sub(/.*::/, "")
|
38
|
+
end
|
30
39
|
end
|
31
|
-
end
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# Take the user id (eg integer 1000) make a string out of it "1000", take
|
40
|
-
# each character and determin hex value "1" => 0x31, "0" => 0x30. You
|
41
|
-
# obtain for "1000" => 31303030 This is what the server is expecting.
|
42
|
-
# Why? I dunno. How did I come to that conclusion? by looking at rbus
|
43
|
-
# code. I have no idea how he found that out.
|
44
|
-
Process.uid.to_s.split(//).map { |d| d.ord.to_s(16) }.join
|
41
|
+
# Anonymous authentication class.
|
42
|
+
# https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-anonymous
|
43
|
+
class Anonymous < Mechanism
|
44
|
+
def call(_challenge)
|
45
|
+
[:MechOk, "Ruby DBus"]
|
46
|
+
end
|
45
47
|
end
|
46
|
-
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
require "etc"
|
56
|
-
# number of retries we have for auth
|
57
|
-
@retries = 1
|
58
|
-
hex_encode(Etc.getlogin).to_s # server expects it to be binary
|
49
|
+
# Class for 'external' type authentication.
|
50
|
+
# https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-external
|
51
|
+
class External < Mechanism
|
52
|
+
# Performs the authentication.
|
53
|
+
def call(_challenge)
|
54
|
+
[:MechOk, Process.uid.to_s]
|
55
|
+
end
|
59
56
|
end
|
60
57
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
58
|
+
# A variant of EXTERNAL that doesn't say our UID.
|
59
|
+
# Seen busctl do this and it worked across a container boundary.
|
60
|
+
class ExternalWithoutUid < External
|
61
|
+
def name
|
62
|
+
"EXTERNAL"
|
63
|
+
end
|
64
|
+
|
65
|
+
def call(_challenge)
|
66
|
+
[:MechContinue, nil]
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
#
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
70
|
+
# Implements the AUTH DBUS_COOKIE_SHA1 mechanism.
|
71
|
+
# https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha
|
72
|
+
class DBusCookieSHA1 < Mechanism
|
73
|
+
# returns the modules name
|
74
|
+
def name
|
75
|
+
"DBUS_COOKIE_SHA1"
|
76
|
+
end
|
77
|
+
|
78
|
+
# First we are called with nil and we reply with our username.
|
79
|
+
# Then we prove that we can read that user's cookie file.
|
80
|
+
def call(challenge)
|
81
|
+
if challenge.nil?
|
82
|
+
require "etc"
|
83
|
+
# number of retries we have for auth
|
84
|
+
@retries = 1
|
85
|
+
return [:MechContinue, Etc.getlogin]
|
86
|
+
end
|
87
|
+
|
88
|
+
require "digest/sha1"
|
89
|
+
# name of cookie file, id of cookie in file, servers random challenge
|
90
|
+
context, id, s_challenge = challenge.split(" ")
|
91
|
+
# Random client challenge
|
92
|
+
c_challenge = 1.upto(s_challenge.bytesize / 2).map { rand(255).to_s }.join
|
93
|
+
# Search cookie file for id
|
94
|
+
path = File.join(ENV["HOME"], ".dbus-keyrings", context)
|
95
|
+
DBus.logger.debug "path: #{path.inspect}"
|
96
|
+
File.foreach(path) do |line|
|
97
|
+
if line.start_with?(id)
|
98
|
+
# Right line of file, read cookie
|
99
|
+
cookie = line.split(" ")[2].chomp
|
100
|
+
DBus.logger.debug "cookie: #{cookie.inspect}"
|
101
|
+
# Concatenate and encrypt
|
102
|
+
to_encrypt = [s_challenge, c_challenge, cookie].join(":")
|
103
|
+
sha = Digest::SHA1.hexdigest(to_encrypt)
|
104
|
+
# Return response
|
105
|
+
response = [:MechOk, "#{c_challenge} #{sha}"]
|
106
|
+
return response
|
107
|
+
end
|
90
108
|
end
|
109
|
+
return if @retries <= 0
|
110
|
+
|
111
|
+
# a little rescue magic
|
112
|
+
puts "ERROR: Could not auth, will now exit."
|
113
|
+
puts "ERROR: Unable to locate cookie, retry in 1 second."
|
114
|
+
@retries -= 1
|
115
|
+
sleep 1
|
116
|
+
call(challenge)
|
91
117
|
end
|
92
|
-
return if @retries <= 0
|
93
|
-
|
94
|
-
# a little rescue magic
|
95
|
-
puts "ERROR: Could not auth, will now exit."
|
96
|
-
puts "ERROR: Unable to locate cookie, retry in 1 second."
|
97
|
-
@retries -= 1
|
98
|
-
sleep 1
|
99
|
-
data(hexdata)
|
100
118
|
end
|
101
119
|
|
102
|
-
#
|
103
|
-
|
104
|
-
|
120
|
+
# Declare client state transitions, for ease of code reading.
|
121
|
+
# It is just a pair.
|
122
|
+
NextState = Struct.new(:state, :command_words)
|
105
123
|
|
106
|
-
|
107
|
-
|
124
|
+
# Authenticates the connection before messages can be exchanged.
|
125
|
+
class Client
|
126
|
+
# @return [Boolean] have we negotiated Unix file descriptor passing
|
127
|
+
# NOTE: not implemented yet in upper layers
|
128
|
+
attr_reader :unix_fd
|
108
129
|
|
109
|
-
|
110
|
-
|
111
|
-
encoded.scan(/[[:xdigit:]]{2}/).map { |h| h.hex.chr }.join
|
112
|
-
end
|
113
|
-
end
|
130
|
+
# @return [String]
|
131
|
+
attr_reader :address_uuid
|
114
132
|
|
115
|
-
|
133
|
+
# Create a new authentication client.
|
134
|
+
# @param mechs [Array<Mechanism,Class>,nil] custom list of auth Mechanism objects or classes
|
135
|
+
def initialize(socket, mechs = nil)
|
136
|
+
@unix_fd = false
|
137
|
+
@address_uuid = nil
|
116
138
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
139
|
+
@socket = socket
|
140
|
+
@state = nil
|
141
|
+
@auth_list = mechs || [
|
142
|
+
External,
|
143
|
+
DBusCookieSHA1,
|
144
|
+
ExternalWithoutUid,
|
145
|
+
Anonymous
|
146
|
+
]
|
147
|
+
end
|
148
|
+
|
149
|
+
# Start the authentication process.
|
150
|
+
# @return [void]
|
151
|
+
# @raise [AuthenticationFailed]
|
152
|
+
def authenticate
|
153
|
+
DBus.logger.debug "Authenticating"
|
154
|
+
send_nul_byte
|
127
155
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
156
|
+
use_next_mechanism
|
157
|
+
|
158
|
+
@state, command = next_state_via_mechanism.to_a
|
159
|
+
send(command)
|
160
|
+
|
161
|
+
loop do
|
162
|
+
DBus.logger.debug "auth STATE: #{@state}"
|
163
|
+
words = next_msg
|
164
|
+
|
165
|
+
@state, command = next_state(words).to_a
|
166
|
+
break if [:TerminatedOk, :TerminatedError].include? @state
|
167
|
+
|
168
|
+
send(command)
|
169
|
+
end
|
170
|
+
|
171
|
+
raise AuthenticationFailed, command.first if @state == :TerminatedError
|
172
|
+
|
173
|
+
send("BEGIN")
|
134
174
|
end
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
175
|
+
|
176
|
+
##########
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
##########
|
181
|
+
|
182
|
+
# The authentication protocol requires a nul byte
|
183
|
+
# that may carry credentials.
|
184
|
+
# @return [void]
|
185
|
+
def send_nul_byte
|
186
|
+
if RbConfig::CONFIG["target_os"] =~ /freebsd/
|
187
|
+
@socket.sendmsg(0.chr, 0, nil, [:SOCKET, :SCM_CREDS, ""])
|
188
|
+
else
|
189
|
+
@socket.write(0.chr)
|
190
|
+
end
|
140
191
|
end
|
141
|
-
true
|
142
|
-
end
|
143
192
|
|
144
|
-
|
193
|
+
# encode plain to hex
|
194
|
+
# @param plain [String,nil]
|
195
|
+
# @return [String,nil]
|
196
|
+
def hex_encode(plain)
|
197
|
+
return nil if plain.nil?
|
145
198
|
|
146
|
-
|
199
|
+
plain.unpack1("H*")
|
200
|
+
end
|
147
201
|
|
148
|
-
|
202
|
+
# decode hex to plain
|
203
|
+
# @param encoded [String,nil]
|
204
|
+
# @return [String,nil]
|
205
|
+
def hex_decode(encoded)
|
206
|
+
return nil if encoded.nil?
|
149
207
|
|
150
|
-
|
151
|
-
|
152
|
-
def send(meth, *args)
|
153
|
-
o = ([meth] + args).join(" ")
|
154
|
-
@socket.write("#{o}\r\n")
|
155
|
-
end
|
208
|
+
[encoded].pack("H*")
|
209
|
+
end
|
156
210
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
auth_msg = ["AUTH", @authenticator.name, @authenticator.authenticate]
|
163
|
-
DBus.logger.debug "auth_msg: #{auth_msg.inspect}"
|
164
|
-
send(auth_msg)
|
165
|
-
rescue AuthenticationFailed
|
166
|
-
@socket.close
|
167
|
-
raise
|
168
|
-
end
|
211
|
+
# Send a string to the socket; good place for test mocks.
|
212
|
+
def write_line(str)
|
213
|
+
DBus.logger.debug "auth_write: #{str.inspect}"
|
214
|
+
@socket.write(str)
|
215
|
+
end
|
169
216
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
left = 1024 # 1024 byte, no idea if it's ever getting bigger
|
176
|
-
while left.positive?
|
177
|
-
buf = @socket.read(left > 1 ? 1 : left)
|
178
|
-
break if buf.nil?
|
179
|
-
|
180
|
-
left -= buf.bytesize
|
181
|
-
data += buf
|
182
|
-
break if data.include? crlf # crlf means line finished, the TCP socket keeps on listening, so we break
|
217
|
+
# Send *words* to the server as a single CRLF terminated string.
|
218
|
+
# @param words [Array<String>,String]
|
219
|
+
def send(words)
|
220
|
+
joined = Array(words).compact.join(" ")
|
221
|
+
write_line("#{joined}\r\n")
|
183
222
|
end
|
184
|
-
readline = data.chomp.split(" ")
|
185
|
-
DBus.logger.debug "readline: #{readline.inspect}"
|
186
|
-
readline
|
187
|
-
end
|
188
223
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
when "OK"
|
202
|
-
@state = :WaitingForOk
|
203
|
-
when "CONTINUE"
|
204
|
-
@state = :WaitingForData
|
205
|
-
when "REJECTED" # needed by tcp, unix-path/abstract doesn't get here
|
206
|
-
@state = :WaitingForData
|
207
|
-
end
|
224
|
+
# Try authentication using the next mechanism.
|
225
|
+
# @raise [AuthenticationFailed] if there are no more left
|
226
|
+
# @return [void]
|
227
|
+
def use_next_mechanism
|
228
|
+
raise AuthenticationFailed, "Authentication mechanisms exhausted" if @auth_list.empty?
|
229
|
+
|
230
|
+
@mechanism = @auth_list.shift
|
231
|
+
@mechanism = @mechanism.new if @mechanism.is_a? Class
|
232
|
+
rescue AuthenticationFailed
|
233
|
+
# TODO: make this caller's responsibility
|
234
|
+
@socket.close
|
235
|
+
raise
|
208
236
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
@state = :WaitingForData
|
232
|
-
when "ERROR"
|
233
|
-
send("CANCEL")
|
234
|
-
@state = :WaitingForReject
|
235
|
-
when "OK"
|
236
|
-
send("BEGIN")
|
237
|
-
@state = :Authenticated
|
238
|
-
else
|
239
|
-
send("ERROR")
|
240
|
-
@state = :WaitingForData
|
237
|
+
|
238
|
+
# Read data (a buffer) from the bus until CR LF is encountered.
|
239
|
+
# Return the buffer without the CR LF characters.
|
240
|
+
# @return [Array<String>] received words
|
241
|
+
def next_msg
|
242
|
+
read_line.chomp.split(" ")
|
243
|
+
end
|
244
|
+
|
245
|
+
# Read a line from the socket; good place for test mocks.
|
246
|
+
# @return [String] CRLF (\r\n) terminated
|
247
|
+
def read_line
|
248
|
+
# TODO: probably can simply call @socket.readline
|
249
|
+
data = ""
|
250
|
+
crlf = "\r\n"
|
251
|
+
left = 1024 # 1024 byte, no idea if it's ever getting bigger
|
252
|
+
while left.positive?
|
253
|
+
buf = @socket.read(left > 1 ? 1 : left)
|
254
|
+
break if buf.nil?
|
255
|
+
|
256
|
+
left -= buf.bytesize
|
257
|
+
data += buf
|
258
|
+
break if data.include? crlf # crlf means line finished, the TCP socket keeps on listening, so we break
|
241
259
|
end
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
260
|
+
DBus.logger.debug "auth_read: #{data.inspect}"
|
261
|
+
data
|
262
|
+
end
|
263
|
+
|
264
|
+
# # Read data (a buffer) from the bus until CR LF is encountered.
|
265
|
+
# # Return the buffer without the CR LF characters.
|
266
|
+
# def next_msg
|
267
|
+
# @socket.readline.chomp.split(" ")
|
268
|
+
# end
|
269
|
+
|
270
|
+
# @param hex_challenge [String,nil] (nil when the server said "DATA\r\n")
|
271
|
+
# @param use_data [Boolean] say DATA instead of AUTH
|
272
|
+
# @return [NextState]
|
273
|
+
def next_state_via_mechanism(hex_challenge = nil, use_data: false)
|
274
|
+
challenge = hex_decode(hex_challenge)
|
275
|
+
|
276
|
+
action, response = @mechanism.call(challenge)
|
277
|
+
DBus.logger.debug "auth mechanism action: #{action.inspect}"
|
278
|
+
|
279
|
+
command = use_data ? ["DATA"] : ["AUTH", @mechanism.name]
|
280
|
+
|
281
|
+
case action
|
282
|
+
when :MechError
|
283
|
+
NextState.new(:WaitingForData, ["ERROR", response])
|
284
|
+
when :MechContinue
|
285
|
+
NextState.new(:WaitingForData, command + [hex_encode(response)])
|
286
|
+
when :MechOk
|
287
|
+
NextState.new(:WaitingForOk, command + [hex_encode(response)])
|
254
288
|
else
|
255
|
-
|
256
|
-
|
289
|
+
raise AuthenticationFailed, "internal error, unknown action #{action.inspect} " \
|
290
|
+
"from our mechanism #{@mechanism.inspect}"
|
257
291
|
end
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
292
|
+
end
|
293
|
+
|
294
|
+
# Try to reach the next state based on the current state.
|
295
|
+
# @param received_words [Array<String>]
|
296
|
+
# @return [NextState]
|
297
|
+
def next_state(received_words)
|
298
|
+
msg = received_words
|
299
|
+
|
300
|
+
case @state
|
301
|
+
when :WaitingForData
|
302
|
+
case msg[0]
|
303
|
+
when "DATA"
|
304
|
+
next_state_via_mechanism(msg[1], use_data: true)
|
305
|
+
when "REJECTED"
|
306
|
+
use_next_mechanism
|
307
|
+
next_state_via_mechanism
|
308
|
+
when "ERROR"
|
309
|
+
NextState.new(:WaitingForReject, ["CANCEL"])
|
310
|
+
when "OK"
|
311
|
+
@address_uuid = msg[1]
|
312
|
+
# NextState.new(:TerminatedOk, [])
|
313
|
+
NextState.new(:WaitingForAgreeUnixFD, ["NEGOTIATE_UNIX_FD"])
|
314
|
+
else
|
315
|
+
NextState.new(:WaitingForData, ["ERROR"])
|
316
|
+
end
|
317
|
+
when :WaitingForOk
|
318
|
+
case msg[0]
|
319
|
+
when "OK"
|
320
|
+
@address_uuid = msg[1]
|
321
|
+
# NextState.new(:TerminatedOk, [])
|
322
|
+
NextState.new(:WaitingForAgreeUnixFD, ["NEGOTIATE_UNIX_FD"])
|
323
|
+
when "REJECTED"
|
324
|
+
use_next_mechanism
|
325
|
+
next_state_via_mechanism
|
326
|
+
when "DATA", "ERROR"
|
327
|
+
NextState.new(:WaitingForReject, ["CANCEL"])
|
328
|
+
else
|
329
|
+
# we don't understand server's response but still wait for a successful auth completion
|
330
|
+
NextState.new(:WaitingForOk, ["ERROR"])
|
331
|
+
end
|
332
|
+
when :WaitingForReject
|
333
|
+
case msg[0]
|
334
|
+
when "REJECTED"
|
335
|
+
use_next_mechanism
|
336
|
+
next_state_via_mechanism
|
337
|
+
else
|
338
|
+
# TODO: spec says to close socket, clarify
|
339
|
+
NextState.new(:TerminatedError, ["Unknown server reply #{msg[0].inspect} when expecting REJECTED"])
|
340
|
+
end
|
341
|
+
when :WaitingForAgreeUnixFD
|
342
|
+
case msg[0]
|
343
|
+
when "AGREE_UNIX_FD"
|
344
|
+
@unix_fd = true
|
345
|
+
NextState.new(:TerminatedOk, [])
|
346
|
+
when "ERROR"
|
347
|
+
@unix_fd = false
|
348
|
+
NextState.new(:TerminatedOk, [])
|
349
|
+
else
|
350
|
+
# TODO: spec says to close socket, clarify
|
351
|
+
NextState.new(:TerminatedError, ["Unknown server reply #{msg[0].inspect} to NEGOTIATE_UNIX_FD"])
|
352
|
+
end
|
264
353
|
else
|
265
|
-
@
|
266
|
-
return false
|
354
|
+
raise "Internal error: unhandled state #{@state.inspect}"
|
267
355
|
end
|
268
356
|
end
|
269
|
-
true
|
270
357
|
end
|
271
358
|
end
|
272
359
|
end
|