minmb-net-ssh 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. data/CHANGELOG.rdoc +291 -0
  2. data/Manifest +132 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +23 -0
  9. data/lib/net/ssh/authentication/agent/java_pageant.rb +85 -0
  10. data/lib/net/ssh/authentication/agent/socket.rb +170 -0
  11. data/lib/net/ssh/authentication/constants.rb +18 -0
  12. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  13. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  14. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  15. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  16. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  17. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  18. data/lib/net/ssh/authentication/pageant.rb +301 -0
  19. data/lib/net/ssh/authentication/session.rb +154 -0
  20. data/lib/net/ssh/buffer.rb +350 -0
  21. data/lib/net/ssh/buffered_io.rb +207 -0
  22. data/lib/net/ssh/config.rb +207 -0
  23. data/lib/net/ssh/connection/channel.rb +630 -0
  24. data/lib/net/ssh/connection/constants.rb +33 -0
  25. data/lib/net/ssh/connection/session.rb +603 -0
  26. data/lib/net/ssh/connection/term.rb +178 -0
  27. data/lib/net/ssh/errors.rb +88 -0
  28. data/lib/net/ssh/key_factory.rb +107 -0
  29. data/lib/net/ssh/known_hosts.rb +141 -0
  30. data/lib/net/ssh/loggable.rb +61 -0
  31. data/lib/net/ssh/packet.rb +102 -0
  32. data/lib/net/ssh/prompt.rb +93 -0
  33. data/lib/net/ssh/proxy/command.rb +75 -0
  34. data/lib/net/ssh/proxy/errors.rb +14 -0
  35. data/lib/net/ssh/proxy/http.rb +94 -0
  36. data/lib/net/ssh/proxy/socks4.rb +70 -0
  37. data/lib/net/ssh/proxy/socks5.rb +142 -0
  38. data/lib/net/ssh/ruby_compat.rb +77 -0
  39. data/lib/net/ssh/service/forward.rb +327 -0
  40. data/lib/net/ssh/test.rb +89 -0
  41. data/lib/net/ssh/test/channel.rb +129 -0
  42. data/lib/net/ssh/test/extensions.rb +152 -0
  43. data/lib/net/ssh/test/kex.rb +44 -0
  44. data/lib/net/ssh/test/local_packet.rb +51 -0
  45. data/lib/net/ssh/test/packet.rb +81 -0
  46. data/lib/net/ssh/test/remote_packet.rb +38 -0
  47. data/lib/net/ssh/test/script.rb +157 -0
  48. data/lib/net/ssh/test/socket.rb +64 -0
  49. data/lib/net/ssh/transport/algorithms.rb +407 -0
  50. data/lib/net/ssh/transport/cipher_factory.rb +106 -0
  51. data/lib/net/ssh/transport/constants.rb +32 -0
  52. data/lib/net/ssh/transport/ctr.rb +95 -0
  53. data/lib/net/ssh/transport/hmac.rb +45 -0
  54. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  55. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  56. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/ripemd160.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  60. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  61. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  62. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  63. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  66. data/lib/net/ssh/transport/kex.rb +28 -0
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +44 -0
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +216 -0
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  70. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +93 -0
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +13 -0
  73. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +13 -0
  74. data/lib/net/ssh/transport/key_expander.rb +26 -0
  75. data/lib/net/ssh/transport/openssl.rb +237 -0
  76. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  77. data/lib/net/ssh/transport/server_version.rb +71 -0
  78. data/lib/net/ssh/transport/session.rb +278 -0
  79. data/lib/net/ssh/transport/state.rb +206 -0
  80. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  81. data/lib/net/ssh/verifiers/null.rb +12 -0
  82. data/lib/net/ssh/verifiers/strict.rb +53 -0
  83. data/lib/net/ssh/version.rb +62 -0
  84. data/net-ssh.gemspec +164 -0
  85. data/setup.rb +1585 -0
  86. data/support/arcfour_check.rb +20 -0
  87. data/support/ssh_tunnel_bug.rb +65 -0
  88. data/test/authentication/methods/common.rb +28 -0
  89. data/test/authentication/methods/test_abstract.rb +51 -0
  90. data/test/authentication/methods/test_hostbased.rb +114 -0
  91. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  92. data/test/authentication/methods/test_password.rb +52 -0
  93. data/test/authentication/methods/test_publickey.rb +148 -0
  94. data/test/authentication/test_agent.rb +205 -0
  95. data/test/authentication/test_key_manager.rb +218 -0
  96. data/test/authentication/test_session.rb +106 -0
  97. data/test/common.rb +107 -0
  98. data/test/configs/eqsign +3 -0
  99. data/test/configs/exact_match +8 -0
  100. data/test/configs/host_plus +10 -0
  101. data/test/configs/multihost +4 -0
  102. data/test/configs/wild_cards +14 -0
  103. data/test/connection/test_channel.rb +467 -0
  104. data/test/connection/test_session.rb +488 -0
  105. data/test/known_hosts/github +1 -0
  106. data/test/test_all.rb +9 -0
  107. data/test/test_buffer.rb +426 -0
  108. data/test/test_buffered_io.rb +63 -0
  109. data/test/test_config.rb +120 -0
  110. data/test/test_key_factory.rb +121 -0
  111. data/test/test_known_hosts.rb +13 -0
  112. data/test/transport/hmac/test_md5.rb +39 -0
  113. data/test/transport/hmac/test_md5_96.rb +25 -0
  114. data/test/transport/hmac/test_none.rb +34 -0
  115. data/test/transport/hmac/test_ripemd160.rb +34 -0
  116. data/test/transport/hmac/test_sha1.rb +34 -0
  117. data/test/transport/hmac/test_sha1_96.rb +25 -0
  118. data/test/transport/hmac/test_sha2_256.rb +35 -0
  119. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  120. data/test/transport/hmac/test_sha2_512.rb +35 -0
  121. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  122. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +13 -0
  123. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  124. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  125. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  126. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +161 -0
  127. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +37 -0
  128. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +37 -0
  129. data/test/transport/test_algorithms.rb +330 -0
  130. data/test/transport/test_cipher_factory.rb +441 -0
  131. data/test/transport/test_hmac.rb +34 -0
  132. data/test/transport/test_identity_cipher.rb +40 -0
  133. data/test/transport/test_packet_stream.rb +1745 -0
  134. data/test/transport/test_server_version.rb +78 -0
  135. data/test/transport/test_session.rb +315 -0
  136. data/test/transport/test_state.rb +179 -0
  137. metadata +208 -0
@@ -0,0 +1,60 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/loggable'
4
+ require 'net/ssh/authentication/constants'
5
+
6
+ module Net; module SSH; module Authentication; module Methods
7
+
8
+ # The base class of all user authentication methods. It provides a few
9
+ # bits of common functionality.
10
+ class Abstract
11
+ include Constants, Loggable
12
+
13
+ # The authentication session object
14
+ attr_reader :session
15
+
16
+ # The key manager object. Not all authentication methods will require
17
+ # this.
18
+ attr_reader :key_manager
19
+
20
+ # Instantiates a new authentication method.
21
+ def initialize(session, options={})
22
+ @session = session
23
+ @key_manager = options[:key_manager]
24
+ @options = options
25
+ self.logger = session.logger
26
+ end
27
+
28
+ # Returns the session-id, as generated during the first key exchange of
29
+ # an SSH connection.
30
+ def session_id
31
+ session.transport.algorithms.session_id
32
+ end
33
+
34
+ # Sends a message via the underlying transport layer abstraction. This
35
+ # will block until the message is completely sent.
36
+ def send_message(msg)
37
+ session.transport.send_message(msg)
38
+ end
39
+
40
+ # Creates a new USERAUTH_REQUEST packet. The extra arguments on the end
41
+ # must be either boolean values or strings, and are tacked onto the end
42
+ # of the packet. The new packet is returned, ready for sending.
43
+ def userauth_request(username, next_service, auth_method, *others)
44
+ buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
45
+ :string, username, :string, next_service, :string, auth_method)
46
+
47
+ others.each do |value|
48
+ case value
49
+ when true, false then buffer.write_bool(value)
50
+ when String then buffer.write_string(value)
51
+ else raise ArgumentError, "don't know how to write #{value.inspect}"
52
+ end
53
+ end
54
+
55
+ buffer
56
+ end
57
+
58
+ end
59
+
60
+ end; end; end; end
@@ -0,0 +1,75 @@
1
+ require 'net/ssh/authentication/methods/abstract'
2
+
3
+ module Net
4
+ module SSH
5
+ module Authentication
6
+ module Methods
7
+
8
+ # Implements the host-based SSH authentication method.
9
+ class Hostbased < Abstract
10
+ include Constants
11
+
12
+ # Attempts to perform host-based authorization of the user by trying
13
+ # all known keys.
14
+ def authenticate(next_service, username, password=nil)
15
+ return false unless key_manager
16
+
17
+ key_manager.each_identity do |identity|
18
+ return true if authenticate_with(identity, next_service,
19
+ username, key_manager)
20
+ end
21
+
22
+ return false
23
+ end
24
+
25
+ private
26
+
27
+ # Returns the hostname as reported by the underlying socket.
28
+ def hostname
29
+ session.transport.socket.client_name
30
+ end
31
+
32
+ # Attempts to perform host-based authentication of the user, using
33
+ # the given host identity (key).
34
+ def authenticate_with(identity, next_service, username, key_manager)
35
+ debug { "trying hostbased (#{identity.fingerprint})" }
36
+ client_username = ENV['USER'] || username
37
+
38
+ req = build_request(identity, next_service, username, "#{hostname}.", client_username)
39
+ sig_data = Buffer.from(:string, session_id, :raw, req)
40
+
41
+ sig = key_manager.sign(identity, sig_data.to_s)
42
+
43
+ message = Buffer.from(:raw, req, :string, sig)
44
+
45
+ send_message(message)
46
+ message = session.next_message
47
+
48
+ case message.type
49
+ when USERAUTH_SUCCESS
50
+ info { "hostbased succeeded (#{identity.fingerprint})" }
51
+ return true
52
+ when USERAUTH_FAILURE
53
+ info { "hostbased failed (#{identity.fingerprint})" }
54
+
55
+ raise Net::SSH::Authentication::DisallowedMethod unless
56
+ message[:authentications].split(/,/).include? 'hostbased'
57
+
58
+ return false
59
+ else
60
+ raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
61
+ end
62
+ end
63
+
64
+ # Build the "core" hostbased request string.
65
+ def build_request(identity, next_service, username, hostname, client_username)
66
+ userauth_request(username, next_service, "hostbased", identity.ssh_type,
67
+ Buffer.from(:key, identity).to_s, hostname, client_username).to_s
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,70 @@
1
+ require 'net/ssh/prompt'
2
+ require 'net/ssh/authentication/methods/abstract'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+
9
+ # Implements the "keyboard-interactive" SSH authentication method.
10
+ class KeyboardInteractive < Abstract
11
+ include Prompt
12
+
13
+ USERAUTH_INFO_REQUEST = 60
14
+ USERAUTH_INFO_RESPONSE = 61
15
+
16
+ # Attempt to authenticate the given user for the given service.
17
+ def authenticate(next_service, username, password=nil)
18
+ debug { "trying keyboard-interactive" }
19
+ send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
20
+
21
+ loop do
22
+ message = session.next_message
23
+
24
+ case message.type
25
+ when USERAUTH_SUCCESS
26
+ debug { "keyboard-interactive succeeded" }
27
+ return true
28
+ when USERAUTH_FAILURE
29
+ debug { "keyboard-interactive failed" }
30
+
31
+ raise Net::SSH::Authentication::DisallowedMethod unless
32
+ message[:authentications].split(/,/).include? 'keyboard-interactive'
33
+
34
+ return false
35
+ when USERAUTH_INFO_REQUEST
36
+ name = message.read_string
37
+ instruction = message.read_string
38
+ debug { "keyboard-interactive info request" }
39
+
40
+ unless password
41
+ puts(name) unless name.empty?
42
+ puts(instruction) unless instruction.empty?
43
+ end
44
+
45
+ lang_tag = message.read_string
46
+ responses =[]
47
+
48
+ message.read_long.times do
49
+ text = message.read_string
50
+ echo = message.read_bool
51
+ responses << (password || prompt(text, echo))
52
+ end
53
+
54
+ # if the password failed the first time around, don't try
55
+ # and use it on subsequent requests.
56
+ password = nil
57
+
58
+ msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses)
59
+ send_message(msg)
60
+ else
61
+ raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})"
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,43 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/authentication/methods/abstract'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+
9
+ # Implements the "password" SSH authentication method.
10
+ class Password < Abstract
11
+ # Attempt to authenticate the given user for the given service. If
12
+ # the password parameter is nil, this will never do anything except
13
+ # return false.
14
+ def authenticate(next_service, username, password=nil)
15
+ return false unless password
16
+
17
+ send_message(userauth_request(username, next_service, "password", false, password))
18
+ message = session.next_message
19
+
20
+ case message.type
21
+ when USERAUTH_SUCCESS
22
+ debug { "password succeeded" }
23
+ return true
24
+ when USERAUTH_FAILURE
25
+ debug { "password failed" }
26
+
27
+ raise Net::SSH::Authentication::DisallowedMethod unless
28
+ message[:authentications].split(/,/).include? 'password'
29
+
30
+ return false
31
+ when USERAUTH_PASSWD_CHANGEREQ
32
+ debug { "password change request received, failing" }
33
+ return false
34
+ else
35
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,96 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/authentication/methods/abstract'
4
+
5
+ module Net
6
+ module SSH
7
+ module Authentication
8
+ module Methods
9
+
10
+ # Implements the "publickey" SSH authentication method.
11
+ class Publickey < Abstract
12
+ # Attempts to perform public-key authentication for the given
13
+ # username, trying each identity known to the key manager. If any of
14
+ # them succeed, returns +true+, otherwise returns +false+. This
15
+ # requires the presence of a key manager.
16
+ def authenticate(next_service, username, password=nil)
17
+ return false unless key_manager
18
+
19
+ key_manager.each_identity do |identity|
20
+ return true if authenticate_with(identity, next_service, username)
21
+ end
22
+
23
+ return false
24
+ end
25
+
26
+ private
27
+
28
+ # Builds a packet that contains the request formatted for sending
29
+ # a public-key request to the server.
30
+ def build_request(pub_key, username, next_service, has_sig)
31
+ blob = Net::SSH::Buffer.new
32
+ blob.write_key pub_key
33
+
34
+ userauth_request(username, next_service, "publickey", has_sig,
35
+ pub_key.ssh_type, blob.to_s)
36
+ end
37
+
38
+ # Builds and sends a request formatted for a public-key
39
+ # authentication request.
40
+ def send_request(pub_key, username, next_service, signature=nil)
41
+ msg = build_request(pub_key, username, next_service, !signature.nil?)
42
+ msg.write_string(signature) if signature
43
+ send_message(msg)
44
+ end
45
+
46
+ # Attempts to perform public-key authentication for the given
47
+ # username, with the given identity (public key). Returns +true+ if
48
+ # successful, or +false+ otherwise.
49
+ def authenticate_with(identity, next_service, username)
50
+ debug { "trying publickey (#{identity.fingerprint})" }
51
+ send_request(identity, username, next_service)
52
+
53
+ message = session.next_message
54
+
55
+ case message.type
56
+ when USERAUTH_PK_OK
57
+ buffer = build_request(identity, username, next_service, true)
58
+ sig_data = Net::SSH::Buffer.new
59
+ sig_data.write_string(session_id)
60
+ sig_data.append(buffer.to_s)
61
+
62
+ sig_blob = key_manager.sign(identity, sig_data)
63
+
64
+ send_request(identity, username, next_service, sig_blob.to_s)
65
+ message = session.next_message
66
+
67
+ case message.type
68
+ when USERAUTH_SUCCESS
69
+ debug { "publickey succeeded (#{identity.fingerprint})" }
70
+ return true
71
+ when USERAUTH_FAILURE
72
+ debug { "publickey failed (#{identity.fingerprint})" }
73
+
74
+ raise Net::SSH::Authentication::DisallowedMethod unless
75
+ message[:authentications].split(/,/).include? 'publickey'
76
+
77
+ return false
78
+ else
79
+ raise Net::SSH::Exception,
80
+ "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
81
+ end
82
+
83
+ when USERAUTH_FAILURE
84
+ return false
85
+
86
+ else
87
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,301 @@
1
+ require 'dl/import'
2
+
3
+ if RUBY_VERSION < "1.9"
4
+ require 'dl/struct'
5
+ end
6
+
7
+ if RUBY_VERSION =~ /^1.9/
8
+ require 'dl/types'
9
+ require 'dl'
10
+ end
11
+
12
+ require 'net/ssh/errors'
13
+
14
+ module Net; module SSH; module Authentication
15
+
16
+ # This module encapsulates the implementation of a socket factory that
17
+ # uses the PuTTY "pageant" utility to obtain information about SSH
18
+ # identities.
19
+ #
20
+ # This code is a slightly modified version of the original implementation
21
+ # by Guillaume Mar�ais (guillaume.marcais@free.fr). It is used and
22
+ # relicensed by permission.
23
+ module Pageant
24
+
25
+ # From Putty pageant.c
26
+ AGENT_MAX_MSGLEN = 8192
27
+ AGENT_COPYDATA_ID = 0x804e50ba
28
+
29
+ # The definition of the Windows methods and data structures used in
30
+ # communicating with the pageant process.
31
+ module Win
32
+ if RUBY_VERSION < "1.9"
33
+ extend DL::Importable
34
+
35
+ dlload 'user32'
36
+ dlload 'kernel32'
37
+ end
38
+
39
+ if RUBY_VERSION =~ /^1.9/
40
+ extend DL::Importer
41
+ dlload 'user32','kernel32'
42
+ include DL::Win32Types
43
+ end
44
+
45
+ typealias("LPCTSTR", "char *") # From winnt.h
46
+ typealias("LPVOID", "void *") # From winnt.h
47
+ typealias("LPCVOID", "const void *") # From windef.h
48
+ typealias("LRESULT", "long") # From windef.h
49
+ typealias("WPARAM", "unsigned int *") # From windef.h
50
+ typealias("LPARAM", "long *") # From windef.h
51
+ typealias("PDWORD_PTR", "long *") # From basetsd.h
52
+
53
+ # From winbase.h, winnt.h
54
+ INVALID_HANDLE_VALUE = -1
55
+ NULL = nil
56
+ PAGE_READWRITE = 0x0004
57
+ FILE_MAP_WRITE = 2
58
+ WM_COPYDATA = 74
59
+
60
+ SMTO_NORMAL = 0 # From winuser.h
61
+
62
+ # args: lpClassName, lpWindowName
63
+ extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
64
+
65
+ # args: none
66
+ extern 'DWORD GetCurrentThreadId()'
67
+
68
+ # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
69
+ # dwMaximumSizeLow, lpName
70
+ extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' +
71
+ 'DWORD, LPCTSTR)'
72
+
73
+ # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
74
+ # dwfileOffsetLow, dwNumberOfBytesToMap
75
+ extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
76
+
77
+ # args: lpBaseAddress
78
+ extern 'BOOL UnmapViewOfFile(LPCVOID)'
79
+
80
+ # args: hObject
81
+ extern 'BOOL CloseHandle(HANDLE)'
82
+
83
+ # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
84
+ extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
85
+ 'UINT, UINT, PDWORD_PTR)'
86
+ if RUBY_VERSION < "1.9"
87
+ alias_method :FindWindow,:findWindow
88
+ module_function :FindWindow
89
+ end
90
+ end
91
+
92
+ # This is the pseudo-socket implementation that mimics the interface of
93
+ # a socket, translating each request into a Windows messaging call to
94
+ # the pageant daemon. This allows pageant support to be implemented
95
+ # simply by replacing the socket factory used by the Agent class.
96
+ class Socket
97
+
98
+ private_class_method :new
99
+
100
+ # The factory method for creating a new Socket instance. The location
101
+ # parameter is ignored, and is only needed for compatibility with
102
+ # the general Socket interface.
103
+ def self.open(location=nil)
104
+ new
105
+ end
106
+
107
+ # Create a new instance that communicates with the running pageant
108
+ # instance. If no such instance is running, this will cause an error.
109
+ def initialize
110
+ @win = Win.FindWindow("Pageant", "Pageant")
111
+
112
+ if @win == 0
113
+ raise Net::SSH::Exception,
114
+ "pageant process not running"
115
+ end
116
+
117
+ @pending_query = nil
118
+ @res = nil
119
+ @pos = 0
120
+ end
121
+
122
+ # Forwards the data to #send_query, ignoring any arguments after
123
+ # the first. Returns 0.
124
+ def send(data, *args)
125
+ @res = send_query(data)
126
+ @pos = 0
127
+ end
128
+
129
+ # Packages the given query string and sends it to the pageant
130
+ # process via the Windows messaging subsystem. The result is
131
+ # cached, to be returned piece-wise when #read is called.
132
+ def send_query(query)
133
+ res = nil
134
+ filemap = 0
135
+ ptr = nil
136
+ id = DL::PtrData.malloc(DL.sizeof("L"))
137
+
138
+ mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId()
139
+ filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE,
140
+ Win::NULL,
141
+ Win::PAGE_READWRITE, 0,
142
+ AGENT_MAX_MSGLEN, mapname)
143
+ if filemap == 0
144
+ raise Net::SSH::Exception,
145
+ "Creation of file mapping failed"
146
+ end
147
+
148
+ ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
149
+ AGENT_MAX_MSGLEN)
150
+
151
+ if ptr.nil? || ptr.null?
152
+ raise Net::SSH::Exception, "Mapping of file failed"
153
+ end
154
+
155
+ ptr[0] = query
156
+
157
+ cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
158
+ pack("LLp").to_ptr
159
+ succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
160
+ cds, Win::SMTO_NORMAL, 5000, id)
161
+
162
+ if succ > 0
163
+ retlen = 4 + ptr.to_s(4).unpack("N")[0]
164
+ res = ptr.to_s(retlen)
165
+ end
166
+
167
+ return res
168
+ ensure
169
+ Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
170
+ Win.closeHandle(filemap) if filemap != 0
171
+ end
172
+
173
+ # Conceptually close the socket. This doesn't really do anthing
174
+ # significant, but merely complies with the Socket interface.
175
+ def close
176
+ # hack. probably need to move closed state to a flag,
177
+ # since ready and closed will look equivalent
178
+ # also basically we never need to actually be closed
179
+
180
+ # @res = nil
181
+ @pos = 0
182
+ end
183
+
184
+ # Conceptually asks if the socket is closed. As with #close,
185
+ # this doesn't really do anything significant, but merely
186
+ # complies with the Socket interface.
187
+ def closed?
188
+ @res.nil? && @pos.zero?
189
+ end
190
+
191
+ # Reads +n+ bytes from the cached result of the last query. If +n+
192
+ # is +nil+, returns all remaining data from the last query.
193
+ def read(n = nil)
194
+ return nil unless @res
195
+
196
+ if n.nil?
197
+ start, @pos = @pos, @res.size
198
+ return @res[start..-1]
199
+ else
200
+ start, @pos = @pos, @pos + n
201
+ return @res[start, n]
202
+ end
203
+ end
204
+
205
+ # methods we need since they dont come from buffer/buffered_io any more
206
+ def available
207
+ return @res.nil? ? 0 : @res.length - @pos
208
+ end
209
+
210
+ def read_available(n=nil)
211
+ read(n)
212
+ end
213
+
214
+ def fill(n=nil)
215
+ @res = read(n)
216
+ return @res.length
217
+ end
218
+
219
+ def enqueue(data)
220
+ @pending_query = (@pending_query || '') + data
221
+ end
222
+
223
+ def send_pending
224
+ return if @pending_query.nil?
225
+
226
+ @res = send_query(@pending_query)
227
+ @pos = 0
228
+ @pending_query = nil
229
+ end
230
+
231
+ def shutdown(reason)
232
+ #no op
233
+ end
234
+ end
235
+
236
+ # Socket changes for Ruby 1.9
237
+ # Functionality is the same as Ruby 1.8 but it includes the new calls to
238
+ # the DL module as well as other pointer transformations
239
+ class Socket19 < Socket
240
+ # Packages the given query string and sends it to the pageant
241
+ # process via the Windows messaging subsystem. The result is
242
+ # cached, to be returned piece-wise when #read is called.
243
+ def send_query(query)
244
+ query = @always_query || query
245
+
246
+ res = nil
247
+ filemap = 0
248
+ ptr = nil
249
+ id = DL.malloc(DL::SIZEOF_LONG)
250
+
251
+ mapname = "PageantRequest%08x\000" % Win.GetCurrentThreadId()
252
+
253
+ filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
254
+ Win::NULL,
255
+ Win::PAGE_READWRITE, 0,
256
+ AGENT_MAX_MSGLEN, mapname)
257
+
258
+ if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
259
+ raise Net::SSH::Exception,
260
+ "Creation of file mapping failed"
261
+ end
262
+
263
+ ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
264
+ 0)
265
+
266
+ if ptr.nil? || ptr.null?
267
+ raise Net::SSH::Exception, "Mapping of file failed"
268
+ end
269
+
270
+ DL::CPtr.new(ptr)[0,query.size]=query
271
+
272
+ cds = DL::CPtr.to_ptr [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
273
+ pack("LLp")
274
+ succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
275
+ cds, Win::SMTO_NORMAL, 5000, id)
276
+
277
+ if succ > 0
278
+ retlen = 4 + ptr.to_s(4).unpack("N")[0]
279
+ res = ptr.to_s(retlen)
280
+ end
281
+
282
+ return res
283
+ ensure
284
+ Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
285
+ Win.CloseHandle(filemap) if filemap != 0
286
+ end
287
+ end
288
+
289
+ # Selects which socket to use depending on the ruby version
290
+ # This is needed due changes in the DL module.
291
+ def self.socket_factory
292
+ if RUBY_VERSION < "1.9"
293
+ Socket
294
+ else
295
+ Socket19
296
+ end
297
+ end
298
+
299
+ end
300
+
301
+ end; end; end