Class: EventMachine::Ssh::PacketStream
- Inherits:
-
Object
- Object
- EventMachine::Ssh::PacketStream
- Includes:
- Log, Net::SSH::BufferedIo
- Defined in:
- lib/em-ssh/packet-stream.rb
Instance Attribute Summary collapse
-
#client ⇒ Object
readonly
The client state object, which encapsulates the algorithms used to build packets to send to the server.
-
#hints ⇒ Object
readonly
The map of “hints” that can be used to modify the behavior of the packet stream.
-
#input ⇒ Object
readonly
The input stream.
-
#output ⇒ Object
readonly
The output stream.
-
#server ⇒ Object
readonly
The server state object, which encapsulates the algorithms used to interpret packets coming from the server.
Instance Method Summary collapse
-
#cleanup ⇒ Object
Performs any pending cleanup necessary on the IO and its associated state objects.
-
#close ⇒ Object
initialize(content=“”).
-
#consume!(*args) ⇒ Object
Consumes n bytes from the buffer, where n is the current position unless otherwise specified.
-
#if_needs_rekey? ⇒ Boolean
If the IO object requires a rekey operation (as indicated by either its client or server state objects, see State#needs_rekey?), this will yield.
-
#initialize(connection) ⇒ PacketStream
constructor
A new instance of PacketStream.
-
#poll_next_packet ⇒ Object
Tries to read the next packet.
-
#send_packet(payload) ⇒ Object
Copyright © 2008 Jamis Buck.
Methods included from Log
#debug, #error, #fatal, #info, #log, #warn
Constructor Details
#initialize(connection) ⇒ PacketStream
Returns a new instance of PacketStream.
27 28 29 30 31 32 33 34 35 |
# File 'lib/em-ssh/packet-stream.rb', line 27 def initialize(connection) @connection = connection @input = Net::SSH::Buffer.new @output = Net::SSH::Buffer.new @hints = {} @server = Net::SSH::Transport::State.new(self, :server) @client = Net::SSH::Transport::State.new(self, :client) @packet = nil end |
Instance Attribute Details
#client ⇒ Object (readonly)
The client state object, which encapsulates the algorithms used to build packets to send to the server.
20 21 22 |
# File 'lib/em-ssh/packet-stream.rb', line 20 def client @client end |
#hints ⇒ Object (readonly)
The map of “hints” that can be used to modify the behavior of the packet stream. For instance, when authentication succeeds, an “authenticated” hint is set, which is used to determine whether or not to compress the data when using the “delayed” compression algorithm.
12 13 14 |
# File 'lib/em-ssh/packet-stream.rb', line 12 def hints @hints end |
#input ⇒ Object (readonly)
The input stream
23 24 25 |
# File 'lib/em-ssh/packet-stream.rb', line 23 def input @input end |
#output ⇒ Object (readonly)
The output stream
25 26 27 |
# File 'lib/em-ssh/packet-stream.rb', line 25 def output @output end |
#server ⇒ Object (readonly)
The server state object, which encapsulates the algorithms used to interpret packets coming from the server.
16 17 18 |
# File 'lib/em-ssh/packet-stream.rb', line 16 def server @server end |
Instance Method Details
#cleanup ⇒ Object
Performs any pending cleanup necessary on the IO and its associated state objects. (See State#cleanup).
146 147 148 149 |
# File 'lib/em-ssh/packet-stream.rb', line 146 def cleanup client.cleanup server.cleanup end |
#close ⇒ Object
initialize(content=“”)
37 38 39 40 41 |
# File 'lib/em-ssh/packet-stream.rb', line 37 def close # remove reference to the connection to facilitate Garbage Collection return super.tap { @connection = nil } if respond_to?(:super) @connection = nil end |
#consume!(*args) ⇒ Object
Consumes n bytes from the buffer, where n is the current position unless otherwise specified. This is useful for removing data from the buffer that has previously been read, when you are expecting more data to be appended. It helps to keep the size of buffers down when they would otherwise tend to grow without bound.
Returns the buffer object itself.
50 51 52 |
# File 'lib/em-ssh/packet-stream.rb', line 50 def consume!(*args) input.consume!(*args) end |
#if_needs_rekey? ⇒ Boolean
If the IO object requires a rekey operation (as indicated by either its client or server state objects, see State#needs_rekey?), this will yield. Otherwise, this does nothing. Copyright © 2008 Jamis Buck
155 156 157 158 159 160 161 |
# File 'lib/em-ssh/packet-stream.rb', line 155 def if_needs_rekey? if client.needs_rekey? || server.needs_rekey? yield client.reset! if client.needs_rekey? server.reset! if server.needs_rekey? end end |
#poll_next_packet ⇒ Object
Tries to read the next packet. If there is insufficient data to read an entire packet, this returns immediately, otherwise the packet is read, post-processed according to the cipher, hmac, and compression algorithms specified in the server state object, and returned as a new Packet object. Copyright © 2008 Jamis Buck
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/em-ssh/packet-stream.rb', line 60 def poll_next_packet if @packet.nil? minimum = server.block_size < 4 ? 4 : server.block_size return nil if available < minimum data = read_available(minimum) # decipher it @packet = Net::SSH::Buffer.new(server.update_cipher(data)) @packet_length = @packet.read_long end need = @packet_length + 4 - server.block_size if need % server.block_size != 0 @connection.fire(:error, SshError.new("padding error, need #{need} block #{server.block_size}")) end return nil if available < need + server.hmac.mac_length if need > 0 # read the remainder of the packet and decrypt it. data = read_available(need) @packet.append(server.update_cipher(data)) end # get the hmac from the tail of the packet (if one exists), and # then validate it. real_hmac = read_available(server.hmac.mac_length) || "" @packet.append(server.final_cipher) padding_length = @packet.read_byte payload = @packet.read(@packet_length - padding_length - 1) padding = @packet.read(padding_length) if padding_length > 0 my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*")) if real_hmac != my_computed_hmac @connection.fire(:error, Net::SSH::Exception.new("corrupted mac detected")) return end # try to decompress the payload, in case compression is active payload = server.decompress(payload) log.debug("received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}") server.increment(@packet_length) @packet = nil return Net::SSH::Packet.new(payload) end |
#send_packet(payload) ⇒ Object
Copyright © 2008 Jamis Buck
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/em-ssh/packet-stream.rb', line 110 def send_packet(payload) # try to compress the packet payload = client.compress(payload) # the length of the packet, minus the padding actual_length = 4 + payload.length + 1 # compute the padding length padding_length = client.block_size - (actual_length % client.block_size) padding_length += client.block_size if padding_length < 4 # compute the packet length (sans the length field itself) packet_length = payload.length + padding_length + 1 if packet_length < 16 padding_length += client.block_size packet_length = payload.length + padding_length + 1 end padding = Array.new(padding_length) { rand(256) }.pack("C*") unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*") mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*")) encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher = encrypted_data + mac log.debug("queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}") @connection.send_data() log.debug("sent #{.length} bytes") client.increment(packet_length) self end |