net-ssh 4.1.0 → 6.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 (111) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +5 -0
  5. data/.rubocop.yml +8 -2
  6. data/.rubocop_todo.yml +405 -552
  7. data/.travis.yml +23 -22
  8. data/CHANGES.txt +112 -1
  9. data/Gemfile +1 -7
  10. data/{Gemfile.norbnacl → Gemfile.noed25519} +1 -1
  11. data/Manifest +4 -5
  12. data/README.md +287 -0
  13. data/Rakefile +40 -29
  14. data/appveyor.yml +12 -6
  15. data/lib/net/ssh.rb +68 -32
  16. data/lib/net/ssh/authentication/agent.rb +234 -222
  17. data/lib/net/ssh/authentication/certificate.rb +175 -164
  18. data/lib/net/ssh/authentication/constants.rb +17 -14
  19. data/lib/net/ssh/authentication/ed25519.rb +162 -141
  20. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -29
  21. data/lib/net/ssh/authentication/key_manager.rb +40 -9
  22. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  23. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  24. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +1 -1
  25. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  26. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  27. data/lib/net/ssh/authentication/methods/publickey.rb +56 -55
  28. data/lib/net/ssh/authentication/pageant.rb +468 -465
  29. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  30. data/lib/net/ssh/authentication/session.rb +130 -122
  31. data/lib/net/ssh/buffer.rb +345 -312
  32. data/lib/net/ssh/buffered_io.rb +163 -163
  33. data/lib/net/ssh/config.rb +316 -238
  34. data/lib/net/ssh/connection/channel.rb +670 -650
  35. data/lib/net/ssh/connection/constants.rb +30 -26
  36. data/lib/net/ssh/connection/event_loop.rb +108 -105
  37. data/lib/net/ssh/connection/keepalive.rb +54 -50
  38. data/lib/net/ssh/connection/session.rb +682 -671
  39. data/lib/net/ssh/connection/term.rb +180 -176
  40. data/lib/net/ssh/errors.rb +101 -99
  41. data/lib/net/ssh/key_factory.rb +195 -108
  42. data/lib/net/ssh/known_hosts.rb +161 -152
  43. data/lib/net/ssh/loggable.rb +57 -55
  44. data/lib/net/ssh/packet.rb +82 -78
  45. data/lib/net/ssh/prompt.rb +55 -53
  46. data/lib/net/ssh/proxy/command.rb +104 -89
  47. data/lib/net/ssh/proxy/errors.rb +12 -8
  48. data/lib/net/ssh/proxy/http.rb +93 -91
  49. data/lib/net/ssh/proxy/https.rb +42 -39
  50. data/lib/net/ssh/proxy/jump.rb +50 -47
  51. data/lib/net/ssh/proxy/socks4.rb +0 -2
  52. data/lib/net/ssh/proxy/socks5.rb +11 -12
  53. data/lib/net/ssh/service/forward.rb +370 -317
  54. data/lib/net/ssh/test.rb +83 -77
  55. data/lib/net/ssh/test/channel.rb +146 -142
  56. data/lib/net/ssh/test/extensions.rb +150 -146
  57. data/lib/net/ssh/test/kex.rb +35 -31
  58. data/lib/net/ssh/test/local_packet.rb +48 -44
  59. data/lib/net/ssh/test/packet.rb +87 -84
  60. data/lib/net/ssh/test/remote_packet.rb +35 -31
  61. data/lib/net/ssh/test/script.rb +173 -171
  62. data/lib/net/ssh/test/socket.rb +59 -55
  63. data/lib/net/ssh/transport/algorithms.rb +430 -364
  64. data/lib/net/ssh/transport/cipher_factory.rb +95 -91
  65. data/lib/net/ssh/transport/constants.rb +33 -25
  66. data/lib/net/ssh/transport/ctr.rb +33 -11
  67. data/lib/net/ssh/transport/hmac.rb +15 -13
  68. data/lib/net/ssh/transport/hmac/abstract.rb +82 -63
  69. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  70. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  71. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  72. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  73. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  74. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  75. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  76. data/lib/net/ssh/transport/kex.rb +14 -13
  77. data/lib/net/ssh/transport/kex/abstract.rb +123 -0
  78. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  79. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +38 -0
  80. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  81. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  82. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +112 -217
  83. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -62
  84. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  85. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  86. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  87. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  88. data/lib/net/ssh/transport/key_expander.rb +29 -25
  89. data/lib/net/ssh/transport/openssl.rb +116 -116
  90. data/lib/net/ssh/transport/packet_stream.rb +223 -190
  91. data/lib/net/ssh/transport/server_version.rb +64 -66
  92. data/lib/net/ssh/transport/session.rb +306 -257
  93. data/lib/net/ssh/transport/state.rb +198 -196
  94. data/lib/net/ssh/verifiers/accept_new.rb +35 -0
  95. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +34 -0
  96. data/lib/net/ssh/verifiers/always.rb +56 -0
  97. data/lib/net/ssh/verifiers/never.rb +21 -0
  98. data/lib/net/ssh/version.rb +55 -53
  99. data/net-ssh-public_cert.pem +18 -19
  100. data/net-ssh.gemspec +12 -11
  101. data/support/ssh_tunnel_bug.rb +2 -2
  102. metadata +86 -75
  103. metadata.gz.sig +0 -0
  104. data/Gemfile.norbnacl.lock +0 -41
  105. data/README.rdoc +0 -169
  106. data/lib/net/ssh/ruby_compat.rb +0 -24
  107. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  108. data/lib/net/ssh/verifiers/null.rb +0 -12
  109. data/lib/net/ssh/verifiers/secure.rb +0 -52
  110. data/lib/net/ssh/verifiers/strict.rb +0 -24
  111. data/support/arcfour_check.rb +0 -20
@@ -1,61 +1,63 @@
1
- module Net; module SSH
2
-
3
- # A simple module to make logging easier to deal with. It assumes that the
4
- # logger instance (if not nil) quacks like a Logger object (in Ruby's
5
- # standard library). Although used primarily internally by Net::SSH, it
6
- # can easily be used to add Net::SSH-like logging to your own programs.
7
- #
8
- # class MyClass
9
- # include Net::SSH::Loggable
10
- # end
11
- #
12
- # Net::SSH.start(...) do |ssh|
13
- # obj = MyClass.new
14
- # obj.logger = ssh.logger
15
- # ...
16
- # end
17
- module Loggable
18
- # The logger instance that will be used to log messages. If nil, nothing
19
- # will be logged.
20
- attr_accessor :logger
21
-
22
- # Displays the result of yielding if the log level is Logger::DEBUG or
23
- # greater.
24
- def debug
25
- logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
26
- end
27
-
28
- # Displays the result of yielding if the log level is Logger::INFO or
29
- # greater.
30
- def info
31
- logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
32
- end
33
-
34
- # Displays the result of yielding if the log level is Logger::WARN or
35
- # greater. (Called lwarn to avoid shadowing with Kernel#warn.)
36
- def lwarn
37
- logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
38
- end
39
-
40
- # Displays the result of yielding if the log level is Logger:ERROR or
41
- # greater.
42
- def error
43
- logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
44
- end
45
-
46
- # Displays the result of yielding if the log level is Logger::FATAL or
47
- # greater.
48
- def fatal
49
- logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
50
- end
51
-
52
- private
53
-
1
+ module Net
2
+ module SSH
3
+
4
+ # A simple module to make logging easier to deal with. It assumes that the
5
+ # logger instance (if not nil) quacks like a Logger object (in Ruby's
6
+ # standard library). Although used primarily internally by Net::SSH, it
7
+ # can easily be used to add Net::SSH-like logging to your own programs.
8
+ #
9
+ # class MyClass
10
+ # include Net::SSH::Loggable
11
+ # end
12
+ #
13
+ # Net::SSH.start(...) do |ssh|
14
+ # obj = MyClass.new
15
+ # obj.logger = ssh.logger
16
+ # ...
17
+ # end
18
+ module Loggable
19
+ # The logger instance that will be used to log messages. If nil, nothing
20
+ # will be logged.
21
+ attr_accessor :logger
22
+
23
+ # Displays the result of yielding if the log level is Logger::DEBUG or
24
+ # greater.
25
+ def debug
26
+ logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
27
+ end
28
+
29
+ # Displays the result of yielding if the log level is Logger::INFO or
30
+ # greater.
31
+ def info
32
+ logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
33
+ end
34
+
35
+ # Displays the result of yielding if the log level is Logger::WARN or
36
+ # greater. (Called lwarn to avoid shadowing with Kernel#warn.)
37
+ def lwarn
38
+ logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
39
+ end
40
+
41
+ # Displays the result of yielding if the log level is Logger:ERROR or
42
+ # greater.
43
+ def error
44
+ logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
45
+ end
46
+
47
+ # Displays the result of yielding if the log level is Logger::FATAL or
48
+ # greater.
49
+ def fatal
50
+ logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
51
+ end
52
+
53
+ private
54
+
54
55
  # Sets the "facility" value, used for reporting where a log message
55
56
  # originates. It defaults to the name of class with the object_id
56
57
  # appended.
57
58
  def facility
58
- @facility ||= self.class.name.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
59
+ @facility ||= self.class.to_s.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
59
60
  end
61
+ end
60
62
  end
61
- end; end
63
+ end
@@ -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
6
+ module Net
7
+ module SSH
7
8
 
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.
9
+ # A specialization of Buffer that knows the format of certain common
10
+ # packet types. It auto-parses those packet types, and allows them to
11
+ # be accessed via the #[] accessor.
28
12
  #
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.
13
+ # data = some_channel_request_packet
14
+ # packet = Net::SSH::Packet.new(data)
32
15
  #
33
- # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
34
- def self.register(type, *pairs)
35
- @@types[type] = pairs
36
- end
16
+ # p packet.type #-> 98 (CHANNEL_REQUEST)
17
+ # p packet[:request]
18
+ # p packet[:want_reply]
19
+ #
20
+ # This is used exclusively internally by Net::SSH, and unless you're doing
21
+ # protocol-level manipulation or are extending Net::SSH in some way, you'll
22
+ # never need to use this class directly.
23
+ class Packet < Buffer
24
+ @@types = {}
37
25
 
38
- include Transport::Constants, Authentication::Constants, Connection::Constants
26
+ # Register a new packet type that should be recognized and auto-parsed by
27
+ # Net::SSH::Packet. Note that any packet type that is not preregistered
28
+ # will not be autoparsed.
29
+ #
30
+ # The +pairs+ parameter must be either empty, or an array of two-element
31
+ # tuples, where the first element of each tuple is the name of the field,
32
+ # and the second is the type.
33
+ #
34
+ # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
35
+ def self.register(type, *pairs)
36
+ @@types[type] = pairs
37
+ end
39
38
 
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
- #++
39
+ include Connection::Constants
40
+ include Authentication::Constants
41
+ include Transport::Constants
45
42
 
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]
43
+ #--
44
+ # These are the recognized packet types. All other packet types will be
45
+ # accepted, but not auto-parsed, requiring the client to parse the
46
+ # fields using the methods provided by Net::SSH::Buffer.
47
+ #++
65
48
 
66
- # The (integer) type of this packet.
67
- attr_reader :type
49
+ register DISCONNECT, %i[reason_code long], %i[description string], %i[language string]
50
+ register IGNORE, %i[data string]
51
+ register UNIMPLEMENTED, %i[number long]
52
+ register DEBUG, %i[always_display bool], %i[message string], %i[language string]
53
+ register SERVICE_ACCEPT, %i[service_name string]
54
+ register USERAUTH_BANNER, %i[message string], %i[language string]
55
+ register USERAUTH_FAILURE, %i[authentications string], %i[partial_success bool]
56
+ register GLOBAL_REQUEST, %i[request_type string], %i[want_reply bool], %i[request_data buffer]
57
+ register CHANNEL_OPEN, %i[channel_type string], %i[remote_id long], %i[window_size long], %i[packet_size long]
58
+ register CHANNEL_OPEN_CONFIRMATION, %i[local_id long], %i[remote_id long], %i[window_size long], %i[packet_size long]
59
+ register CHANNEL_OPEN_FAILURE, %i[local_id long], %i[reason_code long], %i[description string], %i[language string]
60
+ register CHANNEL_WINDOW_ADJUST, %i[local_id long], %i[extra_bytes long]
61
+ register CHANNEL_DATA, %i[local_id long], %i[data string]
62
+ register CHANNEL_EXTENDED_DATA, %i[local_id long], %i[data_type long], %i[data string]
63
+ register CHANNEL_EOF, %i[local_id long]
64
+ register CHANNEL_CLOSE, %i[local_id long]
65
+ register CHANNEL_REQUEST, %i[local_id long], %i[request string], %i[want_reply bool], %i[request_data buffer]
66
+ register CHANNEL_SUCCESS, %i[local_id long]
67
+ register CHANNEL_FAILURE, %i[local_id long]
68
68
 
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
69
+ # The (integer) type of this packet.
70
+ attr_reader :type
79
71
 
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
72
+ # Create a new packet from the given payload. This will automatically
73
+ # parse the packet if it is one that has been previously registered with
74
+ # Packet.register; otherwise, the packet will need to be manually parsed
75
+ # using the methods provided in the Net::SSH::Buffer superclass.
76
+ def initialize(payload)
77
+ @named_elements = {}
78
+ super
79
+ @type = read_byte
80
+ instantiate!
81
+ end
87
82
 
88
- private
83
+ # Access one of the auto-parsed fields by name. Raises an error if no
84
+ # element by the given name exists.
85
+ def [](name)
86
+ name = name.to_sym
87
+ raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
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,62 +1,64 @@
1
1
  require 'io/console'
2
2
 
3
- module Net; module SSH
3
+ module Net
4
+ module SSH
4
5
 
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)
25
- end
26
-
27
- def initialize(options = {}); end
28
-
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?
6
+ # Default prompt implementation, called for asking password from user.
7
+ # It will never be instantiated directly, but will instead be created for
8
+ # you automatically.
9
+ #
10
+ # A custom prompt objects can implement caching, or different UI. The prompt
11
+ # object should implemnted a start method, which should return something implementing
12
+ # ask and success. Net::SSH uses it like:
13
+ #
14
+ # prompter = options[:password_prompt].start({type:'password'})
15
+ # while !ok && max_retries < 3
16
+ # user = prompter.ask("user: ", {}, true)
17
+ # password = prompter.ask("password: ", {}, false)
18
+ # ok = send(user, password)
19
+ # prompter.sucess if ok
20
+ # end
21
+ #
22
+ class Prompt
23
+ # factory
24
+ def self.default(options = {})
25
+ @default ||= new(options)
26
+ end
27
+
28
+ def initialize(options = {}); end
29
+
30
+ # default prompt object implementation. More sophisticated implemenetations
31
+ # might implement caching.
32
+ class Prompter
33
+ def initialize(info)
34
+ if info[:type] == 'keyboard-interactive'
35
+ $stdout.puts(info[:name]) unless info[:name].empty?
36
+ $stdout.puts(info[:instruction]) unless info[:instruction].empty?
37
+ end
38
+ end
39
+
40
+ # ask input from user, a prompter might ask for multiple inputs
41
+ # (like user and password) in a single session.
42
+ def ask(prompt, echo=true)
43
+ $stdout.print(prompt)
44
+ $stdout.flush
45
+ ret = $stdin.noecho(&:gets).chomp
46
+ $stdout.print("\n")
47
+ ret
36
48
  end
49
+
50
+ # success method will be called when the password was accepted
51
+ # It's a good time to save password asked to a cache.
52
+ def success; end
37
53
  end
38
-
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
54
+
55
+ # start password session. Multiple questions might be asked multiple times
56
+ # on the returned object. Info hash tries to uniquely identify the password
57
+ # session, so caching implementations can save passwords properly.
58
+ def start(info)
59
+ Prompter.new(info)
47
60
  end
48
-
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
52
61
  end
53
62
 
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)
59
- end
60
63
  end
61
-
62
- end; end
64
+ 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
7
8
 
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
9
+ # An implementation of a command proxy. To use it, instantiate it,
10
+ # then pass the instantiated object via the :proxy key to
11
+ # Net::SSH.start:
12
+ #
13
+ # require 'net/ssh/proxy/command'
14
+ #
15
+ # proxy = Net::SSH::Proxy::Command.new('ssh relay nc %h %p')
16
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
17
+ # ...
18
+ # end
19
+ class Command
20
+ # The command line template
21
+ attr_reader :command_line_template
19
22
 
20
- # The command line template
21
- attr_reader :command_line_template
23
+ # The command line for the session
24
+ attr_reader :command_line
22
25
 
23
- # The command line for the session
24
- attr_reader :command_line
26
+ # Timeout in seconds in open, defaults to 60
27
+ attr_accessor :timeout
25
28
 
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)
29
+ # Create a new socket factory that tunnels via a command executed
30
+ # with the user's shell, which is composed from the given command
31
+ # template. In the command template, `%h' will be substituted by
32
+ # the host name to connect and `%p' by the port.
33
+ def initialize(command_line_template)
34
+ @command_line_template = command_line_template
35
+ @command_line = nil
36
+ @timeout = 60
75
37
  end
76
38
 
77
- def io.recv(size)
78
- sysread(size)
79
- end
80
- else
81
- def io.send(data, flag)
39
+ # Return a new socket connected to the given host and port via the
40
+ # proxy that was requested when the socket factory was instantiated.
41
+ def open(host, port, connection_options = nil)
42
+ command_line = @command_line_template.gsub(/%(.)/) {
43
+ case $1
44
+ when 'h'
45
+ host
46
+ when 'p'
47
+ port.to_s
48
+ when 'r'
49
+ remote_user = connection_options && connection_options[:remote_user]
50
+ if remote_user
51
+ remote_user
52
+ else
53
+ raise ArgumentError, "remote user name not available"
54
+ end
55
+ when '%'
56
+ '%'
57
+ else
58
+ raise ArgumentError, "unknown key: #{$1}"
59
+ end
60
+ }
82
61
  begin
83
- result = write_nonblock(data)
84
- rescue IO::WaitWritable, Errno::EINTR
85
- IO.select(nil, [self])
86
- retry
62
+ io = IO.popen(command_line, "r+")
63
+ begin
64
+ if result = IO.select([io], nil, [io], @timeout)
65
+ if result.last.any? || io.eof?
66
+ raise "command failed"
67
+ end
68
+ else
69
+ raise "command timed out"
70
+ end
71
+ rescue StandardError
72
+ close_on_error(io)
73
+ raise
74
+ end
75
+ rescue StandardError => e
76
+ raise ConnectError, "#{e}: #{command_line}"
87
77
  end
88
- result
89
- end
78
+ @command_line = command_line
79
+ if Gem.win_platform?
80
+ # read_nonblock and write_nonblock are not available on Windows
81
+ # pipe. Use sysread and syswrite as a replacement works.
82
+ def io.send(data, flag)
83
+ syswrite(data)
84
+ end
90
85
 
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"
86
+ def io.recv(size)
87
+ sysread(size)
88
+ end
89
+ else
90
+ def io.send(data, flag)
91
+ begin
92
+ result = write_nonblock(data)
93
+ rescue IO::WaitWritable, Errno::EINTR
94
+ IO.select(nil, [self])
95
+ retry
96
+ end
97
+ result
98
+ end
99
+
100
+ def io.recv(size)
101
+ begin
102
+ result = read_nonblock(size)
103
+ rescue IO::WaitReadable, Errno::EINTR
104
+ timeout_in_seconds = 20
105
+ if IO.select([self], nil, [self], timeout_in_seconds) == nil
106
+ raise "Unexpected spurious read wakeup"
107
+ end
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