net-ssh 1.1.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (297) hide show
  1. data/CHANGELOG.rdoc +37 -0
  2. data/Manifest +101 -0
  3. data/README.rdoc +110 -0
  4. data/Rakefile +26 -0
  5. data/{THANKS → THANKS.rdoc} +2 -5
  6. data/lib/net/ssh.rb +189 -57
  7. data/lib/net/ssh/authentication/agent.rb +175 -0
  8. data/lib/net/ssh/authentication/constants.rb +18 -0
  9. data/lib/net/ssh/authentication/key_manager.rb +166 -0
  10. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  11. data/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
  12. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
  13. data/lib/net/ssh/authentication/methods/password.rb +39 -0
  14. data/lib/net/ssh/authentication/methods/publickey.rb +92 -0
  15. data/lib/net/ssh/authentication/pageant.rb +176 -0
  16. data/lib/net/ssh/authentication/session.rb +116 -0
  17. data/lib/net/ssh/buffer.rb +339 -0
  18. data/lib/net/ssh/buffered_io.rb +149 -0
  19. data/lib/net/ssh/config.rb +173 -0
  20. data/lib/net/ssh/connection/channel.rb +575 -454
  21. data/lib/net/ssh/connection/constants.rb +31 -45
  22. data/lib/net/ssh/connection/session.rb +569 -0
  23. data/lib/net/ssh/connection/term.rb +176 -88
  24. data/lib/net/ssh/errors.rb +83 -61
  25. data/lib/net/ssh/key_factory.rb +85 -0
  26. data/lib/net/ssh/known_hosts.rb +129 -0
  27. data/lib/net/ssh/loggable.rb +61 -0
  28. data/lib/net/ssh/packet.rb +102 -0
  29. data/lib/net/ssh/prompt.rb +93 -0
  30. data/lib/net/ssh/proxy/errors.rb +8 -28
  31. data/lib/net/ssh/proxy/http.rb +75 -107
  32. data/lib/net/ssh/proxy/socks4.rb +35 -48
  33. data/lib/net/ssh/proxy/socks5.rb +76 -108
  34. data/lib/net/ssh/service/forward.rb +267 -0
  35. data/lib/net/ssh/test.rb +89 -0
  36. data/lib/net/ssh/test/channel.rb +129 -0
  37. data/lib/net/ssh/test/extensions.rb +152 -0
  38. data/lib/net/ssh/test/kex.rb +44 -0
  39. data/lib/net/ssh/test/local_packet.rb +51 -0
  40. data/lib/net/ssh/test/packet.rb +81 -0
  41. data/lib/net/ssh/test/remote_packet.rb +38 -0
  42. data/lib/net/ssh/test/script.rb +157 -0
  43. data/lib/net/ssh/test/socket.rb +59 -0
  44. data/lib/net/ssh/transport/algorithms.rb +384 -0
  45. data/lib/net/ssh/transport/cipher_factory.rb +72 -0
  46. data/lib/net/ssh/transport/constants.rb +22 -58
  47. data/lib/net/ssh/transport/hmac.rb +31 -0
  48. data/lib/net/ssh/transport/hmac/abstract.rb +48 -0
  49. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  50. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  51. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  52. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  53. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  54. data/lib/net/ssh/transport/identity_cipher.rb +40 -0
  55. data/lib/net/ssh/transport/kex.rb +13 -0
  56. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  57. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
  58. data/lib/net/ssh/{util → transport}/openssl.rb +22 -40
  59. data/lib/net/ssh/transport/packet_stream.rb +230 -0
  60. data/lib/net/ssh/transport/server_version.rb +61 -0
  61. data/lib/net/ssh/transport/session.rb +225 -303
  62. data/lib/net/ssh/transport/state.rb +170 -0
  63. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  64. data/lib/net/ssh/verifiers/null.rb +12 -0
  65. data/lib/net/ssh/verifiers/strict.rb +53 -0
  66. data/lib/net/ssh/version.rb +57 -26
  67. data/net-ssh.gemspec +54 -0
  68. data/setup.rb +1585 -0
  69. data/test/authentication/methods/common.rb +28 -0
  70. data/test/authentication/methods/test_abstract.rb +51 -0
  71. data/test/authentication/methods/test_hostbased.rb +108 -0
  72. data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  73. data/test/authentication/methods/test_password.rb +50 -0
  74. data/test/authentication/methods/test_publickey.rb +123 -0
  75. data/test/authentication/test_agent.rb +205 -0
  76. data/test/authentication/test_key_manager.rb +100 -0
  77. data/test/authentication/test_session.rb +93 -0
  78. data/test/common.rb +106 -0
  79. data/test/configs/exact_match +8 -0
  80. data/test/configs/wild_cards +14 -0
  81. data/test/connection/test_channel.rb +452 -0
  82. data/test/connection/test_session.rb +483 -0
  83. data/test/test_all.rb +6 -0
  84. data/test/test_buffer.rb +336 -0
  85. data/test/test_buffered_io.rb +63 -0
  86. data/test/test_config.rb +78 -0
  87. data/test/test_key_factory.rb +67 -0
  88. data/test/transport/hmac/test_md5.rb +34 -0
  89. data/test/transport/hmac/test_md5_96.rb +25 -0
  90. data/test/transport/hmac/test_none.rb +34 -0
  91. data/test/transport/hmac/test_sha1.rb +34 -0
  92. data/test/transport/hmac/test_sha1_96.rb +25 -0
  93. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  94. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  95. data/test/transport/test_algorithms.rb +302 -0
  96. data/test/transport/test_cipher_factory.rb +163 -0
  97. data/test/transport/test_hmac.rb +34 -0
  98. data/test/transport/test_identity_cipher.rb +40 -0
  99. data/test/transport/test_packet_stream.rb +433 -0
  100. data/test/transport/test_server_version.rb +55 -0
  101. data/test/transport/test_session.rb +312 -0
  102. data/test/transport/test_state.rb +173 -0
  103. metadata +102 -253
  104. data/ChangeLog +0 -560
  105. data/LICENSE +0 -7
  106. data/NEWS +0 -152
  107. data/README +0 -14
  108. data/bin/rb-keygen +0 -210
  109. data/doc/LICENSE-BSD +0 -27
  110. data/doc/LICENSE-GPL +0 -280
  111. data/doc/LICENSE-RUBY +0 -56
  112. data/doc/manual-html/chapter-1.html +0 -388
  113. data/doc/manual-html/chapter-2.html +0 -552
  114. data/doc/manual-html/chapter-3.html +0 -470
  115. data/doc/manual-html/chapter-4.html +0 -413
  116. data/doc/manual-html/chapter-5.html +0 -525
  117. data/doc/manual-html/chapter-6.html +0 -456
  118. data/doc/manual-html/chapter-7.html +0 -343
  119. data/doc/manual-html/index.html +0 -235
  120. data/doc/manual-html/stylesheets/manual.css +0 -270
  121. data/doc/manual-html/stylesheets/ruby.css +0 -17
  122. data/doc/manual/chapter.erb +0 -38
  123. data/doc/manual/example.erb +0 -18
  124. data/doc/manual/index.erb +0 -29
  125. data/doc/manual/manual.rb +0 -311
  126. data/doc/manual/manual.yml +0 -73
  127. data/doc/manual/page.erb +0 -87
  128. data/doc/manual/parts/0000.txt +0 -5
  129. data/doc/manual/parts/0001.txt +0 -3
  130. data/doc/manual/parts/0002.txt +0 -40
  131. data/doc/manual/parts/0003.txt +0 -6
  132. data/doc/manual/parts/0004.txt +0 -7
  133. data/doc/manual/parts/0005.txt +0 -1
  134. data/doc/manual/parts/0006.txt +0 -49
  135. data/doc/manual/parts/0007.txt +0 -67
  136. data/doc/manual/parts/0008.txt +0 -43
  137. data/doc/manual/parts/0009.txt +0 -14
  138. data/doc/manual/parts/0010.txt +0 -7
  139. data/doc/manual/parts/0011.txt +0 -14
  140. data/doc/manual/parts/0012.txt +0 -3
  141. data/doc/manual/parts/0013.txt +0 -20
  142. data/doc/manual/parts/0014.txt +0 -32
  143. data/doc/manual/parts/0015.txt +0 -14
  144. data/doc/manual/parts/0016.txt +0 -28
  145. data/doc/manual/parts/0017.txt +0 -50
  146. data/doc/manual/parts/0018.txt +0 -35
  147. data/doc/manual/parts/0019.txt +0 -7
  148. data/doc/manual/parts/0020.txt +0 -72
  149. data/doc/manual/parts/0021.txt +0 -50
  150. data/doc/manual/parts/0022.txt +0 -42
  151. data/doc/manual/parts/0023.txt +0 -51
  152. data/doc/manual/parts/0024.txt +0 -18
  153. data/doc/manual/parts/0025.txt +0 -18
  154. data/doc/manual/parts/0026.txt +0 -15
  155. data/doc/manual/parts/0027.txt +0 -37
  156. data/doc/manual/parts/0028.txt +0 -16
  157. data/doc/manual/parts/0029.txt +0 -1
  158. data/doc/manual/parts/0030.txt +0 -52
  159. data/doc/manual/parts/0031.txt +0 -25
  160. data/doc/manual/stylesheets/manual.css +0 -270
  161. data/doc/manual/stylesheets/ruby.css +0 -17
  162. data/doc/manual/tutorial.erb +0 -30
  163. data/examples/auth-forward.rb +0 -41
  164. data/examples/channel-demo.rb +0 -81
  165. data/examples/port-forward.rb +0 -51
  166. data/examples/process-demo.rb +0 -91
  167. data/examples/remote-net-port-forward.rb +0 -45
  168. data/examples/remote-port-forward.rb +0 -80
  169. data/examples/shell-demo.rb +0 -46
  170. data/examples/ssh-client.rb +0 -67
  171. data/examples/sync-shell-demo.rb +0 -69
  172. data/examples/tail-demo.rb +0 -49
  173. data/lib/net/ssh/connection/driver.rb +0 -446
  174. data/lib/net/ssh/connection/services.rb +0 -72
  175. data/lib/net/ssh/host-key-verifier.rb +0 -52
  176. data/lib/net/ssh/known-hosts.rb +0 -96
  177. data/lib/net/ssh/lenient-host-key-verifier.rb +0 -25
  178. data/lib/net/ssh/null-host-key-verifier.rb +0 -14
  179. data/lib/net/ssh/service/agentforward/driver.rb +0 -78
  180. data/lib/net/ssh/service/agentforward/services.rb +0 -41
  181. data/lib/net/ssh/service/forward/driver.rb +0 -319
  182. data/lib/net/ssh/service/forward/local-network-handler.rb +0 -71
  183. data/lib/net/ssh/service/forward/remote-network-handler.rb +0 -83
  184. data/lib/net/ssh/service/forward/services.rb +0 -76
  185. data/lib/net/ssh/service/process/driver.rb +0 -153
  186. data/lib/net/ssh/service/process/open.rb +0 -193
  187. data/lib/net/ssh/service/process/popen3.rb +0 -178
  188. data/lib/net/ssh/service/process/services.rb +0 -66
  189. data/lib/net/ssh/service/services.rb +0 -60
  190. data/lib/net/ssh/service/shell/driver.rb +0 -86
  191. data/lib/net/ssh/service/shell/services.rb +0 -54
  192. data/lib/net/ssh/service/shell/shell.rb +0 -222
  193. data/lib/net/ssh/service/shell/sync.rb +0 -114
  194. data/lib/net/ssh/session.rb +0 -305
  195. data/lib/net/ssh/transport/algorithm-negotiator.rb +0 -275
  196. data/lib/net/ssh/transport/compress/compressor.rb +0 -53
  197. data/lib/net/ssh/transport/compress/decompressor.rb +0 -53
  198. data/lib/net/ssh/transport/compress/none-compressor.rb +0 -39
  199. data/lib/net/ssh/transport/compress/none-decompressor.rb +0 -39
  200. data/lib/net/ssh/transport/compress/services.rb +0 -68
  201. data/lib/net/ssh/transport/compress/zlib-compressor.rb +0 -60
  202. data/lib/net/ssh/transport/compress/zlib-decompressor.rb +0 -52
  203. data/lib/net/ssh/transport/errors.rb +0 -47
  204. data/lib/net/ssh/transport/identity-cipher.rb +0 -61
  205. data/lib/net/ssh/transport/kex/dh-gex.rb +0 -106
  206. data/lib/net/ssh/transport/kex/dh.rb +0 -249
  207. data/lib/net/ssh/transport/kex/services.rb +0 -62
  208. data/lib/net/ssh/transport/ossl/buffer-factory.rb +0 -52
  209. data/lib/net/ssh/transport/ossl/buffer.rb +0 -87
  210. data/lib/net/ssh/transport/ossl/cipher-factory.rb +0 -98
  211. data/lib/net/ssh/transport/ossl/digest-factory.rb +0 -51
  212. data/lib/net/ssh/transport/ossl/hmac-factory.rb +0 -71
  213. data/lib/net/ssh/transport/ossl/hmac/hmac.rb +0 -62
  214. data/lib/net/ssh/transport/ossl/hmac/md5-96.rb +0 -44
  215. data/lib/net/ssh/transport/ossl/hmac/md5.rb +0 -46
  216. data/lib/net/ssh/transport/ossl/hmac/none.rb +0 -46
  217. data/lib/net/ssh/transport/ossl/hmac/services.rb +0 -68
  218. data/lib/net/ssh/transport/ossl/hmac/sha1-96.rb +0 -44
  219. data/lib/net/ssh/transport/ossl/hmac/sha1.rb +0 -45
  220. data/lib/net/ssh/transport/ossl/key-factory.rb +0 -116
  221. data/lib/net/ssh/transport/ossl/services.rb +0 -149
  222. data/lib/net/ssh/transport/packet-stream.rb +0 -236
  223. data/lib/net/ssh/transport/services.rb +0 -146
  224. data/lib/net/ssh/transport/version-negotiator.rb +0 -73
  225. data/lib/net/ssh/userauth/agent.rb +0 -222
  226. data/lib/net/ssh/userauth/constants.rb +0 -35
  227. data/lib/net/ssh/userauth/driver.rb +0 -183
  228. data/lib/net/ssh/userauth/methods/hostbased.rb +0 -119
  229. data/lib/net/ssh/userauth/methods/keyboard-interactive.rb +0 -104
  230. data/lib/net/ssh/userauth/methods/password.rb +0 -70
  231. data/lib/net/ssh/userauth/methods/publickey.rb +0 -137
  232. data/lib/net/ssh/userauth/methods/services.rb +0 -90
  233. data/lib/net/ssh/userauth/pageant.rb +0 -197
  234. data/lib/net/ssh/userauth/services.rb +0 -141
  235. data/lib/net/ssh/userauth/userkeys.rb +0 -258
  236. data/lib/net/ssh/util/buffer.rb +0 -274
  237. data/lib/net/ssh/util/prompter.rb +0 -73
  238. data/test/ALL-TESTS.rb +0 -18
  239. data/test/connection/tc_channel.rb +0 -136
  240. data/test/connection/tc_driver.rb +0 -287
  241. data/test/connection/tc_integration.rb +0 -87
  242. data/test/proxy/tc_http.rb +0 -209
  243. data/test/proxy/tc_socks4.rb +0 -148
  244. data/test/proxy/tc_socks5.rb +0 -214
  245. data/test/service/agentforward/tc_driver.rb +0 -138
  246. data/test/service/forward/tc_driver.rb +0 -289
  247. data/test/service/forward/tc_local_network_handler.rb +0 -123
  248. data/test/service/forward/tc_remote_network_handler.rb +0 -111
  249. data/test/service/process/tc_driver.rb +0 -79
  250. data/test/service/process/tc_integration.rb +0 -119
  251. data/test/service/process/tc_open.rb +0 -179
  252. data/test/service/process/tc_popen3.rb +0 -164
  253. data/test/tc_integration.rb +0 -80
  254. data/test/transport/compress/tc_none_compress.rb +0 -41
  255. data/test/transport/compress/tc_none_decompress.rb +0 -45
  256. data/test/transport/compress/tc_zlib_compress.rb +0 -61
  257. data/test/transport/compress/tc_zlib_decompress.rb +0 -48
  258. data/test/transport/kex/tc_dh.rb +0 -312
  259. data/test/transport/kex/tc_dh_gex.rb +0 -71
  260. data/test/transport/ossl/fixtures/dsa-encrypted +0 -15
  261. data/test/transport/ossl/fixtures/dsa-encrypted-bad +0 -15
  262. data/test/transport/ossl/fixtures/dsa-unencrypted +0 -12
  263. data/test/transport/ossl/fixtures/dsa-unencrypted-bad +0 -12
  264. data/test/transport/ossl/fixtures/dsa-unencrypted.pub +0 -1
  265. data/test/transport/ossl/fixtures/not-a-private-key +0 -4
  266. data/test/transport/ossl/fixtures/not-supported +0 -2
  267. data/test/transport/ossl/fixtures/rsa-encrypted +0 -18
  268. data/test/transport/ossl/fixtures/rsa-encrypted-bad +0 -18
  269. data/test/transport/ossl/fixtures/rsa-unencrypted +0 -15
  270. data/test/transport/ossl/fixtures/rsa-unencrypted-bad +0 -15
  271. data/test/transport/ossl/fixtures/rsa-unencrypted.pub +0 -1
  272. data/test/transport/ossl/hmac/tc_hmac.rb +0 -58
  273. data/test/transport/ossl/hmac/tc_md5.rb +0 -50
  274. data/test/transport/ossl/hmac/tc_md5_96.rb +0 -50
  275. data/test/transport/ossl/hmac/tc_none.rb +0 -50
  276. data/test/transport/ossl/hmac/tc_sha1.rb +0 -50
  277. data/test/transport/ossl/hmac/tc_sha1_96.rb +0 -50
  278. data/test/transport/ossl/tc_buffer.rb +0 -97
  279. data/test/transport/ossl/tc_buffer_factory.rb +0 -67
  280. data/test/transport/ossl/tc_cipher_factory.rb +0 -84
  281. data/test/transport/ossl/tc_digest_factory.rb +0 -39
  282. data/test/transport/ossl/tc_hmac_factory.rb +0 -72
  283. data/test/transport/ossl/tc_key_factory.rb +0 -199
  284. data/test/transport/tc_algorithm_negotiator.rb +0 -170
  285. data/test/transport/tc_identity_cipher.rb +0 -52
  286. data/test/transport/tc_integration.rb +0 -115
  287. data/test/transport/tc_packet_stream.rb +0 -184
  288. data/test/transport/tc_session.rb +0 -296
  289. data/test/transport/tc_version_negotiator.rb +0 -86
  290. data/test/userauth/methods/tc_hostbased.rb +0 -136
  291. data/test/userauth/methods/tc_password.rb +0 -89
  292. data/test/userauth/methods/tc_publickey.rb +0 -167
  293. data/test/userauth/tc_agent.rb +0 -223
  294. data/test/userauth/tc_driver.rb +0 -190
  295. data/test/userauth/tc_integration.rb +0 -97
  296. data/test/userauth/tc_userkeys.rb +0 -265
  297. data/test/util/tc_buffer.rb +0 -217
@@ -1,47 +1,33 @@
1
- #--
2
- # =============================================================================
3
- # Copyright (c) 2004,2005 Jamis Buck (jamis@37signals.com)
4
- # All rights reserved.
5
- #
6
- # This source file is distributed as part of the Net::SSH Secure Shell Client
7
- # library for Ruby. This file (and the library as a whole) may be used only as
8
- # allowed by either the BSD license, or the Ruby license (or, by association
9
- # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
- # distribution for the texts of these licenses.
11
- # -----------------------------------------------------------------------------
12
- # net-ssh website : http://net-ssh.rubyforge.org
13
- # project website: http://rubyforge.org/projects/net-ssh
14
- # =============================================================================
15
- #++
1
+ module Net; module SSH; module Connection
2
+
3
+ # Definitions of constants that are specific to the connection layer of the
4
+ # SSH protocol.
5
+ module Constants
6
+
7
+ #--
8
+ # Connection protocol generic messages
9
+ #++
10
+
11
+ GLOBAL_REQUEST = 80
12
+ REQUEST_SUCCESS = 81
13
+ REQUEST_FAILURE = 82
14
+
15
+ #--
16
+ # Channel related messages
17
+ #++
18
+
19
+ CHANNEL_OPEN = 90
20
+ CHANNEL_OPEN_CONFIRMATION = 91
21
+ CHANNEL_OPEN_FAILURE = 92
22
+ CHANNEL_WINDOW_ADJUST = 93
23
+ CHANNEL_DATA = 94
24
+ CHANNEL_EXTENDED_DATA = 95
25
+ CHANNEL_EOF = 96
26
+ CHANNEL_CLOSE = 97
27
+ CHANNEL_REQUEST = 98
28
+ CHANNEL_SUCCESS = 99
29
+ CHANNEL_FAILURE = 100
16
30
 
17
- module Net
18
- module SSH
19
- module Connection
20
-
21
- module Constants
22
-
23
- # Connection protocol generic messages
24
-
25
- GLOBAL_REQUEST = 80
26
- REQUEST_SUCCESS = 81
27
- REQUEST_FAILURE = 82
28
-
29
- # Channel related messages
30
-
31
- CHANNEL_OPEN = 90
32
- CHANNEL_OPEN_CONFIRMATION = 91
33
- CHANNEL_OPEN_FAILURE = 92
34
- CHANNEL_WINDOW_ADJUST = 93
35
- CHANNEL_DATA = 94
36
- CHANNEL_EXTENDED_DATA = 95
37
- CHANNEL_EOF = 96
38
- CHANNEL_CLOSE = 97
39
- CHANNEL_REQUEST = 98
40
- CHANNEL_SUCCESS = 99
41
- CHANNEL_FAILURE = 100
42
-
43
- end
44
-
45
- end
46
31
  end
47
- end
32
+
33
+ end; end end
@@ -0,0 +1,569 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/ssh/connection/channel'
3
+ require 'net/ssh/connection/constants'
4
+ require 'net/ssh/service/forward'
5
+
6
+ module Net; module SSH; module Connection
7
+
8
+ # A session class representing the connection service running on top of
9
+ # the SSH transport layer. It manages the creation of channels (see
10
+ # #open_channel), and the dispatching of messages to the various channels.
11
+ # It also encapsulates the SSH event loop (via #loop and #process),
12
+ # and serves as a central point-of-reference for all SSH-related services (e.g.
13
+ # port forwarding, SFTP, SCP, etc.).
14
+ #
15
+ # You will rarely (if ever) need to instantiate this class directly; rather,
16
+ # you'll almost always use Net::SSH.start to initialize a new network
17
+ # connection, authenticate a user, and return a new connection session,
18
+ # all in one call.
19
+ #
20
+ # Net::SSH.start("localhost", "user") do |ssh|
21
+ # # 'ssh' is an instance of Net::SSH::Connection::Session
22
+ # ssh.exec! "/etc/init.d/some_process start"
23
+ # end
24
+ class Session
25
+ include Constants, Loggable
26
+
27
+ # The underlying transport layer abstraction (see Net::SSH::Transport::Session).
28
+ attr_reader :transport
29
+
30
+ # The map of options that were used to initialize this instance.
31
+ attr_reader :options
32
+
33
+ # The collection of custom properties for this instance. (See #[] and #[]=).
34
+ attr_reader :properties
35
+
36
+ # The map of channels, each key being the local-id for the channel.
37
+ attr_reader :channels #:nodoc:
38
+
39
+ # The map of listeners that the event loop knows about. See #listen_to.
40
+ attr_reader :listeners #:nodoc:
41
+
42
+ # The map of specialized handlers for opening specific channel types. See
43
+ # #on_open_channel.
44
+ attr_reader :channel_open_handlers #:nodoc:
45
+
46
+ # The list of callbacks for pending requests. See #send_global_request.
47
+ attr_reader :pending_requests #:nodoc:
48
+
49
+ # Create a new connection service instance atop the given transport
50
+ # layer. Initializes the listeners to be only the underlying socket object.
51
+ def initialize(transport, options={})
52
+ self.logger = transport.logger
53
+
54
+ @transport = transport
55
+ @options = options
56
+
57
+ @channel_id_counter = -1
58
+ @channels = {}
59
+ @listeners = { transport.socket => nil }
60
+ @pending_requests = []
61
+ @channel_open_handlers = {}
62
+ @on_global_request = {}
63
+ @properties = (options[:properties] || {}).dup
64
+ end
65
+
66
+ # Retrieves a custom property from this instance. This can be used to
67
+ # store additional state in applications that must manage multiple
68
+ # SSH connections.
69
+ def [](key)
70
+ @properties[key]
71
+ end
72
+
73
+ # Sets a custom property for this instance.
74
+ def []=(key, value)
75
+ @properties[key] = value
76
+ end
77
+
78
+ # Returns the name of the host that was given to the transport layer to
79
+ # connect to.
80
+ def host
81
+ transport.host
82
+ end
83
+
84
+ # Closes the session gracefully, blocking until all channels have
85
+ # successfully closed, and then closes the underlying transport layer
86
+ # connection.
87
+ def close
88
+ info { "closing remaining channels (#{channels.length} open)" }
89
+ channels.each { |id, channel| channel.close }
90
+ loop(0) { channels.any? }
91
+ transport.close
92
+ end
93
+
94
+ # preserve a reference to Kernel#loop
95
+ alias :loop_forever :loop
96
+
97
+ # Returns +true+ if there are any channels currently active on this
98
+ # session. By default, this will not include "invisible" channels
99
+ # (such as those created by forwarding ports and such), but if you pass
100
+ # a +true+ value for +include_invisible+, then those will be counted.
101
+ #
102
+ # This can be useful for determining whether the event loop should continue
103
+ # to be run.
104
+ #
105
+ # ssh.loop { ssh.busy? }
106
+ def busy?(include_invisible=false)
107
+ if include_invisible
108
+ channels.any?
109
+ else
110
+ channels.any? { |id, ch| !ch[:invisible] }
111
+ end
112
+ end
113
+
114
+ # The main event loop. Calls #process until #process returns false. If a
115
+ # block is given, it is passed to #process, otherwise a default proc is
116
+ # used that just returns true if there are any channels active (see #busy?).
117
+ # The # +wait+ parameter is also passed through to #process (where it is
118
+ # interpreted as the maximum number of seconds to wait for IO.select to return).
119
+ #
120
+ # # loop for as long as there are any channels active
121
+ # ssh.loop
122
+ #
123
+ # # loop for as long as there are any channels active, but make sure
124
+ # # the event loop runs at least once per 0.1 second
125
+ # ssh.loop(0.1)
126
+ #
127
+ # # loop until ctrl-C is pressed
128
+ # int_pressed = false
129
+ # trap("INT") { int_pressed = true }
130
+ # ssh.loop(0.1) { not int_pressed }
131
+ def loop(wait=nil, &block)
132
+ running = block || Proc.new { busy? }
133
+ loop_forever { break unless process(wait, &running) }
134
+ end
135
+
136
+ # The core of the event loop. It processes a single iteration of the event
137
+ # loop. If a block is given, it should return false when the processing
138
+ # should abort, which causes #process to return false. Otherwise,
139
+ # #process returns true. The session itself is yielded to the block as its
140
+ # only argument.
141
+ #
142
+ # If +wait+ is nil (the default), this method will block until any of the
143
+ # monitored IO objects are ready to be read from or written to. If you want
144
+ # it to not block, you can pass 0, or you can pass any other numeric value
145
+ # to indicate that it should block for no more than that many seconds.
146
+ # Passing 0 is a good way to poll the connection, but if you do it too
147
+ # frequently it can make your CPU quite busy!
148
+ #
149
+ # This will also cause all active channels to be processed once each (see
150
+ # Net::SSH::Connection::Channel#on_process).
151
+ #
152
+ # # process multiple Net::SSH connections in parallel
153
+ # connections = [
154
+ # Net::SSH.start("host1", ...),
155
+ # Net::SSH.start("host2", ...)
156
+ # ]
157
+ #
158
+ # connections.each do |ssh|
159
+ # ssh.exec "grep something /in/some/files"
160
+ # end
161
+ #
162
+ # condition = Proc.new { |s| s.busy? }
163
+ #
164
+ # loop do
165
+ # connections.delete_if { |ssh| !ssh.process(0.1, &condition) }
166
+ # break if connections.empty?
167
+ # end
168
+ def process(wait=nil, &block)
169
+ return false unless preprocess(&block)
170
+
171
+ r = listeners.keys
172
+ w = r.select { |w| w.respond_to?(:pending_write?) && w.pending_write? }
173
+ readers, writers, = IO.select(r, w, nil, wait)
174
+
175
+ postprocess(readers, writers)
176
+ end
177
+
178
+ # This is called internally as part of #process. It dispatches any
179
+ # available incoming packets, and then runs Net::SSH::Connection::Channel#process
180
+ # for any active channels. If a block is given, it is invoked at the
181
+ # start of the method and again at the end, and if the block ever returns
182
+ # false, this method returns false. Otherwise, it returns true.
183
+ def preprocess
184
+ return false if block_given? && !yield(self)
185
+ dispatch_incoming_packets
186
+ channels.each { |id, channel| channel.process unless channel.closing? }
187
+ return false if block_given? && !yield(self)
188
+ return true
189
+ end
190
+
191
+ # This is called internally as part of #process. It loops over the given
192
+ # arrays of reader IO's and writer IO's, processing them as needed, and
193
+ # then calls Net::SSH::Transport::Session#rekey_as_needed to allow the
194
+ # transport layer to rekey. Then returns true.
195
+ def postprocess(readers, writers)
196
+ Array(readers).each do |reader|
197
+ if listeners[reader]
198
+ listeners[reader].call(reader)
199
+ else
200
+ if reader.fill.zero?
201
+ reader.close
202
+ stop_listening_to(reader)
203
+ end
204
+ end
205
+ end
206
+
207
+ Array(writers).each do |writer|
208
+ writer.send_pending
209
+ end
210
+
211
+ transport.rekey_as_needed
212
+
213
+ return true
214
+ end
215
+
216
+ # Send a global request of the given type. The +extra+ parameters must
217
+ # be even in number, and conform to the same format as described for
218
+ # Net::SSH::Buffer.from. If a callback is not specified, the request will
219
+ # not require a response from the server, otherwise the server is required
220
+ # to respond and indicate whether the request was successful or not. This
221
+ # success or failure is indicated by the callback being invoked, with the
222
+ # first parameter being true or false (success, or failure), and the second
223
+ # being the packet itself.
224
+ #
225
+ # Generally, Net::SSH will manage global requests that need to be sent
226
+ # (e.g. port forward requests and such are handled in the Net::SSH::Service::Forward
227
+ # class, for instance). However, there may be times when you need to
228
+ # send a global request that isn't explicitly handled by Net::SSH, and so
229
+ # this method is available to you.
230
+ #
231
+ # ssh.send_global_request("keep-alive@openssh.com")
232
+ def send_global_request(type, *extra, &callback)
233
+ info { "sending global request #{type}" }
234
+ msg = Buffer.from(:byte, GLOBAL_REQUEST, :string, type.to_s, :bool, !callback.nil?, *extra)
235
+ send_message(msg)
236
+ pending_requests << callback if callback
237
+ self
238
+ end
239
+
240
+ # Requests that a new channel be opened. By default, the channel will be
241
+ # of type "session", but if you know what you're doing you can select any
242
+ # of the channel types supported by the SSH protocol. The +extra+ parameters
243
+ # must be even in number and conform to the same format as described for
244
+ # Net::SSH::Buffer.from. If a callback is given, it will be invoked when
245
+ # the server confirms that the channel opened successfully. The sole parameter
246
+ # for the callback is the channel object itself.
247
+ #
248
+ # In general, you'll use #open_channel without any arguments; the only
249
+ # time you'd want to set the channel type or pass additional initialization
250
+ # data is if you were implementing an SSH extension.
251
+ #
252
+ # channel = ssh.open_channel do |ch|
253
+ # ch.exec "grep something /some/files" do |ch, success|
254
+ # ...
255
+ # end
256
+ # end
257
+ #
258
+ # channel.wait
259
+ def open_channel(type="session", *extra, &on_confirm)
260
+ local_id = get_next_channel_id
261
+ channel = Channel.new(self, type, local_id, &on_confirm)
262
+
263
+ msg = Buffer.from(:byte, CHANNEL_OPEN, :string, type, :long, local_id,
264
+ :long, channel.local_maximum_window_size,
265
+ :long, channel.local_maximum_packet_size, *extra)
266
+ send_message(msg)
267
+
268
+ channels[local_id] = channel
269
+ end
270
+
271
+ # A convenience method for executing a command and interacting with it. If
272
+ # no block is given, all output is printed via $stdout and $stderr. Otherwise,
273
+ # the block is called for each data and extended data packet, with three
274
+ # arguments: the channel object, a symbol indicating the data type
275
+ # (:stdout or :stderr), and the data (as a string).
276
+ #
277
+ # Note that this method returns immediately, and requires an event loop
278
+ # (see Session#loop) in order for the command to actually execute.
279
+ #
280
+ # This is effectively identical to calling #open_channel, and then
281
+ # Net::SSH::Connection::Channel#exec, and then setting up the channel
282
+ # callbacks. However, for most uses, this will be sufficient.
283
+ #
284
+ # ssh.exec "grep something /some/files" do |ch, stream, data|
285
+ # if stream == :stderr
286
+ # puts "ERROR: #{data}"
287
+ # else
288
+ # puts data
289
+ # end
290
+ # end
291
+ def exec(command, &block)
292
+ open_channel do |channel|
293
+ channel.exec(command) do |ch, success|
294
+ raise "could not execute command: #{command.inspect}" unless success
295
+
296
+ channel.on_data do |ch, data|
297
+ if block
298
+ block.call(ch, :stdout, data)
299
+ else
300
+ $stdout.print(data)
301
+ end
302
+ end
303
+
304
+ channel.on_extended_data do |ch, type, data|
305
+ if block
306
+ block.call(ch, :stderr, data)
307
+ else
308
+ $stderr.print(data)
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ # Same as #exec, except this will block until the command finishes. Also,
316
+ # if a block is not given, this will return all output (stdout and stderr)
317
+ # as a single string.
318
+ #
319
+ # matches = ssh.exec!("grep something /some/files")
320
+ def exec!(command, &block)
321
+ block ||= Proc.new do |ch, type, data|
322
+ ch[:result] ||= ""
323
+ ch[:result] << data
324
+ end
325
+
326
+ channel = exec(command, &block)
327
+ channel.wait
328
+
329
+ return channel[:result]
330
+ end
331
+
332
+ # Enqueues a message to be sent to the server as soon as the socket is
333
+ # available for writing. Most programs will never need to call this, but
334
+ # if you are implementing an extension to the SSH protocol, or if you
335
+ # need to send a packet that Net::SSH does not directly support, you can
336
+ # use this to send it.
337
+ #
338
+ # ssh.send_message(Buffer.from(:byte, REQUEST_SUCCESS).to_s)
339
+ def send_message(message)
340
+ transport.enqueue_message(message)
341
+ end
342
+
343
+ # Adds an IO object for the event loop to listen to. If a callback
344
+ # is given, it will be invoked when the io is ready to be read, otherwise,
345
+ # the io will merely have its #fill method invoked.
346
+ #
347
+ # Any +io+ value passed to this method _must_ have mixed into it the
348
+ # Net::SSH::BufferedIo functionality, typically by calling #extend on the
349
+ # object.
350
+ #
351
+ # The following example executes a process on the remote server, opens
352
+ # a socket to somewhere, and then pipes data from that socket to the
353
+ # remote process' stdin stream:
354
+ #
355
+ # channel = ssh.open_channel do |ch|
356
+ # ch.exec "/some/process/that/wants/input" do |ch, success|
357
+ # abort "can't execute!" unless success
358
+ #
359
+ # io = TCPSocket.new(somewhere, port)
360
+ # io.extend(Net::SSH::BufferedIo)
361
+ # ssh.listen_to(io)
362
+ #
363
+ # ch.on_process do
364
+ # if io.available > 0
365
+ # ch.send_data(io.read_available)
366
+ # end
367
+ # end
368
+ #
369
+ # ch.on_close do
370
+ # ssh.stop_listening_to(io)
371
+ # io.close
372
+ # end
373
+ # end
374
+ # end
375
+ #
376
+ # channel.wait
377
+ def listen_to(io, &callback)
378
+ listeners[io] = callback
379
+ end
380
+
381
+ # Removes the given io object from the listeners collection, so that the
382
+ # event loop will no longer monitor it.
383
+ def stop_listening_to(io)
384
+ listeners.delete(io)
385
+ end
386
+
387
+ # Returns a reference to the Net::SSH::Service::Forward service, which can
388
+ # be used for forwarding ports over SSH.
389
+ def forward
390
+ @forward ||= Service::Forward.new(self)
391
+ end
392
+
393
+ # Registers a handler to be invoked when the server wants to open a
394
+ # channel on the client. The callback receives the connection object,
395
+ # the new channel object, and the packet itself as arguments, and should
396
+ # raise ChannelOpenFailed if it is unable to open the channel for some
397
+ # reason. Otherwise, the channel will be opened and a confirmation message
398
+ # sent to the server.
399
+ #
400
+ # This is used by the Net::SSH::Service::Forward service to open a channel
401
+ # when a remote forwarded port receives a connection. However, you are
402
+ # welcome to register handlers for other channel types, as needed.
403
+ def on_open_channel(type, &block)
404
+ channel_open_handlers[type] = block
405
+ end
406
+
407
+ # Registers a handler to be invoked when the server sends a global request
408
+ # of the given type. The callback receives the request data as the first
409
+ # parameter, and true/false as the second (indicating whether a response
410
+ # is required). If the callback sends the response, it should return
411
+ # :sent. Otherwise, if it returns true, REQUEST_SUCCESS will be sent, and
412
+ # if it returns false, REQUEST_FAILURE will be sent.
413
+ def on_global_request(type, &block)
414
+ old, @on_global_request[type] = @on_global_request[type], block
415
+ old
416
+ end
417
+
418
+ private
419
+
420
+ # Read all pending packets from the connection and dispatch them as
421
+ # appropriate. Returns as soon as there are no more pending packets.
422
+ def dispatch_incoming_packets
423
+ while packet = transport.poll_message
424
+ unless MAP.key?(packet.type)
425
+ raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})"
426
+ end
427
+
428
+ send(MAP[packet.type], packet)
429
+ end
430
+ end
431
+
432
+ # Returns the next available channel id to be assigned, and increments
433
+ # the counter.
434
+ def get_next_channel_id
435
+ @channel_id_counter += 1
436
+ end
437
+
438
+ # Invoked when a global request is received. The registered global
439
+ # request callback will be invoked, if one exists, and the necessary
440
+ # reply returned.
441
+ def global_request(packet)
442
+ info { "global request received: #{packet[:request_type]} #{packet[:want_reply]}" }
443
+ callback = @on_global_request[packet[:request_type]]
444
+ result = callback ? callback.call(packet[:request_data], packet[:want_reply]) : false
445
+
446
+ if result != :sent && result != true && result != false
447
+ raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}"
448
+ end
449
+
450
+ if packet[:want_reply] && result != :sent
451
+ msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE)
452
+ send_message(msg)
453
+ end
454
+ end
455
+
456
+ # Invokes the next pending request callback with +true+.
457
+ def request_success(packet)
458
+ info { "global request success" }
459
+ callback = pending_requests.shift
460
+ callback.call(true, packet) if callback
461
+ end
462
+
463
+ # Invokes the next pending request callback with +false+.
464
+ def request_failure(packet)
465
+ info { "global request failure" }
466
+ callback = pending_requests.shift
467
+ callback.call(false, packet) if callback
468
+ end
469
+
470
+ # Called when the server wants to open a channel. If no registered
471
+ # channel handler exists for the given channel type, CHANNEL_OPEN_FAILURE
472
+ # is returned, otherwise the callback is invoked and everything proceeds
473
+ # accordingly.
474
+ def channel_open(packet)
475
+ info { "channel open #{packet[:channel_type]}" }
476
+
477
+ local_id = get_next_channel_id
478
+ channel = Channel.new(self, packet[:channel_type], local_id)
479
+ channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
480
+
481
+ callback = channel_open_handlers[packet[:channel_type]]
482
+
483
+ if callback
484
+ begin
485
+ callback[self, channel, packet]
486
+ rescue ChannelOpenFailed => err
487
+ failure = [err.code, err.reason]
488
+ else
489
+ channels[local_id] = channel
490
+ msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size)
491
+ end
492
+ else
493
+ failure = [3, "unknown channel type #{channel.type}"]
494
+ end
495
+
496
+ if failure
497
+ error { failure.inspect }
498
+ msg = Buffer.from(:byte, CHANNEL_OPEN_FAILURE, :long, channel.remote_id, :long, failure[0], :string, failure[1], :string, "")
499
+ end
500
+
501
+ send_message(msg)
502
+ end
503
+
504
+ def channel_open_confirmation(packet)
505
+ info { "channel_open_confirmation: #{packet[:local_id]} #{packet[:remote_id]} #{packet[:window_size]} #{packet[:packet_size]}" }
506
+ channel = channels[packet[:local_id]]
507
+ channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
508
+ end
509
+
510
+ def channel_open_failure(packet)
511
+ error { "channel_open_failed: #{packet[:local_id]} #{packet[:reason_code]} #{packet[:description]}" }
512
+ channel = channels.delete(packet[:local_id])
513
+ channel.do_open_failed(packet[:reason_code], packet[:description])
514
+ end
515
+
516
+ def channel_window_adjust(packet)
517
+ info { "channel_window_adjust: #{packet[:local_id]} +#{packet[:extra_bytes]}" }
518
+ channels[packet[:local_id]].do_window_adjust(packet[:extra_bytes])
519
+ end
520
+
521
+ def channel_request(packet)
522
+ info { "channel_request: #{packet[:local_id]} #{packet[:request]} #{packet[:want_reply]}" }
523
+ channels[packet[:local_id]].do_request(packet[:request], packet[:want_reply], packet[:request_data])
524
+ end
525
+
526
+ def channel_data(packet)
527
+ info { "channel_data: #{packet[:local_id]} #{packet[:data].length}b" }
528
+ channels[packet[:local_id]].do_data(packet[:data])
529
+ end
530
+
531
+ def channel_extended_data(packet)
532
+ info { "channel_extended_data: #{packet[:local_id]} #{packet[:data_type]} #{packet[:data].length}b" }
533
+ channels[packet[:local_id]].do_extended_data(packet[:data_type], packet[:data])
534
+ end
535
+
536
+ def channel_eof(packet)
537
+ info { "channel_eof: #{packet[:local_id]}" }
538
+ channels[packet[:local_id]].do_eof
539
+ end
540
+
541
+ def channel_close(packet)
542
+ info { "channel_close: #{packet[:local_id]}" }
543
+
544
+ channel = channels[packet[:local_id]]
545
+ channel.close
546
+
547
+ channels.delete(packet[:local_id])
548
+ channel.do_close
549
+ end
550
+
551
+ def channel_success(packet)
552
+ info { "channel_success: #{packet[:local_id]}" }
553
+ channels[packet[:local_id]].do_success
554
+ end
555
+
556
+ def channel_failure(packet)
557
+ info { "channel_failure: #{packet[:local_id]}" }
558
+ channels[packet[:local_id]].do_failure
559
+ end
560
+
561
+ MAP = Constants.constants.inject({}) do |memo, name|
562
+ value = const_get(name)
563
+ next unless Integer === value
564
+ memo[value] = name.downcase.to_sym
565
+ memo
566
+ end
567
+ end
568
+
569
+ end; end; end