net-ssh 5.0.0.beta1 → 5.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.rubocop_todo.yml +98 -258
  5. data/CHANGES.txt +8 -0
  6. data/Gemfile +1 -3
  7. data/Rakefile +37 -39
  8. data/lib/net/ssh.rb +26 -25
  9. data/lib/net/ssh/authentication/agent.rb +228 -225
  10. data/lib/net/ssh/authentication/certificate.rb +166 -164
  11. data/lib/net/ssh/authentication/constants.rb +17 -14
  12. data/lib/net/ssh/authentication/ed25519.rb +107 -104
  13. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
  14. data/lib/net/ssh/authentication/key_manager.rb +5 -3
  15. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  16. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  17. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
  18. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  19. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  20. data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
  21. data/lib/net/ssh/authentication/pageant.rb +468 -465
  22. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
  23. data/lib/net/ssh/authentication/session.rb +127 -123
  24. data/lib/net/ssh/buffer.rb +305 -303
  25. data/lib/net/ssh/buffered_io.rb +163 -162
  26. data/lib/net/ssh/config.rb +230 -227
  27. data/lib/net/ssh/connection/channel.rb +659 -654
  28. data/lib/net/ssh/connection/constants.rb +30 -26
  29. data/lib/net/ssh/connection/event_loop.rb +108 -104
  30. data/lib/net/ssh/connection/keepalive.rb +54 -50
  31. data/lib/net/ssh/connection/session.rb +677 -678
  32. data/lib/net/ssh/connection/term.rb +180 -176
  33. data/lib/net/ssh/errors.rb +101 -99
  34. data/lib/net/ssh/key_factory.rb +108 -108
  35. data/lib/net/ssh/known_hosts.rb +148 -154
  36. data/lib/net/ssh/loggable.rb +56 -54
  37. data/lib/net/ssh/packet.rb +82 -78
  38. data/lib/net/ssh/prompt.rb +55 -53
  39. data/lib/net/ssh/proxy/command.rb +103 -102
  40. data/lib/net/ssh/proxy/errors.rb +12 -8
  41. data/lib/net/ssh/proxy/http.rb +92 -91
  42. data/lib/net/ssh/proxy/https.rb +42 -39
  43. data/lib/net/ssh/proxy/jump.rb +50 -47
  44. data/lib/net/ssh/proxy/socks4.rb +0 -2
  45. data/lib/net/ssh/proxy/socks5.rb +11 -11
  46. data/lib/net/ssh/ruby_compat.rb +1 -0
  47. data/lib/net/ssh/service/forward.rb +364 -362
  48. data/lib/net/ssh/test.rb +85 -83
  49. data/lib/net/ssh/test/channel.rb +146 -142
  50. data/lib/net/ssh/test/extensions.rb +148 -146
  51. data/lib/net/ssh/test/kex.rb +35 -31
  52. data/lib/net/ssh/test/local_packet.rb +48 -44
  53. data/lib/net/ssh/test/packet.rb +87 -84
  54. data/lib/net/ssh/test/remote_packet.rb +35 -31
  55. data/lib/net/ssh/test/script.rb +173 -171
  56. data/lib/net/ssh/test/socket.rb +59 -55
  57. data/lib/net/ssh/transport/algorithms.rb +413 -412
  58. data/lib/net/ssh/transport/cipher_factory.rb +108 -105
  59. data/lib/net/ssh/transport/constants.rb +35 -31
  60. data/lib/net/ssh/transport/ctr.rb +1 -1
  61. data/lib/net/ssh/transport/hmac.rb +1 -1
  62. data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
  63. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  66. data/lib/net/ssh/transport/kex.rb +2 -4
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
  70. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
  73. data/lib/net/ssh/transport/key_expander.rb +29 -25
  74. data/lib/net/ssh/transport/openssl.rb +17 -30
  75. data/lib/net/ssh/transport/packet_stream.rb +193 -192
  76. data/lib/net/ssh/transport/server_version.rb +64 -66
  77. data/lib/net/ssh/transport/session.rb +286 -284
  78. data/lib/net/ssh/transport/state.rb +198 -196
  79. data/lib/net/ssh/verifiers/lenient.rb +29 -25
  80. data/lib/net/ssh/verifiers/null.rb +13 -9
  81. data/lib/net/ssh/verifiers/secure.rb +45 -45
  82. data/lib/net/ssh/verifiers/strict.rb +20 -16
  83. data/lib/net/ssh/version.rb +55 -53
  84. data/net-ssh.gemspec +4 -4
  85. data/support/ssh_tunnel_bug.rb +2 -2
  86. metadata +25 -24
  87. metadata.gz.sig +0 -0
@@ -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