net-ssh 5.0.0.beta1 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.rubocop_todo.yml +98 -258
  5. data/CHANGES.txt +8 -0
  6. data/Gemfile +1 -3
  7. data/Rakefile +37 -39
  8. data/lib/net/ssh.rb +26 -25
  9. data/lib/net/ssh/authentication/agent.rb +228 -225
  10. data/lib/net/ssh/authentication/certificate.rb +166 -164
  11. data/lib/net/ssh/authentication/constants.rb +17 -14
  12. data/lib/net/ssh/authentication/ed25519.rb +107 -104
  13. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
  14. data/lib/net/ssh/authentication/key_manager.rb +5 -3
  15. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  16. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  17. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
  18. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  19. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  20. data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
  21. data/lib/net/ssh/authentication/pageant.rb +468 -465
  22. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
  23. data/lib/net/ssh/authentication/session.rb +127 -123
  24. data/lib/net/ssh/buffer.rb +305 -303
  25. data/lib/net/ssh/buffered_io.rb +163 -162
  26. data/lib/net/ssh/config.rb +230 -227
  27. data/lib/net/ssh/connection/channel.rb +659 -654
  28. data/lib/net/ssh/connection/constants.rb +30 -26
  29. data/lib/net/ssh/connection/event_loop.rb +108 -104
  30. data/lib/net/ssh/connection/keepalive.rb +54 -50
  31. data/lib/net/ssh/connection/session.rb +677 -678
  32. data/lib/net/ssh/connection/term.rb +180 -176
  33. data/lib/net/ssh/errors.rb +101 -99
  34. data/lib/net/ssh/key_factory.rb +108 -108
  35. data/lib/net/ssh/known_hosts.rb +148 -154
  36. data/lib/net/ssh/loggable.rb +56 -54
  37. data/lib/net/ssh/packet.rb +82 -78
  38. data/lib/net/ssh/prompt.rb +55 -53
  39. data/lib/net/ssh/proxy/command.rb +103 -102
  40. data/lib/net/ssh/proxy/errors.rb +12 -8
  41. data/lib/net/ssh/proxy/http.rb +92 -91
  42. data/lib/net/ssh/proxy/https.rb +42 -39
  43. data/lib/net/ssh/proxy/jump.rb +50 -47
  44. data/lib/net/ssh/proxy/socks4.rb +0 -2
  45. data/lib/net/ssh/proxy/socks5.rb +11 -11
  46. data/lib/net/ssh/ruby_compat.rb +1 -0
  47. data/lib/net/ssh/service/forward.rb +364 -362
  48. data/lib/net/ssh/test.rb +85 -83
  49. data/lib/net/ssh/test/channel.rb +146 -142
  50. data/lib/net/ssh/test/extensions.rb +148 -146
  51. data/lib/net/ssh/test/kex.rb +35 -31
  52. data/lib/net/ssh/test/local_packet.rb +48 -44
  53. data/lib/net/ssh/test/packet.rb +87 -84
  54. data/lib/net/ssh/test/remote_packet.rb +35 -31
  55. data/lib/net/ssh/test/script.rb +173 -171
  56. data/lib/net/ssh/test/socket.rb +59 -55
  57. data/lib/net/ssh/transport/algorithms.rb +413 -412
  58. data/lib/net/ssh/transport/cipher_factory.rb +108 -105
  59. data/lib/net/ssh/transport/constants.rb +35 -31
  60. data/lib/net/ssh/transport/ctr.rb +1 -1
  61. data/lib/net/ssh/transport/hmac.rb +1 -1
  62. data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
  63. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  66. data/lib/net/ssh/transport/kex.rb +2 -4
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
  70. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
  73. data/lib/net/ssh/transport/key_expander.rb +29 -25
  74. data/lib/net/ssh/transport/openssl.rb +17 -30
  75. data/lib/net/ssh/transport/packet_stream.rb +193 -192
  76. data/lib/net/ssh/transport/server_version.rb +64 -66
  77. data/lib/net/ssh/transport/session.rb +286 -284
  78. data/lib/net/ssh/transport/state.rb +198 -196
  79. data/lib/net/ssh/verifiers/lenient.rb +29 -25
  80. data/lib/net/ssh/verifiers/null.rb +13 -9
  81. data/lib/net/ssh/verifiers/secure.rb +45 -45
  82. data/lib/net/ssh/verifiers/strict.rb +20 -16
  83. data/lib/net/ssh/version.rb +55 -53
  84. data/net-ssh.gemspec +4 -4
  85. data/support/ssh_tunnel_bug.rb +2 -2
  86. metadata +25 -24
  87. metadata.gz.sig +0 -0
@@ -0,0 +1,44 @@
1
+
2
+ require 'openssl'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ # Public key fingerprinting utility module - internal not part of API.
8
+ # This is included in pubkey classes and called from there. All RSA, DSA, and ECC keys
9
+ # are supported.
10
+ #
11
+ # require 'net/ssh'
12
+ # my_pubkey_text = File.read('/path/to/id_ed25519.pub')
13
+ # #=> "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDB2NBh4GJPPUN1kXPMu8b633Xcv55WoKC3OkBjFAbzJ alice@example.com"
14
+ # my_pubkey = Net::SSH::KeyFactory.load_data_public_key(my_pubkey_text)
15
+ # #=> #<Net::SSH::Authentication::ED25519::PubKey:0x00007fc8e91819b0
16
+ # my_pubkey.fingerprint
17
+ # #=> "2f:7f:97:21:76:a4:0f:38:c4:fe:d8:b4:6a:39:72:30"
18
+ # my_pubkey.fingerprint('SHA256')
19
+ # #=> "SHA256:u6mXnY8P1b0FODGp8mckqOB33u8+jvkSCtJbD5Q9klg"
20
+ module PubKeyFingerprint # :nodoc:
21
+ # Return the key's fingerprint. Algorithm may be either +MD5+ (default),
22
+ # or +SHA256+. For +SHA256+, fingerprints are in the same format
23
+ # returned by OpenSSH's <tt>`ssh-add -l -E SHA256`</tt>, i.e.,
24
+ # trailing base64 padding '=' characters are stripped and the
25
+ # literal string +SHA256:+ is prepended.
26
+ def fingerprint(algorithm='MD5')
27
+ @fingerprint ||= {}
28
+ @fingerprint[algorithm] ||= PubKeyFingerprint.fingerprint(to_blob, algorithm)
29
+ end
30
+
31
+ def self.fingerprint(blob, algorithm='MD5')
32
+ case algorithm.to_s.upcase
33
+ when 'MD5'
34
+ OpenSSL::Digest.hexdigest(algorithm, blob).scan(/../).join(":")
35
+ when 'SHA256'
36
+ "SHA256:#{Base64.encode64(OpenSSL::Digest.digest(algorithm, blob)).chomp.gsub(/=+\z/, '')}"
37
+ else
38
+ raise OpenSSL::Digest::DigestError, "unsupported ssh key digest #{algorithm}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -8,149 +8,153 @@ require 'net/ssh/authentication/methods/hostbased'
8
8
  require 'net/ssh/authentication/methods/password'
9
9
  require 'net/ssh/authentication/methods/keyboard_interactive'
10
10
 
11
- module Net; module SSH; module Authentication
11
+ module Net
12
+ module SSH
13
+ module Authentication
12
14
 
13
- # Raised if the current authentication method is not allowed
14
- class DisallowedMethod < Net::SSH::Exception
15
- end
15
+ # Raised if the current authentication method is not allowed
16
+ class DisallowedMethod < Net::SSH::Exception
17
+ end
16
18
 
17
- # Represents an authentication session. It manages the authentication of
18
- # a user over an established connection (the "transport" object, see
19
- # Net::SSH::Transport::Session).
20
- #
21
- # The use of an authentication session to manage user authentication is
22
- # internal to Net::SSH (specifically Net::SSH.start). Consumers of the
23
- # Net::SSH library will never need to access this class directly.
24
- class Session
25
- include Transport::Constants, Constants, Loggable
19
+ # Represents an authentication session. It manages the authentication of
20
+ # a user over an established connection (the "transport" object, see
21
+ # Net::SSH::Transport::Session).
22
+ #
23
+ # The use of an authentication session to manage user authentication is
24
+ # internal to Net::SSH (specifically Net::SSH.start). Consumers of the
25
+ # Net::SSH library will never need to access this class directly.
26
+ class Session
27
+ include Loggable
28
+ include Constants
29
+ include Transport::Constants
26
30
 
27
- # transport layer abstraction
28
- attr_reader :transport
31
+ # transport layer abstraction
32
+ attr_reader :transport
29
33
 
30
- # the list of authentication methods to try
31
- attr_reader :auth_methods
34
+ # the list of authentication methods to try
35
+ attr_reader :auth_methods
32
36
 
33
- # the list of authentication methods that are allowed
34
- attr_reader :allowed_auth_methods
37
+ # the list of authentication methods that are allowed
38
+ attr_reader :allowed_auth_methods
35
39
 
36
- # a hash of options, given at construction time
37
- attr_reader :options
40
+ # a hash of options, given at construction time
41
+ attr_reader :options
38
42
 
39
- # Instantiates a new Authentication::Session object over the given
40
- # transport layer abstraction.
41
- def initialize(transport, options={})
42
- self.logger = transport.logger
43
- @transport = transport
43
+ # Instantiates a new Authentication::Session object over the given
44
+ # transport layer abstraction.
45
+ def initialize(transport, options={})
46
+ self.logger = transport.logger
47
+ @transport = transport
44
48
 
45
- @auth_methods = options[:auth_methods] || Net::SSH::Config.default_auth_methods
46
- @options = options
49
+ @auth_methods = options[:auth_methods] || Net::SSH::Config.default_auth_methods
50
+ @options = options
47
51
 
48
- @allowed_auth_methods = @auth_methods
49
- end
52
+ @allowed_auth_methods = @auth_methods
53
+ end
50
54
 
51
- # Attempts to authenticate the given user, in preparation for the next
52
- # service request. Returns true if an authentication method succeeds in
53
- # authenticating the user, and false otherwise.
54
- def authenticate(next_service, username, password=nil)
55
- debug { "beginning authentication of `#{username}'" }
56
-
57
- transport.send_message(transport.service_request("ssh-userauth"))
58
- expect_message(SERVICE_ACCEPT)
59
-
60
- key_manager = KeyManager.new(logger, options)
61
- keys.each { |key| key_manager.add(key) } unless keys.empty?
62
- key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
63
-
64
- attempted = []
65
-
66
- @auth_methods.each do |name|
67
- begin
68
- next unless @allowed_auth_methods.include?(name)
69
- attempted << name
70
-
71
- debug { "trying #{name}" }
72
- begin
73
- auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
74
- method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
75
- rescue NameError
76
- debug{"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
77
- next
55
+ # Attempts to authenticate the given user, in preparation for the next
56
+ # service request. Returns true if an authentication method succeeds in
57
+ # authenticating the user, and false otherwise.
58
+ def authenticate(next_service, username, password=nil)
59
+ debug { "beginning authentication of `#{username}'" }
60
+
61
+ transport.send_message(transport.service_request("ssh-userauth"))
62
+ expect_message(SERVICE_ACCEPT)
63
+
64
+ key_manager = KeyManager.new(logger, options)
65
+ keys.each { |key| key_manager.add(key) } unless keys.empty?
66
+ key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
67
+
68
+ attempted = []
69
+
70
+ @auth_methods.each do |name|
71
+ begin
72
+ next unless @allowed_auth_methods.include?(name)
73
+ attempted << name
74
+
75
+ debug { "trying #{name}" }
76
+ begin
77
+ auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
78
+ method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
79
+ rescue NameError
80
+ debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
81
+ next
82
+ end
83
+
84
+ return true if method.authenticate(next_service, username, password)
85
+ rescue Net::SSH::Authentication::DisallowedMethod
86
+ end
78
87
  end
79
88
 
80
- return true if method.authenticate(next_service, username, password)
81
- rescue Net::SSH::Authentication::DisallowedMethod
89
+ error { "all authorization methods failed (tried #{attempted.join(', ')})" }
90
+ return false
91
+ ensure
92
+ key_manager.finish if key_manager
82
93
  end
83
- end
84
-
85
- error { "all authorization methods failed (tried #{attempted.join(', ')})" }
86
- return false
87
- ensure
88
- key_manager.finish if key_manager
89
- end
90
-
91
- # Blocks until a packet is received. It silently handles USERAUTH_BANNER
92
- # packets, and will raise an error if any packet is received that is not
93
- # valid during user authentication.
94
- def next_message
95
- loop do
96
- packet = transport.next_message
97
-
98
- case packet.type
99
- when USERAUTH_BANNER
100
- info { packet[:message] }
101
- # TODO add a hook for people to retrieve the banner when it is sent
102
-
103
- when USERAUTH_FAILURE
104
- @allowed_auth_methods = packet[:authentications].split(/,/)
105
- debug { "allowed methods: #{packet[:authentications]}" }
106
- return packet
107
-
108
- when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
109
- return packet
110
-
111
- when USERAUTH_SUCCESS
112
- transport.hint :authenticated
113
- return packet
114
94
 
115
- else
116
- raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
95
+ # Blocks until a packet is received. It silently handles USERAUTH_BANNER
96
+ # packets, and will raise an error if any packet is received that is not
97
+ # valid during user authentication.
98
+ def next_message
99
+ loop do
100
+ packet = transport.next_message
101
+
102
+ case packet.type
103
+ when USERAUTH_BANNER
104
+ info { packet[:message] }
105
+ # TODO add a hook for people to retrieve the banner when it is sent
106
+
107
+ when USERAUTH_FAILURE
108
+ @allowed_auth_methods = packet[:authentications].split(/,/)
109
+ debug { "allowed methods: #{packet[:authentications]}" }
110
+ return packet
111
+
112
+ when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
113
+ return packet
114
+
115
+ when USERAUTH_SUCCESS
116
+ transport.hint :authenticated
117
+ return packet
118
+
119
+ else
120
+ raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
121
+ end
122
+ end
117
123
  end
118
- end
119
- end
120
124
 
121
- # Blocks until a packet is received, and returns it if it is of the given
122
- # type. If it is not, an exception is raised.
123
- def expect_message(type)
124
- message = next_message
125
- unless message.type == type
126
- raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})"
127
- end
128
- message
129
- end
125
+ # Blocks until a packet is received, and returns it if it is of the given
126
+ # type. If it is not, an exception is raised.
127
+ def expect_message(type)
128
+ message = next_message
129
+ raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" unless message.type == type
130
+ message
131
+ end
130
132
 
131
- private
133
+ private
132
134
 
133
- # Returns an array of paths to the key files usually defined
134
- # by system default.
135
- def default_keys
136
- if defined?(OpenSSL::PKey::EC)
137
- %w(~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa
138
- ~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa)
139
- else
140
- %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa)
135
+ # Returns an array of paths to the key files usually defined
136
+ # by system default.
137
+ def default_keys
138
+ if defined?(OpenSSL::PKey::EC)
139
+ %w[~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa
140
+ ~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa]
141
+ else
142
+ %w[~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa]
143
+ end
141
144
  end
142
- end
143
145
 
144
- # Returns an array of paths to the key files that should be used when
145
- # attempting any key-based authentication mechanism.
146
- def keys
147
- Array(options[:keys] || default_keys)
148
- end
146
+ # Returns an array of paths to the key files that should be used when
147
+ # attempting any key-based authentication mechanism.
148
+ def keys
149
+ Array(options[:keys])
150
+ end
149
151
 
150
- # Returns an array of the key data that should be used when
151
- # attempting any key-based authentication mechanism.
152
- def key_data
153
- Array(options[:key_data])
152
+ # Returns an array of the key data that should be used when
153
+ # attempting any key-based authentication mechanism.
154
+ def key_data
155
+ Array(options[:key_data])
156
+ end
154
157
  end
158
+ end
155
159
  end
156
- end; end; end
160
+ end
@@ -4,254 +4,255 @@ require 'net/ssh/transport/openssl'
4
4
  require 'net/ssh/authentication/certificate'
5
5
  require 'net/ssh/authentication/ed25519_loader'
6
6
 
7
- module Net; module SSH
8
-
9
- # Net::SSH::Buffer is a flexible class for building and parsing binary
10
- # data packets. It provides a stream-like interface for sequentially
11
- # reading data items from the buffer, as well as a useful helper method
12
- # for building binary packets given a signature.
13
- #
14
- # Writing to a buffer always appends to the end, regardless of where the
15
- # read cursor is. Reading, on the other hand, always begins at the first
16
- # byte of the buffer and increments the read cursor, with subsequent reads
17
- # taking up where the last left off.
18
- #
19
- # As a consumer of the Net::SSH library, you will rarely come into contact
20
- # with these buffer objects directly, but it could happen. Also, if you
21
- # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer
22
- # class can be quite handy.
23
- class Buffer
24
- # This is a convenience method for creating and populating a new buffer
25
- # from a single command. The arguments must be even in length, with the
26
- # first of each pair of arguments being a symbol naming the type of the
27
- # data that follows. If the type is :raw, the value is written directly
28
- # to the hash.
29
- #
30
- # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
31
- # #-> "\1\0\0\0\5hello\1\2\3\4"
32
- #
33
- # The supported data types are:
7
+ module Net
8
+ module SSH
9
+
10
+ # Net::SSH::Buffer is a flexible class for building and parsing binary
11
+ # data packets. It provides a stream-like interface for sequentially
12
+ # reading data items from the buffer, as well as a useful helper method
13
+ # for building binary packets given a signature.
34
14
  #
35
- # * :raw => write the next value verbatim (#write)
36
- # * :int64 => write an 8-byte integer (#write_int64)
37
- # * :long => write a 4-byte integer (#write_long)
38
- # * :byte => write a single byte (#write_byte)
39
- # * :string => write a 4-byte length followed by character data (#write_string)
40
- # * :mstring => same as string, but caller cannot resuse the string, avoids potential duplication (#write_moved)
41
- # * :bool => write a single byte, interpreted as a boolean (#write_bool)
42
- # * :bignum => write an SSH-encoded bignum (#write_bignum)
43
- # * :key => write an SSH-encoded key value (#write_key)
15
+ # Writing to a buffer always appends to the end, regardless of where the
16
+ # read cursor is. Reading, on the other hand, always begins at the first
17
+ # byte of the buffer and increments the read cursor, with subsequent reads
18
+ # taking up where the last left off.
44
19
  #
45
- # Any of these, except for :raw, accepts an Array argument, to make it
46
- # easier to write multiple values of the same type in a briefer manner.
47
- def self.from(*args)
48
- raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
49
-
50
- buffer = new
51
- 0.step(args.length-1, 2) do |index|
52
- type = args[index]
53
- value = args[index+1]
54
- if type == :raw
55
- buffer.append(value.to_s)
56
- elsif Array === value
57
- buffer.send("write_#{type}", *value)
58
- else
59
- buffer.send("write_#{type}", value)
20
+ # As a consumer of the Net::SSH library, you will rarely come into contact
21
+ # with these buffer objects directly, but it could happen. Also, if you
22
+ # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer
23
+ # class can be quite handy.
24
+ class Buffer
25
+ # This is a convenience method for creating and populating a new buffer
26
+ # from a single command. The arguments must be even in length, with the
27
+ # first of each pair of arguments being a symbol naming the type of the
28
+ # data that follows. If the type is :raw, the value is written directly
29
+ # to the hash.
30
+ #
31
+ # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
32
+ # #-> "\1\0\0\0\5hello\1\2\3\4"
33
+ #
34
+ # The supported data types are:
35
+ #
36
+ # * :raw => write the next value verbatim (#write)
37
+ # * :int64 => write an 8-byte integer (#write_int64)
38
+ # * :long => write a 4-byte integer (#write_long)
39
+ # * :byte => write a single byte (#write_byte)
40
+ # * :string => write a 4-byte length followed by character data (#write_string)
41
+ # * :mstring => same as string, but caller cannot resuse the string, avoids potential duplication (#write_moved)
42
+ # * :bool => write a single byte, interpreted as a boolean (#write_bool)
43
+ # * :bignum => write an SSH-encoded bignum (#write_bignum)
44
+ # * :key => write an SSH-encoded key value (#write_key)
45
+ #
46
+ # Any of these, except for :raw, accepts an Array argument, to make it
47
+ # easier to write multiple values of the same type in a briefer manner.
48
+ def self.from(*args)
49
+ raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
50
+
51
+ buffer = new
52
+ 0.step(args.length - 1, 2) do |index|
53
+ type = args[index]
54
+ value = args[index + 1]
55
+ if type == :raw
56
+ buffer.append(value.to_s)
57
+ elsif Array === value
58
+ buffer.send("write_#{type}", *value)
59
+ else
60
+ buffer.send("write_#{type}", value)
61
+ end
60
62
  end
61
- end
62
63
 
63
- buffer
64
- end
64
+ buffer
65
+ end
65
66
 
66
- # exposes the raw content of the buffer
67
- attr_reader :content
67
+ # exposes the raw content of the buffer
68
+ attr_reader :content
68
69
 
69
- # the current position of the pointer in the buffer
70
- attr_accessor :position
70
+ # the current position of the pointer in the buffer
71
+ attr_accessor :position
71
72
 
72
- # Creates a new buffer, initialized to the given content. The position
73
- # is initialized to the beginning of the buffer.
74
- def initialize(content="")
75
- @content = content.to_s
76
- @position = 0
77
- end
73
+ # Creates a new buffer, initialized to the given content. The position
74
+ # is initialized to the beginning of the buffer.
75
+ def initialize(content="")
76
+ @content = content.to_s
77
+ @position = 0
78
+ end
78
79
 
79
- # Returns the length of the buffer's content.
80
- def length
81
- @content.length
82
- end
80
+ # Returns the length of the buffer's content.
81
+ def length
82
+ @content.length
83
+ end
83
84
 
84
- # Returns the number of bytes available to be read (e.g., how many bytes
85
- # remain between the current position and the end of the buffer).
86
- def available
87
- length - position
88
- end
85
+ # Returns the number of bytes available to be read (e.g., how many bytes
86
+ # remain between the current position and the end of the buffer).
87
+ def available
88
+ length - position
89
+ end
89
90
 
90
- # Returns a copy of the buffer's content.
91
- def to_s
92
- (@content || "").dup
93
- end
91
+ # Returns a copy of the buffer's content.
92
+ def to_s
93
+ (@content || "").dup
94
+ end
94
95
 
95
- # Compares the contents of the two buffers, returning +true+ only if they
96
- # are identical in size and content.
97
- def ==(buffer)
98
- to_s == buffer.to_s
99
- end
96
+ # Compares the contents of the two buffers, returning +true+ only if they
97
+ # are identical in size and content.
98
+ def ==(buffer)
99
+ to_s == buffer.to_s
100
+ end
100
101
 
101
- # Returns +true+ if the buffer contains no data (e.g., it is of zero length).
102
- def empty?
103
- @content.empty?
104
- end
102
+ # Returns +true+ if the buffer contains no data (e.g., it is of zero length).
103
+ def empty?
104
+ @content.empty?
105
+ end
105
106
 
106
- # Resets the pointer to the start of the buffer. Subsequent reads will
107
- # begin at position 0.
108
- def reset!
109
- @position = 0
110
- end
107
+ # Resets the pointer to the start of the buffer. Subsequent reads will
108
+ # begin at position 0.
109
+ def reset!
110
+ @position = 0
111
+ end
111
112
 
112
- # Returns true if the pointer is at the end of the buffer. Subsequent
113
- # reads will return nil, in this case.
114
- def eof?
115
- @position >= length
116
- end
113
+ # Returns true if the pointer is at the end of the buffer. Subsequent
114
+ # reads will return nil, in this case.
115
+ def eof?
116
+ @position >= length
117
+ end
117
118
 
118
- # Resets the buffer, making it empty. Also, resets the read position to
119
- # 0.
120
- def clear!
121
- @content = ""
122
- @position = 0
123
- end
119
+ # Resets the buffer, making it empty. Also, resets the read position to
120
+ # 0.
121
+ def clear!
122
+ @content = ""
123
+ @position = 0
124
+ end
124
125
 
125
- # Consumes n bytes from the buffer, where n is the current position
126
- # unless otherwise specified. This is useful for removing data from the
127
- # buffer that has previously been read, when you are expecting more data
128
- # to be appended. It helps to keep the size of buffers down when they
129
- # would otherwise tend to grow without bound.
130
- #
131
- # Returns the buffer object itself.
132
- def consume!(n=position)
133
- if n >= length
134
- # optimize for a fairly common case
135
- clear!
136
- elsif n > 0
137
- @content = @content[n..-1] || ""
138
- @position -= n
139
- @position = 0 if @position < 0
140
- end
141
- self
142
- end
126
+ # Consumes n bytes from the buffer, where n is the current position
127
+ # unless otherwise specified. This is useful for removing data from the
128
+ # buffer that has previously been read, when you are expecting more data
129
+ # to be appended. It helps to keep the size of buffers down when they
130
+ # would otherwise tend to grow without bound.
131
+ #
132
+ # Returns the buffer object itself.
133
+ def consume!(n=position)
134
+ if n >= length
135
+ # optimize for a fairly common case
136
+ clear!
137
+ elsif n > 0
138
+ @content = @content[n..-1] || ""
139
+ @position -= n
140
+ @position = 0 if @position < 0
141
+ end
142
+ self
143
+ end
143
144
 
144
- # Appends the given text to the end of the buffer. Does not alter the
145
- # read position. Returns the buffer object itself.
146
- def append(text)
147
- @content << text
148
- self
149
- end
145
+ # Appends the given text to the end of the buffer. Does not alter the
146
+ # read position. Returns the buffer object itself.
147
+ def append(text)
148
+ @content << text
149
+ self
150
+ end
150
151
 
151
- # Returns all text from the current pointer to the end of the buffer as
152
- # a new Net::SSH::Buffer object.
153
- def remainder_as_buffer
154
- Buffer.new(@content[@position..-1])
155
- end
152
+ # Returns all text from the current pointer to the end of the buffer as
153
+ # a new Net::SSH::Buffer object.
154
+ def remainder_as_buffer
155
+ Buffer.new(@content[@position..-1])
156
+ end
156
157
 
157
- # Reads all data up to and including the given pattern, which may be a
158
- # String, Fixnum, or Regexp and is interpreted exactly as String#index
159
- # does. Returns nil if nothing matches. Increments the position to point
160
- # immediately after the pattern, if it does match. Returns all data up to
161
- # and including the text that matched the pattern.
162
- def read_to(pattern)
163
- index = @content.index(pattern, @position) or return nil
164
- length = case pattern
165
- when String then pattern.length
166
- when Integer then 1
167
- when Regexp then $&.length
168
- end
169
- index && read(index+length)
170
- end
158
+ # Reads all data up to and including the given pattern, which may be a
159
+ # String, Fixnum, or Regexp and is interpreted exactly as String#index
160
+ # does. Returns nil if nothing matches. Increments the position to point
161
+ # immediately after the pattern, if it does match. Returns all data up to
162
+ # and including the text that matched the pattern.
163
+ def read_to(pattern)
164
+ index = @content.index(pattern, @position) or return nil
165
+ length = case pattern
166
+ when String then pattern.length
167
+ when Integer then 1
168
+ when Regexp then $&.length
169
+ end
170
+ index && read(index + length)
171
+ end
171
172
 
172
- # Reads and returns the next +count+ bytes from the buffer, starting from
173
- # the read position. If +count+ is +nil+, this will return all remaining
174
- # text in the buffer. This method will increment the pointer.
175
- def read(count=nil)
176
- count ||= length
177
- count = length - @position if @position + count > length
178
- @position += count
179
- @content[@position-count, count]
180
- end
173
+ # Reads and returns the next +count+ bytes from the buffer, starting from
174
+ # the read position. If +count+ is +nil+, this will return all remaining
175
+ # text in the buffer. This method will increment the pointer.
176
+ def read(count=nil)
177
+ count ||= length
178
+ count = length - @position if @position + count > length
179
+ @position += count
180
+ @content[@position - count, count]
181
+ end
181
182
 
182
- # Reads (as #read) and returns the given number of bytes from the buffer,
183
- # and then consumes (as #consume!) all data up to the new read position.
184
- def read!(count=nil)
185
- data = read(count)
186
- consume!
187
- data
188
- end
183
+ # Reads (as #read) and returns the given number of bytes from the buffer,
184
+ # and then consumes (as #consume!) all data up to the new read position.
185
+ def read!(count=nil)
186
+ data = read(count)
187
+ consume!
188
+ data
189
+ end
189
190
 
190
- # Calls block(self) until the buffer is empty, and returns all results.
191
- def read_all(&block)
192
- Enumerator.new { |e| e << yield(self) until eof? }.to_a
193
- end
191
+ # Calls block(self) until the buffer is empty, and returns all results.
192
+ def read_all(&block)
193
+ Enumerator.new { |e| e << yield(self) until eof? }.to_a
194
+ end
194
195
 
195
- # Return the next 8 bytes as a 64-bit integer (in network byte order).
196
- # Returns nil if there are less than 8 bytes remaining to be read in the
197
- # buffer.
198
- def read_int64
199
- hi = read_long or return nil
200
- lo = read_long or return nil
201
- return (hi << 32) + lo
202
- end
196
+ # Return the next 8 bytes as a 64-bit integer (in network byte order).
197
+ # Returns nil if there are less than 8 bytes remaining to be read in the
198
+ # buffer.
199
+ def read_int64
200
+ hi = read_long or return nil
201
+ lo = read_long or return nil
202
+ return (hi << 32) + lo
203
+ end
203
204
 
204
- # Return the next four bytes as a long integer (in network byte order).
205
- # Returns nil if there are less than 4 bytes remaining to be read in the
206
- # buffer.
207
- def read_long
208
- b = read(4) or return nil
209
- b.unpack("N").first
210
- end
205
+ # Return the next four bytes as a long integer (in network byte order).
206
+ # Returns nil if there are less than 4 bytes remaining to be read in the
207
+ # buffer.
208
+ def read_long
209
+ b = read(4) or return nil
210
+ b.unpack("N").first
211
+ end
211
212
 
212
- # Read and return the next byte in the buffer. Returns nil if called at
213
- # the end of the buffer.
214
- def read_byte
215
- b = read(1) or return nil
216
- b.getbyte(0)
217
- end
213
+ # Read and return the next byte in the buffer. Returns nil if called at
214
+ # the end of the buffer.
215
+ def read_byte
216
+ b = read(1) or return nil
217
+ b.getbyte(0)
218
+ end
218
219
 
219
- # Read and return an SSH2-encoded string. The string starts with a long
220
- # integer that describes the number of bytes remaining in the string.
221
- # Returns nil if there are not enough bytes to satisfy the request.
222
- def read_string
223
- length = read_long or return nil
224
- read(length)
225
- end
220
+ # Read and return an SSH2-encoded string. The string starts with a long
221
+ # integer that describes the number of bytes remaining in the string.
222
+ # Returns nil if there are not enough bytes to satisfy the request.
223
+ def read_string
224
+ length = read_long or return nil
225
+ read(length)
226
+ end
226
227
 
227
- # Read a single byte and convert it into a boolean, using 'C' rules
228
- # (i.e., zero is false, non-zero is true).
229
- def read_bool
230
- b = read_byte or return nil
231
- b != 0
232
- end
228
+ # Read a single byte and convert it into a boolean, using 'C' rules
229
+ # (i.e., zero is false, non-zero is true).
230
+ def read_bool
231
+ b = read_byte or return nil
232
+ b != 0
233
+ end
233
234
 
234
- # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is
235
- # essentially just a string, which is reinterpreted to be a bignum in
236
- # binary format.
237
- def read_bignum
238
- data = read_string
239
- return unless data
240
- OpenSSL::BN.new(data, 2)
241
- end
235
+ # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is
236
+ # essentially just a string, which is reinterpreted to be a bignum in
237
+ # binary format.
238
+ def read_bignum
239
+ data = read_string
240
+ return unless data
241
+ OpenSSL::BN.new(data, 2)
242
+ end
242
243
 
243
- # Read a key from the buffer. The key will start with a string
244
- # describing its type. The remainder of the key is defined by the
245
- # type that was read.
246
- def read_key
247
- type = read_string
248
- return (type ? read_keyblob(type) : nil)
249
- end
244
+ # Read a key from the buffer. The key will start with a string
245
+ # describing its type. The remainder of the key is defined by the
246
+ # type that was read.
247
+ def read_key
248
+ type = read_string
249
+ return (type ? read_keyblob(type) : nil)
250
+ end
250
251
 
251
- # Read a keyblob of the given type from the buffer, and return it as
252
- # a key. Only RSA, DSA, and ECDSA keys are supported.
253
- def read_keyblob(type)
254
- case type
252
+ # Read a keyblob of the given type from the buffer, and return it as
253
+ # a key. Only RSA, DSA, and ECDSA keys are supported.
254
+ def read_keyblob(type)
255
+ case type
255
256
  when /^(.*)-cert-v01@openssh\.com$/
256
257
  key = Net::SSH::Authentication::Certificate.read_certblob(self, $1)
257
258
  when /^ssh-dss$/
@@ -293,104 +294,105 @@ module Net; module SSH
293
294
  end
294
295
  else
295
296
  raise NotImplementedError, "unsupported key type `#{type}'"
296
- end
297
+ end
297
298
 
298
- return key
299
- end
299
+ return key
300
+ end
300
301
 
301
- # Reads the next string from the buffer, and returns a new Buffer
302
- # object that wraps it.
303
- def read_buffer
304
- Buffer.new(read_string)
305
- end
302
+ # Reads the next string from the buffer, and returns a new Buffer
303
+ # object that wraps it.
304
+ def read_buffer
305
+ Buffer.new(read_string)
306
+ end
306
307
 
307
- # Writes the given data literally into the string. Does not alter the
308
- # read position. Returns the buffer object.
309
- def write(*data)
310
- data.each { |datum| @content << datum.dup.force_encoding('BINARY') }
311
- self
312
- end
308
+ # Writes the given data literally into the string. Does not alter the
309
+ # read position. Returns the buffer object.
310
+ def write(*data)
311
+ data.each { |datum| @content << datum.dup.force_encoding('BINARY') }
312
+ self
313
+ end
313
314
 
314
- # Optimized version of write where the caller gives up ownership of string
315
- # to the method. This way we can mutate the string.
316
- def write_moved(string)
317
- @content << string.force_encoding('BINARY')
318
- self
319
- end
315
+ # Optimized version of write where the caller gives up ownership of string
316
+ # to the method. This way we can mutate the string.
317
+ def write_moved(string)
318
+ @content << string.force_encoding('BINARY')
319
+ self
320
+ end
320
321
 
321
- # Writes each argument to the buffer as a network-byte-order-encoded
322
- # 64-bit integer (8 bytes). Does not alter the read position. Returns the
323
- # buffer object.
324
- def write_int64(*n)
325
- n.each do |i|
326
- hi = (i >> 32) & 0xFFFFFFFF
327
- lo = i & 0xFFFFFFFF
328
- @content << [hi, lo].pack("N2")
322
+ # Writes each argument to the buffer as a network-byte-order-encoded
323
+ # 64-bit integer (8 bytes). Does not alter the read position. Returns the
324
+ # buffer object.
325
+ def write_int64(*n)
326
+ n.each do |i|
327
+ hi = (i >> 32) & 0xFFFFFFFF
328
+ lo = i & 0xFFFFFFFF
329
+ @content << [hi, lo].pack("N2")
330
+ end
331
+ self
329
332
  end
330
- self
331
- end
332
333
 
333
- # Writes each argument to the buffer as a network-byte-order-encoded
334
- # long (4-byte) integer. Does not alter the read position. Returns the
335
- # buffer object.
336
- def write_long(*n)
337
- @content << n.pack("N*")
338
- self
339
- end
334
+ # Writes each argument to the buffer as a network-byte-order-encoded
335
+ # long (4-byte) integer. Does not alter the read position. Returns the
336
+ # buffer object.
337
+ def write_long(*n)
338
+ @content << n.pack("N*")
339
+ self
340
+ end
340
341
 
341
- # Writes each argument to the buffer as a byte. Does not alter the read
342
- # position. Returns the buffer object.
343
- def write_byte(*n)
344
- n.each { |b| @content << b.chr }
345
- self
346
- end
342
+ # Writes each argument to the buffer as a byte. Does not alter the read
343
+ # position. Returns the buffer object.
344
+ def write_byte(*n)
345
+ n.each { |b| @content << b.chr }
346
+ self
347
+ end
347
348
 
348
- # Writes each argument to the buffer as an SSH2-encoded string. Each
349
- # string is prefixed by its length, encoded as a 4-byte long integer.
350
- # Does not alter the read position. Returns the buffer object.
351
- def write_string(*text)
352
- text.each do |string|
353
- s = string.to_s
354
- write_long(s.bytesize)
355
- write(s)
349
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
350
+ # string is prefixed by its length, encoded as a 4-byte long integer.
351
+ # Does not alter the read position. Returns the buffer object.
352
+ def write_string(*text)
353
+ text.each do |string|
354
+ s = string.to_s
355
+ write_long(s.bytesize)
356
+ write(s)
357
+ end
358
+ self
356
359
  end
357
- self
358
- end
359
360
 
360
- # Writes each argument to the buffer as an SSH2-encoded string. Each
361
- # string is prefixed by its length, encoded as a 4-byte long integer.
362
- # Does not alter the read position. Returns the buffer object.
363
- # Might alter arguments see write_moved
364
- def write_mstring(*text)
365
- text.each do |string|
366
- s = string.to_s
367
- write_long(s.bytesize)
368
- write_moved(s)
369
- end
370
- self
371
- end
361
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
362
+ # string is prefixed by its length, encoded as a 4-byte long integer.
363
+ # Does not alter the read position. Returns the buffer object.
364
+ # Might alter arguments see write_moved
365
+ def write_mstring(*text)
366
+ text.each do |string|
367
+ s = string.to_s
368
+ write_long(s.bytesize)
369
+ write_moved(s)
370
+ end
371
+ self
372
+ end
372
373
 
373
- # Writes each argument to the buffer as a (C-style) boolean, with 1
374
- # meaning true, and 0 meaning false. Does not alter the read position.
375
- # Returns the buffer object.
376
- def write_bool(*b)
377
- b.each { |v| @content << (v ? "\1" : "\0") }
378
- self
379
- end
374
+ # Writes each argument to the buffer as a (C-style) boolean, with 1
375
+ # meaning true, and 0 meaning false. Does not alter the read position.
376
+ # Returns the buffer object.
377
+ def write_bool(*b)
378
+ b.each { |v| @content << (v ? "\1" : "\0") }
379
+ self
380
+ end
380
381
 
381
- # Writes each argument to the buffer as a bignum (SSH2-style). No
382
- # checking is done to ensure that the arguments are, in fact, bignums.
383
- # Does not alter the read position. Returns the buffer object.
384
- def write_bignum(*n)
385
- @content << n.map { |b| b.to_ssh }.join
386
- self
387
- end
382
+ # Writes each argument to the buffer as a bignum (SSH2-style). No
383
+ # checking is done to ensure that the arguments are, in fact, bignums.
384
+ # Does not alter the read position. Returns the buffer object.
385
+ def write_bignum(*n)
386
+ @content << n.map { |b| b.to_ssh }.join
387
+ self
388
+ end
388
389
 
389
- # Writes the given arguments to the buffer as SSH2-encoded keys. Does not
390
- # alter the read position. Returns the buffer object.
391
- def write_key(*key)
392
- key.each { |k| append(k.to_blob) }
393
- self
390
+ # Writes the given arguments to the buffer as SSH2-encoded keys. Does not
391
+ # alter the read position. Returns the buffer object.
392
+ def write_key(*key)
393
+ key.each { |k| append(k.to_blob) }
394
+ self
395
+ end
394
396
  end
395
397
  end
396
- end; end;
398
+ end;