Module: Net::SSH::Transport::PacketStream
- Includes:
- BufferedIo
- Defined in:
- lib/net/ssh/transport/packet_stream.rb
Overview
A module that builds additional functionality onto the Net::SSH::BufferedIo module. It adds SSH encryption, compression, and packet validation, as per the SSH2 protocol. It also adds an abstraction for polling packets, to allow for both blocking and non-blocking reads.
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.
-
#server ⇒ Object
readonly
The server state object, which encapsulates the algorithms used to interpret packets coming from the server.
Attributes included from Loggable
Class Method Summary collapse
Instance Method Summary collapse
-
#available_for_read? ⇒ Boolean
Returns true if the IO is available for reading, and false otherwise.
-
#cleanup ⇒ Object
Performs any pending cleanup necessary on the IO and its associated state objects.
-
#client_name ⇒ Object
The name of the client (local) end of the socket, as reported by the socket.
-
#enqueue_packet(payload) ⇒ Object
Enqueues a packet to be sent, but does not immediately send the packet.
-
#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.
-
#next_packet(mode = :nonblock) ⇒ Object
Returns the next full packet.
-
#peer_ip ⇒ Object
The IP address of the peer (remote) end of the socket, as reported by the socket.
-
#send_packet(payload) ⇒ Object
Enqueues a packet to be sent, and blocks until the entire packet is sent.
Methods included from BufferedIo
#available, #enqueue, #fill, #pending_write?, #read_available, #read_buffer, #send_pending, #wait_for_pending_sends, #write_buffer
Methods included from Loggable
#debug, #error, #fatal, #info, #lwarn
Instance Attribute Details
#client ⇒ Object (readonly)
The client state object, which encapsulates the algorithms used to build packets to send to the server.
33 34 35 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 33 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.
25 26 27 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 25 def hints @hints end |
#server ⇒ Object (readonly)
The server state object, which encapsulates the algorithms used to interpret packets coming from the server.
29 30 31 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 29 def server @server end |
Class Method Details
.extended(object) ⇒ Object
17 18 19 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 17 def self.extended(object) object.__send__(:initialize_ssh) end |
Instance Method Details
#available_for_read? ⇒ Boolean
Returns true if the IO is available for reading, and false otherwise.
67 68 69 70 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 67 def available_for_read? result = IO.select([self], nil, nil, 0) result && result.first.any? end |
#cleanup ⇒ Object
Performs any pending cleanup necessary on the IO and its associated state objects. (See State#cleanup).
150 151 152 153 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 150 def cleanup client.cleanup server.cleanup end |
#client_name ⇒ Object
The name of the client (local) end of the socket, as reported by the socket.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 37 def client_name @client_name ||= begin sockaddr = getsockname begin Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first rescue begin Socket.getnameinfo(sockaddr).first rescue begin Socket.gethostbyname(Socket.gethostname).first rescue lwarn { "the client ipaddr/name could not be determined" } "unknown" end end end end end |
#enqueue_packet(payload) ⇒ Object
Enqueues a packet to be sent, but does not immediately send the packet. The given payload is pre-processed according to the algorithms specified in the client state (compression, cipher, and hmac).
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 143 144 145 146 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 113 def enqueue_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 debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" } enqueue() client.increment(packet_length) self 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.
158 159 160 161 162 163 164 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 158 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 |
#next_packet(mode = :nonblock) ⇒ Object
Returns the next full packet. If the mode parameter is :nonblock (the default), then this will return immediately, whether a packet is available or not, and will return nil if there is no packet ready to be returned. If the mode parameter is :block, then this method will block until a packet is available.
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 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 77 def next_packet(mode=:nonblock) case mode when :nonblock then fill if available_for_read? poll_next_packet when :block then loop do packet = poll_next_packet return packet if packet loop do result = IO.select([self]) or next break if result.first.any? end if fill <= 0 raise Net::SSH::Disconnect, "connection closed by remote host" end end else raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}" end end |
#peer_ip ⇒ Object
The IP address of the peer (remote) end of the socket, as reported by the socket.
59 60 61 62 63 64 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 59 def peer_ip @peer_ip ||= begin addr = getpeername Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first end end |
#send_packet(payload) ⇒ Object
Enqueues a packet to be sent, and blocks until the entire packet is sent.
105 106 107 108 |
# File 'lib/net/ssh/transport/packet_stream.rb', line 105 def send_packet(payload) enqueue_packet(payload) wait_for_pending_sends end |