class Net::SSH::Authentication::Pageant::Socket

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.

Public Class Methods

new() click to toggle source

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

# File lib/net/ssh/authentication/pageant.rb, line 403
def initialize
  @win = Win.FindWindow("Pageant", "Pageant")
    
  if @win.to_i == 0
    raise Net::SSH::Exception,
      "pageant process not running"
  end
    
  @input_buffer = Net::SSH::Buffer.new
  @output_buffer = Net::SSH::Buffer.new
end
open() click to toggle source

The factory method for creating a new Socket instance.

# File lib/net/ssh/authentication/pageant.rb, line 397
def self.open
  new
end

Public Instance Methods

close() click to toggle source
# File lib/net/ssh/authentication/pageant.rb, line 439
def close; end
read(n = nil) click to toggle source

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

# File lib/net/ssh/authentication/pageant.rb, line 435
def read(n = nil)
  @output_buffer.read(n)
end
send(data, *args) click to toggle source

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

# File lib/net/ssh/authentication/pageant.rb, line 417
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) click to toggle source

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.

# File lib/net/ssh/authentication/pageant.rb, line 444
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