net-ssh 3.2.0.rc2 → 7.1.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.
Files changed (204) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +2 -2
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +16 -0
  8. data/.gitignore +13 -0
  9. data/.rubocop.yml +22 -0
  10. data/.rubocop_todo.yml +1081 -0
  11. data/CHANGES.txt +228 -7
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +13 -0
  15. data/Gemfile.noed25519 +12 -0
  16. data/ISSUE_TEMPLATE.md +30 -0
  17. data/Manifest +4 -5
  18. data/README.md +297 -0
  19. data/Rakefile +125 -74
  20. data/SECURITY.md +4 -0
  21. data/appveyor.yml +58 -0
  22. data/docker-compose.yml +23 -0
  23. data/lib/net/ssh/authentication/agent.rb +279 -18
  24. data/lib/net/ssh/authentication/certificate.rb +183 -0
  25. data/lib/net/ssh/authentication/constants.rb +17 -15
  26. data/lib/net/ssh/authentication/ed25519.rb +186 -0
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  28. data/lib/net/ssh/authentication/key_manager.rb +86 -39
  29. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
  32. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  33. data/lib/net/ssh/authentication/methods/password.rb +27 -17
  34. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  35. data/lib/net/ssh/authentication/pageant.rb +471 -367
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  37. data/lib/net/ssh/authentication/session.rb +131 -121
  38. data/lib/net/ssh/buffer.rb +399 -300
  39. data/lib/net/ssh/buffered_io.rb +154 -150
  40. data/lib/net/ssh/config.rb +308 -185
  41. data/lib/net/ssh/connection/channel.rb +635 -613
  42. data/lib/net/ssh/connection/constants.rb +29 -29
  43. data/lib/net/ssh/connection/event_loop.rb +123 -0
  44. data/lib/net/ssh/connection/keepalive.rb +55 -51
  45. data/lib/net/ssh/connection/session.rb +620 -551
  46. data/lib/net/ssh/connection/term.rb +125 -123
  47. data/lib/net/ssh/errors.rb +101 -99
  48. data/lib/net/ssh/key_factory.rb +197 -105
  49. data/lib/net/ssh/known_hosts.rb +214 -127
  50. data/lib/net/ssh/loggable.rb +50 -49
  51. data/lib/net/ssh/packet.rb +83 -79
  52. data/lib/net/ssh/prompt.rb +50 -81
  53. data/lib/net/ssh/proxy/command.rb +105 -90
  54. data/lib/net/ssh/proxy/errors.rb +12 -10
  55. data/lib/net/ssh/proxy/http.rb +82 -79
  56. data/lib/net/ssh/proxy/https.rb +50 -0
  57. data/lib/net/ssh/proxy/jump.rb +54 -0
  58. data/lib/net/ssh/proxy/socks4.rb +2 -6
  59. data/lib/net/ssh/proxy/socks5.rb +14 -17
  60. data/lib/net/ssh/service/forward.rb +370 -317
  61. data/lib/net/ssh/test/channel.rb +145 -136
  62. data/lib/net/ssh/test/extensions.rb +131 -110
  63. data/lib/net/ssh/test/kex.rb +34 -32
  64. data/lib/net/ssh/test/local_packet.rb +46 -44
  65. data/lib/net/ssh/test/packet.rb +89 -70
  66. data/lib/net/ssh/test/remote_packet.rb +32 -30
  67. data/lib/net/ssh/test/script.rb +156 -142
  68. data/lib/net/ssh/test/socket.rb +49 -48
  69. data/lib/net/ssh/test.rb +82 -77
  70. data/lib/net/ssh/transport/algorithms.rb +441 -360
  71. data/lib/net/ssh/transport/cipher_factory.rb +96 -98
  72. data/lib/net/ssh/transport/constants.rb +32 -24
  73. data/lib/net/ssh/transport/ctr.rb +42 -22
  74. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  75. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  80. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  82. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  83. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  84. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  85. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  86. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  87. data/lib/net/ssh/transport/hmac.rb +14 -12
  88. data/lib/net/ssh/transport/identity_cipher.rb +54 -52
  89. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  90. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  91. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  92. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  96. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  97. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  99. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  100. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  101. data/lib/net/ssh/transport/kex.rb +15 -12
  102. data/lib/net/ssh/transport/key_expander.rb +24 -20
  103. data/lib/net/ssh/transport/openssl.rb +161 -124
  104. data/lib/net/ssh/transport/packet_stream.rb +225 -185
  105. data/lib/net/ssh/transport/server_version.rb +55 -56
  106. data/lib/net/ssh/transport/session.rb +306 -255
  107. data/lib/net/ssh/transport/state.rb +178 -176
  108. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  109. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  110. data/lib/net/ssh/verifiers/always.rb +58 -0
  111. data/lib/net/ssh/verifiers/never.rb +19 -0
  112. data/lib/net/ssh/version.rb +55 -53
  113. data/lib/net/ssh.rb +110 -47
  114. data/net-ssh-public_cert.pem +18 -18
  115. data/net-ssh.gemspec +36 -205
  116. data/support/ssh_tunnel_bug.rb +5 -5
  117. data.tar.gz.sig +0 -0
  118. metadata +153 -118
  119. metadata.gz.sig +0 -0
  120. data/.travis.yml +0 -18
  121. data/README.rdoc +0 -182
  122. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  123. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  124. data/lib/net/ssh/ruby_compat.rb +0 -46
  125. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  126. data/lib/net/ssh/verifiers/null.rb +0 -12
  127. data/lib/net/ssh/verifiers/secure.rb +0 -52
  128. data/lib/net/ssh/verifiers/strict.rb +0 -24
  129. data/setup.rb +0 -1585
  130. data/support/arcfour_check.rb +0 -20
  131. data/test/README.txt +0 -18
  132. data/test/authentication/methods/common.rb +0 -28
  133. data/test/authentication/methods/test_abstract.rb +0 -51
  134. data/test/authentication/methods/test_hostbased.rb +0 -114
  135. data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
  136. data/test/authentication/methods/test_none.rb +0 -41
  137. data/test/authentication/methods/test_password.rb +0 -95
  138. data/test/authentication/methods/test_publickey.rb +0 -148
  139. data/test/authentication/test_agent.rb +0 -232
  140. data/test/authentication/test_key_manager.rb +0 -240
  141. data/test/authentication/test_session.rb +0 -107
  142. data/test/common.rb +0 -125
  143. data/test/configs/auth_off +0 -5
  144. data/test/configs/auth_on +0 -4
  145. data/test/configs/empty +0 -0
  146. data/test/configs/eqsign +0 -3
  147. data/test/configs/exact_match +0 -8
  148. data/test/configs/host_plus +0 -10
  149. data/test/configs/multihost +0 -4
  150. data/test/configs/negative_match +0 -6
  151. data/test/configs/nohost +0 -19
  152. data/test/configs/numeric_host +0 -4
  153. data/test/configs/proxy_remote_user +0 -2
  154. data/test/configs/send_env +0 -2
  155. data/test/configs/substitutes +0 -8
  156. data/test/configs/wild_cards +0 -14
  157. data/test/connection/test_channel.rb +0 -487
  158. data/test/connection/test_session.rb +0 -564
  159. data/test/integration/README.txt +0 -17
  160. data/test/integration/Vagrantfile +0 -12
  161. data/test/integration/common.rb +0 -63
  162. data/test/integration/playbook.yml +0 -56
  163. data/test/integration/test_forward.rb +0 -637
  164. data/test/integration/test_id_rsa_keys.rb +0 -96
  165. data/test/integration/test_proxy.rb +0 -93
  166. data/test/known_hosts/github +0 -1
  167. data/test/known_hosts/github_hash +0 -1
  168. data/test/manual/test_pageant.rb +0 -37
  169. data/test/start/test_connection.rb +0 -53
  170. data/test/start/test_options.rb +0 -57
  171. data/test/start/test_transport.rb +0 -28
  172. data/test/start/test_user_nil.rb +0 -27
  173. data/test/test_all.rb +0 -12
  174. data/test/test_buffer.rb +0 -433
  175. data/test/test_buffered_io.rb +0 -63
  176. data/test/test_config.rb +0 -268
  177. data/test/test_key_factory.rb +0 -191
  178. data/test/test_known_hosts.rb +0 -66
  179. data/test/transport/hmac/test_md5.rb +0 -41
  180. data/test/transport/hmac/test_md5_96.rb +0 -27
  181. data/test/transport/hmac/test_none.rb +0 -34
  182. data/test/transport/hmac/test_ripemd160.rb +0 -36
  183. data/test/transport/hmac/test_sha1.rb +0 -36
  184. data/test/transport/hmac/test_sha1_96.rb +0 -27
  185. data/test/transport/hmac/test_sha2_256.rb +0 -37
  186. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  187. data/test/transport/hmac/test_sha2_512.rb +0 -37
  188. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  189. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  190. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
  191. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
  192. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
  193. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  194. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  195. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  196. data/test/transport/test_algorithms.rb +0 -328
  197. data/test/transport/test_cipher_factory.rb +0 -443
  198. data/test/transport/test_hmac.rb +0 -34
  199. data/test/transport/test_identity_cipher.rb +0 -40
  200. data/test/transport/test_packet_stream.rb +0 -1762
  201. data/test/transport/test_server_version.rb +0 -74
  202. data/test/transport/test_session.rb +0 -331
  203. data/test/transport/test_state.rb +0 -181
  204. data/test/verifiers/test_secure.rb +0 -40
@@ -3,100 +3,104 @@ require 'net/ssh/transport/constants'
3
3
  require 'net/ssh/authentication/constants'
4
4
  require 'net/ssh/connection/constants'
5
5
 
6
- module Net; module SSH
7
-
8
- # A specialization of Buffer that knows the format of certain common
9
- # packet types. It auto-parses those packet types, and allows them to
10
- # be accessed via the #[] accessor.
11
- #
12
- # data = some_channel_request_packet
13
- # packet = Net::SSH::Packet.new(data)
14
- #
15
- # p packet.type #-> 98 (CHANNEL_REQUEST)
16
- # p packet[:request]
17
- # p packet[:want_reply]
18
- #
19
- # This is used exclusively internally by Net::SSH, and unless you're doing
20
- # protocol-level manipulation or are extending Net::SSH in some way, you'll
21
- # never need to use this class directly.
22
- class Packet < Buffer
23
- @@types = {}
24
-
25
- # Register a new packet type that should be recognized and auto-parsed by
26
- # Net::SSH::Packet. Note that any packet type that is not preregistered
27
- # will not be autoparsed.
6
+ module Net
7
+ module SSH
8
+ # A specialization of Buffer that knows the format of certain common
9
+ # packet types. It auto-parses those packet types, and allows them to
10
+ # be accessed via the #[] accessor.
28
11
  #
29
- # The +pairs+ parameter must be either empty, or an array of two-element
30
- # tuples, where the first element of each tuple is the name of the field,
31
- # and the second is the type.
12
+ # data = some_channel_request_packet
13
+ # packet = Net::SSH::Packet.new(data)
32
14
  #
33
- # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
34
- def self.register(type, *pairs)
35
- @@types[type] = pairs
36
- end
15
+ # p packet.type #-> 98 (CHANNEL_REQUEST)
16
+ # p packet[:request]
17
+ # p packet[:want_reply]
18
+ #
19
+ # This is used exclusively internally by Net::SSH, and unless you're doing
20
+ # protocol-level manipulation or are extending Net::SSH in some way, you'll
21
+ # never need to use this class directly.
22
+ class Packet < Buffer
23
+ @@types = {}
37
24
 
38
- include Transport::Constants, Authentication::Constants, Connection::Constants
25
+ # Register a new packet type that should be recognized and auto-parsed by
26
+ # Net::SSH::Packet. Note that any packet type that is not preregistered
27
+ # will not be autoparsed.
28
+ #
29
+ # The +pairs+ parameter must be either empty, or an array of two-element
30
+ # tuples, where the first element of each tuple is the name of the field,
31
+ # and the second is the type.
32
+ #
33
+ # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
34
+ def self.register(type, *pairs)
35
+ @@types[type] = pairs
36
+ end
39
37
 
40
- #--
41
- # These are the recognized packet types. All other packet types will be
42
- # accepted, but not auto-parsed, requiring the client to parse the
43
- # fields using the methods provided by Net::SSH::Buffer.
44
- #++
38
+ include Connection::Constants
39
+ include Authentication::Constants
40
+ include Transport::Constants
45
41
 
46
- register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
47
- register IGNORE, [:data, :string]
48
- register UNIMPLEMENTED, [:number, :long]
49
- register DEBUG, [:always_display, :bool], [:message, :string], [:language, :string]
50
- register SERVICE_ACCEPT, [:service_name, :string]
51
- register USERAUTH_BANNER, [:message, :string], [:language, :string]
52
- register USERAUTH_FAILURE, [:authentications, :string], [:partial_success, :bool]
53
- register GLOBAL_REQUEST, [:request_type, :string], [:want_reply, :bool], [:request_data, :buffer]
54
- register CHANNEL_OPEN, [:channel_type, :string], [:remote_id, :long], [:window_size, :long], [:packet_size, :long]
55
- register CHANNEL_OPEN_CONFIRMATION, [:local_id, :long], [:remote_id, :long], [:window_size, :long], [:packet_size, :long]
56
- register CHANNEL_OPEN_FAILURE, [:local_id, :long], [:reason_code, :long], [:description, :string], [:language, :string]
57
- register CHANNEL_WINDOW_ADJUST, [:local_id, :long], [:extra_bytes, :long]
58
- register CHANNEL_DATA, [:local_id, :long], [:data, :string]
59
- register CHANNEL_EXTENDED_DATA, [:local_id, :long], [:data_type, :long], [:data, :string]
60
- register CHANNEL_EOF, [:local_id, :long]
61
- register CHANNEL_CLOSE, [:local_id, :long]
62
- register CHANNEL_REQUEST, [:local_id, :long], [:request, :string], [:want_reply, :bool], [:request_data, :buffer]
63
- register CHANNEL_SUCCESS, [:local_id, :long]
64
- register CHANNEL_FAILURE, [:local_id, :long]
42
+ #--
43
+ # These are the recognized packet types. All other packet types will be
44
+ # accepted, but not auto-parsed, requiring the client to parse the
45
+ # fields using the methods provided by Net::SSH::Buffer.
46
+ #++
65
47
 
66
- # The (integer) type of this packet.
67
- attr_reader :type
48
+ register DISCONNECT, %i[reason_code long], %i[description string], %i[language string]
49
+ register IGNORE, %i[data string]
50
+ register UNIMPLEMENTED, %i[number long]
51
+ register DEBUG, %i[always_display bool], %i[message string], %i[language string]
52
+ register SERVICE_ACCEPT, %i[service_name string]
53
+ register USERAUTH_BANNER, %i[message string], %i[language string]
54
+ register USERAUTH_FAILURE, %i[authentications string], %i[partial_success bool]
55
+ register GLOBAL_REQUEST, %i[request_type string], %i[want_reply bool], %i[request_data buffer]
56
+ register CHANNEL_OPEN, %i[channel_type string], %i[remote_id long], %i[window_size long], %i[packet_size long]
57
+ register CHANNEL_OPEN_CONFIRMATION, %i[local_id long], %i[remote_id long], %i[window_size long], %i[packet_size long]
58
+ register CHANNEL_OPEN_FAILURE, %i[local_id long], %i[reason_code long], %i[description string], %i[language string]
59
+ register CHANNEL_WINDOW_ADJUST, %i[local_id long], %i[extra_bytes long]
60
+ register CHANNEL_DATA, %i[local_id long], %i[data string]
61
+ register CHANNEL_EXTENDED_DATA, %i[local_id long], %i[data_type long], %i[data string]
62
+ register CHANNEL_EOF, %i[local_id long]
63
+ register CHANNEL_CLOSE, %i[local_id long]
64
+ register CHANNEL_REQUEST, %i[local_id long], %i[request string], %i[want_reply bool], %i[request_data buffer]
65
+ register CHANNEL_SUCCESS, %i[local_id long]
66
+ register CHANNEL_FAILURE, %i[local_id long]
68
67
 
69
- # Create a new packet from the given payload. This will automatically
70
- # parse the packet if it is one that has been previously registered with
71
- # Packet.register; otherwise, the packet will need to be manually parsed
72
- # using the methods provided in the Net::SSH::Buffer superclass.
73
- def initialize(payload)
74
- @named_elements = {}
75
- super
76
- @type = read_byte
77
- instantiate!
78
- end
68
+ # The (integer) type of this packet.
69
+ attr_reader :type
79
70
 
80
- # Access one of the auto-parsed fields by name. Raises an error if no
81
- # element by the given name exists.
82
- def [](name)
83
- name = name.to_sym
84
- raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
85
- @named_elements[name]
86
- end
71
+ # Create a new packet from the given payload. This will automatically
72
+ # parse the packet if it is one that has been previously registered with
73
+ # Packet.register; otherwise, the packet will need to be manually parsed
74
+ # using the methods provided in the Net::SSH::Buffer superclass.
75
+ def initialize(payload)
76
+ @named_elements = {}
77
+ super
78
+ @type = read_byte
79
+ instantiate!
80
+ end
81
+
82
+ # Access one of the auto-parsed fields by name. Raises an error if no
83
+ # element by the given name exists.
84
+ def [](name)
85
+ name = name.to_sym
86
+ raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
87
87
 
88
- private
88
+ @named_elements[name]
89
+ end
90
+
91
+ private
89
92
 
90
93
  # Parse the packet's contents and assign the named elements, as described
91
94
  # by the registered format for the packet.
92
95
  def instantiate!
93
96
  (@@types[type] || []).each do |name, datatype|
94
97
  @named_elements[name.to_sym] = if datatype == :buffer
95
- remainder_as_buffer
96
- else
97
- send("read_#{datatype}")
98
- end
98
+ remainder_as_buffer
99
+ else
100
+ send("read_#{datatype}")
101
+ end
99
102
  end
100
103
  end
104
+ end
101
105
  end
102
- end; end
106
+ end
@@ -1,93 +1,62 @@
1
- module Net; module SSH
2
-
3
- # A basic prompt module that can be mixed into other objects. If HighLine is
4
- # installed, it will be used to display prompts and read input from the
5
- # user. Otherwise, the termios library will be used. If neither HighLine
6
- # nor termios is installed, a simple prompt that echos text in the clear
7
- # will be used.
8
-
9
- module PromptMethods
10
-
11
- # Defines the prompt method to use if the Highline library is installed.
12
- module Highline
13
- # Uses Highline#ask to present a prompt and accept input. If +echo+ is
14
- # +false+, the characters entered by the user will not be echoed to the
15
- # screen.
16
- def prompt(prompt, echo=true)
17
- @highline ||= ::HighLine.new
18
- @highline.ask(prompt + " ") { |q| q.echo = echo }
19
- end
20
- end
21
-
22
- # Defines the prompt method to use if the Termios library is installed.
23
- module Termios
24
- # Displays the prompt to $stdout. If +echo+ is false, the Termios
25
- # library will be used to disable keystroke echoing for the duration of
26
- # this method.
27
- def prompt(prompt, echo=true)
28
- $stdout.print(prompt)
29
- $stdout.flush
30
-
31
- set_echo(false) unless echo
32
- $stdin.gets.chomp
33
- ensure
34
- if !echo
35
- set_echo(true)
36
- $stdout.puts
37
- end
1
+ require 'io/console'
2
+
3
+ module Net
4
+ module SSH
5
+ # Default prompt implementation, called for asking password from user.
6
+ # It will never be instantiated directly, but will instead be created for
7
+ # you automatically.
8
+ #
9
+ # A custom prompt objects can implement caching, or different UI. The prompt
10
+ # object should implemnted a start method, which should return something implementing
11
+ # ask and success. Net::SSH uses it like:
12
+ #
13
+ # prompter = options[:password_prompt].start({type:'password'})
14
+ # while !ok && max_retries < 3
15
+ # user = prompter.ask("user: ", true)
16
+ # password = prompter.ask("password: ", false)
17
+ # ok = send(user, password)
18
+ # prompter.sucess if ok
19
+ # end
20
+ #
21
+ class Prompt
22
+ # factory
23
+ def self.default(options = {})
24
+ @default ||= new(options)
38
25
  end
39
26
 
40
- private
27
+ def initialize(options = {}); end
41
28
 
42
- # Enables or disables keystroke echoing using the Termios library.
43
- def set_echo(enable)
44
- term = ::Termios.getattr($stdin)
45
-
46
- if enable
47
- term.c_lflag |= (::Termios::ECHO | ::Termios::ICANON)
48
- else
49
- term.c_lflag &= ~::Termios::ECHO
29
+ # default prompt object implementation. More sophisticated implemenetations
30
+ # might implement caching.
31
+ class Prompter
32
+ def initialize(info)
33
+ if info[:type] == 'keyboard-interactive'
34
+ $stdout.puts(info[:name]) unless info[:name].empty?
35
+ $stdout.puts(info[:instruction]) unless info[:instruction].empty?
50
36
  end
51
-
52
- ::Termios.setattr($stdin, ::Termios::TCSANOW, term)
53
37
  end
54
- end
55
38
 
56
- # Defines the prompt method to use when neither Highline nor Termios are
57
- # installed.
58
- module Clear
59
- # Displays the prompt to $stdout and pulls the response from $stdin.
60
- # Text is always echoed in the clear, regardless of the +echo+ setting.
61
- # The first time a prompt is given and +echo+ is false, a warning will
62
- # be written to $stderr recommending that either Highline or Termios
63
- # be installed.
64
- def prompt(prompt, echo=true)
65
- @seen_warning ||= false
66
- if !echo && !@seen_warning
67
- $stderr.puts "Text will be echoed in the clear. Please install the HighLine or Termios libraries to suppress echoed text."
68
- @seen_warning = true
39
+ # ask input from user, a prompter might ask for multiple inputs
40
+ # (like user and password) in a single session.
41
+ def ask(prompt, echo = true)
42
+ $stdout.print(prompt)
43
+ $stdout.flush
44
+ ret = $stdin.noecho(&:gets).chomp
45
+ $stdout.print("\n")
46
+ ret
69
47
  end
70
48
 
71
- $stdout.print(prompt)
72
- $stdout.flush
73
- $stdin.gets.chomp
49
+ # success method will be called when the password was accepted
50
+ # It's a good time to save password asked to a cache.
51
+ def success; end
74
52
  end
75
- end
76
- end
77
53
 
78
- # Try to load Highline and Termios in turn, selecting the corresponding
79
- # PromptMethods module to use. If neither are available, choose PromptMethods::Clear.
80
- Prompt = begin
81
- require 'highline'
82
- HighLine.track_eof = false
83
- PromptMethods::Highline
84
- rescue LoadError
85
- begin
86
- require 'termios'
87
- PromptMethods::Termios
88
- rescue LoadError
89
- PromptMethods::Clear
54
+ # start password session. Multiple questions might be asked multiple times
55
+ # on the returned object. Info hash tries to uniquely identify the password
56
+ # session, so caching implementations can save passwords properly.
57
+ def start(info)
58
+ Prompter.new(info)
90
59
  end
91
60
  end
92
-
93
- end; end
61
+ end
62
+ end
@@ -1,108 +1,123 @@
1
1
  require 'socket'
2
2
  require 'rubygems'
3
3
  require 'net/ssh/proxy/errors'
4
- require 'net/ssh/ruby_compat'
5
4
 
6
- module Net; module SSH; module Proxy
5
+ module Net
6
+ module SSH
7
+ module Proxy
8
+ # An implementation of a command proxy. To use it, instantiate it,
9
+ # then pass the instantiated object via the :proxy key to
10
+ # Net::SSH.start:
11
+ #
12
+ # require 'net/ssh/proxy/command'
13
+ #
14
+ # proxy = Net::SSH::Proxy::Command.new('ssh relay nc %h %p')
15
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
16
+ # ...
17
+ # end
18
+ class Command
19
+ # The command line template
20
+ attr_reader :command_line_template
7
21
 
8
- # An implementation of a command proxy. To use it, instantiate it,
9
- # then pass the instantiated object via the :proxy key to
10
- # Net::SSH.start:
11
- #
12
- # require 'net/ssh/proxy/command'
13
- #
14
- # proxy = Net::SSH::Proxy::Command.new('ssh relay nc %h %p')
15
- # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
16
- # ...
17
- # end
18
- class Command
22
+ # The command line for the session
23
+ attr_reader :command_line
19
24
 
20
- # The command line template
21
- attr_reader :command_line_template
25
+ # Timeout in seconds in open, defaults to 60
26
+ attr_accessor :timeout
22
27
 
23
- # The command line for the session
24
- attr_reader :command_line
25
-
26
- # Create a new socket factory that tunnels via a command executed
27
- # with the user's shell, which is composed from the given command
28
- # template. In the command template, `%h' will be substituted by
29
- # the host name to connect and `%p' by the port.
30
- def initialize(command_line_template)
31
- @command_line_template = command_line_template
32
- @command_line = nil
33
- end
34
-
35
- # Return a new socket connected to the given host and port via the
36
- # proxy that was requested when the socket factory was instantiated.
37
- def open(host, port, connection_options = nil)
38
- command_line = @command_line_template.gsub(/%(.)/) {
39
- case $1
40
- when 'h'
41
- host
42
- when 'p'
43
- port.to_s
44
- when 'r'
45
- remote_user = connection_options && connection_options[:remote_user]
46
- if remote_user
47
- remote_user
48
- else
49
- raise ArgumentError, "remote user name not available"
50
- end
51
- when '%'
52
- '%'
53
- else
54
- raise ArgumentError, "unknown key: #{$1}"
55
- end
56
- }
57
- begin
58
- io = IO.popen(command_line, "r+")
59
- if result = Net::SSH::Compat.io_select([io], nil, [io], 60)
60
- if result.last.any?
61
- raise "command failed"
62
- end
63
- else
64
- raise "command timed out"
65
- end
66
- rescue => e
67
- raise ConnectError, "#{e}: #{command_line}"
68
- end
69
- @command_line = command_line
70
- if Gem.win_platform?
71
- # read_nonblock and write_nonblock are not available on Windows
72
- # pipe. Use sysread and syswrite as a replacement works.
73
- def io.send(data, flag)
74
- syswrite(data)
28
+ # Create a new socket factory that tunnels via a command executed
29
+ # with the user's shell, which is composed from the given command
30
+ # template. In the command template, `%h' will be substituted by
31
+ # the host name to connect and `%p' by the port.
32
+ def initialize(command_line_template)
33
+ @command_line_template = command_line_template
34
+ @command_line = nil
35
+ @timeout = 60
75
36
  end
76
37
 
77
- def io.recv(size)
78
- sysread(size)
79
- end
80
- else
81
- def io.send(data, flag)
38
+ # Return a new socket connected to the given host and port via the
39
+ # proxy that was requested when the socket factory was instantiated.
40
+ def open(host, port, connection_options = nil)
41
+ command_line = @command_line_template.gsub(/%(.)/) {
42
+ case $1
43
+ when 'h'
44
+ host
45
+ when 'p'
46
+ port.to_s
47
+ when 'r'
48
+ remote_user = connection_options && connection_options[:remote_user]
49
+ if remote_user
50
+ remote_user
51
+ else
52
+ raise ArgumentError, "remote user name not available"
53
+ end
54
+ when '%'
55
+ '%'
56
+ else
57
+ raise ArgumentError, "unknown key: #{$1}"
58
+ end
59
+ }
82
60
  begin
83
- result = write_nonblock(data)
84
- rescue IO::WaitWritable, Errno::EINTR
85
- IO.select(nil, [self])
86
- retry
61
+ io = IO.popen(command_line, "r+")
62
+ begin
63
+ if result = IO.select([io], nil, [io], @timeout)
64
+ if result.last.any? || io.eof?
65
+ raise "command failed"
66
+ end
67
+ else
68
+ raise "command timed out"
69
+ end
70
+ rescue StandardError
71
+ close_on_error(io)
72
+ raise
73
+ end
74
+ rescue StandardError => e
75
+ raise ConnectError, "#{e}: #{command_line}"
87
76
  end
88
- result
89
- end
77
+ @command_line = command_line
78
+ if Gem.win_platform?
79
+ # read_nonblock and write_nonblock are not available on Windows
80
+ # pipe. Use sysread and syswrite as a replacement works.
81
+ def io.send(data, flag)
82
+ syswrite(data)
83
+ end
90
84
 
91
- def io.recv(size)
92
- begin
93
- result = read_nonblock(size)
94
- rescue IO::WaitReadable, Errno::EINTR
95
- timeout_in_seconds = 20
96
- if IO.select([self], nil, [self], timeout_in_seconds) == nil
97
- raise "Unexpected spurious read wakeup"
85
+ def io.recv(size)
86
+ sysread(size)
87
+ end
88
+ else
89
+ def io.send(data, flag)
90
+ begin
91
+ result = write_nonblock(data)
92
+ rescue IO::WaitWritable, Errno::EINTR
93
+ IO.select(nil, [self])
94
+ retry
95
+ end
96
+ result
97
+ end
98
+
99
+ def io.recv(size)
100
+ begin
101
+ result = read_nonblock(size)
102
+ rescue IO::WaitReadable, Errno::EINTR
103
+ timeout_in_seconds = 20
104
+ if IO.select([self], nil, [self], timeout_in_seconds) == nil
105
+ raise "Unexpected spurious read wakeup"
106
+ end
107
+
108
+ retry
109
+ end
110
+ result
98
111
  end
99
- retry
100
112
  end
101
- result
113
+ io
114
+ end
115
+
116
+ def close_on_error(io)
117
+ Process.kill('TERM', io.pid)
118
+ Thread.new { io.close }
102
119
  end
103
120
  end
104
- io
105
121
  end
106
122
  end
107
-
108
- end; end; end
123
+ end
@@ -1,14 +1,16 @@
1
1
  require 'net/ssh/errors'
2
2
 
3
- module Net; module SSH; module Proxy
3
+ module Net
4
+ module SSH
5
+ module Proxy
6
+ # A general exception class for all Proxy errors.
7
+ class Error < Net::SSH::Exception; end
4
8
 
5
- # A general exception class for all Proxy errors.
6
- class Error < Net::SSH::Exception; end
9
+ # Used for reporting proxy connection errors.
10
+ class ConnectError < Error; end
7
11
 
8
- # Used for reporting proxy connection errors.
9
- class ConnectError < Error; end
10
-
11
- # Used when the server doesn't recognize the user's credentials.
12
- class UnauthorizedError < Error; end
13
-
14
- end; end; end
12
+ # Used when the server doesn't recognize the user's credentials.
13
+ class UnauthorizedError < Error; end
14
+ end
15
+ end
16
+ end