Class: Net::SSH::Authentication::Pageant::Socket

Inherits:
Object
  • Object
show all
Defined in:
lib/net/ssh/authentication/pageant.rb

Overview

This is the pseudo-socket implementation that mimics the interface of a socket, translating each request into a Windows messaging call to the pageant daemon. This allows pageant support to be implemented simply by replacing the socket factory used by the Agent class.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSocket

Create a new instance that communicates with the running pageant instance. If no such instance is running, this will cause an error.



300
301
302
303
304
305
306
307
308
309
310
# File 'lib/net/ssh/authentication/pageant.rb', line 300

def initialize
  @win = Win.FindWindow("Pageant", "Pageant")

  if @win == 0
    raise Net::SSH::Exception,
      "pageant process not running"
  end

  @input_buffer = Net::SSH::Buffer.new
  @output_buffer = Net::SSH::Buffer.new
end

Class Method Details

.open(location = nil) ⇒ Object

The factory method for creating a new Socket instance. The location parameter is ignored, and is only needed for compatibility with the general Socket interface.



294
295
296
# File 'lib/net/ssh/authentication/pageant.rb', line 294

def self.open(location=nil)
  new
end

Instance Method Details

#closeObject



336
337
# File 'lib/net/ssh/authentication/pageant.rb', line 336

def close
end

#read(n = nil) ⇒ Object

Reads n bytes from the cached result of the last query. If n is nil, returns all remaining data from the last query.



332
333
334
# File 'lib/net/ssh/authentication/pageant.rb', line 332

def read(n = nil)
  @output_buffer.read(n)
end

#send(data, *args) ⇒ Object

Forwards the data to #send_query, ignoring any arguments after the first.



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/net/ssh/authentication/pageant.rb', line 314

def send(data, *args)
  @input_buffer.append(data)

  ret = data.length

  while true
    return ret if @input_buffer.length < 4
    msg_length = @input_buffer.read_long + 4
    @input_buffer.reset!

    return ret if @input_buffer.length < msg_length
    msg = @input_buffer.read!(msg_length)
    @output_buffer.append(send_query(msg))
  end
end

#send_query(query) ⇒ Object

Packages the given query string and sends it to the pageant process via the Windows messaging subsystem. The result is cached, to be returned piece-wise when #read is called.



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/net/ssh/authentication/pageant.rb', line 342

def send_query(query)
  res = nil
  filemap = 0
  ptr = nil
  id = Win.malloc_ptr(Win::SIZEOF_DWORD)

  mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
  security_attributes = Win.get_ptr Win.get_security_attributes_for_user

  filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
                                  security_attributes,
                                  Win::PAGE_READWRITE, 0,
                                  AGENT_MAX_MSGLEN, mapname)

  if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
    raise Net::SSH::Exception,
      "Creation of file mapping failed with error: #{Win.GetLastError}"
  end

  ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
                          0)

  if ptr.nil? || ptr.null?
    raise Net::SSH::Exception, "Mapping of file failed"
  end

  Win.set_ptr_data(ptr, query)

  # using struct to achieve proper alignment and field size on 64-bit platform
  cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
  cds.dwData = AGENT_COPYDATA_ID
  cds.cbData = mapname.size + 1
  cds.lpData = Win.get_cstr(mapname)
  succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
                                cds.to_ptr, Win::SMTO_NORMAL, 5000, id)

  if succ > 0
    retlen = 4 + ptr.to_s(4).unpack("N")[0]
    res = ptr.to_s(retlen)
  else
    raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
  end

  return res
ensure
  Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
  Win.CloseHandle(filemap) if filemap != 0
end