poolparty 1.3.4 → 1.3.6

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 (174) hide show
  1. data/Rakefile +2 -2
  2. data/VERSION.yml +1 -1
  3. data/bin/cloud-bootstrap +1 -0
  4. data/bin/cloud-configure +1 -0
  5. data/bin/cloud-contract +1 -0
  6. data/bin/cloud-misc +34 -0
  7. data/bin/cloud-setup +36 -0
  8. data/bin/cloud-ssh +4 -1
  9. data/config/jeweler.rb +4 -3
  10. data/examples/monitored_cloud.rb +1 -1
  11. data/examples/thrift/thrift_example.rb +5 -3
  12. data/examples/vmware.rb +28 -0
  13. data/lib/cloud_providers/cloud_provider_instance.rb +14 -5
  14. data/lib/cloud_providers/connections.rb +1 -1
  15. data/lib/core/file.rb +12 -0
  16. data/lib/core/object.rb +2 -2
  17. data/lib/dependency_resolvers/base.rb +1 -1
  18. data/lib/dependency_resolvers/chef.rb +9 -7
  19. data/lib/dependency_resolvers/proxy_object.rb +11 -3
  20. data/lib/mixins/askable.rb +16 -7
  21. data/lib/poolparty/base.rb +8 -7
  22. data/lib/poolparty/cloud.rb +77 -7
  23. data/lib/poolparty/default.rb +1 -0
  24. data/lib/poolparty/installer.rb +8 -4
  25. data/lib/poolparty/installers/ec2.rb +75 -5
  26. data/lib/poolparty/installers/vmware.rb +17 -5
  27. data/lib/poolparty/plugin.rb +1 -5
  28. data/lib/poolparty/plugins/apache.rb +10 -7
  29. data/lib/poolparty/plugins/apache2/base.conf.erb +2 -2
  30. data/lib/poolparty/plugins/apache2/browser_fixes.conf.erb +1 -1
  31. data/lib/poolparty/plugins/apache2/passenger_site.rb +2 -2
  32. data/lib/poolparty/plugins/collectd/templates/collectd.conf.erb +369 -0
  33. data/lib/poolparty/plugins/collectd.rb +24 -0
  34. data/lib/poolparty/plugins/hermes.rb +89 -0
  35. data/lib/poolparty/pool.rb +33 -3
  36. data/lib/poolparty/resource.rb +32 -18
  37. data/lib/poolparty/resources/directory.rb +5 -1
  38. data/lib/poolparty/resources/exec.rb +2 -2
  39. data/lib/poolparty/resources/file.rb +8 -2
  40. data/lib/poolparty/resources/gem_package.rb +2 -2
  41. data/lib/poolparty/resources/line.rb +23 -6
  42. data/lib/poolparty/resources/mount.rb +2 -2
  43. data/lib/poolparty/resources/package.rb +2 -2
  44. data/lib/poolparty/resources/service.rb +2 -2
  45. data/lib/poolparty/resources/user.rb +2 -2
  46. data/lib/poolparty/resources/variable.rb +4 -3
  47. data/lib/poolparty.rb +5 -3
  48. data/lib/proto/command_interface_handler.rb +17 -1
  49. data/lib/proto/gen-py/cloudthrift/CommandInterface.pyc +0 -0
  50. data/lib/proto/gen-py/cloudthrift/__init__.pyc +0 -0
  51. data/lib/proto/gen-py/cloudthrift/constants.pyc +0 -0
  52. data/lib/proto/gen-py/cloudthrift/ttypes.pyc +0 -0
  53. data/lib/proto/gen-py/thrift/Thrift.pyc +0 -0
  54. data/lib/proto/gen-py/thrift/__init__.pyc +0 -0
  55. data/lib/proto/gen-py/thrift/protocol/TBinaryProtocol.pyc +0 -0
  56. data/lib/proto/gen-py/thrift/protocol/TProtocol.pyc +0 -0
  57. data/lib/proto/gen-py/thrift/protocol/__init__.pyc +0 -0
  58. data/lib/proto/gen-py/thrift/transport/TSocket.pyc +0 -0
  59. data/lib/proto/gen-py/thrift/transport/TTransport.pyc +0 -0
  60. data/lib/proto/gen-py/thrift/transport/__init__.pyc +0 -0
  61. data/test/lib/dependency_resolvers/chef_test.rb +92 -100
  62. data/test/lib/poolparty/base_test.rb +13 -0
  63. data/test/lib/poolparty/cloud_test.rb +50 -2
  64. data/test/lib/poolparty/monitor_test.rb +2 -2
  65. data/test/lib/poolparty/resource_test.rb +5 -0
  66. data/test/lib/poolparty/resources/line_test.rb +3 -3
  67. data/test/lib/poolparty/resources/service_test.rb +1 -1
  68. data/test/lib/poolparty/resources/variable_test.rb +33 -10
  69. data/vendor/gems/net-ssh/CHANGELOG.rdoc +127 -0
  70. data/vendor/gems/net-ssh/Manifest +104 -0
  71. data/vendor/gems/net-ssh/README.rdoc +110 -0
  72. data/vendor/gems/net-ssh/Rakefile +26 -0
  73. data/vendor/gems/net-ssh/THANKS.rdoc +16 -0
  74. data/vendor/gems/net-ssh/lib/net/ssh/authentication/agent.rb +176 -0
  75. data/vendor/gems/net-ssh/lib/net/ssh/authentication/constants.rb +18 -0
  76. data/vendor/gems/net-ssh/lib/net/ssh/authentication/key_manager.rb +193 -0
  77. data/vendor/gems/net-ssh/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  78. data/vendor/gems/net-ssh/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
  79. data/vendor/gems/net-ssh/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
  80. data/vendor/gems/net-ssh/lib/net/ssh/authentication/methods/password.rb +39 -0
  81. data/vendor/gems/net-ssh/lib/net/ssh/authentication/methods/publickey.rb +92 -0
  82. data/vendor/gems/net-ssh/lib/net/ssh/authentication/pageant.rb +183 -0
  83. data/vendor/gems/net-ssh/lib/net/ssh/authentication/session.rb +134 -0
  84. data/vendor/gems/net-ssh/lib/net/ssh/buffer.rb +340 -0
  85. data/vendor/gems/net-ssh/lib/net/ssh/buffered_io.rb +149 -0
  86. data/vendor/gems/net-ssh/lib/net/ssh/config.rb +181 -0
  87. data/vendor/gems/net-ssh/lib/net/ssh/connection/channel.rb +625 -0
  88. data/vendor/gems/net-ssh/lib/net/ssh/connection/constants.rb +33 -0
  89. data/vendor/gems/net-ssh/lib/net/ssh/connection/session.rb +596 -0
  90. data/vendor/gems/net-ssh/lib/net/ssh/connection/term.rb +178 -0
  91. data/vendor/gems/net-ssh/lib/net/ssh/errors.rb +85 -0
  92. data/vendor/gems/net-ssh/lib/net/ssh/key_factory.rb +102 -0
  93. data/vendor/gems/net-ssh/lib/net/ssh/known_hosts.rb +129 -0
  94. data/vendor/gems/net-ssh/lib/net/ssh/loggable.rb +61 -0
  95. data/vendor/gems/net-ssh/lib/net/ssh/packet.rb +102 -0
  96. data/vendor/gems/net-ssh/lib/net/ssh/prompt.rb +93 -0
  97. data/vendor/gems/net-ssh/lib/net/ssh/proxy/errors.rb +14 -0
  98. data/vendor/gems/net-ssh/lib/net/ssh/proxy/http.rb +94 -0
  99. data/vendor/gems/net-ssh/lib/net/ssh/proxy/socks4.rb +70 -0
  100. data/vendor/gems/net-ssh/lib/net/ssh/proxy/socks5.rb +129 -0
  101. data/vendor/gems/net-ssh/lib/net/ssh/ruby_compat.rb +7 -0
  102. data/vendor/gems/net-ssh/lib/net/ssh/service/forward.rb +267 -0
  103. data/vendor/gems/net-ssh/lib/net/ssh/test/channel.rb +129 -0
  104. data/vendor/gems/net-ssh/lib/net/ssh/test/extensions.rb +152 -0
  105. data/vendor/gems/net-ssh/lib/net/ssh/test/kex.rb +44 -0
  106. data/vendor/gems/net-ssh/lib/net/ssh/test/local_packet.rb +51 -0
  107. data/vendor/gems/net-ssh/lib/net/ssh/test/packet.rb +81 -0
  108. data/vendor/gems/net-ssh/lib/net/ssh/test/remote_packet.rb +38 -0
  109. data/vendor/gems/net-ssh/lib/net/ssh/test/script.rb +157 -0
  110. data/vendor/gems/net-ssh/lib/net/ssh/test/socket.rb +59 -0
  111. data/vendor/gems/net-ssh/lib/net/ssh/test.rb +89 -0
  112. data/vendor/gems/net-ssh/lib/net/ssh/transport/algorithms.rb +384 -0
  113. data/vendor/gems/net-ssh/lib/net/ssh/transport/cipher_factory.rb +80 -0
  114. data/vendor/gems/net-ssh/lib/net/ssh/transport/constants.rb +30 -0
  115. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac/abstract.rb +78 -0
  116. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac/md5.rb +12 -0
  117. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  118. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac/none.rb +15 -0
  119. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  120. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  121. data/vendor/gems/net-ssh/lib/net/ssh/transport/hmac.rb +31 -0
  122. data/vendor/gems/net-ssh/lib/net/ssh/transport/identity_cipher.rb +55 -0
  123. data/vendor/gems/net-ssh/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  124. data/vendor/gems/net-ssh/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
  125. data/vendor/gems/net-ssh/lib/net/ssh/transport/kex.rb +13 -0
  126. data/vendor/gems/net-ssh/lib/net/ssh/transport/openssl.rb +128 -0
  127. data/vendor/gems/net-ssh/lib/net/ssh/transport/packet_stream.rb +230 -0
  128. data/vendor/gems/net-ssh/lib/net/ssh/transport/server_version.rb +60 -0
  129. data/vendor/gems/net-ssh/lib/net/ssh/transport/session.rb +276 -0
  130. data/vendor/gems/net-ssh/lib/net/ssh/transport/state.rb +201 -0
  131. data/vendor/gems/net-ssh/lib/net/ssh/verifiers/lenient.rb +30 -0
  132. data/vendor/gems/net-ssh/lib/net/ssh/verifiers/null.rb +12 -0
  133. data/vendor/gems/net-ssh/lib/net/ssh/verifiers/strict.rb +53 -0
  134. data/vendor/gems/net-ssh/lib/net/ssh/version.rb +62 -0
  135. data/vendor/gems/net-ssh/lib/net/ssh.rb +215 -0
  136. data/vendor/gems/net-ssh/net-ssh.gemspec +33 -0
  137. data/vendor/gems/net-ssh/setup.rb +1585 -0
  138. data/vendor/gems/net-ssh/test/authentication/methods/common.rb +28 -0
  139. data/vendor/gems/net-ssh/test/authentication/methods/test_abstract.rb +51 -0
  140. data/vendor/gems/net-ssh/test/authentication/methods/test_hostbased.rb +114 -0
  141. data/vendor/gems/net-ssh/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  142. data/vendor/gems/net-ssh/test/authentication/methods/test_password.rb +50 -0
  143. data/vendor/gems/net-ssh/test/authentication/methods/test_publickey.rb +127 -0
  144. data/vendor/gems/net-ssh/test/authentication/test_agent.rb +205 -0
  145. data/vendor/gems/net-ssh/test/authentication/test_key_manager.rb +105 -0
  146. data/vendor/gems/net-ssh/test/authentication/test_session.rb +93 -0
  147. data/vendor/gems/net-ssh/test/common.rb +106 -0
  148. data/vendor/gems/net-ssh/test/configs/eqsign +3 -0
  149. data/vendor/gems/net-ssh/test/configs/exact_match +8 -0
  150. data/vendor/gems/net-ssh/test/configs/wild_cards +14 -0
  151. data/vendor/gems/net-ssh/test/connection/test_channel.rb +452 -0
  152. data/vendor/gems/net-ssh/test/connection/test_session.rb +488 -0
  153. data/vendor/gems/net-ssh/test/test_all.rb +6 -0
  154. data/vendor/gems/net-ssh/test/test_buffer.rb +336 -0
  155. data/vendor/gems/net-ssh/test/test_buffered_io.rb +63 -0
  156. data/vendor/gems/net-ssh/test/test_config.rb +84 -0
  157. data/vendor/gems/net-ssh/test/test_key_factory.rb +67 -0
  158. data/vendor/gems/net-ssh/test/transport/hmac/test_md5.rb +39 -0
  159. data/vendor/gems/net-ssh/test/transport/hmac/test_md5_96.rb +25 -0
  160. data/vendor/gems/net-ssh/test/transport/hmac/test_none.rb +34 -0
  161. data/vendor/gems/net-ssh/test/transport/hmac/test_sha1.rb +34 -0
  162. data/vendor/gems/net-ssh/test/transport/hmac/test_sha1_96.rb +25 -0
  163. data/vendor/gems/net-ssh/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  164. data/vendor/gems/net-ssh/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  165. data/vendor/gems/net-ssh/test/transport/test_algorithms.rb +302 -0
  166. data/vendor/gems/net-ssh/test/transport/test_cipher_factory.rb +171 -0
  167. data/vendor/gems/net-ssh/test/transport/test_hmac.rb +34 -0
  168. data/vendor/gems/net-ssh/test/transport/test_identity_cipher.rb +40 -0
  169. data/vendor/gems/net-ssh/test/transport/test_packet_stream.rb +435 -0
  170. data/vendor/gems/net-ssh/test/transport/test_server_version.rb +57 -0
  171. data/vendor/gems/net-ssh/test/transport/test_session.rb +315 -0
  172. data/vendor/gems/net-ssh/test/transport/test_state.rb +173 -0
  173. metadata +116 -4
  174. data/bin/install-poolparty +0 -20
@@ -0,0 +1,230 @@
1
+ require 'net/ssh/buffered_io'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/packet'
4
+ require 'net/ssh/transport/cipher_factory'
5
+ require 'net/ssh/transport/hmac'
6
+ require 'net/ssh/transport/state'
7
+
8
+ module Net; module SSH; module Transport
9
+
10
+ # A module that builds additional functionality onto the Net::SSH::BufferedIo
11
+ # module. It adds SSH encryption, compression, and packet validation, as
12
+ # per the SSH2 protocol. It also adds an abstraction for polling packets,
13
+ # to allow for both blocking and non-blocking reads.
14
+ module PacketStream
15
+ include BufferedIo
16
+
17
+ def self.extended(object)
18
+ object.__send__(:initialize_ssh)
19
+ end
20
+
21
+ # The map of "hints" that can be used to modify the behavior of the packet
22
+ # stream. For instance, when authentication succeeds, an "authenticated"
23
+ # hint is set, which is used to determine whether or not to compress the
24
+ # data when using the "delayed" compression algorithm.
25
+ attr_reader :hints
26
+
27
+ # The server state object, which encapsulates the algorithms used to interpret
28
+ # packets coming from the server.
29
+ attr_reader :server
30
+
31
+ # The client state object, which encapsulates the algorithms used to build
32
+ # packets to send to the server.
33
+ attr_reader :client
34
+
35
+ # The name of the client (local) end of the socket, as reported by the
36
+ # socket.
37
+ def client_name
38
+ @client_name ||= begin
39
+ sockaddr = getsockname
40
+ begin
41
+ Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first
42
+ rescue
43
+ begin
44
+ Socket.getnameinfo(sockaddr).first
45
+ rescue
46
+ begin
47
+ Socket.gethostbyname(Socket.gethostname).first
48
+ rescue
49
+ lwarn { "the client ipaddr/name could not be determined" }
50
+ "unknown"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # The IP address of the peer (remote) end of the socket, as reported by
58
+ # the socket.
59
+ def peer_ip
60
+ @peer_ip ||= begin
61
+ addr = getpeername
62
+ Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
63
+ end
64
+ end
65
+
66
+ # Returns true if the IO is available for reading, and false otherwise.
67
+ def available_for_read?
68
+ result = IO.select([self], nil, nil, 0)
69
+ result && result.first.any?
70
+ end
71
+
72
+ # Returns the next full packet. If the mode parameter is :nonblock (the
73
+ # default), then this will return immediately, whether a packet is
74
+ # available or not, and will return nil if there is no packet ready to be
75
+ # returned. If the mode parameter is :block, then this method will block
76
+ # until a packet is available.
77
+ def next_packet(mode=:nonblock)
78
+ case mode
79
+ when :nonblock then
80
+ fill if available_for_read?
81
+ poll_next_packet
82
+
83
+ when :block then
84
+ loop do
85
+ packet = poll_next_packet
86
+ return packet if packet
87
+
88
+ loop do
89
+ result = IO.select([self]) or next
90
+ break if result.first.any?
91
+ end
92
+
93
+ if fill <= 0
94
+ raise Net::SSH::Disconnect, "connection closed by remote host"
95
+ end
96
+ end
97
+
98
+ else
99
+ raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
100
+ end
101
+ end
102
+
103
+ # Enqueues a packet to be sent, and blocks until the entire packet is
104
+ # sent.
105
+ def send_packet(payload)
106
+ enqueue_packet(payload)
107
+ wait_for_pending_sends
108
+ end
109
+
110
+ # Enqueues a packet to be sent, but does not immediately send the packet.
111
+ # The given payload is pre-processed according to the algorithms specified
112
+ # in the client state (compression, cipher, and hmac).
113
+ def enqueue_packet(payload)
114
+ # try to compress the packet
115
+ payload = client.compress(payload)
116
+
117
+ # the length of the packet, minus the padding
118
+ actual_length = 4 + payload.length + 1
119
+
120
+ # compute the padding length
121
+ padding_length = client.cipher.block_size - (actual_length % client.cipher.block_size)
122
+ padding_length += client.cipher.block_size if padding_length < 4
123
+
124
+ # compute the packet length (sans the length field itself)
125
+ packet_length = payload.length + padding_length + 1
126
+
127
+ if packet_length < 16
128
+ padding_length += client.cipher.block_size
129
+ packet_length = payload.length + padding_length + 1
130
+ end
131
+
132
+ padding = Array.new(padding_length) { rand(256) }.pack("C*")
133
+
134
+ unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
135
+ mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
136
+
137
+ encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
138
+ message = encrypted_data + mac
139
+
140
+ debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
141
+ enqueue(message)
142
+
143
+ client.increment(packet_length)
144
+
145
+ self
146
+ end
147
+
148
+ # Performs any pending cleanup necessary on the IO and its associated
149
+ # state objects. (See State#cleanup).
150
+ def cleanup
151
+ client.cleanup
152
+ server.cleanup
153
+ end
154
+
155
+ # If the IO object requires a rekey operation (as indicated by either its
156
+ # client or server state objects, see State#needs_rekey?), this will
157
+ # yield. Otherwise, this does nothing.
158
+ def if_needs_rekey?
159
+ if client.needs_rekey? || server.needs_rekey?
160
+ yield
161
+ client.reset! if client.needs_rekey?
162
+ server.reset! if server.needs_rekey?
163
+ end
164
+ end
165
+
166
+ protected
167
+
168
+ # Called when this module is used to extend an object. It initializes
169
+ # the states and generally prepares the object for use as a packet stream.
170
+ def initialize_ssh
171
+ @hints = {}
172
+ @server = State.new(self, :server)
173
+ @client = State.new(self, :client)
174
+ @packet = nil
175
+ initialize_buffered_io
176
+ end
177
+
178
+ # Tries to read the next packet. If there is insufficient data to read
179
+ # an entire packet, this returns immediately, otherwise the packet is
180
+ # read, post-processed according to the cipher, hmac, and compression
181
+ # algorithms specified in the server state object, and returned as a
182
+ # new Packet object.
183
+ def poll_next_packet
184
+ if @packet.nil?
185
+ minimum = server.cipher.block_size < 4 ? 4 : server.cipher.block_size
186
+ return nil if available < minimum
187
+ data = read_available(minimum)
188
+
189
+ # decipher it
190
+ @packet = Net::SSH::Buffer.new(server.update_cipher(data))
191
+ @packet_length = @packet.read_long
192
+ end
193
+
194
+ need = @packet_length + 4 - server.cipher.block_size
195
+ raise Net::SSH::Exception, "padding error, need #{need} block #{server.cipher.block_size}" if need % server.cipher.block_size != 0
196
+
197
+ return nil if available < need + server.hmac.mac_length
198
+
199
+ if need > 0
200
+ # read the remainder of the packet and decrypt it.
201
+ data = read_available(need)
202
+ @packet.append(server.update_cipher(data))
203
+ end
204
+
205
+ # get the hmac from the tail of the packet (if one exists), and
206
+ # then validate it.
207
+ real_hmac = read_available(server.hmac.mac_length) || ""
208
+
209
+ @packet.append(server.final_cipher)
210
+ padding_length = @packet.read_byte
211
+
212
+ payload = @packet.read(@packet_length - padding_length - 1)
213
+ padding = @packet.read(padding_length) if padding_length > 0
214
+
215
+ my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
216
+ raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac
217
+
218
+ # try to decompress the payload, in case compression is active
219
+ payload = server.decompress(payload)
220
+
221
+ debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" }
222
+
223
+ server.increment(@packet_length)
224
+ @packet = nil
225
+
226
+ return Packet.new(payload)
227
+ end
228
+ end
229
+
230
+ end; end; end
@@ -0,0 +1,60 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/loggable'
3
+ require 'net/ssh/version'
4
+
5
+ module Net; module SSH; module Transport
6
+
7
+ # Negotiates the SSH protocol version and trades information about server
8
+ # and client. This is never used directly--it is always called by the
9
+ # transport layer as part of the initialization process of the transport
10
+ # layer.
11
+ #
12
+ # Note that this class also encapsulates the negotiated version, and acts as
13
+ # the authoritative reference for any queries regarding the version in effect.
14
+ class ServerVersion
15
+ include Loggable
16
+
17
+ # The SSH version string as reported by Net::SSH
18
+ PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}"
19
+
20
+ # Any header text sent by the server prior to sending the version.
21
+ attr_reader :header
22
+
23
+ # The version string reported by the server.
24
+ attr_reader :version
25
+
26
+ # Instantiates a new ServerVersion and immediately (and synchronously)
27
+ # negotiates the SSH protocol in effect, using the given socket.
28
+ def initialize(socket, logger)
29
+ @header = ""
30
+ @version = nil
31
+ @logger = logger
32
+ negotiate!(socket)
33
+ end
34
+
35
+ private
36
+
37
+ # Negotiates the SSH protocol to use, via the given socket. If the server
38
+ # reports an incompatible SSH version (e.g., SSH1), this will raise an
39
+ # exception.
40
+ def negotiate!(socket)
41
+ info { "negotiating protocol version" }
42
+
43
+ loop do
44
+ @version = socket.readline
45
+ break if @version.nil? || @version.match(/^SSH-/)
46
+ @header << @version
47
+ end
48
+
49
+ @version.chomp!
50
+ debug { "remote is `#{@version}'" }
51
+
52
+ unless @version.match(/^SSH-(1\.99|2\.0)-/)
53
+ raise Net::SSH::Exception, "incompatible SSH version `#{@version}'"
54
+ end
55
+
56
+ debug { "local is `#{PROTO_VERSION}'" }
57
+ socket.write "#{PROTO_VERSION}\r\n"
58
+ end
59
+ end
60
+ end; end; end
@@ -0,0 +1,276 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ require 'net/ssh/errors'
5
+ require 'net/ssh/loggable'
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'
14
+
15
+ module Net; module SSH; module Transport
16
+
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
24
+
25
+ # The standard port for the SSH protocol.
26
+ DEFAULT_PORT = 22
27
+
28
+ # The host to connect to, as given to the constructor.
29
+ attr_reader :host
30
+
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
34
+
35
+ # The underlying socket object being used to communicate with the remote
36
+ # host.
37
+ attr_reader :socket
38
+
39
+ # The ServerVersion instance that encapsulates the negotiated protocol
40
+ # version.
41
+ attr_reader :server_version
42
+
43
+ # The Algorithms instance used to perform key exchanges.
44
+ attr_reader :algorithms
45
+
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
49
+
50
+ # The hash of options that were given to the object at initialization.
51
+ attr_reader :options
52
+
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]
58
+
59
+ @host = host
60
+ @port = options[:port] || DEFAULT_PORT
61
+ @options = options
62
+
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
68
+
69
+ debug { "connection established" }
70
+
71
+ @queue = []
72
+
73
+ @host_key_verifier = select_host_key_verifier(options[:paranoid])
74
+
75
+ @server_version = ServerVersion.new(socket, logger)
76
+
77
+ @algorithms = Algorithms.new(self, options)
78
+ wait { algorithms.initialized? }
79
+ end
80
+
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
91
+ end
92
+ string
93
+ end
94
+ end
95
+
96
+ # Returns true if the underlying socket has been closed.
97
+ def closed?
98
+ socket.closed?
99
+ end
100
+
101
+ # Cleans up (see PacketStream#cleanup) and closes the underlying socket.
102
+ def close
103
+ socket.cleanup
104
+ socket.close
105
+ end
106
+
107
+ # Performs a "hard" shutdown of the connection. In general, this should
108
+ # never be done, but it might be necessary (in a rescue clause, for instance,
109
+ # when the connection needs to close but you don't know the status of the
110
+ # underlying protocol's state).
111
+ def shutdown!
112
+ error { "forcing connection closed" }
113
+ socket.close
114
+ end
115
+
116
+ # Returns a new service_request packet for the given service name, ready
117
+ # for sending to the server.
118
+ def service_request(service)
119
+ Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
120
+ end
121
+
122
+ # Requests a rekey operation, and blocks until the operation completes.
123
+ # If a rekey is already pending, this returns immediately, having no
124
+ # effect.
125
+ def rekey!
126
+ if !algorithms.pending?
127
+ algorithms.rekey!
128
+ wait { algorithms.initialized? }
129
+ end
130
+ end
131
+
132
+ # Returns immediately if a rekey is already in process. Otherwise, if a
133
+ # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
134
+ # one is performed, causing this method to block until it completes.
135
+ def rekey_as_needed
136
+ return if algorithms.pending?
137
+ socket.if_needs_rekey? { rekey! }
138
+ end
139
+
140
+ # Returns a hash of information about the peer (remote) side of the socket,
141
+ # including :ip, :port, :host, and :canonized (see #host_as_string).
142
+ def peer
143
+ @peer ||= { :ip => socket.peer_ip, :port => @port.to_i, :host => @host, :canonized => host_as_string }
144
+ end
145
+
146
+ # Blocks until a new packet is available to be read, and returns that
147
+ # packet. See #poll_message.
148
+ def next_message
149
+ poll_message(:block)
150
+ end
151
+
152
+ # Tries to read the next packet from the socket. If mode is :nonblock (the
153
+ # default), this will not block and will return nil if there are no packets
154
+ # waiting to be read. Otherwise, this will block until a packet is
155
+ # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
156
+ # DEBUG, and KEXINIT) are handled silently by this method, and will never
157
+ # be returned.
158
+ #
159
+ # If a key-exchange is in process and a disallowed packet type is
160
+ # received, it will be enqueued and otherwise ignored. When a key-exchange
161
+ # is not in process, and consume_queue is true, packets will be first
162
+ # read from the queue before the socket is queried.
163
+ def poll_message(mode=:nonblock, consume_queue=true)
164
+ loop do
165
+ if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
166
+ return @queue.shift
167
+ end
168
+
169
+ packet = socket.next_packet(mode)
170
+ return nil if packet.nil?
171
+
172
+ case packet.type
173
+ when DISCONNECT
174
+ raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})"
175
+
176
+ when IGNORE
177
+ debug { "IGNORE packet recieved: #{packet[:data].inspect}" }
178
+
179
+ when UNIMPLEMENTED
180
+ lwarn { "UNIMPLEMENTED: #{packet[:number]}" }
181
+
182
+ when DEBUG
183
+ send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
184
+
185
+ when KEXINIT
186
+ algorithms.accept_kexinit(packet)
187
+
188
+ else
189
+ return packet if algorithms.allow?(packet)
190
+ push(packet)
191
+ end
192
+ end
193
+ end
194
+
195
+ # Waits (blocks) until the given block returns true. If no block is given,
196
+ # this just waits long enough to see if there are any pending packets. Any
197
+ # packets read are enqueued (see #push).
198
+ def wait
199
+ loop do
200
+ break if block_given? && yield
201
+ message = poll_message(:nonblock, false)
202
+ push(message) if message
203
+ break if !block_given?
204
+ end
205
+ end
206
+
207
+ # Adds the given packet to the packet queue. If the queue is non-empty,
208
+ # #poll_message will return packets from the queue in the order they
209
+ # were received.
210
+ def push(packet)
211
+ @queue.push(packet)
212
+ end
213
+
214
+ # Sends the given message via the packet stream, blocking until the
215
+ # entire message has been sent.
216
+ def send_message(message)
217
+ socket.send_packet(message)
218
+ end
219
+
220
+ # Enqueues the given message, such that it will be sent at the earliest
221
+ # opportunity. This does not block, but returns immediately.
222
+ def enqueue_message(message)
223
+ socket.enqueue_packet(message)
224
+ end
225
+
226
+ # Configure's the packet stream's client state with the given set of
227
+ # options. This is typically used to define the cipher, compression, and
228
+ # hmac algorithms to use when sending packets to the server.
229
+ def configure_client(options={})
230
+ socket.client.set(options)
231
+ end
232
+
233
+ # Configure's the packet stream's server state with the given set of
234
+ # options. This is typically used to define the cipher, compression, and
235
+ # hmac algorithms to use when reading packets from the server.
236
+ def configure_server(options={})
237
+ socket.server.set(options)
238
+ end
239
+
240
+ # Sets a new hint for the packet stream, which the packet stream may use
241
+ # to change its behavior. (See PacketStream#hints).
242
+ def hint(which, value=true)
243
+ socket.hints[which] = value
244
+ end
245
+
246
+ public
247
+
248
+ # this method is primarily for use in tests
249
+ attr_reader :queue #:nodoc:
250
+
251
+ private
252
+
253
+ # Instantiates a new host-key verification class, based on the value of
254
+ # the parameter. When true or nil, the default Lenient verifier is
255
+ # returned. If it is false, the Null verifier is returned, and if it is
256
+ # :very, the Strict verifier is returned. If the argument happens to
257
+ # respond to :verify, it is returned directly. Otherwise, an exception
258
+ # is raised.
259
+ def select_host_key_verifier(paranoid)
260
+ case paranoid
261
+ when true, nil then
262
+ Net::SSH::Verifiers::Lenient.new
263
+ when false then
264
+ Net::SSH::Verifiers::Null.new
265
+ when :very then
266
+ Net::SSH::Verifiers::Strict.new
267
+ else
268
+ if paranoid.respond_to?(:verify)
269
+ paranoid
270
+ else
271
+ raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}"
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end; end; end