using System;
using System.Net.Sockets;
using System.Net;
namespace AmtScanner.Api.Services
{
///
/// AMT platform data. e.g. provisioning state.
///
public struct PlatformData
{
///
/// The provisioning state of the AMT machine.
///
public ProvisioningState ProvisioningState { get; internal set; }
///
/// The major version of the AMT machine.
///
public uint MajorVersion { get; internal set; }
///
/// The minor version of the AMT machine.
///
public uint MinorVersion { get; internal set; }
}
///
/// The provisioning state of the AMT machine.
///
public enum ProvisioningState
{
Pre = 0,
In = 1,
Post = 2,
Unknown
}
public class RMCPPingApi
{
private const int RMCP_PORT = 623;
private const int RMCP_HEADER_SIZE = 4;
private const int ASF_RMCP_DATA_MINIMUM_SIZE = 8;
private const int RMCP_RESENCE_PONG_DATA_SIZE = 16;
///
/// 获取AMT平台数据,支持自定义超时
///
public static PlatformData GetPlatformData(string address, int timeoutMs = 5000)
{
return GetPlatformDataByRMCPPing(address, timeoutMs);
}
private static PlatformData GetPlatformDataByRMCPPing(string address, int timeoutMs)
{
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;
using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
try
{
// 按照SDK的方式,先用IPEndPoint连接,再用地址和端口连接
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(address), RMCP_PORT);
clientSocket.Connect(ipEndPoint as EndPoint);
clientSocket.Connect(address, RMCP_PORT);
byte[] rmcpPingPacketData = GetRMCPPingPacketData();
clientSocket.Send(rmcpPingPacketData, 0, rmcpPingPacketData.Length, SocketFlags.None);
clientSocket.ReceiveTimeout = timeoutMs;
byte[] byteData = new byte[4];
clientSocket.Receive(byteData, 0, byteData.Length, SocketFlags.None);
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];
clientSocket.Receive(pongBuffer, 0, pongBuffer.Length, SocketFlags.None);
if ((pongBuffer[12] == 0) && (pongBuffer[13] == 0) &&
(pongBuffer[14] == 0x01) && (pongBuffer[15] == 0x57))
{
ushort dashSupportByteVersionB = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + DASH_SUPPORT_BYTE_OFFSET_DSP_0232_100B];
if (0 != (dashSupportByteVersionB & DASH_SUPPORT_INDICATING_BIT))
{
return ParsePlatformDataFromRMCPPing(pongBuffer);
}
}
}
throw new Exception("Not an AMT device");
}
catch (Exception ex)
{
throw new Exception($"Failed to get platform data: {ex.Message}", ex);
}
finally
{
clientSocket.Close();
}
}
}
private static PlatformData ParsePlatformDataFromRMCPPing(byte[] pongBuffer)
{
const int CONFIGURATION_STATE_BYTE = 7;
const int VERSION_BYTE = 6;
const int UPDATED_VERSION_BYTE = 10;
PlatformData platformData = new PlatformData();
if (pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] == 0)
{
platformData.MinorVersion = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + UPDATED_VERSION_BYTE + 1];
platformData.MajorVersion = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + UPDATED_VERSION_BYTE];
}
else
{
platformData.MinorVersion = (uint)(pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] & 0xf);
platformData.MajorVersion = (uint)((pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] & 0xf0) >> 4);
}
platformData.ProvisioningState = (ProvisioningState)(pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + CONFIGURATION_STATE_BYTE] & 0x3);
return platformData;
}
private static byte[] GetRMCPPingPacketData()
{
return new byte[]
{
0x06, 0x00, 0x00, 0x06, // RMCP Header
0x00, 0x00, 0x11, 0xBE, // ASF IANA
0x80, 0x00, 0x00, 0x00 // Presence Ping
};
}
}
}