net-ssh 5.0.0.beta1 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -5,6 +5,7 @@ class String
5
5
  def getbyte(index)
6
6
  self[index]
7
7
  end
8
+
8
9
  def setbyte(index, c)
9
10
  self[index] = c
10
11
  end
@@ -1,423 +1,425 @@
1
- # -*- coding: utf-8 -*-
2
1
  require 'net/ssh/loggable'
3
2
 
4
- module Net; module SSH; module Service
5
-
6
- # This class implements various port forwarding services for use by
7
- # Net::SSH clients. The Forward class should never need to be instantiated
8
- # directly; instead, it should be accessed via the singleton instance
9
- # returned by Connection::Session#forward:
10
- #
11
- # ssh.forward.local(1234, "www.capify.org", 80)
12
- class Forward
13
- include Loggable
14
-
15
- # The underlying connection service instance that the port-forwarding
16
- # services employ.
17
- attr_reader :session
18
-
19
- # A simple class for representing a requested remote forwarded port.
20
- Remote = Struct.new(:host, :port) #:nodoc:
21
-
22
- # Instantiates a new Forward service instance atop the given connection
23
- # service session. This will register new channel open handlers to handle
24
- # the specialized channels that the SSH port forwarding protocols employ.
25
- def initialize(session)
26
- @session = session
27
- self.logger = session.logger
28
- @remote_forwarded_ports = {}
29
- @local_forwarded_ports = {}
30
- @agent_forwarded = false
31
- @local_forwarded_sockets = {}
32
-
33
- session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip))
34
- session.on_open_channel('auth-agent', &method(:auth_agent_channel))
35
- session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel))
36
- end
37
-
38
- # Starts listening for connections on the local host, and forwards them
39
- # to the specified remote host/port via the SSH connection. This method
40
- # accepts either three or four arguments. When four arguments are given,
41
- # they are:
42
- #
43
- # * the local address to bind to
44
- # * the local port to listen on
45
- # * the remote host to forward connections to
46
- # * the port on the remote host to connect to
47
- #
48
- # If three arguments are given, it is as if the local bind address is
49
- # "127.0.0.1", and the rest are applied as above.
50
- #
51
- # To request an ephemeral port on the remote server, provide 0 (zero) for
52
- # the port number. In all cases, this method will return the port that
53
- # has been assigned.
54
- #
55
- # ssh.forward.local(1234, "www.capify.org", 80)
56
- # assigned_port = ssh.forward.local("0.0.0.0", 0, "www.capify.org", 80)
57
- def local(*args)
58
- if args.length < 3 || args.length > 4
59
- raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}"
60
- end
3
+ module Net
4
+ module SSH
5
+ module Service
6
+
7
+ # This class implements various port forwarding services for use by
8
+ # Net::SSH clients. The Forward class should never need to be instantiated
9
+ # directly; instead, it should be accessed via the singleton instance
10
+ # returned by Connection::Session#forward:
11
+ #
12
+ # ssh.forward.local(1234, "www.capify.org", 80)
13
+ class Forward
14
+ include Loggable
15
+
16
+ # The underlying connection service instance that the port-forwarding
17
+ # services employ.
18
+ attr_reader :session
19
+
20
+ # A simple class for representing a requested remote forwarded port.
21
+ Remote = Struct.new(:host, :port) #:nodoc:
22
+
23
+ # Instantiates a new Forward service instance atop the given connection
24
+ # service session. This will register new channel open handlers to handle
25
+ # the specialized channels that the SSH port forwarding protocols employ.
26
+ def initialize(session)
27
+ @session = session
28
+ self.logger = session.logger
29
+ @remote_forwarded_ports = {}
30
+ @local_forwarded_ports = {}
31
+ @agent_forwarded = false
32
+ @local_forwarded_sockets = {}
33
+
34
+ session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip))
35
+ session.on_open_channel('auth-agent', &method(:auth_agent_channel))
36
+ session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel))
37
+ end
61
38
 
62
- local_port_type = :long
39
+ # Starts listening for connections on the local host, and forwards them
40
+ # to the specified remote host/port via the SSH connection. This method
41
+ # accepts either three or four arguments. When four arguments are given,
42
+ # they are:
43
+ #
44
+ # * the local address to bind to
45
+ # * the local port to listen on
46
+ # * the remote host to forward connections to
47
+ # * the port on the remote host to connect to
48
+ #
49
+ # If three arguments are given, it is as if the local bind address is
50
+ # "127.0.0.1", and the rest are applied as above.
51
+ #
52
+ # To request an ephemeral port on the remote server, provide 0 (zero) for
53
+ # the port number. In all cases, this method will return the port that
54
+ # has been assigned.
55
+ #
56
+ # ssh.forward.local(1234, "www.capify.org", 80)
57
+ # assigned_port = ssh.forward.local("0.0.0.0", 0, "www.capify.org", 80)
58
+ def local(*args)
59
+ if args.length < 3 || args.length > 4
60
+ raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}"
61
+ end
63
62
 
64
- socket = begin
65
- if defined?(UNIXServer) and args.first.class == UNIXServer
66
- local_port_type = :string
67
- args.shift
68
- else
69
- bind_address = "127.0.0.1"
70
- bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/
71
- local_port = args.shift.to_i
72
63
  local_port_type = :long
73
- TCPServer.new(bind_address, local_port)
74
- end
75
- end
76
64
 
77
- local_port = socket.addr[1] if local_port == 0 # ephemeral port was requested
78
- remote_host = args.shift
79
- remote_port = args.shift.to_i
65
+ socket = begin
66
+ if defined?(UNIXServer) and args.first.class == UNIXServer
67
+ local_port_type = :string
68
+ args.shift
69
+ else
70
+ bind_address = "127.0.0.1"
71
+ bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/
72
+ local_port = args.shift.to_i
73
+ local_port_type = :long
74
+ TCPServer.new(bind_address, local_port)
75
+ end
76
+ end
80
77
 
81
- @local_forwarded_ports[[local_port, bind_address]] = socket
78
+ local_port = socket.addr[1] if local_port == 0 # ephemeral port was requested
79
+ remote_host = args.shift
80
+ remote_port = args.shift.to_i
82
81
 
83
- session.listen_to(socket) do |server|
84
- client = server.accept
85
- debug { "received connection on #{socket}" }
82
+ @local_forwarded_ports[[local_port, bind_address]] = socket
86
83
 
87
- channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, local_port_type, local_port) do |achannel|
88
- achannel.info { "direct channel established" }
89
- end
84
+ session.listen_to(socket) do |server|
85
+ client = server.accept
86
+ debug { "received connection on #{socket}" }
90
87
 
91
- prepare_client(client, channel, :local)
88
+ channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, local_port_type, local_port) do |achannel|
89
+ achannel.info { "direct channel established" }
90
+ end
92
91
 
93
- channel.on_open_failed do |ch, code, description|
94
- channel.error { "could not establish direct channel: #{description} (#{code})" }
95
- session.stop_listening_to(channel[:socket])
96
- channel[:socket].close
97
- end
98
- end
92
+ prepare_client(client, channel, :local)
99
93
 
100
- local_port
101
- end
94
+ channel.on_open_failed do |ch, code, description|
95
+ channel.error { "could not establish direct channel: #{description} (#{code})" }
96
+ session.stop_listening_to(channel[:socket])
97
+ channel[:socket].close
98
+ end
99
+ end
102
100
 
103
- # Terminates an active local forwarded port.
104
- #
105
- # ssh.forward.cancel_local(1234)
106
- # ssh.forward.cancel_local(1234, "0.0.0.0")
107
- def cancel_local(port, bind_address="127.0.0.1")
108
- socket = @local_forwarded_ports.delete([port, bind_address])
109
- socket.shutdown rescue nil
110
- socket.close rescue nil
111
- session.stop_listening_to(socket)
112
- end
101
+ local_port
102
+ end
113
103
 
114
- # Returns a list of all active locally forwarded ports. The returned value
115
- # is an array of arrays, where each element is a two-element tuple
116
- # consisting of the local port and bind address corresponding to the
117
- # forwarding port.
118
- def active_locals
119
- @local_forwarded_ports.keys
120
- end
104
+ # Terminates an active local forwarded port.
105
+ #
106
+ # ssh.forward.cancel_local(1234)
107
+ # ssh.forward.cancel_local(1234, "0.0.0.0")
108
+ def cancel_local(port, bind_address="127.0.0.1")
109
+ socket = @local_forwarded_ports.delete([port, bind_address])
110
+ socket.shutdown rescue nil
111
+ socket.close rescue nil
112
+ session.stop_listening_to(socket)
113
+ end
121
114
 
122
- # Starts listening for connections on the local host, and forwards them
123
- # to the specified remote socket via the SSH connection. This will
124
- # (re)create the local socket file. The remote server needs to have the
125
- # socket file already available.
126
- #
127
- # ssh.forward.local_socket('/tmp/local.sock', '/tmp/remote.sock')
128
- def local_socket(local_socket_path, remote_socket_path)
129
- File.delete(local_socket_path) if File.exist?(local_socket_path)
130
- socket = Socket.unix_server_socket(local_socket_path)
131
-
132
- @local_forwarded_sockets[local_socket_path] = socket
133
-
134
- session.listen_to(socket) do |server|
135
- client = server.accept[0]
136
- debug { "received connection on #{socket}" }
137
-
138
- channel = session.open_channel("direct-streamlocal@openssh.com",
139
- :string, remote_socket_path,
140
- :string, nil,
141
- :long, 0) do |achannel|
142
- achannel.info { "direct channel established" }
115
+ # Returns a list of all active locally forwarded ports. The returned value
116
+ # is an array of arrays, where each element is a two-element tuple
117
+ # consisting of the local port and bind address corresponding to the
118
+ # forwarding port.
119
+ def active_locals
120
+ @local_forwarded_ports.keys
143
121
  end
144
122
 
145
- prepare_client(client, channel, :local)
123
+ # Starts listening for connections on the local host, and forwards them
124
+ # to the specified remote socket via the SSH connection. This will
125
+ # (re)create the local socket file. The remote server needs to have the
126
+ # socket file already available.
127
+ #
128
+ # ssh.forward.local_socket('/tmp/local.sock', '/tmp/remote.sock')
129
+ def local_socket(local_socket_path, remote_socket_path)
130
+ File.delete(local_socket_path) if File.exist?(local_socket_path)
131
+ socket = Socket.unix_server_socket(local_socket_path)
132
+
133
+ @local_forwarded_sockets[local_socket_path] = socket
134
+
135
+ session.listen_to(socket) do |server|
136
+ client = server.accept[0]
137
+ debug { "received connection on #{socket}" }
138
+
139
+ channel = session.open_channel("direct-streamlocal@openssh.com",
140
+ :string, remote_socket_path,
141
+ :string, nil,
142
+ :long, 0) do |achannel|
143
+ achannel.info { "direct channel established" }
144
+ end
146
145
 
147
- channel.on_open_failed do |ch, code, description|
148
- channel.error { "could not establish direct channel: #{description} (#{code})" }
149
- session.stop_listening_to(channel[:socket])
150
- channel[:socket].close
151
- end
152
- end
146
+ prepare_client(client, channel, :local)
153
147
 
154
- local_socket_path
155
- end
148
+ channel.on_open_failed do |ch, code, description|
149
+ channel.error { "could not establish direct channel: #{description} (#{code})" }
150
+ session.stop_listening_to(channel[:socket])
151
+ channel[:socket].close
152
+ end
153
+ end
156
154
 
157
- # Terminates an active local forwarded socket.
158
- #
159
- # ssh.forward.cancel_local_socket('/tmp/foo.sock')
160
- def cancel_local_socket(local_socket_path)
161
- socket = @local_forwarded_sockets.delete(local_socket_path)
162
- socket.shutdown rescue nil
163
- socket.close rescue nil
164
- session.stop_listening_to(socket)
165
- end
155
+ local_socket_path
156
+ end
166
157
 
167
- # Returns a list of all active locally forwarded sockets. The returned value
168
- # is an array of Unix domain socket file paths.
169
- def active_local_sockets
170
- @local_forwarded_sockets.keys
171
- end
158
+ # Terminates an active local forwarded socket.
159
+ #
160
+ # ssh.forward.cancel_local_socket('/tmp/foo.sock')
161
+ def cancel_local_socket(local_socket_path)
162
+ socket = @local_forwarded_sockets.delete(local_socket_path)
163
+ socket.shutdown rescue nil
164
+ socket.close rescue nil
165
+ session.stop_listening_to(socket)
166
+ end
172
167
 
173
- # Requests that all connections on the given remote-port be forwarded via
174
- # the local host to the given port/host. The last argument describes the
175
- # bind address on the remote host, and defaults to 127.0.0.1.
176
- #
177
- # This method will return immediately, but the port will not actually be
178
- # forwarded immediately. If the remote server is not able to begin the
179
- # listener for this request, an exception will be raised asynchronously.
180
- #
181
- # To request an ephemeral port on the remote server, provide 0 (zero) for
182
- # the port number. The assigned port will show up in the # #active_remotes
183
- # list.
184
- #
185
- # remote_host is interpreted by the server per RFC 4254, which has these
186
- # special values:
187
- #
188
- # - "" means that connections are to be accepted on all protocol
189
- # families supported by the SSH implementation.
190
- # - "0.0.0.0" means to listen on all IPv4 addresses.
191
- # - "::" means to listen on all IPv6 addresses.
192
- # - "localhost" means to listen on all protocol families supported by
193
- # the SSH implementation on loopback addresses only ([RFC3330] and
194
- # [RFC3513]).
195
- # - "127.0.0.1" and "::1" indicate listening on the loopback
196
- # interfaces for IPv4 and IPv6, respectively.
197
- #
198
- # You may pass a block that will be called when the the port forward
199
- # request receives a response. This block will be passed the remote_port
200
- # that was actually bound to, or nil if the binding failed. If the block
201
- # returns :no_exception, the "failed binding" exception will not be thrown.
202
- #
203
- # If you want to block until the port is active, you could do something
204
- # like this:
205
- #
206
- # got_remote_port = nil
207
- # remote(port, host, remote_port, remote_host) do |actual_remote_port|
208
- # got_remote_port = actual_remote_port || :error
209
- # :no_exception # will yield the exception on my own thread
210
- # end
211
- # session.loop { !got_remote_port }
212
- # if got_remote_port == :error
213
- # raise Net::SSH::Exception, "remote forwarding request failed"
214
- # end
215
- #
216
- def remote(port, host, remote_port, remote_host="127.0.0.1")
217
- session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
218
- if success
219
- remote_port = response.read_long if remote_port == 0
220
- debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
221
- @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
222
- yield remote_port, remote_host if block_given?
223
- else
224
- instruction = if block_given?
225
- yield :error
226
- end
227
- unless instruction == :no_exception
228
- error { "remote forwarding request failed" }
229
- raise Net::SSH::Exception, "remote forwarding request failed"
168
+ # Returns a list of all active locally forwarded sockets. The returned value
169
+ # is an array of Unix domain socket file paths.
170
+ def active_local_sockets
171
+ @local_forwarded_sockets.keys
172
+ end
173
+
174
+ # Requests that all connections on the given remote-port be forwarded via
175
+ # the local host to the given port/host. The last argument describes the
176
+ # bind address on the remote host, and defaults to 127.0.0.1.
177
+ #
178
+ # This method will return immediately, but the port will not actually be
179
+ # forwarded immediately. If the remote server is not able to begin the
180
+ # listener for this request, an exception will be raised asynchronously.
181
+ #
182
+ # To request an ephemeral port on the remote server, provide 0 (zero) for
183
+ # the port number. The assigned port will show up in the # #active_remotes
184
+ # list.
185
+ #
186
+ # remote_host is interpreted by the server per RFC 4254, which has these
187
+ # special values:
188
+ #
189
+ # - "" means that connections are to be accepted on all protocol
190
+ # families supported by the SSH implementation.
191
+ # - "0.0.0.0" means to listen on all IPv4 addresses.
192
+ # - "::" means to listen on all IPv6 addresses.
193
+ # - "localhost" means to listen on all protocol families supported by
194
+ # the SSH implementation on loopback addresses only ([RFC3330] and
195
+ # [RFC3513]).
196
+ # - "127.0.0.1" and "::1" indicate listening on the loopback
197
+ # interfaces for IPv4 and IPv6, respectively.
198
+ #
199
+ # You may pass a block that will be called when the the port forward
200
+ # request receives a response. This block will be passed the remote_port
201
+ # that was actually bound to, or nil if the binding failed. If the block
202
+ # returns :no_exception, the "failed binding" exception will not be thrown.
203
+ #
204
+ # If you want to block until the port is active, you could do something
205
+ # like this:
206
+ #
207
+ # got_remote_port = nil
208
+ # remote(port, host, remote_port, remote_host) do |actual_remote_port|
209
+ # got_remote_port = actual_remote_port || :error
210
+ # :no_exception # will yield the exception on my own thread
211
+ # end
212
+ # session.loop { !got_remote_port }
213
+ # if got_remote_port == :error
214
+ # raise Net::SSH::Exception, "remote forwarding request failed"
215
+ # end
216
+ #
217
+ def remote(port, host, remote_port, remote_host="127.0.0.1")
218
+ session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
219
+ if success
220
+ remote_port = response.read_long if remote_port == 0
221
+ debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
222
+ @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
223
+ yield remote_port, remote_host if block_given?
224
+ else
225
+ instruction = if block_given?
226
+ yield :error
227
+ end
228
+ unless instruction == :no_exception
229
+ error { "remote forwarding request failed" }
230
+ raise Net::SSH::Exception, "remote forwarding request failed"
231
+ end
232
+ end
230
233
  end
231
234
  end
232
- end
233
- end
234
235
 
235
- # an alias, for token backwards compatibility with the 1.x API
236
- alias :remote_to :remote
237
-
238
- # Requests that a remote forwarded port be cancelled. The remote forwarded
239
- # port on the remote host, bound to the given address on the remote host,
240
- # will be terminated, but not immediately. This method returns immediately
241
- # after queueing the request to be sent to the server. If for some reason
242
- # the port cannot be cancelled, an exception will be raised (asynchronously).
243
- #
244
- # If you want to know when the connection has been cancelled, it will no
245
- # longer be present in the #active_remotes list. If you want to block until
246
- # the port is no longer active, you could do something like this:
247
- #
248
- # ssh.forward.cancel_remote(1234, "0.0.0.0")
249
- # ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
250
- def cancel_remote(port, host="127.0.0.1")
251
- session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response|
252
- if success
253
- @remote_forwarded_ports.delete([port, host])
254
- else
255
- raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}"
236
+ # an alias, for token backwards compatibility with the 1.x API
237
+ alias :remote_to :remote
238
+
239
+ # Requests that a remote forwarded port be cancelled. The remote forwarded
240
+ # port on the remote host, bound to the given address on the remote host,
241
+ # will be terminated, but not immediately. This method returns immediately
242
+ # after queueing the request to be sent to the server. If for some reason
243
+ # the port cannot be cancelled, an exception will be raised (asynchronously).
244
+ #
245
+ # If you want to know when the connection has been cancelled, it will no
246
+ # longer be present in the #active_remotes list. If you want to block until
247
+ # the port is no longer active, you could do something like this:
248
+ #
249
+ # ssh.forward.cancel_remote(1234, "0.0.0.0")
250
+ # ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
251
+ def cancel_remote(port, host="127.0.0.1")
252
+ session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response|
253
+ if success
254
+ @remote_forwarded_ports.delete([port, host])
255
+ else
256
+ raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}"
257
+ end
258
+ end
256
259
  end
257
- end
258
- end
259
260
 
260
- # Returns all active forwarded remote ports. The returned value is an
261
- # array of two-element tuples, where the first element is the port on the
262
- # remote host and the second is the bind address.
263
- def active_remotes
264
- @remote_forwarded_ports.keys
265
- end
261
+ # Returns all active forwarded remote ports. The returned value is an
262
+ # array of two-element tuples, where the first element is the port on the
263
+ # remote host and the second is the bind address.
264
+ def active_remotes
265
+ @remote_forwarded_ports.keys
266
+ end
266
267
 
267
- # Returns all active remote forwarded ports and where they forward to. The
268
- # returned value is a hash from [<forwarding port on the local host>, <local forwarding address>]
269
- # to [<port on the remote host>, <remote bind address>].
270
- def active_remote_destinations
271
- @remote_forwarded_ports.inject({}) do |result, (remote, local)|
272
- result[[local.port, local.host]] = remote
273
- result
274
- end
275
- end
268
+ # Returns all active remote forwarded ports and where they forward to. The
269
+ # returned value is a hash from [<forwarding port on the local host>, <local forwarding address>]
270
+ # to [<port on the remote host>, <remote bind address>].
271
+ def active_remote_destinations
272
+ @remote_forwarded_ports.each_with_object({}) do |(remote, local), result|
273
+ result[[local.port, local.host]] = remote
274
+ end
275
+ end
276
276
 
277
- # Enables SSH agent forwarding on the given channel. The forwarded agent
278
- # will remain active even after the channel closes--the channel is only
279
- # used as the transport for enabling the forwarded connection. You should
280
- # never need to call this directly--it is called automatically the first
281
- # time a session channel is opened, when the connection was created with
282
- # :forward_agent set to true:
283
- #
284
- # Net::SSH.start("remote.host", "me", :forward_agent => true) do |ssh|
285
- # ssh.open_channel do |ch|
286
- # # agent will be automatically forwarded by this point
287
- # end
288
- # ssh.loop
289
- # end
290
- def agent(channel)
291
- return if @agent_forwarded
292
- @agent_forwarded = true
293
-
294
- channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success|
295
- if success
296
- debug { "authentication agent forwarding is active" }
297
- else
298
- achannel.send_channel_request("auth-agent-req") do |a2channel, success2|
299
- if success2
277
+ # Enables SSH agent forwarding on the given channel. The forwarded agent
278
+ # will remain active even after the channel closes--the channel is only
279
+ # used as the transport for enabling the forwarded connection. You should
280
+ # never need to call this directly--it is called automatically the first
281
+ # time a session channel is opened, when the connection was created with
282
+ # :forward_agent set to true:
283
+ #
284
+ # Net::SSH.start("remote.host", "me", :forward_agent => true) do |ssh|
285
+ # ssh.open_channel do |ch|
286
+ # # agent will be automatically forwarded by this point
287
+ # end
288
+ # ssh.loop
289
+ # end
290
+ def agent(channel)
291
+ return if @agent_forwarded
292
+ @agent_forwarded = true
293
+
294
+ channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success|
295
+ if success
300
296
  debug { "authentication agent forwarding is active" }
301
297
  else
302
- error { "could not establish forwarding of authentication agent" }
298
+ achannel.send_channel_request("auth-agent-req") do |a2channel, success2|
299
+ if success2
300
+ debug { "authentication agent forwarding is active" }
301
+ else
302
+ error { "could not establish forwarding of authentication agent" }
303
+ end
304
+ end
303
305
  end
304
306
  end
305
307
  end
306
- end
307
- end
308
308
 
309
- private
309
+ private
310
310
 
311
- # Perform setup operations that are common to all forwarded channels.
312
- # +client+ is a socket, +channel+ is the channel that was just created,
313
- # and +type+ is an arbitrary string describing the type of the channel.
314
- def prepare_client(client, channel, type)
315
- client.extend(Net::SSH::BufferedIo)
316
- client.extend(Net::SSH::ForwardedBufferedIo)
317
- client.logger = logger
311
+ # Perform setup operations that are common to all forwarded channels.
312
+ # +client+ is a socket, +channel+ is the channel that was just created,
313
+ # and +type+ is an arbitrary string describing the type of the channel.
314
+ def prepare_client(client, channel, type)
315
+ client.extend(Net::SSH::BufferedIo)
316
+ client.extend(Net::SSH::ForwardedBufferedIo)
317
+ client.logger = logger
318
318
 
319
- session.listen_to(client)
320
- channel[:socket] = client
319
+ session.listen_to(client)
320
+ channel[:socket] = client
321
321
 
322
- channel.on_data do |ch, data|
323
- debug { "data:#{data.length} on #{type} forwarded channel" }
324
- ch[:socket].enqueue(data)
325
- end
322
+ channel.on_data do |ch, data|
323
+ debug { "data:#{data.length} on #{type} forwarded channel" }
324
+ ch[:socket].enqueue(data)
325
+ end
326
326
 
327
- channel.on_eof do |ch|
328
- debug { "eof #{type} on #{type} forwarded channel" }
329
- begin
330
- ch[:socket].send_pending
331
- ch[:socket].shutdown Socket::SHUT_WR
332
- rescue IOError => e
333
- if e.message =~ /closed/ then
327
+ channel.on_eof do |ch|
328
+ debug { "eof #{type} on #{type} forwarded channel" }
329
+ begin
330
+ ch[:socket].send_pending
331
+ ch[:socket].shutdown Socket::SHUT_WR
332
+ rescue IOError => e
333
+ if e.message =~ /closed/ then
334
+ debug { "epipe in on_eof => shallowing exception:#{e}" }
335
+ else
336
+ raise
337
+ end
338
+ rescue Errno::EPIPE => e
334
339
  debug { "epipe in on_eof => shallowing exception:#{e}" }
335
- else
336
- raise
340
+ rescue Errno::ENOTCONN => e
341
+ debug { "enotconn in on_eof => shallowing exception:#{e}" }
337
342
  end
338
- rescue Errno::EPIPE => e
339
- debug { "epipe in on_eof => shallowing exception:#{e}" }
340
- rescue Errno::ENOTCONN => e
341
- debug { "enotconn in on_eof => shallowing exception:#{e}" }
342
343
  end
343
- end
344
344
 
345
- channel.on_close do |ch|
346
- debug { "closing #{type} forwarded channel" }
347
- ch[:socket].close if !client.closed?
348
- session.stop_listening_to(ch[:socket])
349
- end
345
+ channel.on_close do |ch|
346
+ debug { "closing #{type} forwarded channel" }
347
+ ch[:socket].close if !client.closed?
348
+ session.stop_listening_to(ch[:socket])
349
+ end
350
350
 
351
- channel.on_process do |ch|
352
- if ch[:socket].closed?
353
- ch.info { "#{type} forwarded connection closed" }
354
- ch.close
355
- elsif ch[:socket].available > 0
356
- data = ch[:socket].read_available(8192)
357
- ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
358
- ch.send_data(data)
351
+ channel.on_process do |ch|
352
+ if ch[:socket].closed?
353
+ ch.info { "#{type} forwarded connection closed" }
354
+ ch.close
355
+ elsif ch[:socket].available > 0
356
+ data = ch[:socket].read_available(8192)
357
+ ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
358
+ ch.send_data(data)
359
+ end
359
360
  end
360
361
  end
361
- end
362
362
 
363
- # not a real socket, so use a simpler behaviour
364
- def prepare_simple_client(client, channel, type)
365
- channel[:socket] = client
363
+ # not a real socket, so use a simpler behaviour
364
+ def prepare_simple_client(client, channel, type)
365
+ channel[:socket] = client
366
366
 
367
- channel.on_data do |ch, data|
368
- ch.debug { "data:#{data.length} on #{type} forwarded channel" }
369
- ch[:socket].send(data)
370
- end
367
+ channel.on_data do |ch, data|
368
+ ch.debug { "data:#{data.length} on #{type} forwarded channel" }
369
+ ch[:socket].send(data)
370
+ end
371
371
 
372
- channel.on_process do |ch|
373
- data = ch[:socket].read(8192)
374
- if data
375
- ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
376
- ch.send_data(data)
372
+ channel.on_process do |ch|
373
+ data = ch[:socket].read(8192)
374
+ if data
375
+ ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
376
+ ch.send_data(data)
377
+ end
377
378
  end
378
379
  end
379
- end
380
380
 
381
- # The callback used when a new "forwarded-tcpip" channel is requested
382
- # by the server. This will open a new socket to the host/port specified
383
- # when the forwarded connection was first requested.
384
- def forwarded_tcpip(session, channel, packet)
385
- connected_address = packet.read_string
386
- connected_port = packet.read_long
387
- originator_address = packet.read_string
388
- originator_port = packet.read_long
381
+ # The callback used when a new "forwarded-tcpip" channel is requested
382
+ # by the server. This will open a new socket to the host/port specified
383
+ # when the forwarded connection was first requested.
384
+ def forwarded_tcpip(session, channel, packet)
385
+ connected_address = packet.read_string
386
+ connected_port = packet.read_long
387
+ originator_address = packet.read_string
388
+ originator_port = packet.read_long
389
389
 
390
- remote = @remote_forwarded_ports[[connected_port, connected_address]]
390
+ remote = @remote_forwarded_ports[[connected_port, connected_address]]
391
391
 
392
- if remote.nil?
393
- raise Net::SSH::ChannelOpenFailed.new(1, "unknown request from remote forwarded connection on #{connected_address}:#{connected_port}")
394
- end
392
+ if remote.nil?
393
+ raise Net::SSH::ChannelOpenFailed.new(1, "unknown request from remote forwarded connection on #{connected_address}:#{connected_port}")
394
+ end
395
395
 
396
- client = TCPSocket.new(remote.host, remote.port)
397
- info { "connected #{connected_address}:#{connected_port} originator #{originator_address}:#{originator_port}" }
396
+ client = TCPSocket.new(remote.host, remote.port)
397
+ info { "connected #{connected_address}:#{connected_port} originator #{originator_address}:#{originator_port}" }
398
398
 
399
- prepare_client(client, channel, :remote)
400
- rescue SocketError => err
401
- raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to remote host (#{remote.host}:#{remote.port}): #{err.message}")
402
- end
399
+ prepare_client(client, channel, :remote)
400
+ rescue SocketError => err
401
+ raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to remote host (#{remote.host}:#{remote.port}): #{err.message}")
402
+ end
403
+
404
+ # The callback used when an auth-agent channel is requested by the server.
405
+ def auth_agent_channel(session, channel, packet)
406
+ info { "opening auth-agent channel" }
407
+ channel[:invisible] = true
403
408
 
404
- # The callback used when an auth-agent channel is requested by the server.
405
- def auth_agent_channel(session, channel, packet)
406
- info { "opening auth-agent channel" }
407
- channel[:invisible] = true
408
-
409
- begin
410
- agent = Authentication::Agent.connect(logger, session.options[:agent_socket_factory])
411
- if (agent.socket.is_a? ::IO)
412
- prepare_client(agent.socket, channel, :agent)
413
- else
414
- prepare_simple_client(agent.socket, channel, :agent)
409
+ begin
410
+ agent = Authentication::Agent.connect(logger, session.options[:agent_socket_factory])
411
+ if (agent.socket.is_a? ::IO)
412
+ prepare_client(agent.socket, channel, :agent)
413
+ else
414
+ prepare_simple_client(agent.socket, channel, :agent)
415
+ end
416
+ rescue Exception => e
417
+ error { "attempted to connect to agent but failed: #{e.class.name} (#{e.message})" }
418
+ raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to authentication agent")
415
419
  end
416
- rescue Exception => e
417
- error { "attempted to connect to agent but failed: #{e.class.name} (#{e.message})" }
418
- raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to authentication agent")
419
420
  end
420
421
  end
421
- end
422
422
 
423
- end; end; end
423
+ end
424
+ end
425
+ end