net-ssh 1.1.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,340 +1,262 @@
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
- #++
16
-
17
1
  require 'socket'
18
2
  require 'timeout'
19
- require 'net/ssh/transport/constants'
20
- require 'net/ssh/transport/errors'
3
+
4
+ require 'net/ssh/errors'
5
+ require 'net/ssh/loggable'
21
6
  require 'net/ssh/version'
7
+ require 'net/ssh/transport/algorithms'
8
+ require 'net/ssh/transport/constants'
9
+ require 'net/ssh/transport/packet_stream'
10
+ require 'net/ssh/transport/server_version'
11
+ require 'net/ssh/verifiers/null'
12
+ require 'net/ssh/verifiers/strict'
13
+ require 'net/ssh/verifiers/lenient'
22
14
 
23
- module Net
24
- module SSH
25
- module Transport
26
-
27
- # Represents a low-level SSH session, at the transport protocol level.
28
- # This handles the algorithm negotiation and key exchange for any SSH
29
- # connection.
30
- class Session
31
- include Constants
32
-
33
- # the unique session identifier
34
- attr_reader :session_id
35
-
36
- # the collection of algorithms currently being used
37
- attr_reader :algorithms
38
-
39
- # the hostname that was requested
40
- attr_reader :host
41
-
42
- # the port that was requested
43
- attr_reader :port
44
-
45
- attr_writer :logger
46
- attr_writer :default_port
47
- attr_writer :version_negotiator
48
- attr_writer :algorithm_negotiator
49
- attr_writer :socket_factory
50
- attr_writer :packet_sender
51
- attr_writer :packet_receiver
52
- attr_writer :ciphers
53
- attr_writer :hmacs
54
- attr_writer :kexs
55
- attr_writer :compressors
56
- attr_writer :decompressors
57
-
58
- # The name that Net::SSH reports for itself
59
- NAME = "Ruby/Net::SSH"
60
-
61
- # The SSH protocol supported by Net::SSH.
62
- PROTOCOL = "SSH-2.0"
63
-
64
- # Returns the version string of this client.
65
- def self.version
66
- "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}"
67
- end
15
+ module Net; module SSH; module Transport
68
16
 
69
- VALID_OPTIONS = [ :port, :host_key, :kex, :encryption, :hmac,
70
- :compression, :languages, :compression_level, :proxy,
71
- :timeout ]
17
+ # The transport layer represents the lowest level of the SSH protocol, and
18
+ # implements basic message exchanging and protocol initialization. It will
19
+ # never be instantiated directly (unless you really know what you're about),
20
+ # but will instead be created for you automatically when you create a new
21
+ # SSH session via Net::SSH.start.
22
+ class Session
23
+ include Constants, Loggable
72
24
 
73
- # Create a new connection to the given host. This will negotiate the
74
- # algorithms to use and exchange the keys. A block must be given. The
75
- # uninitialized +self+ will be passed to the block, so that dependencies
76
- # may be injected.
77
- def initialize( host, options={} )
78
- @saved_message = nil
79
- @session_id = nil
80
- @host = host
25
+ # The standard port for the SSH protocol.
26
+ DEFAULT_PORT = 22
81
27
 
82
- yield self
28
+ # The host to connect to, as given to the constructor.
29
+ attr_reader :host
83
30
 
84
- invalid_options = options.keys - VALID_OPTIONS
31
+ # The port number to connect to, as given in the options to the constructor.
32
+ # If no port number was given, this will default to DEFAULT_PORT.
33
+ attr_reader :port
85
34
 
86
- unless invalid_options.empty?
87
- raise ArgumentError,
88
- "invalid option(s) to #{self.class}: #{invalid_options.inspect}"
89
- end
35
+ # The underlying socket object being used to communicate with the remote
36
+ # host.
37
+ attr_reader :socket
90
38
 
91
- @logger.debug "connecting" if @logger.debug?
39
+ # The ServerVersion instance that encapsulates the negotiated protocol
40
+ # version.
41
+ attr_reader :server_version
92
42
 
93
- @port = options[ :port ] || @default_port
94
- @socket = timeout( options[:timeout] || 0 ) do
95
- ( options[:proxy] || @socket_factory ).open( host, @port )
96
- end
43
+ # The Algorithms instance used to perform key exchanges.
44
+ attr_reader :algorithms
97
45
 
98
- @packet_sender.socket = @socket
99
- @packet_receiver.socket = @socket
46
+ # The host-key verifier object used to verify host keys, to ensure that
47
+ # the connection is not being spoofed.
48
+ attr_reader :host_key_verifier
100
49
 
101
- @kex_info = {
102
- :client_version_string => self.class.version,
103
- :server_version_string =>
104
- @version_negotiator.negotiate( @socket, self.class.version ) }
50
+ # The hash of options that were given to the object at initialization.
51
+ attr_reader :options
105
52
 
106
- @options = options
107
- kexinit
108
- end
53
+ # Instantiates a new transport layer abstraction. This will block until
54
+ # the initial key exchange completes, leaving you with a ready-to-use
55
+ # transport session.
56
+ def initialize(host, options={})
57
+ self.logger = options[:logger]
109
58
 
110
- # Returns info about the remote peer
111
- def peer
112
- @peer ||= begin
113
- addr = @socket.getpeername
114
- ip_address = Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
115
- { :ip => ip_address, :port => @port.to_i, :host => @host }
116
- end
117
- end
59
+ @host = host
60
+ @port = options[:port] || DEFAULT_PORT
61
+ @options = options
118
62
 
119
- # Returns the name of the client's host, as reported by the socket.
120
- def client_name
121
- return @hostname if defined? @hostname
122
-
123
- sockaddr = @socket.getsockname
124
- begin
125
- @hostname =
126
- Socket.getnameinfo( sockaddr, Socket::NI_NAMEREQD ).first
127
- rescue
128
- begin
129
- @hostname = Socket.getnameinfo( sockaddr ).first
130
- rescue
131
- begin
132
- @hostname = Socket.gethostbyname( Socket.gethostname ).first
133
- rescue
134
- @logger.error "the client ipaddr/name could not be determined"
135
- end
136
- end
137
- end
63
+ debug { "establishing connection to #{@host}:#{@port}" }
64
+ factory = options[:proxy] || TCPSocket
65
+ @socket = timeout(options[:timeout] || 0) { factory.open(@host, @port) }
66
+ @socket.extend(PacketStream)
67
+ @socket.logger = @logger
138
68
 
139
- return @hostname
140
- end
69
+ debug { "connection established" }
141
70
 
142
- def kexinit
143
- @doing_kexinit = true
144
- @algorithms = @algorithm_negotiator.negotiate( self, @options )
71
+ @queue = []
145
72
 
146
- @kex_info[ :server_algorithm_packet ] = @algorithms.server_packet
147
- @kex_info[ :client_algorithm_packet ] = @algorithms.client_packet
73
+ @host_key_verifier = select_host_key_verifier(options[:paranoid])
148
74
 
149
- exchange_keys
150
- @doing_kexinit = false
151
- end
152
- private :kexinit
75
+ @server_version = ServerVersion.new(socket, logger)
76
+
77
+ @algorithms = Algorithms.new(self, options)
78
+ wait { algorithms.initialized? }
79
+ end
153
80
 
154
- # Closes the connection.
155
- def close
156
- # TODO: send a DISCONNECT message to the server to close gracefully
157
- @socket.close
81
+ # Returns the host (and possibly IP address) in a format compatible with
82
+ # SSH known-host files.
83
+ def host_as_string
84
+ @host_as_string ||= begin
85
+ string = "#{host}"
86
+ string = "[#{string}]:#{port}" if port != DEFAULT_PORT
87
+ if socket.peer_ip != host
88
+ string2 = socket.peer_ip
89
+ string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
90
+ string << "," << string2
158
91
  end
92
+ string
93
+ end
94
+ end
159
95
 
160
- def get_kex_byte_requirement
161
- need = 0
96
+ # Cleans up (see PacketStream#cleanup) and closes the underlying socket.
97
+ def close
98
+ socket.cleanup
99
+ socket.close
100
+ end
162
101
 
163
- [ @algorithms.encryption_s2c,
164
- @algorithms.encryption_c2s
165
- ].each do |alg|
166
- key_len, block_size = @ciphers.get_lengths( alg )
167
- need = key_len if need < key_len
168
- need = block_size if need < block_size
169
- end
102
+ # Returns a new service_request packet for the given service name, ready
103
+ # for sending to the server.
104
+ def service_request(service)
105
+ Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
106
+ end
170
107
 
171
- [ @algorithms.mac_c2s, @algorithms.mac_s2c ].each do |alg|
172
- key_len = @hmacs.get_key_length( alg )
173
- need = key_len if need < key_len
174
- end
108
+ # Requests a rekey operation, and blocks until the operation completes.
109
+ # If a rekey is already pending, this returns immediately, having no
110
+ # effect.
111
+ def rekey!
112
+ if !algorithms.pending?
113
+ algorithms.rekey!
114
+ wait { algorithms.initialized? }
115
+ end
116
+ end
175
117
 
176
- return need
177
- end
178
- private :get_kex_byte_requirement
179
-
180
- # Exchanges keys with the server, using the kex algorithm negotiated
181
- # during the algorithm negotiation phase. After finishing this phase,
182
- # further packets sent to or from the server will be encrypted and
183
- # (possibly) compressed.
184
- def exchange_keys
185
- @kex_info[ :need_bytes ] = get_kex_byte_requirement
186
-
187
- kex = @kexs.fetch( @algorithms.kex )
188
- result = kex.exchange_keys( self, @kex_info )
189
-
190
- @shared_secret = result[ :shared_secret ]
191
- hash = result[ :session_id ]
192
- @session_id = hash unless @session_id
193
- @server_key = result[ :server_key ]
194
- @hashing_algorithm = result[ :hashing_algorithm ]
195
-
196
- # prepare the ciphers, et. al.
197
- secret_bin = @shared_secret.to_ssh
198
-
199
- iv_c2s = @hashing_algorithm.digest( secret_bin +
200
- hash +
201
- "A" +
202
- @session_id )
203
- iv_s2c = @hashing_algorithm.digest( secret_bin +
204
- hash +
205
- "B" +
206
- @session_id )
207
- key_c2s = @hashing_algorithm.digest( secret_bin +
208
- hash +
209
- "C" +
210
- @session_id )
211
- key_s2c = @hashing_algorithm.digest( secret_bin +
212
- hash +
213
- "D" +
214
- @session_id )
215
- mac_key_c2s = @hashing_algorithm.digest( secret_bin +
216
- hash +
217
- "E" +
218
- @session_id )
219
- mac_key_s2c = @hashing_algorithm.digest( secret_bin +
220
- hash +
221
- "F" +
222
- @session_id )
223
-
224
- cipher_c2s = @ciphers.get(
225
- @algorithms.encryption_c2s, iv_c2s, key_c2s,
226
- secret_bin, hash, @hashing_algorithm,
227
- true )
228
-
229
- cipher_s2c = @ciphers.get(
230
- @algorithms.encryption_s2c, iv_s2c, key_s2c,
231
- secret_bin, hash, @hashing_algorithm,
232
- false )
233
-
234
- mac_c2s = @hmacs.get( @algorithms.mac_c2s, mac_key_c2s );
235
- mac_s2c = @hmacs.get( @algorithms.mac_s2c, mac_key_s2c );
236
-
237
- compression_c2s = @compressors[ @algorithms.compression_c2s ].new(
238
- :level => @algorithms.compression_level )
239
- compression_s2c = @decompressors[ @algorithms.compression_s2c ].new
240
-
241
- @packet_sender.set_algorithms cipher_c2s, mac_c2s, compression_c2s
242
- @packet_receiver.set_algorithms cipher_s2c, mac_s2c, compression_s2c
243
- end
244
- private :exchange_keys
245
-
246
- # Waits for the next message from the server, handling common requests
247
- # like DISCONNECT, IGNORE, DEBUG, and KEXINIT in the background. The
248
- # next message is returned as a [ type, buffer ] tuple, where the buffer
249
- # is a Net::SSH::Util::ReaderBuffer.
250
- def wait_for_message
251
- buffer = type = nil
252
-
253
- if @saved_message
254
- type, buffer = @saved_message
255
- @logger.debug "returning saved message: #{type}" if @logger.debug?
256
- @saved_message = nil
257
- else
258
- loop do
259
- if @logger.debug?
260
- @logger.debug "waiting for packet from server..."
261
- end
262
-
263
- buffer = @packet_receiver.get
264
- next unless buffer
265
-
266
- type = buffer.read_byte
267
- @logger.debug "got packet of type #{type}" if @logger.debug?
268
-
269
- case type
270
- when DISCONNECT
271
- reason_code = buffer.read_long
272
- description = buffer.read_string
273
- language = buffer.read_string
274
- raise Net::SSH::Transport::Disconnect,
275
- "disconnected: #{description} (#{reason_code})"
276
-
277
- when IGNORE
278
- # do nothing
279
- @logger.info "received IGNORE message " +
280
- "(#{buffer.read_string.inspect})" if @logger.debug?
281
-
282
- when DEBUG
283
- # do nothing
284
- @logger.info "received DEBUG message" if @logger.debug?
285
- always_display = buffer.read_bool
286
- message = buffer.read_string
287
- language = buffer.read_string
288
- if always_display
289
- @logger.warn "#{message} (#{language})" if @logger.warn?
290
- else
291
- @logger.debug "#{message} (#{language})" if @logger.debug?
292
- end
293
-
294
- when KEXINIT
295
- # unless we're already doing a key-exchange, do key
296
- # re-exchange
297
- if !@doing_kexinit
298
- @logger.info "re-key requested" if @logger.info?
299
- @saved_message = [ type, buffer ]
300
- kexinit
301
- else
302
- break
303
- end
304
-
305
- else
306
- break
307
- end
308
- end
309
- end
118
+ # Returns immediately if a rekey is already in process. Otherwise, if a
119
+ # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
120
+ # one is performed, causing this method to block until it completes.
121
+ def rekey_as_needed
122
+ return if algorithms.pending?
123
+ socket.if_needs_rekey? { rekey! }
124
+ end
310
125
 
311
- return type, buffer
312
- end
126
+ # Returns a hash of information about the peer (remote) side of the socket,
127
+ # including :ip, :port, :host, and :canonized (see #host_as_string).
128
+ def peer
129
+ @peer ||= { :ip => socket.peer_ip, :port => @port.to_i, :host => @host, :canonized => host_as_string }
130
+ end
313
131
 
314
- # Sends the given payload, using the currently configured
315
- # OutgoingPacketStream.
316
- def send_message( message )
317
- if @logger.debug?
318
- @logger.debug "sending message >>#{message.to_s.inspect}<<"
319
- end
132
+ # Blocks until a new packet is available to be read, and returns that
133
+ # packet. See #poll_message.
134
+ def next_message
135
+ poll_message(:block)
136
+ end
320
137
 
321
- @packet_sender.send message
138
+ # Tries to read the next packet from the socket. If mode is :nonblock (the
139
+ # default), this will not block and will return nil if there are no packets
140
+ # waiting to be read. Otherwise, this will block until a packet is
141
+ # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
142
+ # DEBUG, and KEXINIT) are handled silently by this method, and will never
143
+ # be returned.
144
+ #
145
+ # If a key-exchange is in process and a disallowed packet type is
146
+ # received, it will be enqueued and otherwise ignored. When a key-exchange
147
+ # is not in process, and consume_queue is true, packets will be first
148
+ # read from the queue before the socket is queried.
149
+ def poll_message(mode=:nonblock, consume_queue=true)
150
+ loop do
151
+ if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
152
+ return @queue.shift
322
153
  end
323
154
 
324
- # Returns true if there are bytes to be read on the socket. Note that
325
- # this only means there is an encrypted packet ready to be read, not
326
- # that there is data available to any particular SSH channel.
327
- def reader_ready?
328
- IO.select([@socket],nil,nil,0) != nil
329
- end
155
+ packet = socket.next_packet(mode)
156
+ return nil if packet.nil?
157
+
158
+ case packet.type
159
+ when DISCONNECT
160
+ raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})"
161
+
162
+ when IGNORE
163
+ debug { "IGNORE packet recieved: #{packet[:data].inspect}" }
164
+
165
+ when UNIMPLEMENTED
166
+ lwarn { "UNIMPLEMENTED: #{packet[:number]}" }
330
167
 
331
- # Sends an IGNORE packet to the server, as a way to ping the connection
332
- # and make sure the server knows the client is still active.
333
- def ping!
334
- send_message [IGNORE, 4, "ping"].pack("cNA4")
168
+ when DEBUG
169
+ send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
170
+
171
+ when KEXINIT
172
+ algorithms.accept_kexinit(packet)
173
+
174
+ else
175
+ return packet if algorithms.allow?(packet)
176
+ push(packet)
335
177
  end
336
178
  end
179
+ end
180
+
181
+ # Waits (blocks) until the given block returns true. If no block is given,
182
+ # this just waits long enough to see if there are any pending packets. Any
183
+ # packets read are enqueued (see #push).
184
+ def wait
185
+ loop do
186
+ break if block_given? && yield
187
+ message = poll_message(:nonblock, false)
188
+ push(message) if message
189
+ break if !block_given?
190
+ end
191
+ end
192
+
193
+ # Adds the given packet to the packet queue. If the queue is non-empty,
194
+ # #poll_message will return packets from the queue in the order they
195
+ # were received.
196
+ def push(packet)
197
+ @queue.push(packet)
198
+ end
199
+
200
+ # Sends the given message via the packet stream, blocking until the
201
+ # entire message has been sent.
202
+ def send_message(message)
203
+ socket.send_packet(message)
204
+ end
205
+
206
+ # Enqueues the given message, such that it will be sent at the earliest
207
+ # opportunity. This does not block, but returns immediately.
208
+ def enqueue_message(message)
209
+ socket.enqueue_packet(message)
210
+ end
211
+
212
+ # Configure's the packet stream's client state with the given set of
213
+ # options. This is typically used to define the cipher, compression, and
214
+ # hmac algorithms to use when sending packets to the server.
215
+ def configure_client(options={})
216
+ socket.client.set(options)
217
+ end
337
218
 
219
+ # Configure's the packet stream's server state with the given set of
220
+ # options. This is typically used to define the cipher, compression, and
221
+ # hmac algorithms to use when reading packets from the server.
222
+ def configure_server(options={})
223
+ socket.server.set(options)
338
224
  end
225
+
226
+ # Sets a new hint for the packet stream, which the packet stream may use
227
+ # to change its behavior. (See PacketStream#hints).
228
+ def hint(which, value=true)
229
+ socket.hints[which] = value
230
+ end
231
+
232
+ public
233
+
234
+ # this method is primarily for use in tests
235
+ attr_reader :queue #:nodoc:
236
+
237
+ private
238
+
239
+ # Instantiates a new host-key verification class, based on the value of
240
+ # the parameter. When true or nil, the default Lenient verifier is
241
+ # returned. If it is false, the Null verifier is returned, and if it is
242
+ # :very, the Strict verifier is returned. If the argument happens to
243
+ # respond to :verify, it is returned directly. Otherwise, an exception
244
+ # is raised.
245
+ def select_host_key_verifier(paranoid)
246
+ case paranoid
247
+ when true, nil then
248
+ Net::SSH::Verifiers::Lenient.new
249
+ when false then
250
+ Net::SSH::Verifiers::Null.new
251
+ when :very then
252
+ Net::SSH::Verifiers::Strict.new
253
+ else
254
+ if paranoid.respond_to?(:verify)
255
+ paranoid
256
+ else
257
+ raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}"
258
+ end
259
+ end
260
+ end
339
261
  end
340
- end
262
+ end; end; end