1269 lines
48 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Security;
using Common.Utils;
namespace Intel.Management.Wsman
{
/// <summary>
/// Implementation of the IWsmanConnection interface
/// </summary>
[Guid("73131C56-E7EA-4c1d-A6E2-03B6350A9B60")]
[ComVisible(true)]
public class WsmanConnection : IWsmanConnection
{
#region Consts
/// <summary>
/// consts defining username and password limits
/// </summary>
public const int MAX_PWD_LENGTH = 32;
public const int MIN_PWD_LENGTH = 8;
public const int MAX_USERNAME_LENGTH = 32;
private const string DIGEST_AUTH = "Digest";
private const string KERBEROS_AUTH = "Negotiate";
private const int CONNECTION_TIMEOUT = 10060;
private const int CONNECTION_REFUSED = 10061;
#endregion
#region Fields
private Uri _address = new Uri("http://localhost:16992/wsman");
private string _username = string.Empty;
private SecureString _password = new SecureString();
private string _scheme = KERBEROS_AUTH;
private SecureString _dmp = null;
private DateTime _serverTime;
private HttpClient _client;
private readonly WsmanConnectionOptions _options = new WsmanConnectionOptions();
private IWsmanFault _lastError = null;
private ClientRequest _reqObj = null;
private bool _changed = true;
private static readonly object _nsLock = new object();
private static IDictionary<string, string> _nsResolver;
private static readonly System.Diagnostics.TraceSource _trace = new System.Diagnostics.TraceSource("Intel.Management.Wsman");
internal bool? allowCertificateError = null;
#endregion
#region Events
/// <summary>
/// Event that raised once we get the server certificate
/// </summary>
public static event Action<X509Certificate2> ServerCertRaised;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the WsmanConnection class
/// </summary>
public WsmanConnection()
{
// add default namespaces
lock (_nsLock)
{
if (_nsResolver == null)
{
_nsResolver = new Dictionary<string, string>
{
{ "CIM", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/" },
{ "AMT", "http://intel.com/wbem/wscim/1/amt-schema/1/" },
{ "IPS", "http://intel.com/wbem/wscim/1/ips-schema/1/" },
{ "Win32", "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/" },
{ "Datetime", "http://schemas.dmtf.org/wbem/wscim/1/common" },
{ "Interval", "http://schemas.dmtf.org/wbem/wscim/1/common" },
{ "AssociatedInstances", "http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd" },
{ "*", "http://schemas.dmtf.org/wbem/wscim/1" }
};
}
}
// Enforcing TLS1.2 and TLS1.1 - This is a W\A when debugging with Visual Studio (.NET doesn't add these protocols when debugging)
// Issue does not occur when running with CLI.
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
}
#endregion
#region Finalizer
/// <summary>
/// Disposes Client and Handler
/// </summary>
~WsmanConnection()
{
Dispose(false);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the username the connection will use to authenticate
/// </summary>
public string Username
{
get
{
return _username;
}
set
{
int pos = -1;
if (value == null)
value = string.Empty;
else
pos = value.IndexOf('\\');
if (pos > 0 || value == string.Empty) //Kerberos
{
AuthenticationScheme = KERBEROS_AUTH;
//Accepting null value for Kerberos connection
var newName = value;
_changed = _changed || _username != newName;
_username = newName;
if (_username.Length > MAX_USERNAME_LENGTH)
throw new WsmanConnectionException("Username can contain up to 32 characters.");
}
else //Digest
{
AuthenticationScheme = DIGEST_AUTH;
if (pos == 0)
{
value = value.Substring(1);
}
if (string.IsNullOrEmpty(value))
throw new WsmanConnectionException("Username null or an empty string.");
_changed = _changed || _username != value;
_username = value;
if (_username.Length > MAX_USERNAME_LENGTH)
throw new WsmanConnectionException("Username can contain up to 32 characters.");
}
_dmp = null;
ReqObj = null;
}
}
/// <summary>
/// Gets or sets the password the connection will use to authenticate
/// </summary>
public SecureString Password
{
get
{
return _password;
}
set
{
if (string.IsNullOrEmpty(_scheme))
throw new WsmanConnectionException("Cannot initialize password, authentication scheme is null or empty.");
if (value == null)
{
//Kerberos allows empty password
if (_scheme == KERBEROS_AUTH)
{
_changed = _changed || _password.Length > 0 ;
_password?.Dispose();
_password = new SecureString();
}
else
throw new WsmanConnectionException("Password is null or an empty string.");
}
else
{
if (value.Length < MIN_PWD_LENGTH || value.Length > MAX_PWD_LENGTH)
throw new WsmanConnectionException("Password can contain between 8 to 32 characters.");
_changed = _changed || !_password.ValueEquals(value);
_password?.Dispose();
_password = value;
}
_dmp = null;
ReqObj = null;
}
}
/// <summary>
/// Gets or sets the address that will be associated with this connection
/// </summary>
///
public string Address
{
get
{
return _address.ToString();
}
set
{
if (!Uri.IsWellFormedUriString(value, 0))
throw new WsmanConnectionException("Invalid argument - Address is not a valid uri");
var newUri = new Uri(value);
_changed = _changed || _address != newUri;
_address = newUri;
ReqObj = null;
}
}
/// <summary>
/// Gets or sets the authentication scheme the connection will use
/// </summary>
public string AuthenticationScheme
{
get
{
return _scheme;
}
set
{
_changed = _changed || _scheme != value;
_scheme = value;
ReqObj = null;
}
}
/// <summary>
/// Gets the Connection Options
/// </summary>
public IWsmanConnectionOptions Options => _options;
private ClientRequest ReqObj
{
get => _reqObj;
set
{
_reqObj?.Dispose();
_reqObj = value;
}
}
/// <summary>
/// Gets the last error that occured when using the connection
/// </summary>
public IWsmanFault LastError => _lastError;
public DateTime ServerTime => _serverTime;
#endregion
#region Public Methods
private bool _disposed = false;
/// <summary>
/// Implement IDisposable method
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_client?.Dispose();
_client = null;
_dmp?.Dispose();
Password?.Dispose();
Options?.Dispose();
ReqObj?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Resolves a class name prefix to a full resourceUri
/// </summary>
/// <param name="prefix"></param>
/// <return>The resoved resourceUri</return>
public string ResolveNamespace(string prefix)
{
string result = null;
lock (_nsLock)
{
_nsResolver.TryGetValue(prefix, out result);
}
return result;
}
/// <summary>
/// Registers a namespaceUri for a class name prefix
/// </summary>
/// <param name="prefix"></param>
/// <param name="ns"></param>
public void RegisterNamespace(string prefix, string ns)
{
lock (_nsLock)
{
_nsResolver[prefix] = ns;
}
}
/// <summary>
/// Closes the connection
/// </summary>
/// <remarks>Forces future request to use a new network connection</remarks>
public void Close()
{
ReqObj?.Dispose();
_dmp?.Dispose();
_client?.Dispose();
_client = null;
}
/// <summary>
/// Tests the connection to see if the endpoint supports Wsman
/// </summary>
public IManagedInstance Identify()
{
return (IManagedInstance)SubmitRequest(WsmanResources.IDENTIFY_XML, null, null);
}
/// <summary>
/// Creates a reference to a object supported by a Wsman Service
/// </summary>
/// <param name="resourceUri">A string describing the object reference</param>
/// <return>A IManagedReference pointing to the object</return>
public IManagedReference NewReference(string resourceUri)
{
ManagedReference wsRef = new ManagedReference(this, resourceUri);
return wsRef;
}
/// <summary>
/// Creates a new representation of a CIM object that can be exchanged with the server
/// </summary>
/// <param name="resourceUri">A string describing the instance</param>
/// <return>A IManagedInstance representing the resource</return>
public IManagedInstance NewInstance(string resourceUri)
{
ManagedInstance inst = new ManagedInstance(this, resourceUri);
return inst;
}
/// <summary>
/// Executes a simple SQL type query
/// </summary>
/// <param name="queryString"></param>
/// <return>An IWsmanEnumeration containing resulting items</return>
public IWsmanEnumeration ExecQuery(string queryString)
{
IWsmanEnumeration result = null;
IManagedReference objRef = GetReferenceFromSQL(queryString);
try
{
result = objRef.Enumerate(null, null);
}
catch (WsmanUnreachableException)
{
result = new WsmanEnumeration(this, null, null);
}
return result;
}
/// <summary>
/// Executes a simple SQL type query
/// </summary>
/// <param name="queryString"></param>
/// <return>An IManagedReference to the object described by the SQL query string</return>
public IManagedReference GetReferenceFromSQL(string queryString)
{
string tableName = string.Empty;
int pos = -1;
IManagedReference result = null;
const string SELECT_CLAUSE = "select * from ";
tableName = queryString.Substring(SELECT_CLAUSE.Length).TrimStart();
queryString = tableName;
pos = tableName.IndexOf(" ");
if (pos > 0)
{
tableName = tableName.Substring(0, pos);
queryString = queryString.Substring(pos).Trim();
}
if (tableName.Equals(string.Empty))
{
throw new WsmanQueryFormatException("Missing Resource Name");
}
result = NewReference(tableName);
//check where clause
if (queryString.ToLower().StartsWith("where"))
{
string name = null;
string value = null;
queryString = queryString.Substring("where".Length).Trim();
while (!queryString.Equals(string.Empty))
{
pos = queryString.IndexOf("=");
if (pos <= 0)
throw new WsmanQueryFormatException("Missing property name");
name = queryString.Substring(0, pos);
value = queryString.Substring(pos + 1).TrimStart();
queryString = value;
if (value.StartsWith("'"))
{
pos = value.IndexOf("'", 1);
if (pos > 0)
{
value = value.Substring(1, pos - 1);
queryString = queryString.Substring(pos + 1).TrimStart();
}
else
throw new WsmanQueryFormatException("Missing close quote");
}
else if (value.StartsWith("\""))
{
pos = value.IndexOf("\"", 1);
if (pos > 0)
{
value = value.Substring(1, pos - 1);
queryString = queryString.Substring(pos + 1).TrimStart();
}
else
throw new WsmanQueryFormatException("Missing close quote");
}
else
{
pos = value.IndexOf(" ");
if (pos > 0)
{
value = value.Substring(0, pos);
queryString = queryString.Substring(pos).TrimStart();
}
else
{
queryString = queryString.Substring(value.Length).TrimStart();
}
}
if (queryString.ToLower().StartsWith("and "))
{
queryString = queryString.Substring("and ".Length).TrimStart();
}
result.AddSelector(name, value);
}//end while where
}//end if where
return result;
}
/// <summary>
/// Submits a Wsman command to the server
/// </summary>
/// <param name="requestString">The string command to send</param>
/// <param name="refObj">The reference the Wsman command will use</param>
/// <param name="input">Message Parameters</param>
/// <returns>An object representing the results of the command</returns>
public object SubmitRequest(string requestString, IManagedReference refObj, IManagedInstance input)
{
XmlDocument reqDoc = new XmlDocument();
reqDoc.LoadXml(requestString);
return SubmitRequest(reqDoc, refObj, input);
}
/// <summary>
/// Submits a Wsman command to the server
/// </summary>
/// <param name="reqDoc">The XML document to send</param>
/// <param name="refObj">The reference the Wsman command will use</param>
/// <param name="input">Message Parameters</param>
/// <returns>An object representing the results of the command</returns>
public object SubmitRequest(XmlDocument reqDoc, IManagedReference refObj, IManagedInstance input)
{
//load the requesting namespace manager
XmlNamespaceManager ns = LoadNamespaces(reqDoc);
// Set the MessageID
string reqUUID = "uuid:" + Guid.NewGuid().ToString().ToUpper();
XmlNode node = reqDoc.DocumentElement.SelectSingleNode("s:Header/wsa:MessageID", ns);
if (node != null)
node.InnerText = reqUUID;
SetEpr(refObj, reqDoc, ns);
//add input
if (input != null)
{
node = reqDoc.DocumentElement.SelectSingleNode("s:Body", ns);
if (node != null)
node.InnerXml = input.Xml;
}
MergeNamespaces(reqDoc, ns);
TraceDocument(reqDoc);
if (ReqObj == null)
{
if (Options.ClientCertificate != null)
{
X509Certificate2 cert2 = (X509Certificate2)Options.ClientCertificate;
//check provisioning cert
if (_options.IsSetupCertificate(cert2))
{
ReqObj = CreateRequest();
}
}
}
return ReqObj != null
? SendObjectRequest(reqUUID, reqDoc, refObj, input)
: SendWebRequest(reqUUID, reqDoc, refObj);
}
public void TraceDocument(XmlDocument doc)
{
TraceDocument(doc, System.Diagnostics.TraceEventType.Information);
}
public void TraceDocument(XmlDocument doc, System.Diagnostics.TraceEventType eventType)
{
if (_trace.Switch.ShouldTrace(eventType))
{
StringBuilder builder = new StringBuilder(Environment.NewLine);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.Unicode;
settings.ConformanceLevel = ConformanceLevel.Document;
settings.Indent = true;
settings.IndentChars = (" ");
XmlWriter writer = XmlWriter.Create(builder, settings);
doc.WriteContentTo(writer);
writer.Close();
_trace.TraceInformation(builder.ToString());
}
}
public static System.Diagnostics.TraceSource TraceSource
{
get { return _trace; }
}
/// <summary>
/// Sets the host and port used by the connection
/// </summary>
/// <param name="hostname">The host name or IP address of the Wsman service</param>
/// <param name="port">The port the Wsman service is listening on</param>
public void SetHost(string hostname, string port)
{
StringBuilder builder = new StringBuilder();
if (port.Equals("16993"))
builder.Append("https://");
else
builder.Append("http://");
builder.Append(hostname);
if (!port.Equals(string.Empty))
{
builder.Append(":");
builder.Append(port);
}
builder.Append("/wsman");
Address = builder.ToString();
}
/// <summary>
/// Sets the connection credentials
/// </summary>
/// <param name="name">The user name</param>
/// <param name="pass">Secure Password object</param>
/// <remarks>Use this for passing a System.Security.SecureString</remarks>
public void SetCredentials(string name, SecureString pass)
{
if (name.Equals(string.Empty) || name.IndexOf('\\') > 0)
{
AuthenticationScheme = KERBEROS_AUTH;
}
else
{
AuthenticationScheme = DIGEST_AUTH;
}
Username = name;
Password = pass;
}
public bool Equals(WsmanConnection other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other._username, _username) && Equals(other.Password.ValueEquals(Password)) && Equals(other._address, _address);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(WsmanConnection)) return false;
return Equals((WsmanConnection)obj);
}
public override int GetHashCode()
{
unchecked
{
int result = (_username != null ? _username.GetHashCode() : 0);
result = (result * 397) ^ (Password != null ? Password.GetHashCode() : 0);
result = (result * 397) ^ (Address != null ? Address.GetHashCode() : 0);
return result;
}
}
#endregion //Public Methods
#region Private Methods
private object SendObjectRequest(string msgId, XmlDocument reqDoc, IManagedReference refObj, IManagedInstance input)
{
XmlDocument resDoc = null;
Exception reqExp;
TraceDocument(reqDoc);
ServerResponse response = RetryLoop(reqDoc, out reqExp);
if (response == null)
{
Close();
throw new WsmanConnectionException(reqExp.Message, reqExp);
}
if (response.StatusCode >= 400)
{
XmlDocument faultDoc = null;
switch (response.StatusCode)
{
case 400:
faultDoc = response.GetXml();
_lastError = WsmanFault.GetXmlFault(faultDoc);
if (_lastError.Subcode.Equals("DestinationUnreachable"))
{
TraceDocument(faultDoc, System.Diagnostics.TraceEventType.Warning);
throw new WsmanUnreachableException(_lastError.Reason);
}
TraceDocument(faultDoc, System.Diagnostics.TraceEventType.Error);
throw new WsmanException(_lastError.Reason);
case 500:
faultDoc = response.GetXml();
_lastError = WsmanFault.GetXmlFault(faultDoc);
TraceDocument(faultDoc, System.Diagnostics.TraceEventType.Error);
if (response.StatusCode == 500)
throw new WsmanRecieverFault(_lastError.Reason);
break;
case 401:
throw new WsmanUnauthorizedException("Unauthorized");
default:
throw new WsmanException(response.StatusText);
}
}
_serverTime = response.Date;
resDoc = response.GetXml();
TraceDocument(resDoc);
return GetResponseObject(refObj, msgId, resDoc);
}
/// <summary>
/// Initiate HTTPClient Handler
/// </summary>
/// <returns> HTTPClient Handler </returns>
private HttpClientHandler CreateHttpHandler()
{
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = false;
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.Proxy = _options.Proxy;
//register callback to the certificate call back
if (Options.ServerCertificateValidationCallback != null)
handler.ServerCertificateCustomValidationCallback += CertificateValidationCallback;
if (Options.AcceptSelfSignedCertificate)
handler.ServerCertificateCustomValidationCallback += SelfSignedCertificateCallback;
if (Options.ClientCertificate != null)
{
handler.ClientCertificates.Add(Options.ClientCertificate);
}
NetworkCredential networkCredential = null;
switch (_scheme)
{
case KERBEROS_AUTH:
int pos = _username.IndexOf('\\');
string domain = null;
string user = null;
if (pos > 0)
{
domain = _username.Substring(0, pos);
user = _username.Substring(pos + 1);
}
if (domain != null && !domain.Equals(string.Empty))
{
networkCredential = new NetworkCredential(user, Password.Copy(), domain);
}
if (!AuthenticationManager.CustomTargetNameDictionary.ContainsKey(_address.ToString()))
{
var spnPort = "HTTP/" + _address.Authority.ToUpper();
AuthenticationManager.CustomTargetNameDictionary.Add(_address.ToString(), spnPort);
}
handler.PreAuthenticate = true;
break;
case DIGEST_AUTH:
networkCredential = new NetworkCredential(_username, Password.Copy());
break;
}
if (networkCredential != null)
{
handler.Credentials = new CredentialCache { { _address, _scheme, networkCredential } };
}
else
{
handler.Credentials = CredentialCache.DefaultNetworkCredentials.GetCredential(_address, KERBEROS_AUTH);
}
return handler;
}
/// <summary>
/// Initiate HTTClient
/// </summary>
/// <returns>Initiated HTTP Client</returns>
private void CreateHttpClient()
{
_client = new HttpClient(CreateHttpHandler());
_client.BaseAddress = _address;
_client.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
_client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
_client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
_client.DefaultRequestHeaders.Connection.Add("keep-alive");
}
/// <summary>
/// Build, send HTTPClient and send request
/// </summary>
/// <param name="reqDoc"></param>
/// <param name="refObj"></param>
/// <param name="msgId"></param>
/// <exception cref="WsmanUnreachableException"></exception>
/// <exception cref="WsmanSenderException"></exception>
/// <exception cref="WsmanRecieverFault"></exception>
/// <exception cref="WsmanUnauthorizedException"></exception>
/// <exception cref="WsmanConnectionException"></exception>
private async Task<object> SendHttpClient(XmlDocument reqDoc, IManagedReference refObj, string msgId)
{
int retry = 2;
if (_changed && _client != null)
{
Close();
_changed = false;
}
if (_client == null)
{
CreateHttpClient();
}
while (retry > 0) //for PG use case
{
DateTime startTime = DateTime.Now;
string response = string.Empty;
HttpStatusCode statusCode = HttpStatusCode.OK;
try
{
XmlDocument resDoc;
using (var content = new StringContent(reqDoc.InnerXml, Encoding.UTF8, "application/soap+xml"))
using (var responseMessage = await _client.PostAsync(_address, content).ConfigureAwait(false))
{
response = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
statusCode = responseMessage.StatusCode;
responseMessage.EnsureSuccessStatusCode();
resDoc = new XmlDocument();
resDoc.LoadXml(response);
}
TraceDocument(resDoc);
return GetResponseObject(refObj, msgId, resDoc);
}
catch (XmlException)
{
throw new WsmanConnectionException(response);
}
catch (TaskCanceledException)
{
TimeSpan span = DateTime.Now.Subtract(startTime);
if (span.TotalMilliseconds > Options.Timeout)
{
return null;
}
retry = 1;
}
catch (HttpRequestException httpReqExp)
{
int ErrorCode = 0;
if (httpReqExp.InnerException is SocketException se1) // This exception is received when compiling for .net 6
{
ErrorCode = se1.ErrorCode;
}
else if (httpReqExp.InnerException?.InnerException is SocketException se2) // When compiling for .net framework 48, a webSocket exception is received, with SocketException as an inner exception
{
ErrorCode = se2.ErrorCode;
}
if (ErrorCode == CONNECTION_TIMEOUT || ErrorCode == CONNECTION_REFUSED)
{
TimeSpan span = DateTime.Now.Subtract(startTime);
if (span.TotalMilliseconds > Options.Timeout)
{
return null; //if we exceeded time out don't wait by try one more time
}
System.Threading.Thread.Sleep(1000);
retry--;
continue;
}
if (string.IsNullOrEmpty(response))
{
var exp = httpReqExp.InnerException ?? httpReqExp;
throw new WsmanConnectionException(exp.Message, exp);
}
switch (statusCode)
{
case HttpStatusCode.BadRequest when _lastError?.Subcode != null && _lastError.Subcode.Equals("DestinationUnreachable"):
throw new WsmanUnreachableException(WsmanFault.GetXmlFault(response).Reason, httpReqExp);
case HttpStatusCode.BadRequest:
throw new WsmanSenderException(WsmanFault.GetXmlFault(response).Reason, httpReqExp);
case HttpStatusCode.InternalServerError:
throw new WsmanRecieverFault(WsmanFault.GetXmlFault(response).Reason, httpReqExp);
case HttpStatusCode.Unauthorized:
throw new WsmanUnauthorizedException(statusCode.ToString(), httpReqExp);
}
throw new WsmanConnectionException(statusCode.ToString(), httpReqExp);
}
}
return null;
}
private object SendWebRequest(string msgId, XmlDocument reqDoc, IManagedReference refObj)
{
var output = Task.Run(()=>SendHttpClient(reqDoc, refObj, msgId)).GetAwaiter().GetResult();
if (output == null)
{
throw new WsmanConnectionException("Unable to connect", new WebException("Max Retry attempts reached", WebExceptionStatus.RequestCanceled));
}
return output;
}
private ServerResponse RetryLoop(XmlDocument reqDoc, out Exception resultExp)
{
ServerResponse result = null;
resultExp = null;
DateTime startTime = DateTime.Now;
int retryCount = 13;//limit the retry loop by count and time
while (retryCount > 0)
{
try
{
result = ReqObj.Send(reqDoc);
retryCount = 0;
}
catch (SocketException sockExp)
{
//WSATIMEOUT 10060 WSACONNECTIONREFUSED 10061
if (sockExp.ErrorCode == 10060 || sockExp.ErrorCode == 10061)//
{
retryCount--;
Close();
}
else
{
retryCount = 0;
resultExp = sockExp;
}
}
catch (Exception nonSockExp)
{
retryCount = 0;
resultExp = nonSockExp;
}
TimeSpan span = DateTime.Now.Subtract(startTime);
if (span.TotalSeconds > Options.Timeout)
retryCount = 0;
if (retryCount > 0)
System.Threading.Thread.Sleep(1000);
}
return result;
}
private ClientRequest CreateRequest()
{
ClientRequest reqObj = null;
reqObj = Options.InternetProtocol == InternetProtocolType.Ipv6
? new ClientRequest(_address, AddressFamily.InterNetworkV6)
: new ClientRequest(_address, AddressFamily.InterNetwork);
MpsManager mps = null;
try
{
if (Options.ClientCertificate != null)
{
X509Certificate2 cert2 = (X509Certificate2)Options.ClientCertificate;
reqObj.SetCertificate(cert2);
// We are assuming setup certificate is used
reqObj.SetCertificateOptions(false, false, true);
}
switch (_scheme)
{
case DIGEST_AUTH when Options.UseDigestMasterPassword:
GetDmp();
break;
case DIGEST_AUTH:
_dmp = null;
reqObj.SetDigestAuthorization(_username, Password.Copy());
break;
case KERBEROS_AUTH when Password == null || Password.Length == 0:
{
reqObj.SetWindowsAuthorization(null, null, new SecureString(), Options.ServiceName);
break;
}
case KERBEROS_AUTH:
{
string[] cred = _username.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
reqObj.SetWindowsAuthorization(cred[1], cred[0], Password.Copy(), Options.ServiceName);
break;
}
default:
{
if (_scheme.EndsWith("Anonymous"))
{
reqObj.SetAnonymousAuthorization();
}
break;
}
}
mps = new MpsManager();
HttpProxy proxy = null;
if (!string.IsNullOrEmpty(Options.ProxyAddress))
{
proxy = new HttpProxy(Options.ProxyAddress, Options.ProxyUser, Options.ProxyPassword);
reqObj.SetProxy(proxy);
}
else if (mps.IsConnected(_address.Host))
{
proxy = new HttpProxy(mps.HttpProxy, mps.HttpUser, mps.HttpPassword);
reqObj.SetProxy(proxy);
}
}
catch (Exception)
{
reqObj.Dispose();
}
finally
{
mps?.Dispose();
}
return reqObj;
}
private void GetDmp()
{
ClientRequest reqObj = CreateRequest();
XmlDocument reqDoc = new XmlDocument();
reqDoc.LoadXml(WsmanResources.IDENTIFY_XML);
ServerResponse response = null;
try
{
response = ReqObj.Send(reqDoc);
}
catch (Exception exp)
{
throw new WsmanConnectionException(exp.Message, exp);
}
if (response != null && response.StatusCode >= 400)
{
switch (response.StatusCode)
{
case 400:
_lastError = WsmanFault.GetXmlFault(response.GetXml());
if (_lastError.Subcode.Equals("DestinationUnreachable"))
throw new WsmanUnreachableException(_lastError.Reason);
throw new WsmanException(_lastError.Reason);
case 401:
break;
case 500:
_lastError = WsmanFault.GetXmlFault(response.GetXml());
if (response.StatusCode == 500)
throw new WsmanRecieverFault(_lastError.Reason);
break;
default:
throw new WsmanException(response.StatusText);
}
}
string[] list = ((AnonymousAuthorization)reqObj.GetAuthorization()).GetAuthorizations();
foreach (string item in list)
{
if (item.StartsWith("WWW-Authenticate: Digest"))
{
string token = "realm=\"";
int pos1 = item.IndexOf(token);
if (pos1 > 0)
{
byte[] key = Password.ConvertToByteArray();
pos1 += token.Length;
int pos2 = item.IndexOf("\"", pos1);
string realm = item.Substring(pos1, pos2 - pos1);
if (string.IsNullOrEmpty(_username) || key == null)
{
throw new WsmanConnectionException("Username or password are null or empty.");
}
using (HMACSHA256 sha2 = new HMACSHA256(key))
{
byte[] hash = sha2.ComputeHash(Encoding.ASCII.GetBytes(realm + _username));
_dmp = hash.ConvertByteArrayToSecureString(); ;
reqObj.SetDigestAuthorization(_username, _dmp);
ReqObj = reqObj;
Array.Clear(hash, 0, hash.Length);
}
Array.Clear(key,0,key.Length);
}
}
}
if (ReqObj != reqObj)
reqObj.Dispose();
}
private void SetEpr(IManagedReference refObj, XmlDocument document, XmlNamespaceManager ns)
{
XmlNode node = document.DocumentElement.SelectSingleNode("s:Header/wsa:To", ns);
if (node != null)
node.InnerText = _address.ToString();
if (refObj != null)
{
node = document.DocumentElement.SelectSingleNode("s:Header/wsman:ResourceURI", ns);
if (node != null)
node.InnerText = refObj.ResourceUri;
node = document.DocumentElement.SelectSingleNode("s:Body/n:Enumerate", ns);
if (node == null)
{
node = document.DocumentElement.SelectSingleNode("s:Header/wsman:SelectorSet", ns);
if (node != null)
{
//add namespaces to parent
node.InnerXml = refObj.Xml;
// fix embedding
XmlNode setNode = node.SelectSingleNode("wsa:EndpointReference/wsa:ReferenceParameters/wsman:SelectorSet", ns);
node.InnerXml = setNode != null ? setNode.InnerXml : string.Empty;
}
}
}
}
private void MergeNamespaces(XmlDocument document, XmlNamespaceManager ns)
{
XmlNode addrNode = document.DocumentElement.SelectSingleNode("s:Header/wsa:To", ns);
XmlNode uriNode = document.DocumentElement.SelectSingleNode("s:Header/wsman:ResourceURI", ns);
//Merge Addressing Namespaces
if (addrNode != null && uriNode != null)
{
XmlNodeList list = document.DocumentElement.GetElementsByTagName("*");
foreach (XmlNode listNode in list)
{
if (listNode.NamespaceURI.Equals(addrNode.NamespaceURI))
listNode.Prefix = addrNode.Prefix;
else if (listNode.NamespaceURI.Equals(uriNode.NamespaceURI))
listNode.Prefix = uriNode.Prefix;
}
}
}
private object GetResponseObject(IManagedReference refObj, string reqId, XmlDocument document)
{
object result = null;
XmlNamespaceManager ns = LoadNamespaces(document);
// check request ID
XmlNode node = document.DocumentElement.SelectSingleNode("s:Header/wsa:RelatesTo", ns);
if (node != null)
if (node.InnerText != null && !node.InnerText.Equals(reqId))
throw new IOException(WsmanResources.ID_MISMATCH);
node = document.DocumentElement.SelectSingleNode("s:Header/wsa:Action", ns);
// process a Get response
if (node == null)
{
ns.AddNamespace("wsmid", "http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd");
node = document.DocumentElement.SelectSingleNode("s:Body/wsmid:IdentifyResponse", ns);
if (node != null)
result = new ManagedInstance(this, node.OuterXml);
}
else if (node.InnerText != null && node.InnerText.Equals(WsmanResources.GET_RESPONSE))
{
node = document.DocumentElement.SelectSingleNode("s:Body", ns);
if (node != null)
{
node = node.FirstChild;
if (node != null)
{
while (node != null && node.NodeType != XmlNodeType.Element)
node = node.NextSibling;
result = new ManagedInstance(this, node.OuterXml);
}
}
}
else if (node.InnerText != null && node.InnerText.Equals(WsmanResources.ENUM_RESPONSE))
{
// create a Wsman Pull document
XmlDocument pullDoc = new XmlDocument();
pullDoc.LoadXml(WsmanResources.PULL_XML);
XmlNamespaceManager pullNs = LoadNamespaces(pullDoc);
pullNs.AddNamespace("n", WsmanResources.ENUM_NS);
ns.AddNamespace("n", WsmanResources.ENUM_NS);
// set the EPR
SetEpr(refObj, pullDoc, pullNs);
// Set the enumeration context
node = document.DocumentElement.SelectSingleNode("s:Body/n:EnumerateResponse/n:EnumerationContext", ns);
XmlNode pullNode = pullDoc.DocumentElement.SelectSingleNode("s:Body/n:Pull/n:EnumerationContext", pullNs);
if (pullNode != null && node != null)
pullNode.InnerText = node.InnerText;
//set Max elements
pullNode = pullDoc.DocumentElement.SelectSingleNode("s:Body/n:Pull/n:MaxElements", pullNs);
if (pullNode != null)
pullNode.InnerText = Options.MaxElements.ToString();
result = new WsmanEnumeration(this, pullDoc, pullNs);
}
else if (node.InnerText != null && node.InnerText.Equals(WsmanResources.PULL_RESPONSE))
{
result = document;
}
else if (node.InnerText != null && node.InnerText.Equals(WsmanResources.CREATE_RESPONSE))
{
node = document.DocumentElement.SelectSingleNode("s:Body/wxf:ResourceCreated", ns);
if (node != null)
result = ManagedReference.GetEmbeddedRef(this, node, ns);
}
else if (node.InnerText != null && node.InnerText.Equals(WsmanResources.SUBSCRIBE_RESPONSE))
{
node = document.DocumentElement.SelectSingleNode("s:Body/wse:SubscribeResponse/wse:SubscriptionManager", ns);
if (node != null)
result = ManagedReference.GetEmbeddedRef(this, node, ns);
}
else
{
node = document.DocumentElement.SelectSingleNode("s:Body", ns);
if (node != null)
result = new ManagedInstance(this, node.InnerXml);
}
return result;
}
private static XmlNamespaceManager LoadNamespaces(XmlDocument document)
{
XmlNamespaceManager ns = new XmlNamespaceManager(document.NameTable);
ns.AddNamespace("wsa", WsmanResources.ADDRESSING_NS);
ns.AddNamespace("wsman", WsmanResources.WSMAN_NS);
ns.AddNamespace("s", WsmanResources.SOAP_NS);
ns.AddNamespace("n", WsmanResources.ENUM_NS);
ns.AddNamespace("wxf", WsmanResources.WSTRANSFER_NS);
ns.AddNamespace("wse", WsmanResources.WSEVENTING_NS);
return ns;
}
/// <summary>
/// This callback is used to allow the connection even if the certificate has errors (like hostname mismatch etc)
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="errors"></param>
/// <returns></returns>
private bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors errors)
{
//raise the event only once
if (allowCertificateError == null)
{
allowCertificateError = Options.ServerCertificateValidationCallback(certificate, errors);
}
return allowCertificateError.Value;
}
private bool SelfSignedCertificateCallback(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors error)
{
//If certificate is self signed, ignore all errors
if (certificate.Subject.Equals(certificate.Issuer) && Options.AcceptSelfSignedCertificate)
{
return true;
}
if (error == SslPolicyErrors.None)
{
return true;
}
return false;
}
#endregion //Private Methods
} // end WsmanConnection Object
}//end Intel.Management.Wsman namespace