Class: EventMachine::Ssh::Connection

Inherits:
Connection
  • Object
show all
Includes:
Callbacks, Log
Defined in:
lib/em-ssh/connection.rb

Overview

EventMachine::Ssh::Connection is a EventMachine::Connection that emulates the Net::SSH transport layer. It ties itself into Net::SSH so that the EventMachine reactor loop can take the place of the Net::SSH event loop. Most of the methods here are only for compatibility with Net::SSH

Constant Summary collapse

TIMEOUT =

maximum number of seconds to wait for a connection

20

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Callbacks

#callbacks, #fire, #on, #on_next

Methods included from Log

#debug, #error, #fatal, #info, #log, #warn

Constructor Details

#initialize(options = {}) ⇒ Connection

connection_completed



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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/em-ssh/connection.rb', line 120

def initialize(options = {})
  debug("#{self.class}.new(#{options})")
  @host           = options[:host]
  @port           = options[:port]
  @password       = options[:password]
  @queue          = []
  @options        = options
  @timeout        = options[:timeout] || TIMEOUT

  begin
    on(:connected, &options[:callback]) if options[:callback]
    @nocon          = on(:closed) { raise ConnectionFailed, @host }
    @contimeout     = EM::Timer.new(@timeout) { raise ConnectionTimeout, @host }

    @error_callback = lambda { |code| raise SshError.new(code) }

    @host_key_verifier = select_host_key_verifier(options[:paranoid])
    @server_version    = ServerVersion.new(self)
    on(:version_negotiated) do
      @data.consume!(@server_version.header.length)
      @algorithms = Net::SSH::Transport::Algorithms.new(self, options)

      register_data_handler

      on_next(:algo_init) do
        auth = AuthenticationSession.new(self, options)
        user = options.fetch(:user, user)
        Fiber.new do
          if auth.authenticate("ssh-connection", user, options[:password])
            fire(:connected, Session.new(self, options))
          else
            fire(:error, Net::SSH::AuthenticationFailed.new(user))
            close_connection
          end # auth.authenticate("ssh-connection", user, options[:password])
        end.resume # Fiber
      end # :algo_init
    end # :version_negotiated

  rescue Exception => e
    log.fatal("caught an error during initialization: #{e}\n   #{e.backtrace.join("\n   ")}")
    Process.exit
  end # begin
  self
end

Instance Attribute Details

#algorithmsObject (readonly)

The Algorithms instance used to perform key exchanges.



25
26
27
# File 'lib/em-ssh/connection.rb', line 25

def algorithms
  @algorithms
end

#hostString (readonly)

Returns The host to connect to, as given to the constructor.

Returns:

  • (String)

    The host to connect to, as given to the constructor.



16
17
18
# File 'lib/em-ssh/connection.rb', line 16

def host
  @host
end

#host_key_verifierObject (readonly)

The host-key verifier object used to verify host keys, to ensure that the connection is not being spoofed.



28
29
30
# File 'lib/em-ssh/connection.rb', line 28

def host_key_verifier
  @host_key_verifier
end

#optionsObject (readonly)

The hash of options that were given to the object at initialization.



31
32
33
# File 'lib/em-ssh/connection.rb', line 31

def options
  @options
end

#portFixnum (readonly)

Returns the port number (DEFAULT_PORT) to connect to, as given in the options to the constructor.

Returns:

  • (Fixnum)

    the port number (DEFAULT_PORT) to connect to, as given in the options to the constructor.



19
20
21
# File 'lib/em-ssh/connection.rb', line 19

def port
  @port
end

#server_versionServerVersion (readonly)

Returns The ServerVersion instance that encapsulates the negotiated protocol version.

Returns:

  • (ServerVersion)

    The ServerVersion instance that encapsulates the negotiated protocol version.



22
23
24
# File 'lib/em-ssh/connection.rb', line 22

def server_version
  @server_version
end

#socketPacketStream (readonly)

Returns emulates a socket and ssh packetstream.

Returns:



34
35
36
# File 'lib/em-ssh/connection.rb', line 34

def socket
  @socket
end

Instance Method Details

#closeObject

Close the connection



42
43
44
45
# File 'lib/em-ssh/connection.rb', line 42

def close
  # #unbind will update @closed
  close_connection
end

#closed?Boolean

Returns true if the connection has been closed.

Returns:

  • (Boolean)

    true if the connection has been closed



37
38
39
# File 'lib/em-ssh/connection.rb', line 37

def closed?
  @closed == true
end

#configure_client(options = {}) ⇒ Object

Configure’s the packet stream’s client state with the given set of options. This is typically used to define the cipher, compression, and hmac algorithms to use when sending packets to the server.



188
189
190
# File 'lib/em-ssh/connection.rb', line 188

def configure_client(options={})
  @socket.client.set(options)
end

#configure_server(options = {}) ⇒ Object

Configure’s the packet stream’s server state with the given set of options. This is typically used to define the cipher, compression, and hmac algorithms to use when reading packets from the server.



195
196
197
# File 'lib/em-ssh/connection.rb', line 195

def configure_server(options={})
  @socket.server.set(options)
end

#connection_completedObject



115
116
117
118
# File 'lib/em-ssh/connection.rb', line 115

def connection_completed
  @contimeout.cancel
  @nocon.cancel
end

#hint(which, value = true) ⇒ Object

Sets a new hint for the packet stream, which the packet stream may use to change its behavior. (See PacketStream#hints).



201
202
203
# File 'lib/em-ssh/connection.rb', line 201

def hint(which, value=true)
  @socket.hints[which] = value
end

#host_as_stringObject

Returns the host (and possibly IP address) in a format compatible with SSH known-host files.



172
173
174
175
176
177
178
179
180
# File 'lib/em-ssh/connection.rb', line 172

def host_as_string
  @host_as_string ||= "#{host}".tap do |string|
    string = "[#{string}]:#{port}" if port != DEFAULT_PORT
    _, ip = Socket.unpack_sockaddr_in(get_peername)
    if ip != host
      string << "," << (port != DEFAULT_PORT ? "[#{ip}]:#{port}" : ip)
    end # ip != host
  end #  |string|
end

#next_messageObject



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/em-ssh/connection.rb', line 53

def next_message
  return @queue.shift if @queue.any? && algorithms.allow?(@queue.first)
  f = Fiber.current
  cb = on(:packet) do |packet|
    if @queue.any? && algorithms.allow?(@queue.first)
      cb.cancel
      f.resume(@queue.shift)
    end
  end # :packet
  return Fiber.yield
end

#peerObject

Returns a hash of information about the peer (remote) side of the socket, including :ip, :port, :host, and :canonized (see #host_as_string).



213
214
215
216
217
218
219
220
221
# File 'lib/em-ssh/connection.rb', line 213

def peer
  @peer ||= {}.tap do |p|
    _, ip = Socket.unpack_sockaddr_in(get_peername)
    p[:ip] = ip
    p[:port] = @port.to_i
    p[:host] = @host
    p[:canonized] = host_as_string
  end
end

#post_initObject

EventMachine callbacks



97
98
99
100
# File 'lib/em-ssh/connection.rb', line 97

def post_init
  @socket = PacketStream.new(self)
  @data         = @socket.input
end

#receive_data(data) ⇒ Object



109
110
111
112
113
# File 'lib/em-ssh/connection.rb', line 109

def receive_data(data)
  debug("read #{data.length} bytes")
  @data.append(data)
  fire(:data, data)
end

#rekey!Object

Requests a rekey operation, and simulates a block until the operation completes. If a rekey is already pending, this returns immediately, having no effect.



73
74
75
76
77
78
79
80
81
82
# File 'lib/em-ssh/connection.rb', line 73

def rekey!
  if !algorithms.pending?
    f = Fiber.current
    on_next(:algo_init) do
      f.resume
    end # :algo_init
    algorithms.rekey!
    return Fiber.yield
  end
end

#rekey_as_neededObject

Returns immediately if a rekey is already in process. Otherwise, if a rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?) one is performed, causing this method to block until it completes.



87
88
89
90
# File 'lib/em-ssh/connection.rb', line 87

def rekey_as_needed
  return if algorithms.pending?
  socket.if_needs_rekey? { rekey! }
end

#send_message(message) ⇒ Object Also known as: enqueue_message

Send a packet to the server



48
49
50
# File 'lib/em-ssh/connection.rb', line 48

def send_message(message)
  @socket.send_packet(message)
end

#service_request(service) ⇒ Object

Returns a new service_request packet for the given service name, ready for sending to the server.



67
68
69
# File 'lib/em-ssh/connection.rb', line 67

def service_request(service)
  Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
end

#unbindObject

Returns:



103
104
105
106
107
# File 'lib/em-ssh/connection.rb', line 103

def unbind
  debug("#{self} is unbound")
  fire(:closed)
  @closed = true
end