//---------------------------------------------------------------------------- // // 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 }