net-ssh 6.0.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.dockerignore +6 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/rubocop.yml +13 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +474 -375
- data/CHANGES.txt +39 -2
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/README.md +14 -7
- data/Rakefile +5 -0
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +29 -13
- data/lib/net/ssh/authentication/certificate.rb +12 -9
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +11 -7
- data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
- data/lib/net/ssh/authentication/key_manager.rb +46 -34
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +56 -16
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -2
- data/lib/net/ssh/authentication/session.rb +18 -17
- data/lib/net/ssh/buffer.rb +50 -30
- data/lib/net/ssh/buffered_io.rb +24 -25
- data/lib/net/ssh/config.rb +33 -20
- data/lib/net/ssh/connection/channel.rb +84 -82
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -24
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +108 -107
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +7 -8
- data/lib/net/ssh/known_hosts.rb +84 -15
- data/lib/net/ssh/loggable.rb +8 -9
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -1
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -5
- data/lib/net/ssh/service/forward.rb +7 -7
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +4 -5
- data/lib/net/ssh/transport/algorithms.rb +34 -17
- data/lib/net/ssh/transport/cipher_factory.rb +28 -28
- data/lib/net/ssh/transport/constants.rb +3 -3
- data/lib/net/ssh/transport/ctr.rb +7 -7
- data/lib/net/ssh/transport/hmac/abstract.rb +4 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac.rb +12 -12
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +12 -5
- data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
- data/lib/net/ssh/transport/kex.rb +8 -6
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +51 -26
- data/lib/net/ssh/transport/packet_stream.rb +6 -5
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +43 -43
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +2 -2
- data/lib/net/ssh.rb +5 -6
- data/net-ssh-public_cert.pem +8 -8
- data/net-ssh.gemspec +2 -2
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +23 -15
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
data/lib/net/ssh/buffer.rb
CHANGED
@@ -5,7 +5,6 @@ require 'net/ssh/authentication/ed25519_loader'
|
|
5
5
|
|
6
6
|
module Net
|
7
7
|
module SSH
|
8
|
-
|
9
8
|
# Net::SSH::Buffer is a flexible class for building and parsing binary
|
10
9
|
# data packets. It provides a stream-like interface for sequentially
|
11
10
|
# reading data items from the buffer, as well as a useful helper method
|
@@ -71,7 +70,7 @@ module Net
|
|
71
70
|
|
72
71
|
# Creates a new buffer, initialized to the given content. The position
|
73
72
|
# is initialized to the beginning of the buffer.
|
74
|
-
def initialize(content=
|
73
|
+
def initialize(content = String.new)
|
75
74
|
@content = content.to_s
|
76
75
|
@position = 0
|
77
76
|
end
|
@@ -118,7 +117,7 @@ module Net
|
|
118
117
|
# Resets the buffer, making it empty. Also, resets the read position to
|
119
118
|
# 0.
|
120
119
|
def clear!
|
121
|
-
@content =
|
120
|
+
@content = String.new
|
122
121
|
@position = 0
|
123
122
|
end
|
124
123
|
|
@@ -129,12 +128,12 @@ module Net
|
|
129
128
|
# would otherwise tend to grow without bound.
|
130
129
|
#
|
131
130
|
# Returns the buffer object itself.
|
132
|
-
def consume!(n=position)
|
131
|
+
def consume!(n = position)
|
133
132
|
if n >= length
|
134
133
|
# optimize for a fairly common case
|
135
134
|
clear!
|
136
135
|
elsif n > 0
|
137
|
-
@content = @content[n..-1] ||
|
136
|
+
@content = @content[n..-1] || String.new
|
138
137
|
@position -= n
|
139
138
|
@position = 0 if @position < 0
|
140
139
|
end
|
@@ -172,7 +171,7 @@ module Net
|
|
172
171
|
# Reads and returns the next +count+ bytes from the buffer, starting from
|
173
172
|
# the read position. If +count+ is +nil+, this will return all remaining
|
174
173
|
# text in the buffer. This method will increment the pointer.
|
175
|
-
def read(count=nil)
|
174
|
+
def read(count = nil)
|
176
175
|
count ||= length
|
177
176
|
count = length - @position if @position + count > length
|
178
177
|
@position += count
|
@@ -181,7 +180,7 @@ module Net
|
|
181
180
|
|
182
181
|
# Reads (as #read) and returns the given number of bytes from the buffer,
|
183
182
|
# and then consumes (as #consume!) all data up to the new read position.
|
184
|
-
def read!(count=nil)
|
183
|
+
def read!(count = nil)
|
185
184
|
data = read(count)
|
186
185
|
consume!
|
187
186
|
data
|
@@ -237,6 +236,7 @@ module Net
|
|
237
236
|
def read_bignum
|
238
237
|
data = read_string
|
239
238
|
return unless data
|
239
|
+
|
240
240
|
OpenSSL::BN.new(data, 2)
|
241
241
|
end
|
242
242
|
|
@@ -283,6 +283,8 @@ module Net
|
|
283
283
|
key.iqmp = iqmp
|
284
284
|
end
|
285
285
|
key
|
286
|
+
when /^ecdsa\-sha2\-(\w*)$/
|
287
|
+
OpenSSL::PKey::EC.read_keyblob($1, self)
|
286
288
|
else
|
287
289
|
raise Exception, "Cannot decode private key of type #{type}"
|
288
290
|
end
|
@@ -295,29 +297,42 @@ module Net
|
|
295
297
|
when /^(.*)-cert-v01@openssh\.com$/
|
296
298
|
key = Net::SSH::Authentication::Certificate.read_certblob(self, $1)
|
297
299
|
when /^ssh-dss$/
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
300
|
+
p = read_bignum
|
301
|
+
q = read_bignum
|
302
|
+
g = read_bignum
|
303
|
+
pub_key = read_bignum
|
304
|
+
|
305
|
+
asn1 = OpenSSL::ASN1::Sequence.new(
|
306
|
+
[
|
307
|
+
OpenSSL::ASN1::Sequence.new(
|
308
|
+
[
|
309
|
+
OpenSSL::ASN1::ObjectId.new('DSA'),
|
310
|
+
OpenSSL::ASN1::Sequence.new(
|
311
|
+
[
|
312
|
+
OpenSSL::ASN1::Integer.new(p),
|
313
|
+
OpenSSL::ASN1::Integer.new(q),
|
314
|
+
OpenSSL::ASN1::Integer.new(g)
|
315
|
+
]
|
316
|
+
)
|
317
|
+
]
|
318
|
+
),
|
319
|
+
OpenSSL::ASN1::BitString.new(OpenSSL::ASN1::Integer.new(pub_key).to_der)
|
320
|
+
]
|
321
|
+
)
|
322
|
+
|
323
|
+
key = OpenSSL::PKey::DSA.new(asn1.to_der)
|
311
324
|
when /^ssh-rsa$/
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
325
|
+
e = read_bignum
|
326
|
+
n = read_bignum
|
327
|
+
|
328
|
+
asn1 = OpenSSL::ASN1::Sequence(
|
329
|
+
[
|
330
|
+
OpenSSL::ASN1::Integer(n),
|
331
|
+
OpenSSL::ASN1::Integer(e)
|
332
|
+
]
|
333
|
+
)
|
334
|
+
|
335
|
+
key = OpenSSL::PKey::RSA.new(asn1.to_der)
|
321
336
|
when /^ssh-ed25519$/
|
322
337
|
Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
|
323
338
|
key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
|
@@ -346,7 +361,12 @@ module Net
|
|
346
361
|
# Optimized version of write where the caller gives up ownership of string
|
347
362
|
# to the method. This way we can mutate the string.
|
348
363
|
def write_moved(string)
|
349
|
-
@content <<
|
364
|
+
@content <<
|
365
|
+
if string.frozen?
|
366
|
+
string.dup.force_encoding('BINARY')
|
367
|
+
else
|
368
|
+
string.force_encoding('BINARY')
|
369
|
+
end
|
350
370
|
self
|
351
371
|
end
|
352
372
|
|
data/lib/net/ssh/buffered_io.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
require 'net/ssh/buffer'
|
2
2
|
require 'net/ssh/loggable'
|
3
3
|
|
4
|
-
module Net
|
4
|
+
module Net
|
5
5
|
module SSH
|
6
|
-
|
7
6
|
# This module is used to extend sockets and other IO objects, to allow
|
8
7
|
# them to be buffered for both read and write. This abstraction makes it
|
9
8
|
# quite easy to write a select-based event loop
|
@@ -48,19 +47,19 @@ module Net
|
|
48
47
|
# end
|
49
48
|
module BufferedIo
|
50
49
|
include Loggable
|
51
|
-
|
50
|
+
|
52
51
|
# Called when the #extend is called on an object, with this module as the
|
53
52
|
# argument. It ensures that the modules instance variables are all properly
|
54
53
|
# initialized.
|
55
|
-
def self.extended(object)
|
54
|
+
def self.extended(object) # :nodoc:
|
56
55
|
# need to use __send__ because #send is overridden in Socket
|
57
56
|
object.__send__(:initialize_buffered_io)
|
58
57
|
end
|
59
|
-
|
58
|
+
|
60
59
|
# Tries to read up to +n+ bytes of data from the remote end, and appends
|
61
60
|
# the data to the input buffer. It returns the number of bytes read, or 0
|
62
61
|
# if no data was available to be read.
|
63
|
-
def fill(n=8192)
|
62
|
+
def fill(n = 8192)
|
64
63
|
input.consume!
|
65
64
|
data = recv(n)
|
66
65
|
debug { "read #{data.length} bytes" }
|
@@ -70,31 +69,31 @@ module Net
|
|
70
69
|
@input_errors << e
|
71
70
|
return 0
|
72
71
|
end
|
73
|
-
|
72
|
+
|
74
73
|
# Read up to +length+ bytes from the input buffer. If +length+ is nil,
|
75
74
|
# all available data is read from the buffer. (See #available.)
|
76
|
-
def read_available(length=nil)
|
75
|
+
def read_available(length = nil)
|
77
76
|
input.read(length || available)
|
78
77
|
end
|
79
|
-
|
78
|
+
|
80
79
|
# Returns the number of bytes available to be read from the input buffer.
|
81
80
|
# (See #read_available.)
|
82
81
|
def available
|
83
82
|
input.available
|
84
83
|
end
|
85
|
-
|
84
|
+
|
86
85
|
# Enqueues data in the output buffer, to be written when #send_pending
|
87
86
|
# is called. Note that the data is _not_ sent immediately by this method!
|
88
87
|
def enqueue(data)
|
89
88
|
output.append(data)
|
90
89
|
end
|
91
|
-
|
90
|
+
|
92
91
|
# Returns +true+ if there is data waiting in the output buffer, and
|
93
92
|
# +false+ otherwise.
|
94
93
|
def pending_write?
|
95
94
|
output.length > 0
|
96
95
|
end
|
97
|
-
|
96
|
+
|
98
97
|
# Sends as much of the pending output as possible. Returns +true+ if any
|
99
98
|
# data was sent, and +false+ otherwise.
|
100
99
|
def send_pending
|
@@ -107,7 +106,7 @@ module Net
|
|
107
106
|
return false
|
108
107
|
end
|
109
108
|
end
|
110
|
-
|
109
|
+
|
111
110
|
# Calls #send_pending repeatedly, if necessary, blocking until the output
|
112
111
|
# buffer is empty.
|
113
112
|
def wait_for_pending_sends
|
@@ -115,31 +114,32 @@ module Net
|
|
115
114
|
while output.length > 0
|
116
115
|
result = IO.select(nil, [self]) or next
|
117
116
|
next unless result[1].any?
|
117
|
+
|
118
118
|
send_pending
|
119
119
|
end
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
public # these methods are primarily for use in tests
|
123
|
-
|
124
|
-
def write_buffer
|
123
|
+
|
124
|
+
def write_buffer # :nodoc:
|
125
125
|
output.to_s
|
126
126
|
end
|
127
|
-
|
128
|
-
def read_buffer
|
127
|
+
|
128
|
+
def read_buffer # :nodoc:
|
129
129
|
input.to_s
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
private
|
133
|
-
|
133
|
+
|
134
134
|
#--
|
135
135
|
# Can't use attr_reader here (after +private+) without incurring the
|
136
136
|
# wrath of "ruby -w". We hates it.
|
137
137
|
#++
|
138
|
-
|
138
|
+
|
139
139
|
def input; @input; end
|
140
140
|
|
141
141
|
def output; @output; end
|
142
|
-
|
142
|
+
|
143
143
|
# Initializes the intput and output buffers for this object. This method
|
144
144
|
# is called automatically when the module is mixed into an object via
|
145
145
|
# Object#extend (see Net::SSH::BufferedIo.extended), but must be called
|
@@ -166,7 +166,7 @@ module Net
|
|
166
166
|
# http://github.com/net-ssh/net-ssh/tree/portfwfix
|
167
167
|
#
|
168
168
|
module ForwardedBufferedIo
|
169
|
-
def fill(n=8192)
|
169
|
+
def fill(n = 8192)
|
170
170
|
begin
|
171
171
|
super(n)
|
172
172
|
rescue Errno::ECONNRESET => e
|
@@ -181,7 +181,7 @@ module Net
|
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
184
|
-
|
184
|
+
|
185
185
|
def send_pending
|
186
186
|
begin
|
187
187
|
super
|
@@ -198,6 +198,5 @@ module Net
|
|
198
198
|
end
|
199
199
|
end
|
200
200
|
end
|
201
|
-
|
202
201
|
end
|
203
202
|
end
|
data/lib/net/ssh/config.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Net
|
2
2
|
module SSH
|
3
|
-
|
4
3
|
# The Net::SSH::Config class is used to parse OpenSSH configuration files,
|
5
4
|
# and translates that syntax into the configuration syntax that Net::SSH
|
6
5
|
# understands. This lets Net::SSH scripts read their configuration (to
|
@@ -34,7 +33,7 @@ module Net
|
|
34
33
|
# * ProxyJump => maps to the :proxy option
|
35
34
|
# * PubKeyAuthentication => maps to the :auth_methods option
|
36
35
|
# * RekeyLimit => :rekey_limit
|
37
|
-
# * StrictHostKeyChecking => :
|
36
|
+
# * StrictHostKeyChecking => :verify_host_key
|
38
37
|
# * User => :user
|
39
38
|
# * UserKnownHostsFile => :user_known_hosts_file
|
40
39
|
# * NumberOfPasswordPrompts => :number_of_password_prompts
|
@@ -66,7 +65,7 @@ module Net
|
|
66
65
|
# given +files+ (defaulting to the list of files returned by
|
67
66
|
# #default_files), translates the resulting hash into the options
|
68
67
|
# recognized by Net::SSH, and returns them.
|
69
|
-
def for(host, files=expandable_default_files)
|
68
|
+
def for(host, files = expandable_default_files)
|
70
69
|
translate(files.inject({}) { |settings, file|
|
71
70
|
load(file, host, settings)
|
72
71
|
})
|
@@ -78,7 +77,7 @@ module Net
|
|
78
77
|
# ones. Returns a hash containing the OpenSSH options. (See
|
79
78
|
# #translate for how to convert the OpenSSH options into Net::SSH
|
80
79
|
# options.)
|
81
|
-
def load(path, host, settings={}, base_dir = nil)
|
80
|
+
def load(path, host, settings = {}, base_dir = nil)
|
82
81
|
file = File.expand_path(path)
|
83
82
|
base_dir ||= File.dirname(file)
|
84
83
|
return settings unless File.readable?(file)
|
@@ -186,17 +185,35 @@ module Net
|
|
186
185
|
# Filters default_files down to the files that are expandable.
|
187
186
|
def expandable_default_files
|
188
187
|
default_files.keep_if do |path|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
false
|
194
|
-
end
|
188
|
+
File.expand_path(path)
|
189
|
+
true
|
190
|
+
rescue ArgumentError
|
191
|
+
false
|
195
192
|
end
|
196
193
|
end
|
197
194
|
|
198
195
|
private
|
199
196
|
|
197
|
+
def translate_verify_host_key(value)
|
198
|
+
case value
|
199
|
+
when false
|
200
|
+
:never
|
201
|
+
when true
|
202
|
+
:always
|
203
|
+
when 'accept-new'
|
204
|
+
:accept_new
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def translate_keepalive(hash, value)
|
209
|
+
if value && value.to_i > 0
|
210
|
+
hash[:keepalive] = true
|
211
|
+
hash[:keepalive_interval] = value.to_i
|
212
|
+
else
|
213
|
+
hash[:keepalive] = false
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
200
217
|
TRANSLATE_CONFIG_KEY_RENAME_MAP = {
|
201
218
|
bindaddress: :bind_address,
|
202
219
|
compression: :compression,
|
@@ -211,13 +228,14 @@ module Net
|
|
211
228
|
identityfile: :keys,
|
212
229
|
fingerprinthash: :fingerprint_hash,
|
213
230
|
port: :port,
|
214
|
-
stricthostkeychecking: :strict_host_key_checking,
|
215
231
|
user: :user,
|
216
232
|
userknownhostsfile: :user_known_hosts_file,
|
217
233
|
checkhostip: :check_host_ip
|
218
234
|
}.freeze
|
219
235
|
def translate_config_key(hash, key, value, settings)
|
220
236
|
case key
|
237
|
+
when :stricthostkeychecking
|
238
|
+
hash[:verify_host_key] = translate_verify_host_key(value)
|
221
239
|
when :ciphers
|
222
240
|
hash[:encryption] = value.split(/,/)
|
223
241
|
when :hostbasedauthentication
|
@@ -235,12 +253,7 @@ module Net
|
|
235
253
|
when :serveralivecountmax
|
236
254
|
hash[:keepalive_maxcount] = value.to_i if value
|
237
255
|
when :serveraliveinterval
|
238
|
-
|
239
|
-
hash[:keepalive] = true
|
240
|
-
hash[:keepalive_interval] = value.to_i
|
241
|
-
else
|
242
|
-
hash[:keepalive] = false
|
243
|
-
end
|
256
|
+
translate_keepalive(hash, value)
|
244
257
|
when :passwordauthentication
|
245
258
|
if value
|
246
259
|
(hash[:auth_methods] << 'password').uniq!
|
@@ -302,9 +315,9 @@ module Net
|
|
302
315
|
# host names.
|
303
316
|
def pattern2regex(pattern)
|
304
317
|
tail = pattern
|
305
|
-
prefix =
|
318
|
+
prefix = String.new
|
306
319
|
while !tail.empty? do
|
307
|
-
head,sep,tail = tail.partition(/[\*\?]/)
|
320
|
+
head, sep, tail = tail.partition(/[\*\?]/)
|
308
321
|
prefix = prefix + Regexp.quote(head)
|
309
322
|
case sep
|
310
323
|
when '*'
|
@@ -358,7 +371,7 @@ module Net
|
|
358
371
|
|
359
372
|
conditions = conditions.each_slice(2)
|
360
373
|
condition_matches = []
|
361
|
-
conditions.each do |(kind,exprs)|
|
374
|
+
conditions.each do |(kind, exprs)|
|
362
375
|
exprs = unquote(exprs)
|
363
376
|
|
364
377
|
case kind.downcase
|