net-ssh 5.0.2 → 7.0.1

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 (122) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  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 +13 -0
  8. data/.gitignore +3 -0
  9. data/.rubocop.yml +19 -2
  10. data/.rubocop_todo.yml +623 -511
  11. data/CHANGES.txt +76 -0
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +2 -0
  15. data/Gemfile.noed25519 +2 -0
  16. data/Manifest +0 -1
  17. data/README.md +293 -0
  18. data/Rakefile +6 -2
  19. data/appveyor.yml +4 -2
  20. data/docker-compose.yml +23 -0
  21. data/lib/net/ssh/authentication/agent.rb +36 -14
  22. data/lib/net/ssh/authentication/certificate.rb +19 -7
  23. data/lib/net/ssh/authentication/constants.rb +0 -1
  24. data/lib/net/ssh/authentication/ed25519.rb +83 -50
  25. data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
  26. data/lib/net/ssh/authentication/key_manager.rb +74 -33
  27. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  28. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  29. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +5 -3
  30. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  31. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  32. data/lib/net/ssh/authentication/methods/publickey.rb +58 -16
  33. data/lib/net/ssh/authentication/pageant.rb +97 -97
  34. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
  35. data/lib/net/ssh/authentication/session.rb +27 -23
  36. data/lib/net/ssh/buffer.rb +91 -40
  37. data/lib/net/ssh/buffered_io.rb +24 -26
  38. data/lib/net/ssh/config.rb +99 -53
  39. data/lib/net/ssh/connection/channel.rb +101 -87
  40. data/lib/net/ssh/connection/constants.rb +0 -4
  41. data/lib/net/ssh/connection/event_loop.rb +30 -25
  42. data/lib/net/ssh/connection/keepalive.rb +12 -12
  43. data/lib/net/ssh/connection/session.rb +115 -111
  44. data/lib/net/ssh/connection/term.rb +56 -58
  45. data/lib/net/ssh/errors.rb +12 -12
  46. data/lib/net/ssh/key_factory.rb +108 -22
  47. data/lib/net/ssh/known_hosts.rb +120 -36
  48. data/lib/net/ssh/loggable.rb +10 -11
  49. data/lib/net/ssh/packet.rb +1 -1
  50. data/lib/net/ssh/prompt.rb +9 -11
  51. data/lib/net/ssh/proxy/command.rb +1 -2
  52. data/lib/net/ssh/proxy/errors.rb +2 -4
  53. data/lib/net/ssh/proxy/http.rb +18 -20
  54. data/lib/net/ssh/proxy/https.rb +8 -10
  55. data/lib/net/ssh/proxy/jump.rb +8 -10
  56. data/lib/net/ssh/proxy/socks4.rb +2 -4
  57. data/lib/net/ssh/proxy/socks5.rb +3 -6
  58. data/lib/net/ssh/service/forward.rb +9 -8
  59. data/lib/net/ssh/test/channel.rb +24 -26
  60. data/lib/net/ssh/test/extensions.rb +37 -35
  61. data/lib/net/ssh/test/kex.rb +6 -8
  62. data/lib/net/ssh/test/local_packet.rb +0 -2
  63. data/lib/net/ssh/test/packet.rb +3 -3
  64. data/lib/net/ssh/test/remote_packet.rb +6 -8
  65. data/lib/net/ssh/test/script.rb +25 -27
  66. data/lib/net/ssh/test/socket.rb +12 -15
  67. data/lib/net/ssh/test.rb +12 -12
  68. data/lib/net/ssh/transport/algorithms.rb +177 -118
  69. data/lib/net/ssh/transport/cipher_factory.rb +34 -50
  70. data/lib/net/ssh/transport/constants.rb +13 -9
  71. data/lib/net/ssh/transport/ctr.rb +8 -14
  72. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  73. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  80. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  81. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  82. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  83. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  84. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  85. data/lib/net/ssh/transport/hmac.rb +13 -11
  86. data/lib/net/ssh/transport/identity_cipher.rb +11 -13
  87. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  88. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  89. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  90. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
  92. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  96. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
  97. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
  99. data/lib/net/ssh/transport/kex.rb +15 -10
  100. data/lib/net/ssh/transport/key_expander.rb +7 -8
  101. data/lib/net/ssh/transport/openssl.rb +149 -111
  102. data/lib/net/ssh/transport/packet_stream.rb +53 -22
  103. data/lib/net/ssh/transport/server_version.rb +17 -16
  104. data/lib/net/ssh/transport/session.rb +35 -11
  105. data/lib/net/ssh/transport/state.rb +44 -44
  106. data/lib/net/ssh/verifiers/accept_new.rb +7 -2
  107. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  108. data/lib/net/ssh/verifiers/always.rb +10 -4
  109. data/lib/net/ssh/verifiers/never.rb +4 -2
  110. data/lib/net/ssh/version.rb +2 -2
  111. data/lib/net/ssh.rb +17 -9
  112. data/net-ssh-public_cert.pem +18 -19
  113. data/net-ssh.gemspec +9 -7
  114. data/support/ssh_tunnel_bug.rb +3 -3
  115. data.tar.gz.sig +0 -0
  116. metadata +65 -41
  117. metadata.gz.sig +0 -0
  118. data/.travis.yml +0 -52
  119. data/Gemfile.noed25519.lock +0 -41
  120. data/README.rdoc +0 -169
  121. data/lib/net/ssh/ruby_compat.rb +0 -13
  122. data/support/arcfour_check.rb +0 -20
@@ -1,10 +1,69 @@
1
1
  require 'strscan'
2
2
  require 'openssl'
3
3
  require 'base64'
4
+ require 'delegate'
4
5
  require 'net/ssh/buffer'
6
+ require 'net/ssh/authentication/ed25519_loader'
5
7
 
6
8
  module Net
7
9
  module SSH
10
+ module HostKeyEntries
11
+ # regular public key entry
12
+ class PubKey < Delegator
13
+ def initialize(key, comment: nil) # rubocop:disable Lint/MissingSuper
14
+ @key = key
15
+ @comment = comment
16
+ end
17
+
18
+ def ssh_type
19
+ @key.ssh_type
20
+ end
21
+
22
+ def ssh_types
23
+ [ssh_type]
24
+ end
25
+
26
+ def to_blob
27
+ @key.to_blob
28
+ end
29
+
30
+ def __getobj__
31
+ Kernel.warn("Calling Net::SSH::Buffer methods on HostKeyEntries PubKey is deprecated")
32
+ @key
33
+ end
34
+
35
+ def matches_key?(server_key)
36
+ @key.ssh_type == server_key.ssh_type && @key.to_blob == server_key.to_blob
37
+ end
38
+ end
39
+
40
+ # @cert-authority entry
41
+ class CertAuthority
42
+ def ssh_types
43
+ %w[
44
+ ecdsa-sha2-nistp256-cert-v01@openssh.com
45
+ ecdsa-sha2-nistp384-cert-v01@openssh.com
46
+ ecdsa-sha2-nistp521-cert-v01@openssh.com
47
+ ssh-ed25519-cert-v01@openssh.com
48
+ ssh-rsa-cert-v01@openssh.com
49
+ ssh-rsa-cert-v00@openssh.com
50
+ ]
51
+ end
52
+
53
+ def initialize(key, comment: nil)
54
+ @key = key
55
+ @comment = comment
56
+ end
57
+
58
+ def matches_key?(server_key)
59
+ if ssh_types.include?(server_key.ssh_type)
60
+ server_key.signature_valid? && (server_key.signature_key.to_blob == @key.to_blob)
61
+ else
62
+ false
63
+ end
64
+ end
65
+ end
66
+ end
8
67
 
9
68
  # Represents the result of a search in known hosts
10
69
  # see search_for
@@ -40,26 +99,24 @@ module Net
40
99
  # This is used internally by Net::SSH, and will never need to be used directly
41
100
  # by consumers of the library.
42
101
  class KnownHosts
43
- if defined?(OpenSSL::PKey::EC)
44
- SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
45
- ecdsa-sha2-nistp256
46
- ecdsa-sha2-nistp384
47
- ecdsa-sha2-nistp521]
48
- else
49
- SUPPORTED_TYPE = %w[ssh-rsa ssh-dss]
50
- end
102
+ SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
103
+ ecdsa-sha2-nistp256
104
+ ecdsa-sha2-nistp384
105
+ ecdsa-sha2-nistp521]
106
+
107
+ SUPPORTED_TYPE.push('ssh-ed25519') if Net::SSH::Authentication::ED25519Loader::LOADED
51
108
 
52
- class <<self
109
+ class << self
53
110
  # Searches all known host files (see KnownHosts.hostfiles) for all keys
54
111
  # of the given host. Returns an enumerable of keys found.
55
- def search_for(host, options={})
56
- HostKeys.new(search_in(hostfiles(options), host), host, self, options)
112
+ def search_for(host, options = {})
113
+ HostKeys.new(search_in(hostfiles(options), host, options), host, self, options)
57
114
  end
58
115
 
59
116
  # Search for all known keys for the given host, in every file given in
60
117
  # the +files+ array. Returns the list of keys.
61
- def search_in(files, host)
62
- files.flat_map { |file| KnownHosts.new(file).keys_for(host) }
118
+ def search_in(files, host, options = {})
119
+ files.flat_map { |file| KnownHosts.new(file).keys_for(host, options) }
63
120
  end
64
121
 
65
122
  # Looks in the given +options+ hash for the :user_known_hosts_file and
@@ -71,12 +128,14 @@ module Net
71
128
  #
72
129
  # If you only want the user known host files, you can pass :user as
73
130
  # the second option.
74
- def hostfiles(options, which=:all)
131
+ def hostfiles(options, which = :all)
75
132
  files = []
76
133
 
77
134
  files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
78
135
 
79
- files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2]) if which == :all || which == :global
136
+ if which == :all || which == :global
137
+ files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2])
138
+ end
80
139
 
81
140
  return files
82
141
  end
@@ -84,14 +143,12 @@ module Net
84
143
  # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
85
144
  # add an entry for the given host and key to the first file it is able
86
145
  # to.
87
- def add(host, key, options={})
146
+ def add(host, key, options = {})
88
147
  hostfiles(options, :user).each do |file|
89
- begin
90
- KnownHosts.new(file).add(host, key)
91
- return
92
- rescue SystemCallError
93
- # try the next hostfile
94
- end
148
+ KnownHosts.new(file).add(host, key)
149
+ return
150
+ rescue SystemCallError
151
+ # try the next hostfile
95
152
  end
96
153
  end
97
154
  end
@@ -119,39 +176,66 @@ module Net
119
176
  # "[net.ssh.test]:5555"
120
177
  # "[1,2,3,4]:5555"
121
178
  # "[net.ssh.test]:5555,[1.2.3.4]:5555
122
- def keys_for(host)
179
+ def keys_for(host, options = {})
123
180
  keys = []
124
181
  return keys unless File.readable?(source)
125
182
 
126
183
  entries = host.split(/,/)
184
+ host_name = entries[0]
185
+ host_ip = entries[1]
127
186
 
128
187
  File.open(source) do |file|
129
- scanner = StringScanner.new("")
130
188
  file.each_line do |line|
131
- scanner.string = line
189
+ if line.start_with?('@')
190
+ marker, hosts, type, key_content, comment = line.split(' ')
191
+ else
192
+ marker = nil
193
+ hosts, type, key_content, comment = line.split(' ')
194
+ end
195
+
196
+ # Skip empty line or one that is commented
197
+ next if hosts.nil? || hosts.start_with?('#')
132
198
 
133
- scanner.skip(/\s*/)
134
- next if scanner.match?(/$|#/)
199
+ hostlist = hosts.split(',')
135
200
 
136
- hostlist = scanner.scan(/\S+/).split(/,/)
137
- found = entries.all? { |entry| hostlist.include?(entry) } ||
138
- known_host_hash?(hostlist, entries)
201
+ next unless SUPPORTED_TYPE.include?(type)
202
+
203
+ found = hostlist.any? { |pattern| match(host_name, pattern) } || known_host_hash?(hostlist, entries)
139
204
  next unless found
140
205
 
141
- scanner.skip(/\s*/)
142
- type = scanner.scan(/\S+/)
206
+ found = hostlist.include?(host_ip) if options[:check_host_ip] && entries.size > 1 && hostlist.size > 1
207
+ next unless found
143
208
 
144
- next unless SUPPORTED_TYPE.include?(type)
209
+ blob = key_content.unpack("m*").first
210
+ raw_key = Net::SSH::Buffer.new(blob).read_key
145
211
 
146
- scanner.skip(/\s*/)
147
- blob = scanner.rest.unpack("m*").first
148
- keys << Net::SSH::Buffer.new(blob).read_key
212
+ keys <<
213
+ if marker == "@cert-authority"
214
+ HostKeyEntries::CertAuthority.new(raw_key, comment: comment)
215
+ else
216
+ HostKeyEntries::PubKey.new(raw_key, comment: comment)
217
+ end
149
218
  end
150
219
  end
151
220
 
152
221
  keys
153
222
  end
154
223
 
224
+ def match(host, pattern)
225
+ if pattern.include?('*') || pattern.include?('?')
226
+ # see man 8 sshd for pattern details
227
+ pattern_regexp = pattern.split('*', -1).map do |x|
228
+ x.split('?', -1).map do |y|
229
+ Regexp.escape(y)
230
+ end.join('.')
231
+ end.join('.*')
232
+
233
+ host =~ Regexp.new("\\A#{pattern_regexp}\\z")
234
+ else
235
+ host == pattern
236
+ end
237
+ end
238
+
155
239
  # Indicates whether one of the entries matches an hostname that has been
156
240
  # stored as a HMAC-SHA1 hash in the known hosts.
157
241
  def known_host_hash?(hostlist, entries)
@@ -1,6 +1,5 @@
1
- module Net
1
+ module Net
2
2
  module SSH
3
-
4
3
  # A simple module to make logging easier to deal with. It assumes that the
5
4
  # logger instance (if not nil) quacks like a Logger object (in Ruby's
6
5
  # standard library). Although used primarily internally by Net::SSH, it
@@ -19,45 +18,45 @@ module Net
19
18
  # The logger instance that will be used to log messages. If nil, nothing
20
19
  # will be logged.
21
20
  attr_accessor :logger
22
-
21
+
23
22
  # Displays the result of yielding if the log level is Logger::DEBUG or
24
23
  # greater.
25
24
  def debug
26
25
  logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
27
26
  end
28
-
27
+
29
28
  # Displays the result of yielding if the log level is Logger::INFO or
30
29
  # greater.
31
30
  def info
32
31
  logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
33
32
  end
34
-
33
+
35
34
  # Displays the result of yielding if the log level is Logger::WARN or
36
35
  # greater. (Called lwarn to avoid shadowing with Kernel#warn.)
37
36
  def lwarn
38
37
  logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
39
38
  end
40
-
39
+
41
40
  # Displays the result of yielding if the log level is Logger:ERROR or
42
41
  # greater.
43
42
  def error
44
43
  logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
45
44
  end
46
-
45
+
47
46
  # Displays the result of yielding if the log level is Logger::FATAL or
48
47
  # greater.
49
48
  def fatal
50
49
  logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
51
50
  end
52
-
51
+
53
52
  private
54
-
53
+
55
54
  # Sets the "facility" value, used for reporting where a log message
56
55
  # originates. It defaults to the name of class with the object_id
57
56
  # appended.
58
57
  def facility
59
- @facility ||= self.class.name.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
58
+ @facility ||= self.class.to_s.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
60
59
  end
61
60
  end
62
61
  end
63
- end
62
+ end
@@ -5,7 +5,6 @@ require 'net/ssh/connection/constants'
5
5
 
6
6
  module Net
7
7
  module SSH
8
-
9
8
  # A specialization of Buffer that knows the format of certain common
10
9
  # packet types. It auto-parses those packet types, and allows them to
11
10
  # be accessed via the #[] accessor.
@@ -85,6 +84,7 @@ module Net
85
84
  def [](name)
86
85
  name = name.to_sym
87
86
  raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
87
+
88
88
  @named_elements[name]
89
89
  end
90
90
 
@@ -1,8 +1,7 @@
1
1
  require 'io/console'
2
2
 
3
- module Net
3
+ module Net
4
4
  module SSH
5
-
6
5
  # Default prompt implementation, called for asking password from user.
7
6
  # It will never be instantiated directly, but will instead be created for
8
7
  # you automatically.
@@ -13,8 +12,8 @@ module Net
13
12
  #
14
13
  # prompter = options[:password_prompt].start({type:'password'})
15
14
  # while !ok && max_retries < 3
16
- # user = prompter.ask("user: ", {}, true)
17
- # password = prompter.ask("password: ", {}, false)
15
+ # user = prompter.ask("user: ", true)
16
+ # password = prompter.ask("password: ", false)
18
17
  # ok = send(user, password)
19
18
  # prompter.sucess if ok
20
19
  # end
@@ -24,9 +23,9 @@ module Net
24
23
  def self.default(options = {})
25
24
  @default ||= new(options)
26
25
  end
27
-
26
+
28
27
  def initialize(options = {}); end
29
-
28
+
30
29
  # default prompt object implementation. More sophisticated implemenetations
31
30
  # might implement caching.
32
31
  class Prompter
@@ -36,22 +35,22 @@ module Net
36
35
  $stdout.puts(info[:instruction]) unless info[:instruction].empty?
37
36
  end
38
37
  end
39
-
38
+
40
39
  # ask input from user, a prompter might ask for multiple inputs
41
40
  # (like user and password) in a single session.
42
- def ask(prompt, echo=true)
41
+ def ask(prompt, echo = true)
43
42
  $stdout.print(prompt)
44
43
  $stdout.flush
45
44
  ret = $stdin.noecho(&:gets).chomp
46
45
  $stdout.print("\n")
47
46
  ret
48
47
  end
49
-
48
+
50
49
  # success method will be called when the password was accepted
51
50
  # It's a good time to save password asked to a cache.
52
51
  def success; end
53
52
  end
54
-
53
+
55
54
  # start password session. Multiple questions might be asked multiple times
56
55
  # on the returned object. Info hash tries to uniquely identify the password
57
56
  # session, so caching implementations can save passwords properly.
@@ -59,6 +58,5 @@ module Net
59
58
  Prompter.new(info)
60
59
  end
61
60
  end
62
-
63
61
  end
64
62
  end
@@ -1,12 +1,10 @@
1
1
  require 'socket'
2
2
  require 'rubygems'
3
3
  require 'net/ssh/proxy/errors'
4
- require 'net/ssh/ruby_compat'
5
4
 
6
5
  module Net
7
6
  module SSH
8
7
  module Proxy
9
-
10
8
  # An implementation of a command proxy. To use it, instantiate it,
11
9
  # then pass the instantiated object via the :proxy key to
12
10
  # Net::SSH.start:
@@ -106,6 +104,7 @@ module Net
106
104
  if IO.select([self], nil, [self], timeout_in_seconds) == nil
107
105
  raise "Unexpected spurious read wakeup"
108
106
  end
107
+
109
108
  retry
110
109
  end
111
110
  result
@@ -1,9 +1,8 @@
1
1
  require 'net/ssh/errors'
2
2
 
3
- module Net
4
- module SSH
3
+ module Net
4
+ module SSH
5
5
  module Proxy
6
-
7
6
  # A general exception class for all Proxy errors.
8
7
  class Error < Net::SSH::Exception; end
9
8
 
@@ -12,7 +11,6 @@ module Net
12
11
 
13
12
  # Used when the server doesn't recognize the user's credentials.
14
13
  class UnauthorizedError < Error; end
15
-
16
14
  end
17
15
  end
18
16
  end
@@ -1,10 +1,9 @@
1
1
  require 'socket'
2
2
  require 'net/ssh/proxy/errors'
3
3
 
4
- module Net
5
- module SSH
4
+ module Net
5
+ module SSH
6
6
  module Proxy
7
-
8
7
  # An implementation of an HTTP proxy. To use it, instantiate it, then
9
8
  # pass the instantiated object via the :proxy key to Net::SSH.start:
10
9
  #
@@ -26,14 +25,14 @@ module Net
26
25
  class HTTP
27
26
  # The hostname or IP address of the HTTP proxy.
28
27
  attr_reader :proxy_host
29
-
28
+
30
29
  # The port number of the proxy.
31
30
  attr_reader :proxy_port
32
-
31
+
33
32
  # The map of additional options that were given to the object at
34
33
  # initialization.
35
34
  attr_reader :options
36
-
35
+
37
36
  # Create a new socket factory that tunnels via the given host and
38
37
  # port. The +options+ parameter is a hash of additional settings that
39
38
  # can be used to tweak this proxy connection. Specifically, the following
@@ -41,52 +40,52 @@ module Net
41
40
  #
42
41
  # * :user => the user name to use when authenticating to the proxy
43
42
  # * :password => the password to use when authenticating
44
- def initialize(proxy_host, proxy_port=80, options={})
43
+ def initialize(proxy_host, proxy_port = 80, options = {})
45
44
  @proxy_host = proxy_host
46
45
  @proxy_port = proxy_port
47
46
  @options = options
48
47
  end
49
-
48
+
50
49
  # Return a new socket connected to the given host and port via the
51
50
  # proxy that was requested when the socket factory was instantiated.
52
51
  def open(host, port, connection_options)
53
52
  socket = establish_connection(connection_options[:timeout])
54
53
  socket.write "CONNECT #{host}:#{port} HTTP/1.1\r\n"
55
54
  socket.write "Host: #{host}:#{port}\r\n"
56
-
55
+
57
56
  if options[:user]
58
57
  credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "")
59
58
  socket.write "Proxy-Authorization: Basic #{credentials}\r\n"
60
59
  end
61
-
60
+
62
61
  socket.write "\r\n"
63
-
62
+
64
63
  resp = parse_response(socket)
65
-
64
+
66
65
  return socket if resp[:code] == 200
67
-
66
+
68
67
  socket.close
69
68
  raise ConnectError, resp.inspect
70
69
  end
71
-
70
+
72
71
  protected
73
-
72
+
74
73
  def establish_connection(connect_timeout)
75
74
  Socket.tcp(proxy_host, proxy_port, nil, nil,
76
75
  connect_timeout: connect_timeout)
77
76
  end
78
-
77
+
79
78
  def parse_response(socket)
80
79
  version, code, reason = socket.gets.chomp.split(/ /, 3)
81
80
  headers = {}
82
-
81
+
83
82
  while (line = socket.gets) && (line.chomp! != "")
84
83
  name, value = line.split(/:/, 2)
85
84
  headers[name.strip] = value.strip
86
85
  end
87
-
86
+
88
87
  body = socket.read(headers["Content-Length"].to_i) if headers["Content-Length"]
89
-
88
+
90
89
  return { version: version,
91
90
  code: code.to_i,
92
91
  reason: reason,
@@ -94,7 +93,6 @@ module Net
94
93
  body: body }
95
94
  end
96
95
  end
97
-
98
96
  end
99
97
  end
100
98
  end
@@ -3,10 +3,9 @@ require 'openssl'
3
3
  require 'net/ssh/proxy/errors'
4
4
  require 'net/ssh/proxy/http'
5
5
 
6
- module Net
7
- module SSH
6
+ module Net
7
+ module SSH
8
8
  module Proxy
9
-
10
9
  # A specialization of the HTTP proxy which encrypts the whole connection
11
10
  # using OpenSSL. This has the advantage that proxy authentication
12
11
  # information is not sent in plaintext.
@@ -17,27 +16,27 @@ module Net
17
16
  # taken by Net::SSH::Proxy::HTTP it supports:
18
17
  #
19
18
  # * :ssl_context => the SSL configuration to use for the connection
20
- def initialize(proxy_host, proxy_port=80, options={})
19
+ def initialize(proxy_host, proxy_port = 80, options = {})
21
20
  @ssl_context = options.delete(:ssl_context) ||
22
21
  OpenSSL::SSL::SSLContext.new
23
22
  super(proxy_host, proxy_port, options)
24
23
  end
25
-
24
+
26
25
  protected
27
-
26
+
28
27
  # Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket
29
28
  # for all intents and purposes of Net::SSH::BufferedIo
30
29
  module SSLSocketCompatibility
31
- def self.extended(object) #:nodoc:
30
+ def self.extended(object) # :nodoc:
32
31
  object.define_singleton_method(:recv, object.method(:sysread))
33
32
  object.sync_close = true
34
33
  end
35
-
34
+
36
35
  def send(data, _opts)
37
36
  syswrite(data)
38
37
  end
39
38
  end
40
-
39
+
41
40
  def establish_connection(connect_timeout)
42
41
  plain_socket = super(connect_timeout)
43
42
  OpenSSL::SSL::SSLSocket.new(plain_socket, @ssl_context).tap do |socket|
@@ -46,7 +45,6 @@ module Net
46
45
  end
47
46
  end
48
47
  end
49
-
50
48
  end
51
49
  end
52
50
  end
@@ -1,10 +1,9 @@
1
1
  require 'uri'
2
2
  require 'net/ssh/proxy/command'
3
3
 
4
- module Net
5
- module SSH
4
+ module Net
5
+ module SSH
6
6
  module Proxy
7
-
8
7
  # An implementation of a jump proxy. To use it, instantiate it,
9
8
  # then pass the instantiated object via the :proxy key to
10
9
  # Net::SSH.start:
@@ -18,39 +17,38 @@ module Net
18
17
  class Jump < Command
19
18
  # The jump proxies
20
19
  attr_reader :jump_proxies
21
-
20
+
22
21
  # Create a new socket factory that tunnels via multiple jump proxes as
23
22
  # [user@]host[:port].
24
23
  def initialize(jump_proxies)
25
24
  @jump_proxies = jump_proxies
26
25
  end
27
-
26
+
28
27
  # Return a new socket connected to the given host and port via the jump
29
28
  # proxy that was requested when the socket factory was instantiated.
30
29
  def open(host, port, connection_options = nil)
31
30
  build_proxy_command_equivalent(connection_options)
32
31
  super
33
32
  end
34
-
33
+
35
34
  # We cannot build the ProxyCommand template until we know if the :config
36
35
  # option was specified during `Net::SSH.start`.
37
36
  def build_proxy_command_equivalent(connection_options = nil)
38
37
  first_jump, extra_jumps = jump_proxies.split(",", 2)
39
38
  config = connection_options && connection_options[:config]
40
39
  uri = URI.parse("ssh://#{first_jump}")
41
-
42
- template = "ssh"
40
+
41
+ template = "ssh".dup
43
42
  template << " -l #{uri.user}" if uri.user
44
43
  template << " -p #{uri.port}" if uri.port
45
44
  template << " -J #{extra_jumps}" if extra_jumps
46
45
  template << " -F #{config}" if config != true && config
47
46
  template << " -W %h:%p "
48
47
  template << uri.host
49
-
48
+
50
49
  @command_line_template = template
51
50
  end
52
51
  end
53
-
54
52
  end
55
53
  end
56
54
  end
@@ -6,7 +6,6 @@ require 'net/ssh/proxy/errors'
6
6
  module Net
7
7
  module SSH
8
8
  module Proxy
9
-
10
9
  # An implementation of a SOCKS4 proxy. To use it, instantiate it, then
11
10
  # pass the instantiated object via the :proxy key to Net::SSH.start:
12
11
  #
@@ -38,7 +37,7 @@ module Net
38
37
  # Create a new proxy connection to the given proxy host and port.
39
38
  # Optionally, a :user key may be given to identify the username
40
39
  # with which to authenticate.
41
- def initialize(proxy_host, proxy_port=1080, options={})
40
+ def initialize(proxy_host, proxy_port = 1080, options = {})
42
41
  @proxy_host = proxy_host
43
42
  @proxy_port = proxy_port
44
43
  @options = options
@@ -50,7 +49,7 @@ module Net
50
49
  socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
51
50
  connect_timeout: connection_options[:timeout])
52
51
  ip_addr = IPAddr.new(Resolv.getaddress(host))
53
-
52
+
54
53
  packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*")
55
54
  socket.send packet, 0
56
55
 
@@ -63,7 +62,6 @@ module Net
63
62
  return socket
64
63
  end
65
64
  end
66
-
67
65
  end
68
66
  end
69
67
  end