winrm 1.3.0.dev.3 → 1.3.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -0
- data/Gemfile +1 -0
- data/README.md +8 -27
- data/Rakefile +12 -10
- data/VERSION +1 -1
- data/Vagrantfile +3 -2
- data/bin/rwinrm +21 -15
- data/changelog.md +22 -1
- data/lib/winrm.rb +12 -9
- data/lib/winrm/exceptions/exceptions.rb +5 -10
- data/lib/winrm/helpers/iso8601_duration.rb +22 -15
- data/lib/winrm/helpers/powershell_script.rb +18 -3
- data/lib/winrm/http/response_handler.rb +32 -32
- data/lib/winrm/http/transport.rb +65 -37
- data/lib/winrm/output.rb +16 -0
- data/lib/winrm/soap_provider.rb +17 -14
- data/lib/winrm/winrm_service.rb +14 -6
- data/spec/auth_timeout_spec.rb +2 -1
- data/spec/cmd_spec.rb +12 -11
- data/spec/exception_spec.rb +6 -8
- data/spec/issue_59_spec.rb +15 -0
- data/spec/matchers.rb +5 -3
- data/spec/output_spec.rb +57 -53
- data/spec/powershell_spec.rb +16 -14
- data/spec/response_handler_spec.rb +12 -11
- data/spec/spec_helper.rb +10 -7
- data/spec/winrm_options_spec.rb +6 -5
- data/spec/winrm_primitives_spec.rb +9 -10
- data/spec/wql_spec.rb +2 -1
- data/winrm.gemspec +16 -15
- metadata +20 -4
@@ -1,11 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
1
3
|
# Copyright 2014 Shawn Neal <sneal@sneal.net>
|
2
|
-
#
|
4
|
+
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
5
7
|
# You may obtain a copy of the License at
|
6
|
-
#
|
8
|
+
#
|
7
9
|
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
10
|
+
#
|
9
11
|
# Unless required by applicable law or agreed to in writing, software
|
10
12
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
13
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@@ -18,7 +20,6 @@ module WinRM
|
|
18
20
|
# Handles the raw WinRM HTTP response. Returns the body as an XML doc
|
19
21
|
# or raises the appropriate WinRM error if the response is an error.
|
20
22
|
class ResponseHandler
|
21
|
-
|
22
23
|
# @param [String] The raw unparsed response body, if any
|
23
24
|
# @param [Integer] The HTTP response status code
|
24
25
|
def initialize(response_body, status_code)
|
@@ -30,53 +31,52 @@ module WinRM
|
|
30
31
|
# doc or raises an appropriate error.
|
31
32
|
#
|
32
33
|
# @returns [REXML::Document] The parsed response body
|
33
|
-
def parse_to_xml
|
34
|
-
raise_if_error
|
34
|
+
def parse_to_xml
|
35
|
+
raise_if_error
|
35
36
|
response_xml
|
36
37
|
end
|
37
38
|
|
38
39
|
private
|
39
40
|
|
40
|
-
def response_xml
|
41
|
+
def response_xml
|
41
42
|
@response_xml ||= REXML::Document.new(@response_body)
|
42
43
|
rescue REXML::ParseException => e
|
43
|
-
raise WinRMHTTPTransportError.new(
|
44
|
+
raise WinRMHTTPTransportError.new(
|
45
|
+
"Unable to parse WinRM response: #{e.message}", @status_code)
|
44
46
|
end
|
45
47
|
|
46
|
-
def raise_if_error
|
47
|
-
return if @status_code == 200
|
48
|
-
raise_if_auth_error
|
49
|
-
raise_if_wsman_fault
|
50
|
-
raise_if_wmi_error
|
51
|
-
raise_transport_error
|
48
|
+
def raise_if_error
|
49
|
+
return if @status_code == 200
|
50
|
+
raise_if_auth_error
|
51
|
+
raise_if_wsman_fault
|
52
|
+
raise_if_wmi_error
|
53
|
+
raise_transport_error
|
52
54
|
end
|
53
55
|
|
54
|
-
def raise_if_auth_error
|
55
|
-
|
56
|
+
def raise_if_auth_error
|
57
|
+
fail WinRMAuthorizationError if @status_code == 401
|
56
58
|
end
|
57
59
|
|
58
|
-
def raise_if_wsman_fault
|
60
|
+
def raise_if_wsman_fault
|
59
61
|
soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
|
60
|
-
if
|
61
|
-
|
62
|
-
|
63
|
-
end
|
62
|
+
return if soap_errors.empty?
|
63
|
+
fault = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_FAULT}:WSManFault")
|
64
|
+
fail WinRMWSManFault.new(fault.to_s, fault.attributes['Code']) unless fault.nil?
|
64
65
|
end
|
65
66
|
|
66
|
-
def raise_if_wmi_error
|
67
|
+
def raise_if_wmi_error
|
67
68
|
soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
|
68
|
-
if
|
69
|
-
error = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_MSFT}:MSFT_WmiError")
|
70
|
-
if !error.nil?
|
71
|
-
error_code = REXML::XPath.first(error, "//#{NS_WSMAN_MSFT}:error_Code").text
|
72
|
-
raise WinRMWMIError.new(error.to_s, error_code)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
69
|
+
return if soap_errors.empty?
|
76
70
|
|
77
|
-
|
78
|
-
|
71
|
+
error = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_MSFT}:MSFT_WmiError")
|
72
|
+
return if error.nil?
|
73
|
+
|
74
|
+
error_code = REXML::XPath.first(error, "//#{NS_WSMAN_MSFT}:error_Code").text
|
75
|
+
fail WinRMWMIError.new(error.to_s, error_code)
|
79
76
|
end
|
80
77
|
|
78
|
+
def raise_transport_error
|
79
|
+
fail WinRMHTTPTransportError.new('Bad HTTP response returned from server', @status_code)
|
80
|
+
end
|
81
81
|
end
|
82
82
|
end
|
data/lib/winrm/http/transport.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
1
3
|
# Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
|
2
|
-
#
|
4
|
+
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
5
7
|
# You may obtain a copy of the License at
|
6
|
-
#
|
8
|
+
#
|
7
9
|
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
10
|
+
#
|
9
11
|
# Unless required by applicable law or agreed to in writing, software
|
10
12
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
13
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@@ -16,20 +18,18 @@ require_relative 'response_handler'
|
|
16
18
|
|
17
19
|
module WinRM
|
18
20
|
module HTTP
|
19
|
-
|
20
21
|
# A generic HTTP transport that utilized HTTPClient to send messages back and forth.
|
21
22
|
# This backend will maintain state for every WinRMWebService instance that is instantiated so it
|
22
23
|
# is possible to use GSSAPI with Keep-Alive.
|
23
24
|
class HttpTransport
|
24
|
-
|
25
25
|
# Set this to an unreasonable amount because WinRM has its own timeouts
|
26
|
-
DEFAULT_RECEIVE_TIMEOUT = 3600
|
26
|
+
DEFAULT_RECEIVE_TIMEOUT = 3600
|
27
27
|
|
28
28
|
attr_reader :endpoint
|
29
29
|
|
30
30
|
def initialize(endpoint)
|
31
31
|
@endpoint = endpoint.is_a?(String) ? URI.parse(endpoint) : endpoint
|
32
|
-
@httpcli = HTTPClient.new(:
|
32
|
+
@httpcli = HTTPClient.new(agent_name: 'Ruby WinRM Client')
|
33
33
|
@httpcli.receive_timeout = DEFAULT_RECEIVE_TIMEOUT
|
34
34
|
@logger = Logging.logger[self]
|
35
35
|
end
|
@@ -41,23 +41,25 @@ module WinRM
|
|
41
41
|
# @returns [REXML::Document] The parsed response body
|
42
42
|
def send_request(message)
|
43
43
|
log_soap_message(message)
|
44
|
-
hdr = {
|
44
|
+
hdr = {
|
45
|
+
'Content-Type' => 'application/soap+xml;charset=UTF-8',
|
46
|
+
'Content-Length' => message.length }
|
45
47
|
resp = @httpcli.post(@endpoint, message, hdr)
|
46
48
|
log_soap_message(resp.http_body.content)
|
47
49
|
handler = WinRM::ResponseHandler.new(resp.http_body.content, resp.status)
|
48
|
-
handler.parse_to_xml
|
50
|
+
handler.parse_to_xml
|
49
51
|
end
|
50
52
|
|
51
53
|
# We'll need this to force basic authentication if desired
|
52
54
|
def basic_auth_only!
|
53
55
|
auths = @httpcli.www_auth.instance_variable_get('@authenticator')
|
54
|
-
auths.delete_if {|i| i.scheme !~ /basic/i}
|
56
|
+
auths.delete_if { |i| i.scheme !~ /basic/i }
|
55
57
|
end
|
56
58
|
|
57
59
|
# Disable SSPI Auth
|
58
60
|
def no_sspi_auth!
|
59
61
|
auths = @httpcli.www_auth.instance_variable_get('@authenticator')
|
60
|
-
auths.delete_if {|i| i.is_a? HTTPClient::SSPINegotiateAuth }
|
62
|
+
auths.delete_if { |i| i.is_a? HTTPClient::SSPINegotiateAuth }
|
61
63
|
end
|
62
64
|
|
63
65
|
# Disable SSL Peer Verification
|
@@ -71,7 +73,7 @@ module WinRM
|
|
71
73
|
@httpcli.receive_timeout = sec
|
72
74
|
end
|
73
75
|
|
74
|
-
def receive_timeout
|
76
|
+
def receive_timeout
|
75
77
|
@httpcli.receive_timeout
|
76
78
|
end
|
77
79
|
|
@@ -79,7 +81,7 @@ module WinRM
|
|
79
81
|
|
80
82
|
def log_soap_message(message)
|
81
83
|
return unless @logger.debug?
|
82
|
-
|
84
|
+
|
83
85
|
xml_msg = REXML::Document.new(message)
|
84
86
|
formatter = REXML::Formatters::Pretty.new(2)
|
85
87
|
formatter.compact = true
|
@@ -90,7 +92,7 @@ module WinRM
|
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
93
|
-
|
95
|
+
# Plain text, insecure, HTTP transport
|
94
96
|
class HttpPlaintext < HttpTransport
|
95
97
|
def initialize(endpoint, user, pass, opts)
|
96
98
|
super(endpoint)
|
@@ -114,12 +116,15 @@ module WinRM
|
|
114
116
|
end
|
115
117
|
|
116
118
|
# Uses Kerberos/GSSAPI to authenticate and encrypt messages
|
119
|
+
# rubocop:disable Metrics/ClassLength
|
117
120
|
class HttpGSSAPI < HttpTransport
|
118
121
|
# @param [String,URI] endpoint the WinRM webservice endpoint
|
119
122
|
# @param [String] realm the Kerberos realm we are authenticating to
|
120
123
|
# @param [String<optional>] service the service name, default is HTTP
|
121
124
|
# @param [String<optional>] keytab the path to a keytab file if you are using one
|
125
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
122
126
|
def initialize(endpoint, realm, service = nil, keytab = nil, opts)
|
127
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
123
128
|
super(endpoint)
|
124
129
|
# Remove the GSSAPI auth from HTTPClient because we are doing our own thing
|
125
130
|
no_sspi_auth!
|
@@ -137,18 +142,20 @@ module WinRM
|
|
137
142
|
resp = send_kerberos_request(message)
|
138
143
|
|
139
144
|
if resp.status == 401
|
140
|
-
@logger.debug
|
145
|
+
@logger.debug 'Got 401 - reinitializing Kerberos and retrying one more time'
|
141
146
|
init_krb
|
142
147
|
resp = send_kerberos_request(message)
|
143
148
|
end
|
144
149
|
|
145
150
|
handler = WinRM::ResponseHandler.new(winrm_decrypt(resp.http_body.content), resp.status)
|
146
|
-
handler.parse_to_xml
|
151
|
+
handler.parse_to_xml
|
147
152
|
end
|
148
153
|
|
149
|
-
|
150
154
|
private
|
151
155
|
|
156
|
+
# rubocop:disable Metrics/MethodLength
|
157
|
+
# rubocop:disable Metrics/AbcSize
|
158
|
+
|
152
159
|
# Sends the SOAP payload to the WinRM service and returns the service's
|
153
160
|
# HTTP response.
|
154
161
|
#
|
@@ -159,8 +166,11 @@ module WinRM
|
|
159
166
|
original_length = message.length
|
160
167
|
pad_len, emsg = winrm_encrypt(message)
|
161
168
|
hdr = {
|
162
|
-
|
163
|
-
|
169
|
+
'Connection' => 'Keep-Alive',
|
170
|
+
'Content-Type' =>
|
171
|
+
'multipart/encrypted;' \
|
172
|
+
'protocol="application/HTTP-Kerberos-session-encrypted";' \
|
173
|
+
'boundary="Encrypted Boundary"'
|
164
174
|
}
|
165
175
|
|
166
176
|
body = <<-EOF
|
@@ -183,13 +193,14 @@ Content-Type: application/octet-stream\r
|
|
183
193
|
token = @gsscli.init_context
|
184
194
|
auth = Base64.strict_encode64 token
|
185
195
|
|
186
|
-
hdr = {
|
187
|
-
|
188
|
-
|
196
|
+
hdr = {
|
197
|
+
'Authorization' => "Kerberos #{auth}",
|
198
|
+
'Connection' => 'Keep-Alive',
|
199
|
+
'Content-Type' => 'application/soap+xml;charset=UTF-8'
|
189
200
|
}
|
190
|
-
@logger.debug
|
201
|
+
@logger.debug 'Sending HTTP POST for Kerberos Authentication'
|
191
202
|
r = @httpcli.post(@endpoint, '', hdr)
|
192
|
-
itok = r.header[
|
203
|
+
itok = r.header['WWW-Authenticate'].pop
|
193
204
|
itok = itok.split.last
|
194
205
|
itok = Base64.strict_decode64(itok)
|
195
206
|
@gsscli.init_context(itok)
|
@@ -202,19 +213,30 @@ Content-Type: application/octet-stream\r
|
|
202
213
|
iov = FFI::MemoryPointer.new(GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * iov_cnt)
|
203
214
|
|
204
215
|
iov0 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(FFI::Pointer.new(iov.address))
|
205
|
-
iov0[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_HEADER |
|
216
|
+
iov0[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_HEADER | \
|
217
|
+
GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_FLAG_ALLOCATE)
|
206
218
|
|
207
|
-
iov1 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
219
|
+
iov1 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
220
|
+
FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 1)))
|
208
221
|
iov1[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_DATA)
|
209
222
|
iov1[:buffer].value = str
|
210
223
|
|
211
|
-
iov2 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
212
|
-
|
224
|
+
iov2 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
225
|
+
FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 2)))
|
226
|
+
iov2[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_PADDING | \
|
227
|
+
GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_FLAG_ALLOCATE)
|
213
228
|
|
214
229
|
conf_state = FFI::MemoryPointer.new :uint32
|
215
230
|
min_stat = FFI::MemoryPointer.new :uint32
|
216
231
|
|
217
|
-
|
232
|
+
GSSAPI::LibGSSAPI.gss_wrap_iov(
|
233
|
+
min_stat,
|
234
|
+
@gsscli.context,
|
235
|
+
1,
|
236
|
+
GSSAPI::LibGSSAPI::GSS_C_QOP_DEFAULT,
|
237
|
+
conf_state,
|
238
|
+
iov,
|
239
|
+
iov_cnt)
|
218
240
|
|
219
241
|
token = [iov0[:buffer].length].pack('L')
|
220
242
|
token += iov0[:buffer].value
|
@@ -224,7 +246,6 @@ Content-Type: application/octet-stream\r
|
|
224
246
|
[pad_len, token]
|
225
247
|
end
|
226
248
|
|
227
|
-
|
228
249
|
# @return [String] the unencrypted response string
|
229
250
|
def winrm_decrypt(str)
|
230
251
|
@logger.debug "Decrypting SOAP message:\n#{str}"
|
@@ -232,18 +253,21 @@ Content-Type: application/octet-stream\r
|
|
232
253
|
iov = FFI::MemoryPointer.new(GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * iov_cnt)
|
233
254
|
|
234
255
|
iov0 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(FFI::Pointer.new(iov.address))
|
235
|
-
iov0[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_HEADER |
|
256
|
+
iov0[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_HEADER | \
|
257
|
+
GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_FLAG_ALLOCATE)
|
236
258
|
|
237
|
-
iov1 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
259
|
+
iov1 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
260
|
+
FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 1)))
|
238
261
|
iov1[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_DATA)
|
239
262
|
|
240
|
-
iov2 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
263
|
+
iov2 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(
|
264
|
+
FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 2)))
|
241
265
|
iov2[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_DATA)
|
242
266
|
|
243
267
|
str.force_encoding('BINARY')
|
244
268
|
str.sub!(/^.*Content-Type: application\/octet-stream\r\n(.*)--Encrypted.*$/m, '\1')
|
245
269
|
|
246
|
-
len = str.unpack(
|
270
|
+
len = str.unpack('L').first
|
247
271
|
iov_data = str.unpack("LA#{len}A*")
|
248
272
|
iov0[:buffer].value = iov_data[1]
|
249
273
|
iov1[:buffer].value = iov_data[2]
|
@@ -254,13 +278,17 @@ Content-Type: application/octet-stream\r
|
|
254
278
|
qop_state = FFI::MemoryPointer.new :uint32
|
255
279
|
qop_state.write_int(0)
|
256
280
|
|
257
|
-
maj_stat = GSSAPI::LibGSSAPI.gss_unwrap_iov(
|
281
|
+
maj_stat = GSSAPI::LibGSSAPI.gss_unwrap_iov(
|
282
|
+
min_stat, @gsscli.context, conf_state, qop_state, iov, iov_cnt)
|
258
283
|
|
259
|
-
@logger.debug "SOAP message decrypted (MAJ: #{maj_stat},
|
284
|
+
@logger.debug "SOAP message decrypted (MAJ: #{maj_stat}, " \
|
285
|
+
"MIN: #{min_stat.read_int}):\n#{iov1[:buffer].value}"
|
260
286
|
|
261
287
|
iov1[:buffer].value
|
262
288
|
end
|
263
|
-
|
289
|
+
# rubocop:enable Metrics/MethodLength
|
290
|
+
# rubocop:enable Metrics/AbcSize
|
264
291
|
end
|
292
|
+
# rubocop:enable Metrics/ClassLength
|
265
293
|
end
|
266
294
|
end # WinRM
|
data/lib/winrm/output.rb
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright 2014 Max Lincoln <max@devopsy.com>
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
1
17
|
module WinRM
|
2
18
|
# This class holds raw output as a hash, and has convenience methods to parse.
|
3
19
|
class Output < Hash
|
data/lib/winrm/soap_provider.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
1
3
|
# Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
|
2
|
-
#
|
4
|
+
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
5
7
|
# You may obtain a copy of the License at
|
6
|
-
#
|
8
|
+
#
|
7
9
|
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
10
|
+
#
|
9
11
|
# Unless required by applicable law or agreed to in writing, software
|
10
12
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
13
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@@ -18,18 +20,19 @@ require 'gyoku'
|
|
18
20
|
require 'uuidtools'
|
19
21
|
require 'base64'
|
20
22
|
|
23
|
+
# SOAP constants for WinRM
|
21
24
|
module WinRM
|
22
|
-
NS_SOAP_ENV ='s' # http://www.w3.org/2003/05/soap-envelope
|
23
|
-
NS_ADDRESSING ='a' # http://schemas.xmlsoap.org/ws/2004/08/addressing
|
24
|
-
NS_CIMBINDING ='b' # http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd
|
25
|
-
NS_ENUM ='n' # http://schemas.xmlsoap.org/ws/2004/09/enumeration
|
26
|
-
NS_TRANSFER ='x' # http://schemas.xmlsoap.org/ws/2004/09/transfer
|
27
|
-
NS_WSMAN_DMTF ='w' # http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
|
28
|
-
NS_WSMAN_MSFT ='p' # http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd
|
29
|
-
NS_SCHEMA_INST ='xsi' # http://www.w3.org/2001/XMLSchema-instance
|
30
|
-
NS_WIN_SHELL ='rsp' # http://schemas.microsoft.com/wbem/wsman/1/windows/shell
|
31
|
-
NS_WSMAN_FAULT = 'f'
|
32
|
-
NS_WSMAN_CONF = 'cfg'# http://schemas.microsoft.com/wbem/wsman/1/config
|
25
|
+
NS_SOAP_ENV = 's' # http://www.w3.org/2003/05/soap-envelope
|
26
|
+
NS_ADDRESSING = 'a' # http://schemas.xmlsoap.org/ws/2004/08/addressing
|
27
|
+
NS_CIMBINDING = 'b' # http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd
|
28
|
+
NS_ENUM = 'n' # http://schemas.xmlsoap.org/ws/2004/09/enumeration
|
29
|
+
NS_TRANSFER = 'x' # http://schemas.xmlsoap.org/ws/2004/09/transfer
|
30
|
+
NS_WSMAN_DMTF = 'w' # http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
|
31
|
+
NS_WSMAN_MSFT = 'p' # http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd
|
32
|
+
NS_SCHEMA_INST = 'xsi' # http://www.w3.org/2001/XMLSchema-instance
|
33
|
+
NS_WIN_SHELL = 'rsp' # http://schemas.microsoft.com/wbem/wsman/1/windows/shell
|
34
|
+
NS_WSMAN_FAULT = 'f' # http://schemas.microsoft.com/wbem/wsman/1/wsmanfault
|
35
|
+
NS_WSMAN_CONF = 'cfg' # http://schemas.microsoft.com/wbem/wsman/1/config
|
33
36
|
end
|
34
37
|
|
35
38
|
require 'winrm/exceptions/exceptions'
|
data/lib/winrm/winrm_service.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
1
3
|
# Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
|
2
|
-
#
|
4
|
+
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
5
7
|
# You may obtain a copy of the License at
|
6
|
-
#
|
8
|
+
#
|
7
9
|
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
10
|
+
#
|
9
11
|
# Unless required by applicable law or agreed to in writing, software
|
10
12
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
13
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@@ -17,10 +19,9 @@ require 'rexml/document'
|
|
17
19
|
require_relative 'helpers/powershell_script'
|
18
20
|
|
19
21
|
module WinRM
|
20
|
-
# This is the main class that does the SOAP request/response logic. There are a few helper
|
21
|
-
#
|
22
|
+
# This is the main class that does the SOAP request/response logic. There are a few helper
|
23
|
+
# classes, but pretty much everything comes through here first.
|
22
24
|
class WinRMWebService
|
23
|
-
|
24
25
|
DEFAULT_TIMEOUT = 'PT60S'
|
25
26
|
DEFAULT_MAX_ENV_SIZE = 153600
|
26
27
|
DEFAULT_LOCALE = 'en-US'
|
@@ -249,6 +250,13 @@ module WinRM
|
|
249
250
|
output[:exitcode] = REXML::XPath.first(resp_doc, "//#{NS_WIN_SHELL}:ExitCode").text.to_i
|
250
251
|
end
|
251
252
|
output
|
253
|
+
rescue WinRMWSManFault => e
|
254
|
+
# If no output is available before the wsman:OperationTimeout expires,
|
255
|
+
# the server MUST return a WSManFault with the Code attribute equal to
|
256
|
+
# 2150858793. When the client receives this fault, it SHOULD issue
|
257
|
+
# another Receive request.
|
258
|
+
# http://msdn.microsoft.com/en-us/library/cc251676.aspx
|
259
|
+
retry if e.fault_code == '2150858793'
|
252
260
|
end
|
253
261
|
|
254
262
|
# Clean-up after a command.
|