1091 lines
42 KiB
C#

//----------------------------------------------------------------------------
//
// Copyright (c) Intel Corporation, 2011 - 2013 All Rights Reserved.
//
// File: ConnectionInfoEX.cs
//
// Contents: High Level API: connection Info.
//
// Notes:
//
//----------------------------------------------------------------------------
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Security;
using Intel.Manageability.Exceptions;
using Intel.Manageability.Heci;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using HLAPI.Wireless;
using System.Threading;
// This class replaces the ConnectionInfo class.
// A new user of the HLAPI will have to use this connection object.
// Anyway, the IWSManClient.dll and the DotNetWSManClient.dll are still needed
// because the DotNetWSManClient is part of the AMTInstance API.
// This connection info object supports Secure password and TCP forwarding.
namespace Intel.Manageability
{
/// <summary>
/// The ConnectionInfoEX is an extension of ConnectionInfo.
/// it supports Secure password and TCP forwarding.
/// </summary>
public class ConnectionInfoEX : IDisposable
{
#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;
public const int MIN_USERNAME_LENGTH = 1;
// Max timeout is 100 seconds. This is the default timeout for httpClient timeout
// (https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=netframework-4.8)
private const int MAX_TIME_OUT = 100000;
private const int DEFAULT_TIME_OUT = 30000;
#endregion
#region Enums
/// <summary>
/// Enum that represents the authentication type
/// </summary>
public enum AuthMethod
{
/// <summary>
/// HTTP Digest access authentication
/// </summary>
Digest,
/// <summary>
/// HTTP Kerberos access authentication
/// </summary>
Kerberos
}
#endregion
#region classes
/// <summary>
/// Class that represents a SOCKS proxy.
/// </summary>
public class SocksProxy : IDisposable
{
#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;
public const int MIN_USERNAME_LENGTH = 1;
internal const int MAX_PORT_NUMBER = 65535;
#endregion
#region members
/// <summary>
/// If true, the HLAPI will request the AMT to drop the connection to the proxy
/// before invoking dispose.
/// </summary>
public bool DropAtEnd { get; set; }
/// <summary>
/// Hostname / IP of the machine to connect.
/// </summary>
private string host;
public string Host
{
get
{
return host;
}
set
{
//Validate IP address
if (Uri.CheckHostName(value) == UriHostNameType.Unknown)
throw new ManageabilityIllegalInputException("Argument error - Unknown Host name");
host = value;
}
}
/// <summary>
/// Port of the machine to connect.
/// </summary>
private int port;
public int Port
{
get
{
return port;
}
set
{
if (value < 0 || value > MAX_PORT_NUMBER)
throw new ManageabilityIllegalInputException("Argument error - Invalid port number");
port = value;
}
}
/// <summary>
/// User name of the machine to connect.
/// </summary>
private string userName;
public string UserName
{
get
{
return userName;
}
set
{
if (string.IsNullOrEmpty(value))
userName = "";
else
{
if (userName.Length > MAX_USERNAME_LENGTH)
throw new ManageabilityIllegalInputException("Invalid Argument - Username can contain up to 32 characters.");
userName = value;
}
}
}
/// <summary>
/// Password of the machine to connect as SecureString.
/// </summary>
private SecureString password;
/// <summary>
/// Password of the machine to connect.
/// </summary>
public SecureString Password
{
get
{
return password;
}
set
{
if (value == null || value.Length == 0)
{
password?.Dispose();
password = new SecureString();
}
else
{
if (value.Length < MIN_PWD_LENGTH || value.Length > MAX_PWD_LENGTH)
throw new ManageabilityIllegalInputException("invalid password, password must contain between 8 to 32 characters");
password?.Dispose();
password = value;
}
}
}
#endregion
#region constructor
/// <summary>
/// Initialize a new instance of SocksProxy
/// </summary>
/// <param name="host">Hostname / IP of the machine to connect.</param>
/// <param name="port">Port of the machine to connect.</param>
/// <param name="userName">UserName of the machine to connect.</param>
/// <param name="password">Password of the machine to connect.</param>
public SocksProxy(string host, int port, string userName, SecureString password)
{
Host = host;
Port = port;
UserName = userName;
Password = password;
}
#endregion
#region Idisposible
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Password?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~SocksProxy()
{
Dispose(false);
}
#endregion
}
/// <summary>
/// Class that represents overriding parameters.
/// </summary>
public struct TcpForwarder
{
/// <summary>
/// The forwarder's hostname.
/// </summary>
private string host;
public string Host
{
get
{
return host;
}
set
{
//Validate host name
if (Uri.CheckHostName(value) == UriHostNameType.Unknown)
throw new ManageabilityIllegalInputException("Argument error - Unknown Host name");
host = value;
}
}
/// <summary>
/// The overriding port to use for SOAP/WSMAN.
/// </summary>
private int mainPort;
public int MainPort
{
get
{
return mainPort;
}
set
{
if (value < 0 || value > SocksProxy.MAX_PORT_NUMBER)
throw new ManageabilityIllegalInputException("Argument error - Invalid port number");
mainPort = value;
}
}
/// <summary>
/// The overriding port to use for redirection.
/// </summary>
private int redirectionPort;
public int RedirectPort
{
get
{
return redirectionPort;
}
set
{
if (value < 0 || value > SocksProxy.MAX_PORT_NUMBER)
throw new ManageabilityIllegalInputException("Argument error - Invalid port number");
redirectionPort = value;
}
}
/// <summary>
/// The overriding port to use for RFB for KVM.
/// </summary>
private int rfbPort;
public int RFBPort
{
get
{
return rfbPort;
}
set
{
if (value < 0 || value > SocksProxy.MAX_PORT_NUMBER)
throw new ManageabilityIllegalInputException("Argument error - Invalid port number");
rfbPort = value;
}
}
/// <summary>
/// Initialize a new instance of TcpForwarder.
/// </summary>
/// <param name="host">The forwarder's hostname.</param>
/// <param name="mainPort">The overriding port to use for SOAP/WSMAN.</param>
/// <param name="redirectPort">The overriding port to use to redirection.</param>
/// <param name="rfbPort">The overriding port to use for RFB.</param>
public TcpForwarder(string host, int mainPort, int redirectPort, int rfbPort)
: this()
{
Host = host;
MainPort = mainPort;
RedirectPort = redirectPort;
RFBPort = rfbPort;
}
}
#endregion
/// <summary>
/// Verifies the remote server certificate used for authentication.
/// </summary>
/// <param name="certificate">The certificate used to authenticate</param>
/// <param name="chain">The certificate chain.</param>
/// <param name="errors">One or more errors associated with the remote certificate.</param>
/// <returns>bool, determines whether the specified certificate is accepted for authentication.
/// accept the certificate when there is an error is less secure.</returns>
public delegate bool ServerCertificateValidationCallback(X509Certificate certificate, X509Chain chain,
SslPolicyErrors errors);
#region Constructor
/// <summary>
/// Initialize a new instance of ConnectionInfoEX.
/// </summary>
public ConnectionInfoEX()
{
}
/// <summary>
/// Initialize a new instance of ConnectionInfoEX.
/// </summary>
/// <param name="host">Hostname / IP of the machine to connect.</param>
/// <param name="userName">User name of the machine to connect.</param>
/// <param name="password">Password of the machine to connect.</param>
/// <param name="secure">Indicates whether or not to use TLS in the connection.</param>
/// <param name="certificate">Certificate name (as it appears in the subject field of the certificate).</param>
/// <param name="auth">Enum that represents the authentication type.</param>
/// <param name="proxy">Indicates whether or not to use a proxy in the connection.</param>
/// <param name="redirectionProxy">Indicates whether to use a SOCKS proxy.</param>
/// <param name="tcpForwarder">Indicates whether to use TcpForwarder.</param>
/// <param name="acceptSelfSignedCertificate">Accept Self-signed certificate for TLS connection</param>
public ConnectionInfoEX(string host, string userName, SecureString password, bool secure, string certificate,
AuthMethod auth, SocksProxy proxy, SocksProxy redirectionProxy, TcpForwarder? tcpForwarder,
bool acceptSelfSignedCertificate = false)
: this()
{
Host = host;
Auth = auth;
UserName = userName;
Password = password;
Secure = secure;
Certificate = certificate;
Proxy = proxy;
RedirectionProxy = redirectionProxy;
Forwarder = tcpForwarder;
Timeout = DEFAULT_TIME_OUT;
AcceptSelfSignedCertificate = acceptSelfSignedCertificate;
if (acceptSelfSignedCertificate) { CertificateValidationCallback = SelfSignedCertificateCallback; }
}
/// <summary>
/// Initialize a new instance of ConnectionInfoEX.
/// </summary>
/// <param name="host">Hostname / IP of the machine to connect.</param>
/// <param name="userName">User name of the machine to connect.</param>
/// <param name="password">Digest Master Password in base 64 string.</param>
/// <param name="secure">Indicates whether or not to use TLS in the connection.</param>
/// <param name="certificate">Certificate name (as it appears in the subject field of the certificate).</param>
/// <param name="auth">Enum that represents the authentication type.</param>
/// <param name="proxy">Indicates whether or not to use a proxy in the connection.</param>
/// <param name="redirectionProxy">Indicates whether to use a SOCKS proxy.</param>
/// <param name="tcpForwarder">Indicates whether to use TcpForwarder.</param>
/// <param name="digestMasterPassword">Indicate whether or not to use DMP in the connection.</param>
/// <param name="acceptSelfSignedCertificate">Accept self-Signed certificate for TLS connection</param>
public ConnectionInfoEX(string host, string userName, SecureString password, bool secure, string certificate,
AuthMethod auth, SocksProxy proxy, SocksProxy redirectionProxy, TcpForwarder? tcpForwarder,
bool digestMasterPassword, bool acceptSelfSignedCertificate = false)
: this()
{
Host = host;
Auth = auth;
UserName = userName;
Password = password;
Secure = secure;
Certificate = certificate;
Proxy = proxy;
RedirectionProxy = redirectionProxy;
Forwarder = tcpForwarder;
UseDigestMasterPassword = digestMasterPassword;
Timeout = DEFAULT_TIME_OUT;
AcceptSelfSignedCertificate = acceptSelfSignedCertificate;
if (acceptSelfSignedCertificate) { CertificateValidationCallback = SelfSignedCertificateCallback; }
}
/// <summary>
/// Initialize a new instance of ConnectionInfoEX.
/// </summary>
/// <param name="host">Hostname / IP of the machine to connect.</param>
/// <param name="userName">User name of the machine to connect.</param>
/// <param name="password">Password of the machine to connect.</param>
/// <param name="secure">Indicates whether or not to use TLS in the connection.</param>
/// <param name="certificate">Certificate name (as it appears in the subject field of the certificate).</param>
/// <param name="auth">Enum that represents the authentication type.</param>
/// <param name="proxy">Indicates whether or not to use a proxy in the connection.</param>
/// <param name="redirectionProxy">Indicates whether to use a SOCKS proxy.</param>
/// <param name="tcpForwarder">Indicates whether to use TcpForwarder.</param>
/// <param name="timeout">Sets the time-out value, in milliseconds.</param>
/// <param name="acceptSelfSignedCertificate">Accept Self-signed certificate for TLS connection</param>
public ConnectionInfoEX(string host, string userName, SecureString password, bool secure, string certificate,
AuthMethod auth, SocksProxy proxy, SocksProxy redirectionProxy, TcpForwarder? tcpForwarder, int timeout,
bool acceptSelfSignedCertificate = false)
: this()
{
Host = host;
Auth = auth;
UserName = userName;
Password = password;
Secure = secure;
Certificate = certificate;
Proxy = proxy;
RedirectionProxy = redirectionProxy;
Forwarder = tcpForwarder;
Timeout = timeout;
AcceptSelfSignedCertificate = acceptSelfSignedCertificate;
if (acceptSelfSignedCertificate) { CertificateValidationCallback = SelfSignedCertificateCallback; }
}
/// <summary>
/// Initialize a new instance of ConnectionInfoEX.
/// </summary>
/// <param name="host">Hostname / IP of the machine to connect.</param>
/// <param name="userName">User name of the machine to connect.</param>
/// <param name="password">Digest Master Password in base 64 string.</param>
/// <param name="secure">Indicates whether or not to use TLS in the connection.</param>
/// <param name="certificate">Certificate name (as it appears in the subject field of the certificate).</param>
/// <param name="auth">Enum that represents the authentication type.</param>
/// <param name="proxy">Indicates whether or not to use a proxy in the connection.</param>
/// <param name="redirectionProxy">Indicates whether to use a SOCKS proxy.</param>
/// <param name="tcpForwarder">Indicates whether to use TcpForwarder.</param>
/// <param name="digestMasterPassword">Indicate whether or not to use DMP in the connection.</param>
/// <param name="timeout">Sets the time-out value, in milliseconds.</param>
/// <param name="acceptSelfSignedCertificate">Accept Self-signed certificate for TLS connection</param>
public ConnectionInfoEX(string host, string userName, SecureString password, bool secure, string certificate,
AuthMethod auth, SocksProxy proxy, SocksProxy redirectionProxy, TcpForwarder? tcpForwarder,
bool digestMasterPassword, int timeout, bool acceptSelfSignedCertificate = false)
: this()
{
Host = host;
Auth = auth;
UserName = userName;
Password = password;
Secure = secure;
Certificate = certificate;
Proxy = proxy;
RedirectionProxy = redirectionProxy;
Forwarder = tcpForwarder;
UseDigestMasterPassword = digestMasterPassword;
Timeout = timeout;
AcceptSelfSignedCertificate = acceptSelfSignedCertificate;
if (acceptSelfSignedCertificate) { CertificateValidationCallback = SelfSignedCertificateCallback; }
}
#endregion
#region Public Members
///// <summary>
///// Gets or sets the callback for custom validation by the client of the server certificate.
///// In WSMan Commands all errors are supported, in Redirection command only NameMismatch error is supported.
///// </summary>
public ServerCertificateValidationCallback CertificateValidationCallback { set; get; }
/// <summary>
/// Hostname / IP of the machine to connect.
/// </summary>
private string _host;
public string Host {
get
{
return _host;
}
set
{
//Validate IP address
if (Uri.CheckHostName(value) == UriHostNameType.Unknown)
throw new ManageabilityIllegalInputException("Invalid Argument - Unknown Host name.");
_host = value;
}
}
private string _userName;
/// <summary>
/// User name of the machine to connect.
/// </summary>
public string UserName
{
get
{
return _userName;
}
set
{
if (String.IsNullOrEmpty(value))
{
//Accepting null value for Kerberos connection (setting value to empty string)
if (Auth == AuthMethod.Kerberos)
_userName = "";
else
throw new ManageabilityIllegalInputException("Invalid Argument - Username is null or an empty string.");
}
else
{
_userName = value;
if (_userName.Length > MAX_USERNAME_LENGTH)
throw new ManageabilityIllegalInputException("Invalid Argument - Username can contain up to 32 characters.");
}
}
}
private int _timeout;
/// <summary>
/// Connection time-out value (in milliseconds)
/// </summary>
public int Timeout
{
set
{
_timeout = value > MAX_TIME_OUT ? MAX_TIME_OUT : value;
}
get
{
return _timeout;
}
}
/// <summary>
/// Password of the machine to connect as SecureString.
/// </summary>
private SecureString _password;
/// <summary>
/// Password of the machine to connect.
/// </summary>
public SecureString Password
{
get
{
return _password;
}
set
{
if (value == null || value.Length == 0)
{
//Accepting null value for Kerberos connection (setting value to empty string)
if (Auth == AuthMethod.Kerberos)
{
_password?.Dispose();
_password = new SecureString();
}
else
throw new ManageabilityIllegalInputException("Invalid Argument - Password is null or an empty string.");
}
else
{
if (value.Length < MIN_PWD_LENGTH || value.Length > MAX_PWD_LENGTH)
throw new ManageabilityIllegalInputException("invalid password, password must contain between 8 to 32 characters");
_password?.Dispose();
_password = value;
}
}
}
/// <summary>
/// Indicates whether or not to use TLS in the connection.
/// </summary>
public bool Secure { set; get; }
/// <summary>
/// Indicates whether or not to accept self-signed certificate in a TLS connection.
/// </summary>
public bool AcceptSelfSignedCertificate { set; get; }
/// <summary>
/// Certificate name (as it appears in the subject field of the certificate)
/// </summary>
private string _certificate;
public string Certificate {
get
{
return _certificate;
}
set
{
if (value == null)
_certificate = "";
else
_certificate = value;
// In the common name field of the CN of a X509 certificate, , the limit is up to 64 characters
if (_certificate.Length > 64)
throw new ManageabilityIllegalInputException("Argument error - Invalid certificate");
}
}
/// <summary>
/// Enum that represents the authentication type
/// </summary>
public AuthMethod Auth { set; get; }
/// <summary>
/// Indicates whether or not to use a proxy in the connection.
/// </summary>
public SocksProxy Proxy {set; get; }
/// <summary>
/// Use a proxy. Needed for redirection connections.
/// </summary>
public SocksProxy RedirectionProxy { get; set; }
/// <summary>
/// Use TCP forwarding parameters. Needed for redirected connections.
/// </summary>
public TcpForwarder? Forwarder { get; set; }
/// <summary>
/// Indicates whether the inserted password is Digest Master Password or not.
/// </summary>
public bool UseDigestMasterPassword { get; set; }
#endregion
#region Public Functions
/// <summary>
/// Get information about the AMT machine.
/// Supported by RMCPPing from Intel AMT 6.0 and later.
/// </summary>
/// <returns>Information about the AMT machine, e.g. provisioning state, AMT version.</returns>
public PlatformData GetPlatformData()
{
try
{
// Check if the host name is local machine and get platform data by HECI.
if (true == Utils.Utils.IsLocalHost(this.Host))
{
return GetPlatformDataByHECI();
}
else
{
// If machine is not local - get platform data by rmcp ping.
return GetPlatformDataByRMCPPing(this.Host.ToString());
}
}
catch
{
// If operation failed - the provisioning state is unknown.
PlatformData platformData = new PlatformData();
platformData.ProvisioningState = ProvisioningState.Unknown;
return platformData;
}
}
/// Gets information about the AMT machine.
#endregion
#region Private Functions
private static PlatformData GetPlatformDataByHECI()
{
PlatformData platformData = new PlatformData();
// Initialize new instance of HECI commands.
PTHICommand heciCommand = new PTHICommand();
// Initialize the local HECI properties.
heciCommand.Init();
PTHICommand.ProvisioningState state;
// Get the provisioning state with HECI.
heciCommand.GetProvisioningState(out state);
// Parse the returned value to HLAPI value, and insert to platformData.provisioningState.
platformData.ProvisioningState = ParseHECIProvisiongState(state);
PTHICommand.AMT_VERSION_TYPE[] versions;
string BIOS;
// Get AMT's versions with HECI.
// (List of versions for all firmware components as well as the version of the BIOS.)
if (heciCommand.GetCodeVersions(out BIOS, out versions) == true)
{
// Run on the version list.
foreach (PTHICommand.AMT_VERSION_TYPE version in versions)
{
// If the version is the Recovery Version (e.g. AMT version), keep the version in the struct platform data.
if (version.Description.Text == "Recovery Version")
{
// AMT's version string in format - <major version>.<minor version>.<revision version>
string[] amtVersions = version.Version.Text.Split('.');
// Keep the major version, which is located after the major version.
platformData.MajorVersion = uint.Parse(amtVersions[0]);
// Keep the minor version, which is located after the major version.
platformData.MinorVersion = uint.Parse(amtVersions[1]);
}
}
}
else
{
PTHICommand.FWU_GET_VERSION_MSG_REPLY amtVersions;
if (heciCommand.GetAMTCodeVersions(out amtVersions) == true)
{
platformData.MajorVersion = amtVersions.AMTVersion.Major;
platformData.MinorVersion = amtVersions.AMTVersion.Minor;
}
}
return platformData;
}
/// <summary>
/// Get the Platform Data by RMCPPing protocol.
/// Supported from AMT 6.0 and above.
/// In earlier versions incorrect value may be returned.
/// </summary>
/// <param name="address">The machine address</param>
/// <returns>The Data about the machine platform</returns>
private PlatformData GetPlatformDataByRMCPPing(string address)
{
#region Consts of RMCP ping format DATA.
const int RMCP_PORT = 623;
const int PONG_TIMEOUT = 5000;
const int RMCP_HEADER_SIZE = 4;
const int ASF_RMCP_DATA_MINIMUM_SIZE = 8;
const int RMCP_RESENCE_PONG_DATA_SIZE = 16;
const int DASH_SUPPORT_BYTE_OFFSET_DSP_0232_100B = 9;
const int DASH_SUPPORT_INDICATING_BIT = 32;
const int VERS_BYTE = 0;
const int RSVD_BYTE = 1;
const int SEQ_BYTE = 2;
const int CLASS_BYTE = 3;
const int VERS = 0x06;
const int RSVD = 0x0;
const int SEQ = 0x0;
const int CLASS = 0x86;
#endregion
// Using UDP sockets.
using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
try
{
// Server/AMT is listening on port 623.
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(address), RMCP_PORT);
// Connect to the endpoint .
clientSocket.Connect(ipEndPoint as EndPoint);
// Open connection with the remote host.
clientSocket.Connect(this.Host, RMCP_PORT);
byte[] rmcpPingPacketData = GetRMCPPingPacketData();
// Send request to the AMT.
clientSocket.Send(rmcpPingPacketData, 0, rmcpPingPacketData.Length, SocketFlags.None);
clientSocket.ReceiveTimeout = PONG_TIMEOUT;
byte[] byteData = new byte[4];
// Received the header.
clientSocket.Receive(byteData, 0, byteData.Length, SocketFlags.None);
// Check if the header is the requested request.
if ((byteData[VERS_BYTE] == VERS) && (byteData[RSVD_BYTE] == RSVD) && (byteData[SEQ_BYTE] == SEQ) &&
(byteData[CLASS_BYTE] == CLASS))
{
byte[] pongBuffer = new byte[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + RMCP_RESENCE_PONG_DATA_SIZE];
// Received the requested array from the AMT.
clientSocket.Receive(pongBuffer, 0, RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + RMCP_RESENCE_PONG_DATA_SIZE, SocketFlags.None);
// If found AMT machine.
// Numbers 0x01 and 0x57 in bytes 14 and 15 catenated together present the Intel Corp (343) in decimal.
if ((pongBuffer[12] == 0) && (pongBuffer[13] == 0) && (pongBuffer[14] == 0x01) && (pongBuffer[15] == 0x57))
{
// 4.0 and 5.0 platforms support the RCMP pong according to DSP0232 1.0.0a which identifies the dash bit as
// the 5th bit of the 9th byte of the pong response.
// If the AMT machine version is lower than 5.1, provisioning state and AMT version are not supported.
// 5.1 and newer platforms supports the RCMP pong according to DSP0232 1.0.0b which identifies the dash bit as
// the 5th bit of the 10th byte of the pong response
ushort dashSupportByteVersionB = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + DASH_SUPPORT_BYTE_OFFSET_DSP_0232_100B];
// In version A, get AMT provisioning state.
// When the dash supported version is B.
if (0 != (dashSupportByteVersionB & DASH_SUPPORT_INDICATING_BIT))
{
// Finish the operation if machine data has been found.
return ParsePlatformDataFromRMCPPing(pongBuffer);
}
}
}
// If the operation comes to this line, and didn't return PlatformData yet,
// means the operation failed; therefore - throw an exception.
throw new ManageabilityException("");
}
catch (Exception ex)
{
// Close the socket.
throw new ManageabilityException("Failed to get platform DATA by RMCP ping", ex);
}
finally
{
clientSocket.Close();
}
}
}
/// <summary>
/// Parse the received pong buffer to platform data fields.
/// </summary>
/// <param name="pongBuffer">Pong buffer received data.</param>
/// <returns>Parsed platform data fields.</returns>
private static PlatformData ParsePlatformDataFromRMCPPing(byte[] pongBuffer)
{
const int RMCP_HEADER_SIZE = 4;
const int ASF_RMCP_DATA_MINIMUM_SIZE = 8;
const int CONFIGURATION_STATE_BYTE = 7;
const int VERSION_BYTE = 6;
PlatformData platformData = new PlatformData();
// Minor version.
// Bits 0:3 in byte 7, contains the minor version. 15 number present the binary number 00001111.
platformData.MinorVersion =
(uint)((pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] & 0xf));
// Major version.
// Bits 4:7 in byte 7, contains the major version. 240 number present the binary number 11110000.
platformData.MajorVersion =
(uint)((pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] & 0xf0) >> 4);
// Provisioning state. (The provisioning state located in bit 0 and bit 1.)
platformData.ProvisioningState =
(ProvisioningState)(pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + CONFIGURATION_STATE_BYTE] & 0x3);
return platformData;
}
/// <summary>
/// Get byte array with the RMCP ping data.
/// </summary>
/// <returns>Byte array with the RMCP ping data.</returns>
private static byte[] GetRMCPPingPacketData()
{
const int ASF_VERSION = 0x6;
const int ASF_CLASS = 0x6;
byte[] rmcpPingPacketData = new byte[12]; // RMCP_HEADER_SIZE+ASF_RMCP_DATA_MINIMUM_SIZE
rmcpPingPacketData[0] = (byte)ASF_VERSION;
rmcpPingPacketData[1] = 0;
rmcpPingPacketData[2] = 0;
rmcpPingPacketData[3] = (byte)ASF_CLASS;
rmcpPingPacketData[4] = 0;
rmcpPingPacketData[5] = 0;
rmcpPingPacketData[6] = 0x11; // ((ASF_IANA_ENTERPRISE_NUMBER / 0x100) * 0x10000)
rmcpPingPacketData[7] = 0xBE; // ((ASF_IANA_ENTERPRISE_NUMBER % 0x100) * 0x1000000)
rmcpPingPacketData[8] = 0x80; // PRESENCE_PING_MESSAGE_TYPE
rmcpPingPacketData[9] = 0;
rmcpPingPacketData[10] = 0;
rmcpPingPacketData[11] = 0;
return rmcpPingPacketData;
}
/// <summary>
/// Parse the fields of PTHICommand.ProvisioningState to HLAPI ProvisioningState.
/// </summary>
/// <param name="state">PTHICommand.ProvisioningState requested state to parse</param>
/// <returns>Parsed state to HLAPI ProvisioningState</returns>
private static ProvisioningState ParseHECIProvisiongState(PTHICommand.ProvisioningState state)
{
switch (state)
{
case PTHICommand.ProvisioningState.In: { return ProvisioningState.In; }
case PTHICommand.ProvisioningState.Post: { return ProvisioningState.Post; }
case PTHICommand.ProvisioningState.Pre: { return ProvisioningState.Pre; }
default: { return ProvisioningState.Unknown; }
}
}
private static bool SelfSignedCertificateCallback(X509Certificate certificate, X509Chain chain, SslPolicyErrors error)
{
//If certificate is self signed, ignore all errors
if (certificate.Subject.Equals(certificate.Issuer))
{
return true;
}
if (error == SslPolicyErrors.None)
{
return true;
}
return false;
}
#endregion
#region Idisposible
// implement IDisposable method
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Password?.Dispose();
Proxy?.Dispose();
RedirectionProxy?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ConnectionInfoEX()
{
Dispose(false);
}
#endregion
}
/// <summary>
/// AMT platform data. e.g. provisioning state.
/// </summary>
public struct PlatformData
{
//The prime number 397 was suggested by ReSharper.
private const int PRIME_NUMBER = 397;
#region Members
/// <summary>
/// The provisioning state of the AMT machine.
/// </summary>
public ProvisioningState ProvisioningState { get; internal set; }
/// <summary>
/// The major version of the AMT machine.
/// </summary>
public uint MajorVersion { get; internal set; }
/// <summary>
/// The minor version of the AMT machine.
/// </summary>
public uint MinorVersion { get; internal set; }
#endregion
#region Public Functions
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
public bool Equals(PlatformData other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.ProvisioningState == ProvisioningState &&
other.MajorVersion == MajorVersion &&
other.MinorVersion == MinorVersion;
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns> A hash code for the current <see cref="T:System.Object"/></returns>
public override int GetHashCode()
{
unchecked
{
int result = ProvisioningState.GetHashCode();
result = (result * PRIME_NUMBER) ^ MajorVersion.GetHashCode();
result = (result * PRIME_NUMBER ^ MinorVersion.GetHashCode());
return result;
}
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception><filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(PlatformData)) return false;
return Equals((PlatformData)obj);
}
/// <summary>
/// Compares two Capabilities.
/// </summary>
/// <param name="left">A Capabilities instance to compare.</param>
/// <param name="right">A Capabilities instance to compare.</param>
/// <returns>true if the two Capabilities are equal; otherwise, false</returns>
public static bool operator ==(PlatformData left, PlatformData right)
{
return Equals(left, right);
}
/// <summary>
/// Compares two Capabilities.
/// </summary>
/// <param name="left">A Capabilities instance to compare.</param>
/// <param name="right">A Capabilities instance to compare.</param>
/// <returns>false if the two Capabilities are equal; otherwise, true</returns>
public static bool operator !=(PlatformData left, PlatformData right)
{
return !Equals(left, right);
}
#endregion
}
/// <summary>
/// The provisioning state of the AMT machine.
/// </summary>
public enum ProvisioningState
{
/// <summary>
/// The setup operation has not started.
/// </summary>
Pre = 0,
/// <summary>
/// The setup operation is in progress.
/// </summary>
In = 1,
/// <summary>
/// Intel(R) AMT is configured.
/// </summary>
Post = 2,
/// <summary>
/// Intel(R) AMT is unreachable by RMCP ping or HECI.
/// Can happen if AMT machine is lower than 5.1 version or the AMT machine does not exist.
/// </summary>
Unknown
}
}