net-ssh 5.0.0.beta1 → 5.0.0.beta2

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.rubocop_todo.yml +98 -258
  5. data/CHANGES.txt +8 -0
  6. data/Gemfile +1 -3
  7. data/Rakefile +37 -39
  8. data/lib/net/ssh.rb +26 -25
  9. data/lib/net/ssh/authentication/agent.rb +228 -225
  10. data/lib/net/ssh/authentication/certificate.rb +166 -164
  11. data/lib/net/ssh/authentication/constants.rb +17 -14
  12. data/lib/net/ssh/authentication/ed25519.rb +107 -104
  13. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
  14. data/lib/net/ssh/authentication/key_manager.rb +5 -3
  15. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  16. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  17. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
  18. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  19. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  20. data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
  21. data/lib/net/ssh/authentication/pageant.rb +468 -465
  22. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
  23. data/lib/net/ssh/authentication/session.rb +127 -123
  24. data/lib/net/ssh/buffer.rb +305 -303
  25. data/lib/net/ssh/buffered_io.rb +163 -162
  26. data/lib/net/ssh/config.rb +230 -227
  27. data/lib/net/ssh/connection/channel.rb +659 -654
  28. data/lib/net/ssh/connection/constants.rb +30 -26
  29. data/lib/net/ssh/connection/event_loop.rb +108 -104
  30. data/lib/net/ssh/connection/keepalive.rb +54 -50
  31. data/lib/net/ssh/connection/session.rb +677 -678
  32. data/lib/net/ssh/connection/term.rb +180 -176
  33. data/lib/net/ssh/errors.rb +101 -99
  34. data/lib/net/ssh/key_factory.rb +108 -108
  35. data/lib/net/ssh/known_hosts.rb +148 -154
  36. data/lib/net/ssh/loggable.rb +56 -54
  37. data/lib/net/ssh/packet.rb +82 -78
  38. data/lib/net/ssh/prompt.rb +55 -53
  39. data/lib/net/ssh/proxy/command.rb +103 -102
  40. data/lib/net/ssh/proxy/errors.rb +12 -8
  41. data/lib/net/ssh/proxy/http.rb +92 -91
  42. data/lib/net/ssh/proxy/https.rb +42 -39
  43. data/lib/net/ssh/proxy/jump.rb +50 -47
  44. data/lib/net/ssh/proxy/socks4.rb +0 -2
  45. data/lib/net/ssh/proxy/socks5.rb +11 -11
  46. data/lib/net/ssh/ruby_compat.rb +1 -0
  47. data/lib/net/ssh/service/forward.rb +364 -362
  48. data/lib/net/ssh/test.rb +85 -83
  49. data/lib/net/ssh/test/channel.rb +146 -142
  50. data/lib/net/ssh/test/extensions.rb +148 -146
  51. data/lib/net/ssh/test/kex.rb +35 -31
  52. data/lib/net/ssh/test/local_packet.rb +48 -44
  53. data/lib/net/ssh/test/packet.rb +87 -84
  54. data/lib/net/ssh/test/remote_packet.rb +35 -31
  55. data/lib/net/ssh/test/script.rb +173 -171
  56. data/lib/net/ssh/test/socket.rb +59 -55
  57. data/lib/net/ssh/transport/algorithms.rb +413 -412
  58. data/lib/net/ssh/transport/cipher_factory.rb +108 -105
  59. data/lib/net/ssh/transport/constants.rb +35 -31
  60. data/lib/net/ssh/transport/ctr.rb +1 -1
  61. data/lib/net/ssh/transport/hmac.rb +1 -1
  62. data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
  63. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  66. data/lib/net/ssh/transport/kex.rb +2 -4
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
  70. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
  73. data/lib/net/ssh/transport/key_expander.rb +29 -25
  74. data/lib/net/ssh/transport/openssl.rb +17 -30
  75. data/lib/net/ssh/transport/packet_stream.rb +193 -192
  76. data/lib/net/ssh/transport/server_version.rb +64 -66
  77. data/lib/net/ssh/transport/session.rb +286 -284
  78. data/lib/net/ssh/transport/state.rb +198 -196
  79. data/lib/net/ssh/verifiers/lenient.rb +29 -25
  80. data/lib/net/ssh/verifiers/null.rb +13 -9
  81. data/lib/net/ssh/verifiers/secure.rb +45 -45
  82. data/lib/net/ssh/verifiers/strict.rb +20 -16
  83. data/lib/net/ssh/version.rb +55 -53
  84. data/net-ssh.gemspec +4 -4
  85. data/support/ssh_tunnel_bug.rb +2 -2
  86. metadata +25 -24
  87. metadata.gz.sig +0 -0
@@ -2,143 +2,145 @@ require 'net/ssh/buffer'
2
2
  require 'net/ssh/loggable'
3
3
  require 'net/ssh/ruby_compat'
4
4
 
5
- module Net; module SSH
6
-
7
- # This module is used to extend sockets and other IO objects, to allow
8
- # them to be buffered for both read and write. This abstraction makes it
9
- # quite easy to write a select-based event loop
10
- # (see Net::SSH::Connection::Session#listen_to).
11
- #
12
- # The general idea is that instead of calling #read directly on an IO that
13
- # has been extended with this module, you call #fill (to add pending input
14
- # to the internal read buffer), and then #read_available (to read from that
15
- # buffer). Likewise, you don't call #write directly, you call #enqueue to
16
- # add data to the write buffer, and then #send_pending or #wait_for_pending_sends
17
- # to actually send the data across the wire.
18
- #
19
- # In this way you can easily use the object as an argument to IO.select,
20
- # calling #fill when it is available for read, or #send_pending when it is
21
- # available for write, and then call #enqueue and #read_available during
22
- # the idle times.
23
- #
24
- # socket = TCPSocket.new(address, port)
25
- # socket.extend(Net::SSH::BufferedIo)
26
- #
27
- # ssh.listen_to(socket)
28
- #
29
- # ssh.loop do
30
- # if socket.available > 0
31
- # puts socket.read_available
32
- # socket.enqueue("response\n")
33
- # end
34
- # end
35
- #
36
- # Note that this module must be used to extend an instance, and should not
37
- # be included in a class. If you do want to use it via an include, then you
38
- # must make sure to invoke the private #initialize_buffered_io method in
39
- # your class' #initialize method:
40
- #
41
- # class Foo < IO
42
- # include Net::SSH::BufferedIo
43
- #
44
- # def initialize
45
- # initialize_buffered_io
46
- # # ...
47
- # end
48
- # end
49
- module BufferedIo
50
- include Loggable
51
-
52
- # Called when the #extend is called on an object, with this module as the
53
- # argument. It ensures that the modules instance variables are all properly
54
- # initialized.
55
- def self.extended(object) #:nodoc:
56
- # need to use __send__ because #send is overridden in Socket
57
- object.__send__(:initialize_buffered_io)
58
- end
59
-
60
- # Tries to read up to +n+ bytes of data from the remote end, and appends
61
- # the data to the input buffer. It returns the number of bytes read, or 0
62
- # if no data was available to be read.
63
- def fill(n=8192)
64
- input.consume!
65
- data = recv(n)
66
- debug { "read #{data.length} bytes" }
67
- input.append(data)
68
- return data.length
69
- rescue EOFError => e
70
- @input_errors << e
71
- return 0
72
- end
73
-
74
- # Read up to +length+ bytes from the input buffer. If +length+ is nil,
75
- # all available data is read from the buffer. (See #available.)
76
- def read_available(length=nil)
77
- input.read(length || available)
78
- end
79
-
80
- # Returns the number of bytes available to be read from the input buffer.
81
- # (See #read_available.)
82
- def available
83
- input.available
84
- end
85
-
86
- # Enqueues data in the output buffer, to be written when #send_pending
87
- # is called. Note that the data is _not_ sent immediately by this method!
88
- def enqueue(data)
89
- output.append(data)
90
- end
91
-
92
- # Returns +true+ if there is data waiting in the output buffer, and
93
- # +false+ otherwise.
94
- def pending_write?
95
- output.length > 0
96
- end
97
-
98
- # Sends as much of the pending output as possible. Returns +true+ if any
99
- # data was sent, and +false+ otherwise.
100
- def send_pending
101
- if output.length > 0
102
- sent = send(output.to_s, 0)
103
- debug { "sent #{sent} bytes" }
104
- output.consume!(sent)
105
- return sent > 0
106
- else
107
- return false
5
+ module Net
6
+ module SSH
7
+
8
+ # This module is used to extend sockets and other IO objects, to allow
9
+ # them to be buffered for both read and write. This abstraction makes it
10
+ # quite easy to write a select-based event loop
11
+ # (see Net::SSH::Connection::Session#listen_to).
12
+ #
13
+ # The general idea is that instead of calling #read directly on an IO that
14
+ # has been extended with this module, you call #fill (to add pending input
15
+ # to the internal read buffer), and then #read_available (to read from that
16
+ # buffer). Likewise, you don't call #write directly, you call #enqueue to
17
+ # add data to the write buffer, and then #send_pending or #wait_for_pending_sends
18
+ # to actually send the data across the wire.
19
+ #
20
+ # In this way you can easily use the object as an argument to IO.select,
21
+ # calling #fill when it is available for read, or #send_pending when it is
22
+ # available for write, and then call #enqueue and #read_available during
23
+ # the idle times.
24
+ #
25
+ # socket = TCPSocket.new(address, port)
26
+ # socket.extend(Net::SSH::BufferedIo)
27
+ #
28
+ # ssh.listen_to(socket)
29
+ #
30
+ # ssh.loop do
31
+ # if socket.available > 0
32
+ # puts socket.read_available
33
+ # socket.enqueue("response\n")
34
+ # end
35
+ # end
36
+ #
37
+ # Note that this module must be used to extend an instance, and should not
38
+ # be included in a class. If you do want to use it via an include, then you
39
+ # must make sure to invoke the private #initialize_buffered_io method in
40
+ # your class' #initialize method:
41
+ #
42
+ # class Foo < IO
43
+ # include Net::SSH::BufferedIo
44
+ #
45
+ # def initialize
46
+ # initialize_buffered_io
47
+ # # ...
48
+ # end
49
+ # end
50
+ module BufferedIo
51
+ include Loggable
52
+
53
+ # Called when the #extend is called on an object, with this module as the
54
+ # argument. It ensures that the modules instance variables are all properly
55
+ # initialized.
56
+ def self.extended(object) #:nodoc:
57
+ # need to use __send__ because #send is overridden in Socket
58
+ object.__send__(:initialize_buffered_io)
108
59
  end
109
- end
110
-
111
- # Calls #send_pending repeatedly, if necessary, blocking until the output
112
- # buffer is empty.
113
- def wait_for_pending_sends
114
- send_pending
115
- while output.length > 0
116
- result = IO.select(nil, [self]) or next
117
- next unless result[1].any?
60
+
61
+ # Tries to read up to +n+ bytes of data from the remote end, and appends
62
+ # the data to the input buffer. It returns the number of bytes read, or 0
63
+ # if no data was available to be read.
64
+ def fill(n=8192)
65
+ input.consume!
66
+ data = recv(n)
67
+ debug { "read #{data.length} bytes" }
68
+ input.append(data)
69
+ return data.length
70
+ rescue EOFError => e
71
+ @input_errors << e
72
+ return 0
73
+ end
74
+
75
+ # Read up to +length+ bytes from the input buffer. If +length+ is nil,
76
+ # all available data is read from the buffer. (See #available.)
77
+ def read_available(length=nil)
78
+ input.read(length || available)
79
+ end
80
+
81
+ # Returns the number of bytes available to be read from the input buffer.
82
+ # (See #read_available.)
83
+ def available
84
+ input.available
85
+ end
86
+
87
+ # Enqueues data in the output buffer, to be written when #send_pending
88
+ # is called. Note that the data is _not_ sent immediately by this method!
89
+ def enqueue(data)
90
+ output.append(data)
91
+ end
92
+
93
+ # Returns +true+ if there is data waiting in the output buffer, and
94
+ # +false+ otherwise.
95
+ def pending_write?
96
+ output.length > 0
97
+ end
98
+
99
+ # Sends as much of the pending output as possible. Returns +true+ if any
100
+ # data was sent, and +false+ otherwise.
101
+ def send_pending
102
+ if output.length > 0
103
+ sent = send(output.to_s, 0)
104
+ debug { "sent #{sent} bytes" }
105
+ output.consume!(sent)
106
+ return sent > 0
107
+ else
108
+ return false
109
+ end
110
+ end
111
+
112
+ # Calls #send_pending repeatedly, if necessary, blocking until the output
113
+ # buffer is empty.
114
+ def wait_for_pending_sends
118
115
  send_pending
116
+ while output.length > 0
117
+ result = IO.select(nil, [self]) or next
118
+ next unless result[1].any?
119
+ send_pending
120
+ end
119
121
  end
120
- end
121
-
122
- public # these methods are primarily for use in tests
123
-
122
+
123
+ public # these methods are primarily for use in tests
124
+
124
125
  def write_buffer #:nodoc:
125
126
  output.to_s
126
127
  end
127
-
128
+
128
129
  def read_buffer #:nodoc:
129
130
  input.to_s
130
131
  end
131
-
132
- private
133
-
132
+
133
+ private
134
+
134
135
  #--
135
136
  # Can't use attr_reader here (after +private+) without incurring the
136
137
  # wrath of "ruby -w". We hates it.
137
138
  #++
138
-
139
+
139
140
  def input; @input; end
140
- def output; @output; end
141
141
 
142
+ def output; @output; end
143
+
142
144
  # Initializes the intput and output buffers for this object. This method
143
145
  # is called automatically when the module is mixed into an object via
144
146
  # Object#extend (see Net::SSH::BufferedIo.extended), but must be called
@@ -150,54 +152,53 @@ module Net; module SSH
150
152
  @output = Net::SSH::Buffer.new
151
153
  @output_errors = []
152
154
  end
153
- end
154
-
155
-
155
+ end
156
156
 
157
- # Fixes for two issues by Miklós Fazekas:
158
- #
159
- # * if client closes a forwarded connection, but the server is
160
- # reading, net-ssh terminates with IOError socket closed.
161
- # * if client force closes (RST) a forwarded connection, but
162
- # server is reading, net-ssh terminates with [an exception]
163
- #
164
- # See:
165
- #
166
- # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
167
- # http://github.com/net-ssh/net-ssh/tree/portfwfix
168
- #
169
- module ForwardedBufferedIo
170
- def fill(n=8192)
171
- begin
172
- super(n)
173
- rescue Errno::ECONNRESET => e
174
- debug { "connection was reset => shallowing exception:#{e}" }
175
- return 0
176
- rescue IOError => e
177
- if e.message =~ /closed/ then
157
+ # Fixes for two issues by Miklós Fazekas:
158
+ #
159
+ # * if client closes a forwarded connection, but the server is
160
+ # reading, net-ssh terminates with IOError socket closed.
161
+ # * if client force closes (RST) a forwarded connection, but
162
+ # server is reading, net-ssh terminates with [an exception]
163
+ #
164
+ # See:
165
+ #
166
+ # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
167
+ # http://github.com/net-ssh/net-ssh/tree/portfwfix
168
+ #
169
+ module ForwardedBufferedIo
170
+ def fill(n=8192)
171
+ begin
172
+ super(n)
173
+ rescue Errno::ECONNRESET => e
178
174
  debug { "connection was reset => shallowing exception:#{e}" }
179
175
  return 0
180
- else
181
- raise
176
+ rescue IOError => e
177
+ if e.message =~ /closed/ then
178
+ debug { "connection was reset => shallowing exception:#{e}" }
179
+ return 0
180
+ else
181
+ raise
182
+ end
182
183
  end
183
184
  end
184
- end
185
-
186
- def send_pending
187
- begin
188
- super
189
- rescue Errno::ECONNRESET => e
190
- debug { "connection was reset => shallowing exception:#{e}" }
191
- return 0
192
- rescue IOError => e
193
- if e.message =~ /closed/ then
185
+
186
+ def send_pending
187
+ begin
188
+ super
189
+ rescue Errno::ECONNRESET => e
194
190
  debug { "connection was reset => shallowing exception:#{e}" }
195
191
  return 0
196
- else
197
- raise
192
+ rescue IOError => e
193
+ if e.message =~ /closed/ then
194
+ debug { "connection was reset => shallowing exception:#{e}" }
195
+ return 0
196
+ else
197
+ raise
198
+ end
198
199
  end
199
200
  end
200
201
  end
201
- end
202
202
 
203
- end; end
203
+ end
204
+ end
@@ -1,189 +1,190 @@
1
- module Net; module SSH
1
+ module Net
2
+ module SSH
2
3
 
3
- # The Net::SSH::Config class is used to parse OpenSSH configuration files,
4
- # and translates that syntax into the configuration syntax that Net::SSH
5
- # understands. This lets Net::SSH scripts read their configuration (to
6
- # some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config,
7
- # and so forth).
8
- #
9
- # Only a subset of OpenSSH configuration options are understood:
10
- #
11
- # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive)
12
- # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive
13
- # * Ciphers => maps to the :encryption option
14
- # * Compression => :compression
15
- # * CompressionLevel => :compression_level
16
- # * ConnectTimeout => maps to the :timeout option
17
- # * ForwardAgent => :forward_agent
18
- # * GlobalKnownHostsFile => :global_known_hosts_file
19
- # * HostBasedAuthentication => maps to the :auth_methods option
20
- # * HostKeyAlgorithms => maps to :host_key option
21
- # * HostKeyAlias => :host_key_alias
22
- # * HostName => :host_name
23
- # * IdentityFile => maps to the :keys option
24
- # * IdentitiesOnly => :keys_only
25
- # * Macs => maps to the :hmac option
26
- # * PasswordAuthentication => maps to the :auth_methods option password
27
- # * Port => :port
28
- # * PreferredAuthentications => maps to the :auth_methods option
29
- # * ProxyCommand => maps to the :proxy option
30
- # * ProxyJump => maps to the :proxy option
31
- # * PubKeyAuthentication => maps to the :auth_methods option
32
- # * RekeyLimit => :rekey_limit
33
- # * User => :user
34
- # * UserKnownHostsFile => :user_known_hosts_file
35
- # * NumberOfPasswordPrompts => :number_of_password_prompts
36
- #
37
- # Note that you will never need to use this class directly--you can control
38
- # whether the OpenSSH configuration files are read by passing the :config
39
- # option to Net::SSH.start. (They are, by default.)
40
- class Config
41
- class << self
42
- @@default_files = %w(~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config)
43
- # The following defaults follow the openssh client ssh_config defaults.
44
- # http://lwn.net/Articles/544640/
45
- # "hostbased" is off and "none" is not supported but we allow it since
46
- # it's used by some clients to query the server for allowed auth methods
47
- @@default_auth_methods = %w(none publickey password keyboard-interactive)
4
+ # The Net::SSH::Config class is used to parse OpenSSH configuration files,
5
+ # and translates that syntax into the configuration syntax that Net::SSH
6
+ # understands. This lets Net::SSH scripts read their configuration (to
7
+ # some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config,
8
+ # and so forth).
9
+ #
10
+ # Only a subset of OpenSSH configuration options are understood:
11
+ #
12
+ # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive)
13
+ # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive
14
+ # * Ciphers => maps to the :encryption option
15
+ # * Compression => :compression
16
+ # * CompressionLevel => :compression_level
17
+ # * ConnectTimeout => maps to the :timeout option
18
+ # * ForwardAgent => :forward_agent
19
+ # * GlobalKnownHostsFile => :global_known_hosts_file
20
+ # * HostBasedAuthentication => maps to the :auth_methods option
21
+ # * HostKeyAlgorithms => maps to :host_key option
22
+ # * HostKeyAlias => :host_key_alias
23
+ # * HostName => :host_name
24
+ # * IdentityFile => maps to the :keys option
25
+ # * IdentitiesOnly => :keys_only
26
+ # * Macs => maps to the :hmac option
27
+ # * PasswordAuthentication => maps to the :auth_methods option password
28
+ # * Port => :port
29
+ # * PreferredAuthentications => maps to the :auth_methods option
30
+ # * ProxyCommand => maps to the :proxy option
31
+ # * ProxyJump => maps to the :proxy option
32
+ # * PubKeyAuthentication => maps to the :auth_methods option
33
+ # * RekeyLimit => :rekey_limit
34
+ # * User => :user
35
+ # * UserKnownHostsFile => :user_known_hosts_file
36
+ # * NumberOfPasswordPrompts => :number_of_password_prompts
37
+ # * FingerprintHash => :fingerprint_hash
38
+ #
39
+ # Note that you will never need to use this class directly--you can control
40
+ # whether the OpenSSH configuration files are read by passing the :config
41
+ # option to Net::SSH.start. (They are, by default.)
42
+ class Config
43
+ class << self
44
+ @@default_files = %w[~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config]
45
+ # The following defaults follow the openssh client ssh_config defaults.
46
+ # http://lwn.net/Articles/544640/
47
+ # "hostbased" is off and "none" is not supported but we allow it since
48
+ # it's used by some clients to query the server for allowed auth methods
49
+ @@default_auth_methods = %w[none publickey password keyboard-interactive]
48
50
 
49
- # Returns an array of locations of OpenSSH configuration files
50
- # to parse by default.
51
- def default_files
52
- @@default_files.clone
53
- end
51
+ # Returns an array of locations of OpenSSH configuration files
52
+ # to parse by default.
53
+ def default_files
54
+ @@default_files.clone
55
+ end
54
56
 
55
- def default_auth_methods
56
- @@default_auth_methods.clone
57
- end
57
+ def default_auth_methods
58
+ @@default_auth_methods.clone
59
+ end
58
60
 
59
- # Loads the configuration data for the given +host+ from all of the
60
- # given +files+ (defaulting to the list of files returned by
61
- # #default_files), translates the resulting hash into the options
62
- # recognized by Net::SSH, and returns them.
63
- def for(host, files=expandable_default_files)
64
- translate(files.inject({}) { |settings, file|
65
- load(file, host, settings)
66
- })
67
- end
61
+ # Loads the configuration data for the given +host+ from all of the
62
+ # given +files+ (defaulting to the list of files returned by
63
+ # #default_files), translates the resulting hash into the options
64
+ # recognized by Net::SSH, and returns them.
65
+ def for(host, files=expandable_default_files)
66
+ translate(files.inject({}) { |settings, file|
67
+ load(file, host, settings)
68
+ })
69
+ end
68
70
 
69
- # Load the OpenSSH configuration settings in the given +file+ for the
70
- # given +host+. If +settings+ is given, the options are merged into
71
- # that hash, with existing values taking precedence over newly parsed
72
- # ones. Returns a hash containing the OpenSSH options. (See
73
- # #translate for how to convert the OpenSSH options into Net::SSH
74
- # options.)
75
- def load(path, host, settings={}, base_dir = nil)
76
- file = File.expand_path(path)
77
- base_dir ||= File.dirname(file)
78
- return settings unless File.readable?(file)
71
+ # Load the OpenSSH configuration settings in the given +file+ for the
72
+ # given +host+. If +settings+ is given, the options are merged into
73
+ # that hash, with existing values taking precedence over newly parsed
74
+ # ones. Returns a hash containing the OpenSSH options. (See
75
+ # #translate for how to convert the OpenSSH options into Net::SSH
76
+ # options.)
77
+ def load(path, host, settings={}, base_dir = nil)
78
+ file = File.expand_path(path)
79
+ base_dir ||= File.dirname(file)
80
+ return settings unless File.readable?(file)
79
81
 
80
- globals = {}
81
- block_matched = false
82
- block_seen = false
83
- IO.foreach(file) do |line|
84
- next if line =~ /^\s*(?:#.*)?$/
82
+ globals = {}
83
+ block_matched = false
84
+ block_seen = false
85
+ IO.foreach(file) do |line|
86
+ next if line =~ /^\s*(?:#.*)?$/
85
87
 
86
- if line =~ /^\s*(\S+)\s*=(.*)$/
87
- key, value = $1, $2
88
- else
89
- key, value = line.strip.split(/\s+/, 2)
90
- end
88
+ if line =~ /^\s*(\S+)\s*=(.*)$/
89
+ key, value = $1, $2
90
+ else
91
+ key, value = line.strip.split(/\s+/, 2)
92
+ end
91
93
 
92
- # silently ignore malformed entries
93
- next if value.nil?
94
+ # silently ignore malformed entries
95
+ next if value.nil?
94
96
 
95
- key.downcase!
96
- value = $1 if value =~ /^"(.*)"$/
97
+ key.downcase!
98
+ value = $1 if value =~ /^"(.*)"$/
97
99
 
98
- value = case value.strip
99
- when /^\d+$/ then value.to_i
100
- when /^no$/i then false
101
- when /^yes$/i then true
102
- else value
103
- end
100
+ value = case value.strip
101
+ when /^\d+$/ then value.to_i
102
+ when /^no$/i then false
103
+ when /^yes$/i then true
104
+ else value
105
+ end
104
106
 
105
- if key == 'host'
106
- # Support "Host host1 host2 hostN".
107
- # See http://github.com/net-ssh/net-ssh/issues#issue/6
108
- negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
107
+ if key == 'host'
108
+ # Support "Host host1 host2 hostN".
109
+ # See http://github.com/net-ssh/net-ssh/issues#issue/6
110
+ negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
109
111
 
110
- # Check for negative patterns first. If the host matches, that overrules any other positive match.
111
- # The host substring code is used to strip out the starting "!" so the regexp will be correct.
112
- negative_matched = negative_hosts.any? { |h| host =~ pattern2regex(h[1..-1]) }
112
+ # Check for negative patterns first. If the host matches, that overrules any other positive match.
113
+ # The host substring code is used to strip out the starting "!" so the regexp will be correct.
114
+ negative_matched = negative_hosts.any? { |h| host =~ pattern2regex(h[1..-1]) }
113
115
 
114
- if negative_matched
115
- block_matched = false
116
- else
117
- block_matched = positive_hosts.any? { |h| host =~ pattern2regex(h) }
118
- end
116
+ if negative_matched
117
+ block_matched = false
118
+ else
119
+ block_matched = positive_hosts.any? { |h| host =~ pattern2regex(h) }
120
+ end
119
121
 
120
- block_seen = true
121
- settings[key] = host
122
- elsif key == 'match'
123
- block_matched = eval_match_condition(value, host, settings)
124
- block_seen = true
125
- elsif !block_seen
126
- case key
127
- when 'identityfile'
128
- (globals[key] ||= []) << value
129
- when 'include'
130
- included_file_paths(base_dir, value).each do |file_path|
131
- globals = load(file_path, host, globals, base_dir)
122
+ block_seen = true
123
+ settings[key] = host
124
+ elsif key == 'match'
125
+ block_matched = eval_match_condition(value, host, settings)
126
+ block_seen = true
127
+ elsif !block_seen
128
+ case key
129
+ when 'identityfile'
130
+ (globals[key] ||= []) << value
131
+ when 'include'
132
+ included_file_paths(base_dir, value).each do |file_path|
133
+ globals = load(file_path, host, globals, base_dir)
134
+ end
135
+ else
136
+ globals[key] = value unless settings.key?(key)
137
+ end
138
+ elsif block_matched
139
+ case key
140
+ when 'identityfile'
141
+ (settings[key] ||= []) << value
142
+ when 'include'
143
+ included_file_paths(base_dir, value).each do |file_path|
144
+ settings = load(file_path, host, settings, base_dir)
145
+ end
146
+ else
147
+ settings[key] = value unless settings.key?(key)
132
148
  end
133
- else
134
- globals[key] = value unless settings.key?(key)
135
149
  end
136
- elsif block_matched
150
+ end
151
+
152
+ globals.merge(settings) do |key, oldval, newval|
137
153
  case key
138
154
  when 'identityfile'
139
- (settings[key] ||= []) << value
140
- when 'include'
141
- included_file_paths(base_dir, value).each do |file_path|
142
- settings = load(file_path, host, settings, base_dir)
143
- end
155
+ oldval + newval
144
156
  else
145
- settings[key] = value unless settings.key?(key)
157
+ newval
146
158
  end
147
159
  end
148
160
  end
149
161
 
150
- globals.merge(settings) do |key, oldval, newval|
151
- case key
152
- when 'identityfile'
153
- oldval + newval
154
- else
155
- newval
162
+ # Given a hash of OpenSSH configuration options, converts them into
163
+ # a hash of Net::SSH options. Unrecognized options are ignored. The
164
+ # +settings+ hash must have Strings for keys, all downcased, and
165
+ # the returned hash will have Symbols for keys.
166
+ def translate(settings)
167
+ auth_methods = default_auth_methods.clone
168
+ (auth_methods << 'challenge-response').uniq!
169
+ ret = settings.each_with_object({ auth_methods: auth_methods }) do |(key, value), hash|
170
+ translate_config_key(hash, key.to_sym, value, settings)
156
171
  end
172
+ merge_challenge_response_with_keyboard_interactive(ret)
157
173
  end
158
- end
159
-
160
- # Given a hash of OpenSSH configuration options, converts them into
161
- # a hash of Net::SSH options. Unrecognized options are ignored. The
162
- # +settings+ hash must have Strings for keys, all downcased, and
163
- # the returned hash will have Symbols for keys.
164
- def translate(settings)
165
- auth_methods = default_auth_methods.clone
166
- (auth_methods << 'challenge-response').uniq!
167
- ret = settings.inject({auth_methods: auth_methods}) do |hash, (key, value)|
168
- translate_config_key(hash, key.to_sym, value, settings)
169
- hash
170
- end
171
- merge_challenge_response_with_keyboard_interactive(ret)
172
- end
173
174
 
174
- # Filters default_files down to the files that are expandable.
175
- def expandable_default_files
176
- default_files.keep_if do |path|
177
- begin
178
- File.expand_path(path)
179
- true
180
- rescue ArgumentError
181
- false
175
+ # Filters default_files down to the files that are expandable.
176
+ def expandable_default_files
177
+ default_files.keep_if do |path|
178
+ begin
179
+ File.expand_path(path)
180
+ true
181
+ rescue ArgumentError
182
+ false
183
+ end
182
184
  end
183
185
  end
184
- end
185
186
 
186
- private
187
+ private
187
188
 
188
189
  def translate_config_key(hash, key, value, settings)
189
190
  rename = {
@@ -196,79 +197,80 @@ module Net; module SSH
196
197
  globalknownhostsfile: :global_known_hosts_file,
197
198
  hostkeyalias: :host_key_alias,
198
199
  identityfile: :keys,
200
+ fingerprinthash: :fingerprint_hash,
199
201
  port: :port,
200
202
  user: :user,
201
203
  userknownhostsfile: :user_known_hosts_file
202
204
  }
203
205
  case key
204
- when :ciphers
205
- hash[:encryption] = value.split(/,/)
206
- when :hostbasedauthentication
207
- if value
208
- (hash[:auth_methods] << "hostbased").uniq!
209
- else
210
- hash[:auth_methods].delete("hostbased")
211
- end
212
- when :hostkeyalgorithms
213
- hash[:host_key] = value.split(/,/)
214
- when :hostname
215
- hash[:host_name] = value.gsub(/%h/, settings['host'])
216
- when :macs
217
- hash[:hmac] = value.split(/,/)
218
- when :serveralivecountmax
219
- hash[:keepalive_maxcount] = value.to_i if value
220
- when :serveraliveinterval
221
- if value && value.to_i > 0
222
- hash[:keepalive] = true
223
- hash[:keepalive_interval] = value.to_i
224
- else
225
- hash[:keepalive] = false
226
- end
227
- when :passwordauthentication
228
- if value
229
- (hash[:auth_methods] << 'password').uniq!
230
- else
231
- hash[:auth_methods].delete('password')
232
- end
233
- when :challengeresponseauthentication
234
- if value
235
- (hash[:auth_methods] << 'challenge-response').uniq!
236
- else
237
- hash[:auth_methods].delete('challenge-response')
238
- end
239
- when :kbdinteractiveauthentication
240
- if value
241
- (hash[:auth_methods] << 'keyboard-interactive').uniq!
242
- else
243
- hash[:auth_methods].delete('keyboard-interactive')
244
- end
245
- when :preferredauthentications
246
- hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
247
- when :proxycommand
248
- if value and !(value =~ /^none$/)
249
- require 'net/ssh/proxy/command'
250
- hash[:proxy] = Net::SSH::Proxy::Command.new(value)
251
- end
252
- when :proxyjump
253
- if value
254
- require 'net/ssh/proxy/jump'
255
- hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
256
- end
257
- when :pubkeyauthentication
258
- if value
259
- (hash[:auth_methods] << 'publickey').uniq!
260
- else
261
- hash[:auth_methods].delete('publickey')
262
- end
263
- when :rekeylimit
264
- hash[:rekey_limit] = interpret_size(value)
265
- when :sendenv
266
- multi_send_env = value.to_s.split(/\s+/)
267
- hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
268
- when :numberofpasswordprompts
269
- hash[:number_of_password_prompts] = value.to_i
270
- when *rename.keys
271
- hash[rename[key]] = value
206
+ when :ciphers
207
+ hash[:encryption] = value.split(/,/)
208
+ when :hostbasedauthentication
209
+ if value
210
+ (hash[:auth_methods] << "hostbased").uniq!
211
+ else
212
+ hash[:auth_methods].delete("hostbased")
213
+ end
214
+ when :hostkeyalgorithms
215
+ hash[:host_key] = value.split(/,/)
216
+ when :hostname
217
+ hash[:host_name] = value.gsub(/%h/, settings['host'])
218
+ when :macs
219
+ hash[:hmac] = value.split(/,/)
220
+ when :serveralivecountmax
221
+ hash[:keepalive_maxcount] = value.to_i if value
222
+ when :serveraliveinterval
223
+ if value && value.to_i > 0
224
+ hash[:keepalive] = true
225
+ hash[:keepalive_interval] = value.to_i
226
+ else
227
+ hash[:keepalive] = false
228
+ end
229
+ when :passwordauthentication
230
+ if value
231
+ (hash[:auth_methods] << 'password').uniq!
232
+ else
233
+ hash[:auth_methods].delete('password')
234
+ end
235
+ when :challengeresponseauthentication
236
+ if value
237
+ (hash[:auth_methods] << 'challenge-response').uniq!
238
+ else
239
+ hash[:auth_methods].delete('challenge-response')
240
+ end
241
+ when :kbdinteractiveauthentication
242
+ if value
243
+ (hash[:auth_methods] << 'keyboard-interactive').uniq!
244
+ else
245
+ hash[:auth_methods].delete('keyboard-interactive')
246
+ end
247
+ when :preferredauthentications
248
+ hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
249
+ when :proxycommand
250
+ if value and value !~ /^none$/
251
+ require 'net/ssh/proxy/command'
252
+ hash[:proxy] = Net::SSH::Proxy::Command.new(value)
253
+ end
254
+ when :proxyjump
255
+ if value
256
+ require 'net/ssh/proxy/jump'
257
+ hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
258
+ end
259
+ when :pubkeyauthentication
260
+ if value
261
+ (hash[:auth_methods] << 'publickey').uniq!
262
+ else
263
+ hash[:auth_methods].delete('publickey')
264
+ end
265
+ when :rekeylimit
266
+ hash[:rekey_limit] = interpret_size(value)
267
+ when :sendenv
268
+ multi_send_env = value.to_s.split(/\s+/)
269
+ hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
270
+ when :numberofpasswordprompts
271
+ hash[:number_of_password_prompts] = value.to_i
272
+ when *rename.keys
273
+ hash[rename[key]] = value
272
274
  end
273
275
  end
274
276
 
@@ -340,7 +342,8 @@ module Net; module SSH
340
342
  # return false for unsupported conditions
341
343
  condition_met.nil? ? false : (negated ^ condition_met)
342
344
  end
345
+ end
343
346
  end
344
- end
345
347
 
346
- end; end
348
+ end
349
+ end