//----------------------------------------------------------------------------
//
// Copyright (c) Intel Corporation, 2006 - 2010 All Rights Reserved.
//
// File: DotNetWSManClient.cs
//
// Contents: An implementation of a WS-Management client
//
//----------------------------------------------------------------------------
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Intel.Manageability.WSManagement
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net; //.Sockets
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Cim;
using System.Net.Security;
using System.Security;
using Common.Utils;
///
/// Implementation for wsman client.
///
public class DotNetWSManClient : IWSManClient
{
#region CONSTS
#region CERTIFICATES_ISSUES
private const string HOSTNAME_MISMATCH_HEADER = "Hostname Mismatch";
private const string HOSTNAME_MISMATCH_ERROR = "Mismatched Address.\nThe certificate you are using to connect is issued to a different address.";
#endregion
//default header values
private const string DEFAULT_REPLY_TO = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous";
private const string DEFAULT_OPERATION_TIMEOUT = "PT60.000S";
private const string DEFAULT_WSMAN_SERVICE = "/wsman";
private const string WSMAN_FAULT = "fault";
//WSMan basic functions
private const string ENUMERATE = "http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate";
private const string PULL = "http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull";
private const string GET = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get";
private const string PUT = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put";
private const string DELETE = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete";
private const string CREATE = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create";
private const string RELEASE = "http://schemas.xmlsoap.org/ws/2004/09/enumeration/Release";
private const string IDENTITY = "http://schemas.xmlsoap.org/ws/2005/05/identity";
private const string SUBSCRIBE = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe";
private const string UNSUBSCRIBE = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe";
// WS-Management namespace URI
private const string WSMAN_NAMESPACE_URI = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd";
private const string WSMAN = "wsman";
private const string WSMAN_URI_FIELD = "ResourceURI";
private const string WSMAN_SELECTOR_SET_FIELD = "SelectorSet";
private const string WSMAN_SELECTOR_FIELD = "Selector";
private const string WSMAN_SELECTOR_NAME = "Name";
private const string WSMAN_ACTION = "Action";
private const string WSMAN_BODY = "Body";
private const string WSMAN_ENUMERATE = "Enumerate";
private const string WSMAN_ENUMERATE_PREFIX = "wsen";
private const string WSMAN_ENUMERATE_NAMESPACE_URI = "http://schemas.xmlsoap.org/ws/2004/09/enumeration";
private const string WSMAN_ENUM_CONTEXT = "EnumerationContext";
private const string WSMAN_ENUM_MODE = "EnumerationMode";
private const string WSMAN_ENUM_EPR = "EnumerateEPR";
private const string WSMAN_ENUM_OBJ_N_EPR = "EnumerateObjectAndEPR";
private const string MAX_ELEMENTS = "101";
private const string OID_LOCAL = "2.16.840.1.113741.1.2.2";
private const string OID_REMOTE = "2.16.840.1.113741.1.2.1";
#endregion CONSTS
#region DATA_MEMBERS
private bool FirstCall, AllowCertErrors;
bool? allowCertificateError = null;
private HttpClient httpClient;
public delegate bool ServerCertificateValidationCallback(X509Certificate certificate,
SslPolicyErrors errors);
private ServerCertificateValidationCallback _serverCertificateValidationCallback;
///
/// Event that raised once we get the server certificate
///
public static event Action ServerCertRaised;
public X509Certificate2 Certificate
{
get
{
return CertError.Certificate; ;
}
}
public CertificateError CertError { set; get; }
//Connection Info
private ConnectionInfo _connection;
///
/// A class that represents the connection information.
///
public ConnectionInfo Connection
{
get { return _connection; }
set
{
Connection?.Dispose();
_connection = value;
_X509CertificateCollection = null;
_credentials = null;
if (!string.IsNullOrEmpty(value.Certificate))
{
_X509CertificateCollection = getCertFromStore(value.Certificate);
if (_X509CertificateCollection.Count == 0)
throw new ArgumentException(("Cannot find appropriate certificate in the certificate store."));
}
if (!setConnectionInfo())
{
//throw missing password or user name;
throw new ArgumentException("Problem with credentials");
}
}
}
// user credentials
private X509CertificateCollection _X509CertificateCollection = null;
private CredentialCache _credentials = null;
#endregion DATA_MEMBERS
#region CONSTRUCTOR_FUNCTIONS
///
/// Constructor of the WSMan client using ConnectionInfo
///
/// A class that represents the connection information.
public DotNetWSManClient(ConnectionInfo connection) : this(connection, false) { }
///
/// Constructor of the WSMan client using ConnectionInfo
///
/// A class that represents the connection information.
/// /// true, to allow certificate errors
public DotNetWSManClient(ConnectionInfo connection, bool AllowCertificateErrors)
{
FirstCall = true;
AllowCertErrors = AllowCertificateErrors;
if (connection == null)
throw new ArgumentNullException("connection");
_connection = connection;
init();
}
///
/// Constructor of the WSMan client using ConnectionInfo
///
/// A class that represents the connection information.
/// callback for custom validation by the client of the server certificate.
public DotNetWSManClient(ConnectionInfo connection, ServerCertificateValidationCallback certificateValidationCallback)
{
_serverCertificateValidationCallback = certificateValidationCallback;
FirstCall = true;
if (connection == null)
throw new ArgumentNullException("connection");
_connection = connection;
init();
}
///
/// Constructor of the WSMan client based on .net components
///
/// Hostname / IP of the machine to connect.
/// User name of the machine to connect.
/// Password of the machine to connect.
/// Indicates whether or not to use TLS in the connection.
/// Certificate name (as it appears in the subject field of the certificate)
/// Indicate whether to use kerberos in the connection.
/// Indicates whether or not to use a proxy in the connection.
public DotNetWSManClient(String target, String username, SecureString password, bool secure, bool kerberos, String clientCert, IWebProxy HttpProxy)
: this(target, username, password, secure, kerberos, clientCert, HttpProxy, false)
{ }
///
/// Constructor of the WSMan client based on .net components
///
/// Hostname / IP of the machine to connect.
/// User name of the machine to connect.
/// Password of the machine to connect.
/// Indicates whether or not to use TLS in the connection.
/// Certificate name (as it appears in the subject field of the certificate)
/// Indicate whether to use kerberos in the connection.
/// Indicates whether or not to use a proxy in the connection.
public DotNetWSManClient(String target, String username, SecureString password, bool secure, bool kerberos, String clientCert, IWebProxy HttpProxy, bool AllowCertificateErrors)
{
AllowCertErrors = AllowCertificateErrors;
FirstCall = true;
_connection = new ConnectionInfo(target, username, password, secure, clientCert, (kerberos ? ConnectionInfo.AuthMethod.Kerberos : ConnectionInfo.AuthMethod.Digest), HttpProxy, null);
init();
}
///
/// Common init stuff
///
private void init()
{
if (_connection.Secure)
{
_X509CertificateCollection = getCertFromStore(_connection.Certificate);
if (_X509CertificateCollection.Count == 0 && !String.IsNullOrEmpty(_connection.Certificate))
throw new ArgumentException(("Cannot find appropriate certificate in certificate store."));
}
if (!setConnectionInfo())
{
//throw missing password or user name;
throw new ArgumentException("Problem with credentials");
}
}
private X509CertificateCollection getCertFromStore(String clientCert)
{
X509CertificateCollection certificatesCollection = new X509CertificateCollection();
// Open CurrentUser cert store
using (X509Store currentUserStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
currentUserStore.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certificate in currentUserStore.Certificates)
{
if (certificate.Subject.Contains(clientCert))
{
// Checking that the Enhanced Key Usage in the certificate is the one for AMT
foreach (X509Extension extension in certificate.Extensions)
{
if (extension is X509EnhancedKeyUsageExtension)
{
X509EnhancedKeyUsageExtension ex = (X509EnhancedKeyUsageExtension)extension;
foreach (Oid OID in ex.EnhancedKeyUsages)
{
if (OID.Value == OID_REMOTE || OID.Value == OID_LOCAL)
certificatesCollection.Add(certificate);
}
}
}
}
}
//currentUserStore.Close();
}
// Open LocalMachine cert store
using (X509Store localMachineStore = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
localMachineStore.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certificate in localMachineStore.Certificates)
{
if (certificate.Subject.Contains(clientCert))
{
// Checking that the Enhanced Key Usage in the certificate is the one for AMT
foreach (X509Extension extension in certificate.Extensions)
{
if (extension is X509EnhancedKeyUsageExtension)
{
X509EnhancedKeyUsageExtension ex = (X509EnhancedKeyUsageExtension)extension;
foreach (Oid OID in ex.EnhancedKeyUsages)
{
if (OID.Value == OID_REMOTE || OID.Value == OID_LOCAL)
certificatesCollection.Add(certificate);
}
}
}
}
}
//localMachineStore.Close();
}
return certificatesCollection;
}
private bool setConnectionInfo()
{
bool set = false;
if (_connection.Auth == ConnectionInfo.AuthMethod.Kerberos)
{
SetupAuthManager();
CredentialCache myCache = new CredentialCache();
// Use Kerberos with local credentials
if (_connection.Password == null || _connection.Password.Length == 0)
{
if (!string.IsNullOrEmpty(_connection.UserName))
{
throw new ArgumentException("Missing argument for user credentials: password. ");
}
myCache.Add(_connection.HostUri, "Negotiate", CredentialCache.DefaultNetworkCredentials);
}
else
{
if (string.IsNullOrEmpty(_connection.UserName))
{
throw new ArgumentException("Missing argument for user credentials: userName. ");
}
int i = _connection.UserName.IndexOf("\\");
if (i > 0)
{
string t_username = _connection.UserName.Substring(i + 1);
string t_domain = _connection.UserName.Substring(0, i);
myCache.Add(_connection.HostUri, "Negotiate", new NetworkCredential(t_username, _connection.Password.Copy(), t_domain));
}
else
{
myCache.Add(_connection.HostUri, "Negotiate", new NetworkCredential(_connection.UserName, _connection.Password.Copy()));
}
}
_credentials = myCache;
set = true;
}
if (!string.IsNullOrEmpty(_connection.UserName) && (_connection.Password != null && _connection.Password.Length > 0))
{
CredentialCache myCache = new CredentialCache
{
{ _connection.HostUri, "Digest", new NetworkCredential(_connection.UserName, _connection.Password.Copy()) }
};
_credentials = myCache;
set = true;
}
_changed = _changed || set;
return set;
}
private void SetupAuthManager()
{
string hostName;
IPAddress IpAddress;
//checking if we need to find the host
if (IPAddress.TryParse(_connection.HostUri.Host, out IpAddress))
{
IPHostEntry hostInfo = Dns.GetHostEntry(IpAddress);
hostName = hostInfo.HostName;
if (hostName == null || hostName.Length == 0)
{
throw new ArgumentException("Problem with credentials.");
}
}
else
{
hostName = _connection.Host;
}
if (AuthenticationManager.CustomTargetNameDictionary.ContainsKey(_connection.HostUri.AbsoluteUri) == false)
{
string SPN = "HTTP/" + hostName + ":16992";
if (_connection.HostUri.AbsoluteUri.StartsWith("https", StringComparison.CurrentCultureIgnoreCase) == true)
{
SPN = "HTTP/" + hostName + ":16993";
}
AuthenticationManager.CustomTargetNameDictionary.Add(_connection.HostUri.AbsoluteUri, SPN);
}
}
#endregion CONSTRUCTOR_FUNCTIONS
#region TRNASPORT_LAYER_OPERATION
// Create a WS-Management call, send it and return the WS-Management body response
// Note: In case of identify action, "header.Action" should be equal to constant IDENTIFY
private void WSManSendReceive(Header header, XmlElement[] bodyIn, ref XmlElement[] bodyOut)
{
if (header.Action == IDENTITY)
{
header.Action = null;
}
else
{
if ((header.Action == PUT || header.Action == CREATE) &&
(bodyIn == null || bodyIn.Equals("")))
{
throw new ArgumentException("The required body for the WSMan call is missing.");
}
//fill missing WSMan header values
header.ReplyTo = new ReplyTo();
header.MessageID = Guid.NewGuid().ToString();
header.ReplyTo.Address = DEFAULT_REPLY_TO;
header.OperationTimeout = DEFAULT_OPERATION_TIMEOUT;
header.To = _connection.HostUri.AbsoluteUri;
}
//Create WSMan call including envelope, header and body
WSManCall xmlPostData = new WSManCall
{
Header = header
};
if (null != bodyIn)
{
xmlPostData.Body.Any = bodyIn;
}
byte[] xml;
using (MemoryStream streamPostData = new MemoryStream())
{
//in order to avoid leading bytes:
Encoding utf8Enc = new UTF8Encoding(false);
//serialize all the data
XmlSerializer mySerializer = new XmlSerializer(xmlPostData.GetType());
XmlTextWriter xmlTextWriter = new XmlTextWriter(streamPostData, utf8Enc);
mySerializer.Serialize(xmlTextWriter, xmlPostData);
xml = streamPostData.ToArray();
}
try
{
//send the data and get the response
XmlDocument xmlResp = Task.Run(()=>HttpSendReceive(xml)).GetAwaiter().GetResult();
//extricate the WSMan body from the entire response
XmlNodeList nodesList = xmlResp.GetElementsByTagName(WSMAN_BODY, "*");
XmlElement innerBody = (XmlElement)nodesList.Item(0);
bodyOut = new XmlElement[innerBody.ChildNodes.Count];
int i = 0;
IEnumerator ienum = innerBody.GetEnumerator();
while (ienum.MoveNext())
{
bodyOut[i] = (XmlElement)ienum.Current;// InnerXml;
i++;
}
//checking if the action succeeded
nodesList = xmlResp.GetElementsByTagName(WSMAN_ACTION, "*");
if (nodesList.Count > 0 && nodesList[0].InnerText != null && nodesList[0].InnerText.EndsWith(WSMAN_FAULT))
{
throw new WSManException(DeserializeWSManFault(bodyOut[0]));
}
}
catch (WSManException)
{
throw;
}
catch (WebException ex)
{
throw new WSManException(ex);
}
}
private bool _changed = false;
///
/// Initiate HTTPClient Handler
///
/// HTTPClient Handler
private HttpClientHandler CreateHttpHandler()
{
var handler = new HttpClientHandler();
try
{
handler.PreAuthenticate = true;
handler.Credentials = _credentials;
handler.Proxy = _connection.Proxy;
if (null != _X509CertificateCollection)
{
foreach (var cert in _X509CertificateCollection)
{
handler.ClientCertificates.Add(cert);
}
}
handler.ServerCertificateCustomValidationCallback = ValidationCallback;
}
catch
{
handler.Dispose();
}
return handler;
}
private bool ValidationCallback(HttpRequestMessage message, X509Certificate2 certificate, X509Chain certificateChain, SslPolicyErrors errors)
{
// We want to validate the certificate just on the first time we try to connect to the machine
if (FirstCall && (AllowCertErrors || _serverCertificateValidationCallback != null))
{
// Set the certificate policy in order to allow certificates problems
if (!CertificateValidationCallback(message, certificate, certificateChain, errors))
return false;
}
// Allow self signed certificate only if set to true in the connection, and the other options are not selected
if (_connection.AcceptSelfSignedCertificate && !AllowCertErrors &&
_serverCertificateValidationCallback == null)
{
if (!SelfSignedCertificateCallback(message, certificate, certificateChain, errors))
return false;
}
return true;
}
///
/// Initiate HttpClient
///
/// Initiated HTTP Client
private void CreateHttpClient()
{
httpClient = new HttpClient(CreateHttpHandler());
httpClient.BaseAddress = Connection.HostUri;
}
private async Task HttpSendReceive(byte[] postData)
{
if (postData == null)
{
throw new ArgumentException("Invalid post data");
}
if (_changed && httpClient != null)
{
httpClient.Dispose();
httpClient = null;
_changed = false;
}
if (httpClient == null)
{
CreateHttpClient();
}
string response = string.Empty;
try
{
XmlDocument resDoc;
using (var content = new ByteArrayContent(postData))
{
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/soap+xml");
using (var responseMessage =
await httpClient.PostAsync(_connection.HostUri, content).ConfigureAwait(false))
{
response = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
responseMessage.EnsureSuccessStatusCode();
resDoc = new XmlDocument();
resDoc.LoadXml(response);
}
}
return resDoc;
}
catch (XmlException)
{
throw;
}
catch (HttpRequestException httpReqExp)
{
if (httpReqExp.InnerException is WebException webExp)
{
throw webExp;
}
if (string.IsNullOrEmpty(response))
{
throw new WebException("Invalid HttpResponse");
}
throw new WebException(httpReqExp.Message);
}
}
#endregion TRNASPORT_LAYER_OPERATION
#region WSMAN_CALLS
///
/// Creates a new instance of a resource and returns the URI of the new object.
///
/// Uri, the identifier of the resource to be created
/// XmlElement, the instance data
///
public CimReference Create(Uri resourceUri, XmlElement resource)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
if (resource == null)
throw new ArgumentNullException("resource");
//define WSMan HEADER call
Header WSManHeader = new Header();
WSManHeader.Action = CREATE;
WSManHeader.ResourceURI = resourceUri.AbsoluteUri;
//define WSMan BODY call
XmlElement[] bodyIn = { resource };
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
CimReference retValue = null;
if (bodyOut != null)
{
retValue = new CimReference("ResourceCreated", bodyOut[0].NamespaceURI, bodyOut[0].InnerXml);
}
//ResourceCreatedType retValue = new ResourceCreatedType();
return retValue;
}
///
/// Delete the resource specified in the resource URI
/// (Only if one instance of the object exists)
///
/// Uri, the identifier of the resource to be deleted
/// Selectors to id the object to delete
public void Delete(Uri resourceUri, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
Header WSManHeader = new Header();
WSManHeader.Action = DELETE;
WSManHeader.ResourceURI = resourceUri.AbsoluteUri;
WSManHeader.Any = AddSelectors(selectors);
XmlElement[] bodyIn = null;
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
}
///
/// Delete the resource specified in the resource URI
/// (Only if one instance of the object exists)
///
/// CimReference to delete
public void Delete(CimReference reference)
{
if (reference == null)
throw new ArgumentNullException("reference");
Delete(reference.GetResourceUri(), reference.GetKeys());
}
///
/// Retrieves the resource specified by 'resource' and returns an XML representation
/// of the current instance of the resource.
///
/// Uri, the identifier of the resource to be retrieved
/// Selectors to id the object to be retrieved
/// XmlElement, an XML of the current instance of the resource.
public XmlElement Get(Uri resourceUri, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
Header WSManHeader = new Header();
WSManHeader.Action = GET;
WSManHeader.ResourceURI = resourceUri.AbsoluteUri;
WSManHeader.Any = AddSelectors(selectors);
XmlElement[] bodyIn = null;
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
if (bodyOut == null)
{
return null;
}
return bodyOut[0];
}
///
/// Retrieves the resource specified by 'resource' and returns an XML representation
/// of the current instance of the resource.
///
/// EndpointReference to delete
/// XmlElement, an XML of the current instance of the resource.
public XmlElement Get(CimReference resource)
{
if (resource == null)
throw new ArgumentNullException("resource");
return Get(resource.GetResourceUri(), resource.GetKeys());
}
///
/// Update a resource.
///
/// Uri, the identifier of the resource to update
///
/// Selectors to id the object to update
public void Put(Uri resourceUri, XmlElement content, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
Header WSManHeader = new Header();
WSManHeader.Action = PUT;
WSManHeader.ResourceURI = resourceUri.AbsoluteUri;
WSManHeader.Any = AddSelectors(selectors);
//define WSMan BODY call
XmlElement[] bodyIn = { content };
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
}
///
/// Method to Identify a machine.
///
/// XML which represent the machine
public XmlElement Identify()
{
Header WSManHeader = new Header();
WSManHeader.Action = IDENTITY;
XmlDocument xDoc = new XmlDocument();
String identify = "";
xDoc.LoadXml(identify);
XmlElement elem = xDoc.DocumentElement;
XmlElement[] bodyIn = { elem };
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
if (bodyOut == null)
{
return null;
}
return bodyOut[0];
}
private EnumerateRequest getEnumerateRequest(EnumerationOptions options, IEnumerable selectors)
{
XmlElement retValue = null;
EnumerateRequest enumerate = new EnumerateRequest();
enumerate.EnumerationMode = null;
enumerate.filter = null;
if (selectors != null)
{
SelectorFilter filter = new SelectorFilter();
filter.Any = AddSelectors(selectors);
filter.Dialect = "http://schemas.dmtf.org/wbem/wsman/1/wsman/SelectorFilter";
//Serialize the filter to XmlElement
XmlDocument xDoc = new XmlDocument();
XmlSerializer filterSerializer = new XmlSerializer(typeof(SelectorFilter));
using (StringWriter filterWriter = new StringWriter())
{
filterSerializer.Serialize(filterWriter, filter);
xDoc.LoadXml(filterWriter.ToString());
}
retValue = xDoc.DocumentElement;
}
if (options != null)
{
if (options.EnumMode == EnumerationOptions.EnumerationMode.NONE)
{
enumerate.EnumerationMode = null;
}
else if (options.EnumMode == EnumerationOptions.EnumerationMode.ENUMERATION_REFERENCE)
{
enumerate.EnumerationMode = WSMAN_ENUM_EPR;
}
else if (options.EnumMode == EnumerationOptions.EnumerationMode.ENUMERATION_OBJ_AND_REFERENCE)
{
enumerate.EnumerationMode = WSMAN_ENUM_OBJ_N_EPR;
}
if (options.Filter != null)
{
XmlDocument xDoc = new XmlDocument();
XmlSerializer filterSerializer = new XmlSerializer(options.Filter.GetType());
using (StringWriter filterWriter = new StringWriter())
{
Type ttt = options.Filter.GetType();
filterSerializer.Serialize(filterWriter, options.Filter);
xDoc.LoadXml(filterWriter.ToString());
}
retValue = xDoc.DocumentElement;
}
}
enumerate.filter = retValue;
return enumerate;
}
///
/// Enumerate resource.
///
/// Uri, The identifier of the resource to be retrived
/// Selectors to id the object to enumerate
/// EnumerateOptions, a struct holds special enum options. Case of a NULL regular enumerate will be execute
/// XmlElement array containing the xml response
public XmlElement[] Enumerate(Uri resourceUri, IEnumerable selectors, EnumerationOptions options)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
//will be used later for the pull operation
PullResponse pullResp = new PullResponse();
//will contain the enumeration results
ArrayList container = new ArrayList();
//define the Header of the Enum call
Header WSManHeader = new Header();
WSManHeader.Action = ENUMERATE;
WSManHeader.ResourceURI = resourceUri.AbsoluteUri;
//define the BODY of the Enum call
XmlDocument xDoc = new XmlDocument();
EnumerateRequest enumerateReq = getEnumerateRequest(options, selectors);
//serialize the data
XmlSerializer enumerateSerializer = new XmlSerializer(enumerateReq.GetType());
using (StringWriter enumerateWriter = new StringWriter())
{
enumerateSerializer.Serialize(enumerateWriter, enumerateReq);
xDoc.LoadXml(enumerateWriter.ToString());
}
XmlElement[] bodyIn = { xDoc.DocumentElement };
XmlElement[] bodyOut = null;
try
{
//Get the EnumerationContext
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
//after getting the EnumerationContext we can call Pull to get the Enumeration elements
//create the HEADER for the pull request
WSManHeader.Action = PULL;
//update the BODY for the pull request
Pull pull = new Pull();
if (bodyOut != null)
{
pull.EnumerationContext = bodyOut[0].GetElementsByTagName(WSMAN_ENUM_CONTEXT, "*")[0].InnerText; //node[0].InnerText; //enumResp.EnumerationContext;
pull.MaxElements = MAX_ELEMENTS;
}
//serialize the data
XmlSerializer pullSerializer = new XmlSerializer(pull.GetType());
using (StringWriter pullWriter = new StringWriter())
{
pullSerializer.Serialize(pullWriter, pull);
xDoc.LoadXml(pullWriter.ToString());
}
bodyIn[0] = xDoc.DocumentElement;
bodyOut = null;
XmlSerializer myPullResSerializer = new XmlSerializer(pullResp.GetType());
do
{
//Pull the Enumeration elements
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
//get the results from the body
if (bodyOut != null)
{
pullResp = (PullResponse)myPullResSerializer.Deserialize(new StringReader(bodyOut[0].OuterXml));
}
foreach (XmlElement xmlElement in pullResp.Items)
{
// get all the xml elements from the each item nodes
foreach (XmlNode node in xmlElement.ChildNodes)
{
//In case of EndOfSequence InnerXml = ""
if (node.InnerText != "")
{
container.Add(node);
}
}
}
} while (pullResp.EnumerationContext != null);
}
catch (WSManException)
{
throw;
}
finally
{
//Release the EnumerationContext
if (pullResp.EnumerationContext != null)
{
//update the HEADER for the Release request
WSManHeader.Action = RELEASE;
//update the BODY for the Release request
Release release = new Release();
release.EnumerationContext = pullResp.EnumerationContext;
//serialize the data
XmlSerializer releaseSerializer = new XmlSerializer(release.GetType());
using (StringWriter releaseWriter = new StringWriter())
{
releaseSerializer.Serialize(releaseWriter, release);
xDoc.LoadXml(releaseWriter.ToString());
}
bodyIn[0] = xDoc.DocumentElement;
//send the Release EnumerationContext WSMan call
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
}
}
return (XmlElement[])container.ToArray(typeof(XmlElement));
}
///
/// Enumerate resource.
///
/// Uri, The identifier of the resource to be retrived
///
/// XmlElement array containing the xml response
public XmlElement[] Enumerate(Uri resourceUri, IEnumerable selectors)
{
return Enumerate(resourceUri, selectors, null);
}
///
/// Invokes a method and returns the results of the method call.
///
/// Uri, The identifier of the resource to be retrived
/// Uri, the action to run
/// The input object for the method
/// The selectors of the object to invoke method
/// Method output
public XmlElement Invoke(Uri resourceUri, Uri actionUri, XmlElement request, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
if (actionUri == null)
throw new ArgumentNullException("actionUri");
if (request == null)
throw new ArgumentNullException("request");
Header WSManHeader = new Header();
WSManHeader.Action = actionUri.AbsoluteUri;
WSManHeader.ResourceURI = resourceUri.AbsoluteUri;
WSManHeader.Any = AddSelectors(selectors);
//define WSMan BODY call
XmlElement[] bodyIn = { request };
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
if (bodyOut == null)
{
return null;
}
return bodyOut[0];
}
///
/// Subscribe to specified event.
///
/// Uri, The identifier of the resource to be retrived
/// Class which contains all data regarding the subscription
///
public XmlElement Subscribe(SubscribeInfo info)
{
return Subscribe(info, null);
}
///
/// Subscribe to specified event.
///
/// Uri, The identifier of the resource to be retrived
/// Class which contains all data regarding the subscription
/// for digest authentication
///
public XmlElement Subscribe(SubscribeInfo info, IssuedTokens issuedTokens)
{
if (info == null)
throw new ArgumentNullException("info");
//define WSMan HEADER call
Header WSManHeader = new Header();
WSManHeader.Action = SUBSCRIBE;
WSManHeader.ResourceURI = new Uri("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_FilterCollection").AbsoluteUri;
WSManHeader.Any = AddSelectors(info.Keys);
//when digestAuthentication is required it needed to add the IssuedTokens to the header of the request ant the Auth to the body
//the IssuedTokens are sent as parameter to the function if digestAuthentication is required
//this class will sent with the username and password in it.
//if no it will be null
WSManHeader.IssuedTokens = issuedTokens;
if (issuedTokens != null)
info.Delivery.Auth = new Auth();
XmlDocument xDoc = new XmlDocument();
XmlSerializer SubscribeInfoSerializer = new XmlSerializer(typeof(SubscribeInfo));
using (StringWriter SubscribeWriter = new StringWriter())
{
SubscribeInfoSerializer.Serialize(SubscribeWriter, info);
xDoc.LoadXml(SubscribeWriter.ToString());
}
XmlElement[] bodyIn = { xDoc.DocumentElement };
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
if (bodyOut == null)
{
return null;
}
return bodyOut[0];
}
///
/// Unsubscribe to specified event.
///
/// Uri, The identifier of the resource to be retrived
/// The selectors of the object to Unsubscribe method
public void Unsubscribe(IEnumerable selectors)
{
//define WSMan HEADER call
Header WSManHeader = new Header();
WSManHeader.Action = UNSUBSCRIBE;
WSManHeader.ResourceURI = new Uri("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_FilterCollectionSubscription").AbsoluteUri;
WSManHeader.Any = AddSelectors(selectors);
//WSManHeader.Identifier = identifier;
XmlElement[] bodyIn = null;
XmlElement[] bodyOut = null;
try
{
WSManSendReceive(WSManHeader, bodyIn, ref bodyOut);
}
catch (WSManException)
{
throw;
}
}
///
/// This call back is used to allow the connection even if the certificate has erros (like hostname mismatch etc)
///
///
///
///
///
///
private bool CertificateValidationCallback(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors errors)
{
if (_serverCertificateValidationCallback != null)
{
if (allowCertificateError == null)
{
allowCertificateError = _serverCertificateValidationCallback(certificate, errors);
}
return allowCertificateError.Value;
}
if (AllowCertErrors)
{
if (FirstCall)
{
if (ServerCertRaised != null)
ServerCertRaised(certificate as X509Certificate2);
FirstCall = false;
}
if (errors == SslPolicyErrors.None)
{
CertError = new CertificateError();
return true;
}
if (errors == SslPolicyErrors.RemoteCertificateNameMismatch)
{
CertError = new CertificateError();
CertError.Error = errors;
CertError.Header = HOSTNAME_MISMATCH_HEADER;
CertError.Description = HOSTNAME_MISMATCH_ERROR;
return true;
}
}
return false;
}
private bool SelfSignedCertificateCallback(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors errors)
{
//If certificate is self signed, ignore all errors
if (certificate.Subject.Equals(certificate.Issuer))
{
return true;
}
if (errors == SslPolicyErrors.None)
{
return true;
}
return false;
}
#endregion WSMAN_CALLS
#region WSMAN_UTILITIES
///
/// Get the URI (the identifier) of the resource object
///
/// Type, the type of the resource object
/// The identifier of the resource object
public static string GetResourceUri(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
foreach (object at in type.GetCustomAttributes(false))
{
if (at.GetType() == typeof(XmlRootAttribute))
{
return ((XmlRootAttribute)at).Namespace;
}
}
return "";
}
///
/// create selectors XML element with the key and values.
///
/// selectors to be added
/// XmlElement which represent the selectors
protected XmlElement[] AddSelectors(IEnumerable selectors)
{
XmlElement[] Any = null;
if (selectors != null)
{
XmlDocument paramsDoc = null;
bool firstSelector = true;
IEnumerator enumerator = selectors.GetEnumerator();
while (enumerator.MoveNext())
{
//Create the outer tag only in the first loop
if (firstSelector)
{
Any = new XmlElement[2];
paramsDoc = new XmlDocument();
Any[1] = paramsDoc.CreateElement(WSMAN, WSMAN_SELECTOR_SET_FIELD, WSMAN_NAMESPACE_URI);
firstSelector = false;
}
Key selector = enumerator.Current;
XmlElement newChild = paramsDoc.CreateElement(WSMAN, WSMAN_SELECTOR_FIELD, WSMAN_NAMESPACE_URI);
newChild.SetAttribute(WSMAN_SELECTOR_NAME, selector.Name);
if (selector.cimReference != null)
{
newChild.InnerXml = selector.cimReference.ToString();
}
else if (selector.Value != null)
{
// if Byte Array
if (selector.Value.GetType().FullName == "System.Byte[]")
{
Object temp = selector.Value;
newChild.InnerText = Convert.ToBase64String((Byte[])temp);
}
else
{
newChild.InnerXml = selector.Value;
}
}
Any[1].AppendChild(newChild);
}
}
return Any;
}
///
/// Return the Type of the resource.
///
/// string, an XML representation of the resource
/// Type, resource Type
public static Type GetType(string resource)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(resource);
Type type = Type.GetType(doc.DocumentElement.LocalName);
if (type == null)
{
string className = doc.DocumentElement.NamespaceURI;
int classNameIndex = className.LastIndexOf("/");
className = className.Substring(classNameIndex + 1) + "Type+";
string typeName = "MOF_CLASSES." + className + doc.DocumentElement.LocalName;
type = Type.GetType(typeName);
if (type == null)
type = Type.GetType("MOF_CLASSES." + doc.DocumentElement.LocalName + "Type", true);
}
return type;
}
///
/// Transforming (e.g. Deserialize) the WSMan Fault XML, to a WSMan Fault class
///
/// The WSMan Fault in XML format
/// The WSMan Fault in the Fault class format
private static Fault DeserializeWSManFault(XmlElement XmlFault)
{
Fault WSManFault = new Fault();
XmlSerializer mySerializer = new XmlSerializer(WSManFault.GetType());
WSManFault = (Fault)mySerializer.Deserialize(new StringReader(XmlFault.OuterXml));
return WSManFault;
}
#endregion WSMAN_UTILITIES
#region PROPERTIES
#endregion PROPERTIES
#region IDisposable Implementation
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Connection?.Dispose();
httpClient?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~DotNetWSManClient()
{
Dispose(false);
}
#endregion
}
#region WSMAN_SOAP_STRUCT
///
/// The WS-Management call include 2 elemtns:
/// 1. Header
/// 2. Body
/// each of them contains aditional fields, this class include all the needed fields to
/// creat a WS-Management call.
/// After filling all the data, this class will serialize to a XML format and send to the server
///
[XmlRootAttribute("Envelope", Namespace = "http://www.w3.org/2003/05/soap-envelope", IsNullable = false)]
public class WSManCall
{
///
/// Header of the wsman call.
///
[XmlElement(Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public Header Header;
///
/// Body of the wsman call.
///
[XmlElement(Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public Body Body;
}
///
/// Class which represent the header of the wsman call.
///
public class Header
{
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/08/addressing")]
public string Action;
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/08/addressing")]
public string To;
[XmlElement(Namespace = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd")]
public string ResourceURI;
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/08/addressing")]
public string MessageID;
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/08/addressing")]
public ReplyTo ReplyTo;
[XmlElement(Namespace = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd")]
public string MaxEnvelopeSize;
[XmlElement(Namespace = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd")]
public string OperationTimeout;
[XmlAnyElementAttribute()]
public XmlElement[] Any;
//Optional: To request digest authentication when alerts are sent
//if digest authentication are requested this class is allocated in the subscribe function
//if not this class will be null and will not send as part of the request
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/trust")]
public IssuedTokens IssuedTokens;
public Header()
{
IssuedTokens = null;
ReplyTo = null;
}
}
public class ReplyTo
{
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/08/addressing")]
public string Address;
}
public struct Body
{
[XmlAnyElementAttribute()]
public XmlElement[] Any;
}
//Additional WS-Management sub structures (e.g. WS-Management XML elemets)
public struct EnumerateResponse
{
public string EnumerationContext;
}
[XmlRoot("Enumerate", Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public struct EnumerateRequest
{
[XmlElement(Namespace = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd")]
public string EnumerationMode;
[XmlAnyElementAttribute()]
public XmlElement filter;
}
[XmlRoot(Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public struct Pull
{
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public string EnumerationContext;
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public string MaxElements;
}
[XmlRoot(Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public struct PullResponse
{
public string EnumerationContext;
[XmlAnyElementAttribute()]
public XmlElement[] Items;
}
[XmlRoot(Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public struct Release
{
[XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public string EnumerationContext;
}
#endregion WSMAN_SOAP_STRUCT
#region CLASSES
public class CertificateError
{
public string Description { set; get; }
public string Header { set; get; }
public SslPolicyErrors Error { set; get; }
public X509Certificate2 Certificate { set; get; }
}
public struct CerftificateCallback
{
public bool IgnoreError { get; set; }
}
#endregion
}