Class: ESSH::Transport::PacketStream

Inherits:
Libuv::TCP
  • Object
show all
Includes:
Net::SSH::Loggable, Net::SSH::Transport::Constants
Defined in:
lib/evented-ssh/transport/packet_stream.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(session, **options) ⇒ PacketStream

Returns a new instance of PacketStream.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/evented-ssh/transport/packet_stream.rb', line 15

def initialize(session, **options)
    @hints  = {}
    @server = ::Net::SSH::Transport::State.new(self, :server)
    @client = ::Net::SSH::Transport::State.new(self, :client)
    @packet = nil
    @packets = []
    @packets = []
    @pending_packets = []
    @process_pending = nil
    @awaiting = []
    @input = ::Net::SSH::Buffer.new

    @session = session
    @have_header = false

    self.logger = options[:logger]
    super(session.reactor, **options)

    progress { |data| check_packet(data) }
end

Instance Attribute Details

#algorithmsObject

Returns the value of attribute algorithms.



42
43
44
# File 'lib/evented-ssh/transport/packet_stream.rb', line 42

def algorithms
  @algorithms
end

#clientObject (readonly)

The client state object, which encapsulates the algorithms used to build packets to send to the server.



56
57
58
# File 'lib/evented-ssh/transport/packet_stream.rb', line 56

def client
  @client
end

#hintsObject (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.



48
49
50
# File 'lib/evented-ssh/transport/packet_stream.rb', line 48

def hints
  @hints
end

#serverObject (readonly)

The server state object, which encapsulates the algorithms used to interpret packets coming from the server.



52
53
54
# File 'lib/evented-ssh/transport/packet_stream.rb', line 52

def server
  @server
end

Instance Method Details

#availableObject

Returns the number of bytes available to be read from the input buffer. (See #read_available.)



173
174
175
# File 'lib/evented-ssh/transport/packet_stream.rb', line 173

def available
    @input.available
end

#client_nameObject

The name of the client (local) end of the socket, as reported by the socket.



60
61
62
# File 'lib/evented-ssh/transport/packet_stream.rb', line 60

def client_name
    sockname[0]
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).



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
# File 'lib/evented-ssh/transport/packet_stream.rb', line 73

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.bytesize + 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.bytesize + padding_length + 1

    if packet_length < 16
        padding_length += client.block_size
        packet_length = payload.bytesize + 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
    message = "#{encrypted_data}#{mac}"

    debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }

    client.increment(packet_length)
    direct_write(message)

    self
end

#get_packet(mode = :block) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/evented-ssh/transport/packet_stream.rb', line 108

def get_packet(mode = :block)
    case mode
    when :nonblock
        return @packets.shift
    when :block
        packet = @packets.shift
        return packet unless packet.nil?
        defer = @reactor.defer
        @awaiting << defer
        return defer.promise.value
    else
        raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
    end
rescue
    nil
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.

Returns:

  • (Boolean)


157
158
159
160
161
162
163
# File 'lib/evented-ssh/transport/packet_stream.rb', line 157

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

#peer_ipObject

The IP address of the peer (remote) end of the socket, as reported by the socket.



66
67
68
# File 'lib/evented-ssh/transport/packet_stream.rb', line 66

def peer_ip
    peername[0]
end

#prepare(buff) ⇒ Object



36
37
38
39
40
# File 'lib/evented-ssh/transport/packet_stream.rb', line 36

def prepare(buff)
    progress { |data| check_packet(data) }
    check_packet(buff) unless buff.empty?
    #start_read
end

#process_waitingObject



145
146
147
148
149
150
151
152
# File 'lib/evented-ssh/transport/packet_stream.rb', line 145

def process_waiting
    loop do
        break if @packets.empty?
        waiting = @awaiting.shift
        break unless waiting
        waiting.resolve(@packets.shift)
    end
end

#queue_packet(packet) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/evented-ssh/transport/packet_stream.rb', line 125

def queue_packet(packet)
    pending = @algorithms.pending?
    if not pending
        @packets << packet
    elsif Algorithms.allowed_packet?(packet)
        @packets << packet
    else
        @pending_packets << packet
        if @process_pending.nil?
            @process_pending = pending
            @process_pending.promise.finally do
                @process_pending = nil
                @packets.concat(@pending_packets)
                @pending_packets.clear
                process_waiting
            end
        end
    end
end

#read_available(length = nil) ⇒ Object

Read up to length bytes from the input buffer. If length is nil, all available data is read from the buffer. (See #available.)



167
168
169
# File 'lib/evented-ssh/transport/packet_stream.rb', line 167

def read_available(length = nil)
    @input.read(length || available)
end

#read_bufferObject

:nodoc:



177
178
179
# File 'lib/evented-ssh/transport/packet_stream.rb', line 177

def read_buffer #:nodoc:
    @input.to_s
end