1091 lines
42 KiB
C#
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
|
|
}
|
|
}
|