Merge pull request #1 from open-amt-cloud-toolkit/q2-release

Initial commit.
This commit is contained in:
ramu bachala
2020-06-01 14:33:53 -07:00
committed by GitHub
12 changed files with 1519 additions and 1 deletions

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
build/
.vscode/*

View File

@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2010-2019 Intel Corporation
*/
/*++
@file: GetControlModeCommand.h
--*/
#ifndef __GET_CONTROL_MODE_COMMAND_H__
#define __GET_CONTROL_MODE_COMMAND_H__
#include "AMTHICommand.h"
#include "MEIparser.h"
namespace Intel
{
namespace MEI_Client
{
namespace AMTHI_Client
{
struct GET_CONTROL_MODE_RESPONSE
{
uint32_t ControlMode;
void parse (std::vector<uint8_t>::const_iterator& itr, const std::vector<uint8_t>::const_iterator end)
{
Intel::MEI_Client::parseData(*this, itr, end);
}
};
class GetControlModeRequest;
class GetControlModeCommand : public AMTHICommand
{
public:
GetControlModeCommand();
virtual ~GetControlModeCommand() {}
virtual void reTransact();
GET_CONTROL_MODE_RESPONSE getResponse();
private:
virtual void parseResponse(const std::vector<uint8_t>& buffer);
std::shared_ptr<AMTHICommandResponse<GET_CONTROL_MODE_RESPONSE>> m_response;
static const uint32_t RESPONSE_COMMAND_NUMBER = 0x0480006B;
};
class GetControlModeRequest : public AMTHICommandRequest
{
public:
GetControlModeRequest() {}
virtual ~GetControlModeRequest() {}
private:
static const uint32_t REQUEST_COMMAND_NUMBER = 0x0400006B;
virtual unsigned int requestHeaderCommandNumber()
{
//this is the command number (taken from the AMTHI document)
return REQUEST_COMMAND_NUMBER;
}
virtual uint32_t requestDataSize()
{
return 0;
}
virtual std::vector<uint8_t> SerializeData();
};
} // namespace AMTHI_Client
} // namespace MEI_Client
} // namespace Intel
#endif //__GET_CONTROL_MODE_COMMAND_H__

View File

@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2010-2019 Intel Corporation
*/
/*++
@file: GetControlModeCommand.cpp
--*/
#include "GetControlModeCommand.h"
#include "StatusCodeDefinitions.h"
#include <string.h>
using namespace std;
using namespace Intel::MEI_Client::AMTHI_Client;
GetControlModeCommand::GetControlModeCommand()
{
shared_ptr<MEICommandRequest> tmp(new GetControlModeRequest());
m_request = tmp;
Transact();
}
void GetControlModeCommand::reTransact()
{
shared_ptr<MEICommandRequest> tmp(new GetControlModeRequest());
m_request = tmp;
Transact();
}
GET_CONTROL_MODE_RESPONSE GetControlModeCommand::getResponse()
{
return m_response->getResponse();
}
void
GetControlModeCommand::parseResponse(const vector<uint8_t>& buffer)
{
shared_ptr<AMTHICommandResponse<GET_CONTROL_MODE_RESPONSE>> tmp(
new AMTHICommandResponse<GET_CONTROL_MODE_RESPONSE>(buffer, RESPONSE_COMMAND_NUMBER));
m_response = tmp;
}
std::vector<uint8_t>
GetControlModeRequest::SerializeData()
{
vector<uint8_t> output;
return output;
}

131
CMakeLists.txt Normal file
View File

@@ -0,0 +1,131 @@
cmake_minimum_required (VERSION 3.1)
project (rpc VERSION 1.0.0)
set (CMAKE_CXX_STANDARD 11)
# RPC version info
configure_file(version.h.in
version.h)
include_directories(${PROJECT_BINARY_DIR})
# TODO: figure out how to read the LMS version from repo like the main lms CMakeLists.txt
set (LMS_VERSION_STRING 1932.0.0.0)
# Compiler settings [Obtained from CmakeLists.txt for lms]
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -DDEBUG -D_DEBUG")
string(APPEND CMAKE_C_FLAGS_DEBUG " -DDEBUG -D_DEBUG")
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
if (UNIX)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -z noexecstack -z relro -z now")
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -z noexecstack -z relro -z now")
#CMake issue #14983
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie")
#Secure library usage and secure compile flags
add_definitions (-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -Wformat -Wformat-security)
add_definitions (-fno-strict-overflow -fno-delete-null-pointer-checks -fwrapv)
else (UNIX)
add_definitions (/GS /sdl)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase")
#add_definitions (/D UNICODE /D _UNICODE)
add_definitions (/D UNICODE /D _UNICODE /D_NO_ASYNCRTIMP /D_ASYNCRT_EXPORT /D_NO_PPLXIMP /DWIN32 /DMBCS /D_USRDLL /DCPPREST_EXCLUDE_COMPRESSION /D_WINSOCK_DEPRECATED_NO_WARNINGS)
add_compile_options ($<$<CONFIG:Release>:/O2>)
add_compile_options (/MT$<$<CONFIG:Debug>:d>)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
endif (UNIX)
# Download and unpack lms at configure time
configure_file(lms.cmake.in
lms-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lms-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lms-download )
# Add MEIClient directly to our build. This adds
# the following targets: LmsMEIClient and LIBMETEE
add_subdirectory(${CMAKE_BINARY_DIR}/lms-src/MEIClient
${CMAKE_BINARY_DIR}/lms-build/MEIClient)
add_dependencies(LmsMEIClient libmetee)
if (UNIX)
# Find threads [unix it pthreads]
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
# Find Boost
find_package(Boost COMPONENTS system REQUIRED)
# Find OpenSSL
find_package(OpenSSL)
# Download and build CppRestSDK, If GIT_TAG is changed then need to delete cpprestsdk-prefix because UPDATE_COMMAND is set to ""
include(ExternalProject)
ExternalProject_Add(cpprestsdk
GIT_REPOSITORY https://github.com/Microsoft/cpprestsdk.git
GIT_TAG v2.10.14
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=0 -DBUILD_SAMPLES=OFF -DBUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX=<SOURCE_DIR>/../../install
TEST_COMMAND ""
UPDATE_COMMAND ""
)
ExternalProject_Get_Property(cpprestsdk SOURCE_DIR)
set(CPPRESTSDK_LIBARIES ${SOURCE_DIR}/../../install/lib/)
set(CPPRESTSDK_INCLUDE_DIR ${SOURCE_DIR}/../../install/include/)
add_library(cpprest INTERFACE)
target_link_libraries(cpprest INTERFACE ${CPPRESTSDK_LIBARIES}/libcpprest.a OpenSSL::SSL OpenSSL::Crypto ${Boost_LIBRARIES} Threads::Threads)
target_include_directories(cpprest INTERFACE ${CPPRESTSDK_INCLUDE_DIR})
else (UNIX)
# CppRestSDK
find_package(cpprestsdk CONFIG REQUIRED)
endif (UNIX)
# ccu-poc
add_executable (rpc
AMTHIClient/Include/GetControlModeCommand.h
AMTHIClient/Src/GetControlModeCommand.cpp
commands.h
commands.cpp
lms.h
lms.cpp
main.cpp
)
target_include_directories(rpc PUBLIC
"AMTHIClient/Include/"
)
if (UNIX)
add_dependencies(rpc cpprestsdk)
target_link_libraries (rpc PRIVATE
LmsMEIClient
cpprest
)
else (UNIX)
target_link_libraries (rpc PRIVATE
LmsMEIClient
iphlpapi
cpprestsdk::cpprest
cpprestsdk::cpprestsdk_zlib_internal
cpprestsdk::cpprestsdk_boost_internal
cpprestsdk::cpprestsdk_brotli_internal
${Boost_LIBRARIES}
)
endif (UNIX)

View File

@@ -1 +1,66 @@
# rpc
# Remote Provisioning Client (RPC)
RPC is an application which enables remote capabilities for AMT, such as as device activation. To accomplish this, RPC communicates with the RPS (Remote Provisioning Server).
As a prerequisite, a Local Management Service (LMS) must be installed and running on the operating system.
## Linux
Steps below are for Ubuntu 18.04.
### Dependencies
- sudo apt install git cmake build-essential libboost-system-dev libboost-thread-dev libboost-random-dev libboost-regex-dev libboost-filesystem-dev libssl-dev zlib1g-dev
### Build
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release ..
- Build debug: cmake -DCMAKE_BUILD_TYPE=Debug ..
- cmake --build .
### Run
- See ./rpc --help for details.
- Example
- sudo ./rpc --url wss://localhost:8080 --profile profile1
## Windows
Steps below are for Windows 10 and Visual Studio 2019 Professional.
### Build VCPKG
Open an x64 native command prompt for Visual Studio 2019 as Administrator.
- git clone --branch 2020.01 https://github.com/microsoft/vcpkg.git
- cd vcpkg
- bootstrap-vcpkg.bat
- vcpkg integrate install
### Build C++ REST SDK
Open an x64 native tools command prompt for Visual Studio 2019.
- cd vcpkg
- vcpkg install cpprestsdk:x64-windows-static
### Build
Open an x64 native tools command prompt for Visual Studio 2019.
- mkdir build
- cd build
- cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake
- cmake --build . --config Release
- Build debug: cmake --build . --config Debug
### Run
Open a command prompt as Administrator.
- See rpc.exe --help for details.
- Example
- cd build\Release
- rpc.exe --url wss://localhost:8080 --profile profile1

499
commands.cpp Normal file
View File

@@ -0,0 +1,499 @@
/*
Copyright 2019 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "commands.h"
#ifdef _WIN32
#include <boost/asio.hpp>
#include <winsock2.h>
#include <iphlpapi.h>
#endif
#include "AMTHICommand.h"
#include "MEIClientException.h"
#include "GetUUIDCommand.h"
#include "GetLocalSystemAccountCommand.h"
#include "GetCodeVersionCommand.h"
#include "GetControlModeCommand.h"
#include "GetProvisioningStateCommand.h"
#include "GetDNSSuffixCommand.h"
#include "GetLanInterfaceSettingsCommand.h"
#include "GetCertificateHashEntryCommand.h"
#include "EnumerateHashHandlesCommand.h"
#include "MEIparser.h"
#include "version.h"
#include <boost/algorithm/string.hpp>
#include <cpprest/ws_client.h>
#include <cpprest/json.h>
#include <cpprest/streams.h>
#include <sstream>
#include <iostream>
#include <string>
#include "lms.h"
using namespace std;
using namespace Intel::MEI_Client::AMTHI_Client;
using namespace web::websockets::client;
using namespace web;
#define WORKING_BUFFER_SIZE 15000
#define MAX_TRIES 3
#ifdef _WIN32
std::string getDNSFromMAC(char *macAddress)
{
std::string dnsSuffix = "";
char dns[256];
memset(dns, 0, 256);
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
DWORD dwSize = 0;
DWORD dwRetVal = 0;
ULONG outBufLen = 0;
ULONG Iterations = 0;
outBufLen = WORKING_BUFFER_SIZE;
// get info for all adapters
do {
pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
if (pAddresses == NULL) {
cout << "dns memory error" << std::endl;
return dnsSuffix;
}
dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen);
if (dwRetVal == ERROR_BUFFER_OVERFLOW)
{
free(pAddresses);
pAddresses = NULL;
}
else
{
break;
}
Iterations++;
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
// get DNS from MAC
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
pCurrAddresses = pAddresses;
while (pCurrAddresses)
{
if (pCurrAddresses->PhysicalAddressLength != 0)
{
if (memcmp(macAddress, (char *) pCurrAddresses->PhysicalAddress, 6) == 0)
{
if (wcslen(pCurrAddresses->DnsSuffix) > 0)
{
snprintf(dns, 256, "%ws", pCurrAddresses->DnsSuffix );
break;
}
}
}
pCurrAddresses = pCurrAddresses->Next;
}
dnsSuffix = dns;
return dnsSuffix;
}
#else
std::string getDNSFromMAC(char *macAddress)
{
std::string dnsSuffix = "";
// get socket
SOCKET s = 0;
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0)
{
cout << "couldn't get socket" << endl;
return dnsSuffix;
}
// get info for all adapters
struct ifconf ifc;
memset(&ifc, 0, sizeof(ifconf));
char buffer[8192];
memset(buffer, 0, sizeof(buffer));
ifc.ifc_buf = buffer;
ifc.ifc_len = sizeof(buffer);
if(ioctl(s, SIOCGIFCONF, &ifc) < 0)
{
cout << "ioctl SIOCGIFCONF failed" << endl;
return dnsSuffix;
}
// get DNS from IP associated with MAC
struct ifreq *ifr = ifc.ifc_req;
int interfaceCount = ifc.ifc_len / sizeof(struct ifreq);
char ip[INET6_ADDRSTRLEN] = {0};
struct ifreq *item;
struct sockaddr *addr;
for(int i = 0; i < interfaceCount; i++)
{
item = &ifr[i];
addr = &(item->ifr_addr);
// get IP address
if(ioctl(s, SIOCGIFADDR, item) < 0)
{
cout << "ioctl SIOCGIFADDR failed" << endl;
continue;
}
if (inet_ntop(AF_INET, &( ((struct sockaddr_in *)addr)->sin_addr ),
ip, sizeof(ip) ) == NULL)
{
cout << "inet_ntop" << endl;
continue;
}
// get MAC address
if(ioctl(s, SIOCGIFHWADDR, item) < 0)
{
cout << "ioctl SIOCGIFHWADDR failed" << endl;
continue;
}
if (memcmp(macAddress, (char *) item->ifr_hwaddr.sa_data, 6) == 0)
{
// Get host by using the IP address which AMT device is using
struct in_addr addr = {0};
struct hostent *host;
addr.s_addr = inet_addr(ip);
host = gethostbyaddr((char *)&addr, 4, AF_INET);
if (host == NULL)
{
cout << "gethostbyaddr() failed";
return dnsSuffix;
}
// strip off the hostname to get actual domain name
int domainNameSize = 256;
char domainName[domainNameSize];
memset(domainName, 0, domainNameSize);
char *dn = strchr(host->h_name, '.');
if (dn != NULL)
{
if (domainNameSize >= strlen(dn + 1))
{
snprintf(domainName, domainNameSize, "%s", ++dn);
dnsSuffix = domainName;
}
}
}
}
return dnsSuffix;
}
#endif
json::value getCertificateHashes()
{
json::value certHashes;
vector<json::value> hashValues;
// get the hash handles
EnumerateHashHandlesCommand command;
ENUMERATE_HASH_HANDLES_RESPONSE response = command.getResponse();
vector<unsigned int>::iterator itr = response.HashHandles.begin();
vector<unsigned int>::iterator endItr = response.HashHandles.end();
for (; itr != endItr; ++itr)
{
// get each entry
GetCertificateHashEntryCommand command(*itr);
GET_CERTIFICATE_HASH_ENTRY_RESPONSE response = command.getResponse();
int hashSize;
switch (response.HashAlgorithm) {
case 0: // MD5
hashSize = 16;
break;
case 1: // SHA1
hashSize = 20;
break;
case 2: // SHA256
hashSize = 32;
break;
case 3: // SHA512
hashSize = 64;
break;
default:
hashSize = 64;
break;
}
if (response.IsActive == 1) {
string hashString;
hashString.clear();
for (int i = 0; i < hashSize; i++)
{
char hex[10];
snprintf(hex, 10, "%02x", response.CertificateHash[i]);
hashString += hex;
}
hashValues.push_back( json::value::string( utility::conversions::convertstring(hashString) ) );
}
}
return json::value::array(hashValues);
}
std::string getDNSInfo()
{
std::string dnsSuffix;
// Get interface info which AMT is using. We don't worry about wireless since
// only wired used for configuration
GetLanInterfaceSettingsCommand getLanInterfaceSettingsCommandWired(Intel::MEI_Client::AMTHI_Client::WIRED);
LAN_SETTINGS lanSettings = getLanInterfaceSettingsCommandWired.getResponse();
if (!lanSettings.Enabled)
{
cout << "error: no wired AMT interfaces enabled" << endl;
return "";
}
// Get DNS according to AMT
GetDNSSuffixCommand getDnsSuffixCommand;
dnsSuffix = getDnsSuffixCommand.getResponse();
// get DNS from OS
if (!dnsSuffix.length())
{
dnsSuffix = getDNSFromMAC((char *)&lanSettings.MacAddress);
}
return dnsSuffix;
}
string getActivateInfo(string profile)
{
utility::string_t tmp;
// Activation parameters
json::value activationParams;
// Get code version
GetCodeVersionCommand codeVersionCommand;
CODE_VERSIONS codeVersion = codeVersionCommand.getResponse();
// Additional versions
// UINT8[16] UUID;
// AMT_VERSION_TYPE Version and Description are std::string.
for (vector<AMT_VERSION_TYPE>::iterator it = codeVersion.Versions.begin(); it != codeVersion.Versions.end(); it++)
{
if (boost::iequals(it->Description, "AMT"))
{
tmp = utility::conversions::convertstring(it->Version);
activationParams[U("ver")] = json::value::string(tmp);
}
else if (boost::iequals(it->Description, "Build Number"))
{
tmp = utility::conversions::convertstring(it->Version);
activationParams[U("build")] = json::value::string(tmp);
}
else if (boost::iequals(it->Description, "Sku"))
{
tmp = utility::conversions::convertstring(it->Version);
activationParams[U("sku")] = json::value::string(tmp);
}
}
// Get UUID
GetUUIDCommand get;
GET_UUID_RESPONSE res = get.getResponse();
std::vector<json::value> UUID;
for (int i = 0; i < 16; i++)
{
UUID.push_back(json::value(res.UUID[i]));
}
activationParams[U("uuid")] = json::value::array(UUID);
// Get local system account
// User name in ASCII char-set. The string is NULL terminated. CFG_MAX_ACL_USER_LENGTH is 33
// Password in ASCII char set. From AMT 6.1 this field is in BASE64 format. The string is NULL terminated.
GetLocalSystemAccountCommand sac;
tmp = utility::conversions::convertstring(sac.getResponse().UserName);
activationParams[U("username")] = json::value::string(tmp);
tmp = utility::conversions::convertstring(sac.getResponse().Password);
activationParams[U("password")] = json::value::string(tmp);
// Get Control Mode
GetControlModeCommand controlModeCommand;
GET_CONTROL_MODE_RESPONSE controlMode = controlModeCommand.getResponse();
activationParams[U("currentMode")] = json::value::number(controlMode.ControlMode);
// Get DNS Info
tmp = utility::conversions::convertstring("");
string dnsSuffix = getDNSInfo();
if (dnsSuffix.length())
{
tmp = utility::conversions::convertstring(dnsSuffix);
}
activationParams[U("fqdn")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("PPC");
activationParams[U("client")] = json::value::string(tmp);
tmp = utility::conversions::convertstring(profile);
activationParams[U("profile")] = json::value::string(tmp);
// Get certificate hashes
activationParams[U("certHashes")] = getCertificateHashes();
// Return serialized parameters in base64
string serializedParams = utility::conversions::to_utf8string(activationParams.serialize());
#ifdef DEBUG
cout << "Activation info payload:" << serializedParams << std::endl;
#endif
return encodeBase64(serializedParams);
}
string encodeBase64(string str)
{
std::vector<unsigned char> strVector(str.begin(), str.end());
utility::string_t base64 = utility::conversions::to_base64(strVector);
string encodedString = utility::conversions::to_utf8string(base64);
return encodedString;
}
string decodeBase64(string str)
{
utility::string_t serializedData = utility::conversions::to_string_t(str);
std::vector<unsigned char> strVector = utility::conversions::from_base64(serializedData);
string decodedString(strVector.begin(), strVector.end());
return decodedString;
}
string createActivationRequest(string profile)
{
// Activation parameters
json::value request;
// placeholder stuff; will likely change
utility::string_t tmp = utility::conversions::convertstring("activation");
request[U("method")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("key");
request[U("apiKey")] = json::value::string(tmp);
tmp = utility::conversions::convertstring(PROJECT_VER);
request[U("appVersion")] = json::value::string(tmp);
tmp = utility::conversions::convertstring(PROTOCOL_VERSION);
request[U("protocolVersion")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("ok");
request[U("status")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("all\'s good!");
request[U("message")] = json::value::string(tmp);
// payload
string activationInfo = getActivateInfo(profile);
utility::string_t payload = utility::conversions::to_string_t(activationInfo);
request[U("payload")] = json::value::string(payload);
return utility::conversions::to_utf8string(request.serialize());
}
string createResponse(string payload)
{
// Activation parameters
json::value response;
// placeholder stuff; will likely change
utility::string_t tmp = utility::conversions::convertstring("response");
response[U("method")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("key");
response[U("apiKey")] = json::value::string(tmp);
tmp = utility::conversions::convertstring(PROJECT_VER);
response[U("appVersion")] = json::value::string(tmp);
tmp = utility::conversions::convertstring(PROTOCOL_VERSION);
response[U("protocolVersion")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("ok");
response[U("status")] = json::value::string(tmp);
tmp = utility::conversions::convertstring("all\'s good!");
response[U("message")] = json::value::string(tmp);
// payload
tmp = utility::conversions::convertstring( encodeBase64(payload) );
response[U("payload")] = json::value::string(tmp);
return utility::conversions::to_utf8string(response.serialize());
}
void dumpMessage(utility::string_t tmp)
{
web::json::value parsed = web::json::value::parse(tmp);
if ( !parsed.has_field(U("method")) || !parsed.has_field(U("apiKey")) || !parsed.has_field(U("appVersion")) ||
!parsed.has_field(U("protocolVersion")) || !parsed.has_field(U("status")) || !parsed.has_field(U("message")) ||
!parsed.has_field(U("payload")) ) {
cout << "error: dumpMessage message is empty" << endl;
return;
}
utility::string_t out = parsed[U("method")].as_string();
cout << "method: " << utility::conversions::to_utf8string(out) << endl;
out = parsed[U("apiKey")].as_string();
cout << "apiKey: " << utility::conversions::to_utf8string(out) << endl;
out = parsed[U("appVersion")].as_string();
cout << "appVersion: " << utility::conversions::to_utf8string(out) << endl;
out = parsed[U("protocolVersion")].as_string();
cout << "protocolVersion: " << utility::conversions::to_utf8string(out) << endl;
out = parsed[U("status")].as_string();
cout << "status: " << utility::conversions::to_utf8string(out) << endl;
out = parsed[U("message")].as_string();
cout << "message: " << utility::conversions::to_utf8string(out) << endl;
out = parsed[U("payload")].as_string();
cout << "payload: " << utility::conversions::to_utf8string(out) << endl;
}

48
commands.h Normal file
View File

@@ -0,0 +1,48 @@
/*
Copyright 2019 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __COMMANDS_H__
#define __COMMANDS_H__
#include <iostream>
#include <string>
#include <cpprest/ws_client.h>
#include <cpprest/json.h>
#include <cpprest/streams.h>
using namespace std;
using namespace web::websockets::client;
using namespace web;
#define PROTOCOL_VERSION "2.0.0"
#ifdef _WIN32
#define convertstring to_utf16string
#else
#define convertstring to_utf8string
#endif
string getDNSInfo();
string createActivationRequest(string profile);
json::value getCertificateHashes();
string createResponse(string payload);
string getActivateInfo(string profile);
string encodeBase64(string str);
string decodeBase64(string str);
void dumpMessage(string tmp);
#endif

15
lms.cmake.in Normal file
View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.1)
project(lms-download NONE)
include(ExternalProject)
ExternalProject_Add(lms
GIT_REPOSITORY https://github.com/intel/lms.git
GIT_TAG v1932.0.0.0
SOURCE_DIR "${CMAKE_BINARY_DIR}/lms-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/lms-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

88
lms.cpp Normal file
View File

@@ -0,0 +1,88 @@
/*
Copyright 2019 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "lms.h"
#include <string.h>
#ifdef _WIN32
// Windows
#include <Ws2tcpip.h>
#else
// Linux
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
SOCKET lmsConnect()
{
std::string lmsAddress = "localhost";
std::string lmsPort = "16992";
SOCKET s = INVALID_SOCKET;
struct addrinfo *addr, hints;
#ifdef _WIN32
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
throw std::runtime_error("error: unable to connect to LMS");
}
#endif
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(lmsAddress.c_str(), lmsPort.c_str(), &hints, &addr) != 0)
{
throw std::runtime_error("error: unable to connect to LMS");
}
if (addr == NULL)
{
throw std::runtime_error("error: unable to connect to LMS");
}
for (addr; addr != NULL; addr = addr->ai_next)
{
s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (s == INVALID_SOCKET)
{
continue;
}
if (connect(s, addr->ai_addr, (int)addr->ai_addrlen) == 0)
{
break;
}
closesocket(s);
s = INVALID_SOCKET;
}
if (addr != NULL)
{
freeaddrinfo(addr);
}
if (s == INVALID_SOCKET)
{
throw std::runtime_error("error: unable to connect to LMS");
}
return s;
}

44
lms.h Normal file
View File

@@ -0,0 +1,44 @@
/*
Copyright 2019 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __LMS_H__
#define __LMS_H__
#include <iostream>
#include <string>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <unistd.h>
typedef int SOCKET;
#define INVALID_SOCKET (SOCKET)(-1)
#endif
#ifdef _WIN32
// Windows
#else
// Linux
static inline int closesocket(int fd)
{
return close(fd);
}
#define SD_BOTH SHUT_RDWR
#endif
SOCKET lmsConnect();
#endif

480
main.cpp Normal file
View File

@@ -0,0 +1,480 @@
/*
Copyright 2019 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <cpprest/ws_client.h>
#include <cpprest/streams.h>
#include <iostream>
#include <string>
#include <thread>
#include <boost/algorithm/string.hpp>
#include "commands.h"
#include "lms.h"
#include "version.h"
using namespace std;
using namespace web;
using namespace web::websockets::client;
#include <cpprest/json.h>
void showUsage();
string websocket_address = "";
string server_profile = "";
string websocket_proxy = "";
long long timeoutTimer = 0;
const int MAX_TIMEOUT = 10; // seconds
bool timeoutThreadAlive = true;
void timeoutFunc(std::condition_variable *cv, std::mutex *mx)
{
while (timeoutThreadAlive)
{
std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
long long currTime = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
if (currTime > timeoutTimer)
{
if (currTime - timeoutTimer >= MAX_TIMEOUT)
{
cv->notify_all();
// check if timeoutTimer is not 0 since we explicitly set to zero when an
// activation is successfull. If it's not zero, we are in a time out scenario.
if (timeoutTimer)
{
cout << endl << "Timed-out due to inactivity." <<endl;
}
break;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
}
int main(int argc, char *argv[])
{
string activationInfo;
bool gotURL = false;
bool gotProfile = false;
bool gotProxy = false;
bool gotDns = false;
if (argc==1)
{
std::cout << "Use --h, --help for help." << std::endl;
return 0;
}
for (int i=1; i<argc; i++)
{
if ( (boost::equals(argv[i], "--help")) || (boost::equals(argv[i], "--h")) )
{
showUsage();
return 0;
}
}
for (int i=1; i<argc; i++)
{
if ( (boost::equals(argv[i], "--url")) || (boost::equals(argv[i], "--u")) )
{
if (i+1<argc)
{
gotURL = true;
websocket_address = argv[++i];
}
}
else if ( (boost::equals(argv[i], "--profile")) || (boost::equals(argv[i], "--p")) )
{
if (i+1<argc)
{
gotProfile = true;
server_profile = argv[++i];
}
}
else if ( (boost::equals(argv[i], "--proxy")) ||(boost::equals(argv[i], "--x")) )
{
if (i+1<argc)
{
gotProxy = true;
websocket_proxy = argv[++i];
}
}
else
{
std::cout << "Unrecognized command line arguments." << std::endl;
std::cout << "Use --h, --help for help." << std::endl;
return 0;
}
}
if (!gotURL || !gotProfile)
{
std::cout << "Incorrect or missing arguments." << std::endl;
std::cout << "Use --h, --help for help." << std::endl;
return 0;
}
// Print version info
cout << PROJECT_NAME << " v" PROJECT_VER << endl;
try {
// Get activation info
activationInfo = createActivationRequest(server_profile);
}
catch (...)
{
std::cerr << endl << "Unable to get activation info. Check AMT configuration." << endl;
return 1;
}
try
{
// Check if LMS is available
SOCKET s = lmsConnect();
closesocket(s);
}
catch (...)
{
std::cerr << endl << "Unable to connect to Local Management Service (LMS). Please ensure LMS is running." << endl;
return 1;
}
// Show activation info
#ifdef DEBUG
cout << "Activation info: " << endl << activationInfo << endl;
#endif
// WebSocket Interface
websocket_client_config client_config;
if (!websocket_proxy.empty())
{
client_config.set_proxy(web::web_proxy(utility::conversions::to_string_t(websocket_proxy)));
}
#ifdef DEBUG
// skip certificate verification if debug build
cout << "!!! SKIPPING CERTIFICATE VERIFICATION !!!" << endl;
client_config.set_validate_certificates(false);
#endif
websocket_callback_client client(client_config);
std::condition_variable cv;
std::mutex mx;
SOCKET s;
// set receive handler
client.set_message_handler([&client, &mx, &cv, &s](websocket_incoming_message ret_msg)
{
// kick the timer
std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
timeoutTimer = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
try
{
// handle message from server...
string rcv_websocket_msg = ret_msg.extract_string().get();
#ifdef DEBUG
cout << endl << "<<<<< Received Message " << endl;
cout << rcv_websocket_msg << endl;
#endif
cout << "." << std::flush; // dot status output
// parse incoming JSON message
utility::string_t tmp = utility::conversions::convertstring(rcv_websocket_msg);
web::json::value parsed = web::json::value::parse(tmp);
utility::string_t out = U("");
string msgMethod = "";
string msgApiKey = "";
string msgAppVersion = "";
string msgProtocolVersion = "";
string msgStatus = "";
string msgMessage = "";
string msgPayload = "";
string payloadDecoded = "";
if ( !parsed.has_field(U("method")) || !parsed.has_field(U("apiKey")) || !parsed.has_field(U("appVersion")) ||
!parsed.has_field(U("protocolVersion")) || !parsed.has_field(U("status")) || !parsed.has_field(U("message")) ||
!parsed.has_field(U("payload")) ) {
std::cerr << endl << "Received incorrectly formatted message." << endl;
cv.notify_all();
timeoutThreadAlive = false;
return;
}
try
{
out = parsed[U("method")].as_string();
msgMethod = utility::conversions::to_utf8string(out);
out = parsed[U("apiKey")].as_string();
msgApiKey = utility::conversions::to_utf8string(out);
out = parsed[U("appVersion")].as_string();
msgAppVersion = utility::conversions::to_utf8string(out);
out = parsed[U("protocolVersion")].as_string();
msgProtocolVersion = utility::conversions::to_utf8string(out);
out = parsed[U("status")].as_string();
msgStatus = utility::conversions::to_utf8string(out);
out = parsed[U("message")].as_string();
msgMessage = utility::conversions::to_utf8string(out);
}
catch (...)
{
std::cerr << endl << "Received message parse error." << endl;
return;
}
#ifdef DEBUG
cout << msgMethod << ", " << msgStatus << ", " << msgMessage << endl;
cout << rcv_websocket_msg << endl;
#endif
// process any messages we can
// - if success, done
// - if error, get out
if (boost::iequals(msgMethod, "success"))
{
// cleanup
timeoutTimer = 0;
// exit
cout << endl << msgMessage << endl;
return;
}
else if (boost::iequals(msgMethod, "error"))
{
// cleanup
timeoutTimer = 0;
// exit
cout << endl << msgMessage << endl;
return;
}
// process payload afterward since success/error messages have zero length
// payloads which cause an exception to be thrown
try
{
out = parsed[U("payload")].as_string();
if (out.length()>0)
{
msgPayload = utility::conversions::to_utf8string(out);
// decode payload
payloadDecoded = decodeBase64(msgPayload);
}
else
{
// no payload, nothing to do
return;
}
}
catch (...)
{
std::cerr << endl << "JSON format error. Unable to parse message." << endl;
return;
}
try
{
// conntect to lms
s = lmsConnect();
}
catch (...)
{
std::cerr << endl << "Unable to connect to Local Management Service (LMS). Please ensure LMS is running." << endl;
return;
}
#ifdef DEBUG
cout << endl << "vvvvv Sending Message " << endl;
cout << payloadDecoded << endl;
#endif
// send message to LMS
if (send(s, payloadDecoded.c_str(), (int)payloadDecoded.length(), 0) < 0)
{
throw std::runtime_error("error: socket send");
}
// handle response message from LMS
int fd = ((int) s) + 1;
fd_set rset;
FD_ZERO(&rset);
FD_SET(s, &rset);
timeval timeout;
memset(&timeout, 0, sizeof(timeval));
timeout.tv_sec = 2;
timeout.tv_usec = 0;
// read until connection is closed by LMS
while (1)
{
string superBuffer = "";
while (1)
{
int res = select(fd, &rset, NULL, NULL, &timeout);
if (res == 0)
{
// we timed-out waiting for the ME. ME usually delivers data very fast. If
// we time out, it means that there is no more data that the ME needs to send,
// but it's keeping the connection open.
break;
}
// read from LMS
char recv_buffer[4096];
memset(recv_buffer, 0, 4096);
res = recv(s, recv_buffer, 4096, 0);
if (res > 0)
{
#ifdef DEBUG
cout << endl << "^^^^^ Received Message" << endl;
cout << recv_buffer << endl;
#endif
superBuffer += recv_buffer;
}
else if (res < 0)
{
// on LMS read exception
break;
}
else
{
// case where res is zero bytes
// discussion below, but select returns 1 with recv returning 0 to indicate close
// https://stackoverflow.com/questions/2992547/waiting-for-data-via-select-not-working
break;
}
} // while select()
// if there is some data send it
if (superBuffer.length() > 0)
{
string response = createResponse(superBuffer.c_str());
websocket_outgoing_message send_websocket_msg;
string send_websocket_buffer(response);
send_websocket_msg.set_utf8_message(send_websocket_buffer);
#ifdef DEBUG
cout << endl << ">>>>> Sending Message" << endl;
cout << superBuffer << endl;
cout << send_websocket_buffer << endl;
#endif
client.send(send_websocket_msg).wait();
// done
closesocket(s);
return;
}
}
closesocket(s);
}
catch (...)
{
std::cerr << endl << "Communication error in receive handler." << endl;
closesocket(s);
}
});
// set close handler
client.set_close_handler([&mx,&cv](websocket_close_status status, const utility::string_t &reason, const std::error_code &code)
{
// websocket closed by server, notify main thread
cv.notify_all();
});
try
{
// Connect to web socket server; AMT activation server
client.connect(utility::conversions::to_string_t(websocket_address)).wait();
}
catch (...)
{
std::cerr << "Unable to connect to websocket server. Please check url." << endl;
exit(1);
}
try
{
// Send activationParams to websocket
websocket_outgoing_message out_msg;
out_msg.set_utf8_message(activationInfo);
#ifdef DEBUG
cout << endl << ">>>>> Sending Activiation Info" << endl;
cout << activationInfo << endl;
#endif
client.send(out_msg).wait();
}
catch (...)
{
std::cerr << endl << "Unable to send message to websocket server." << endl;
exit(1);
}
std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
timeoutTimer = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
std::thread timeoutThread(timeoutFunc, &cv, &mx);
// wait for server to send success/failure command
std::unique_lock<std::mutex> lock(mx);
cv.wait(lock);
// wait for timeout thread
timeoutThread.join();
// clean-up websocket
client.close().wait();
// clean-up socket
if (s) {
shutdown(s, SD_BOTH);
closesocket(s);
}
exit(0);
}
void showUsage()
{
cout << "Usage: " << PROJECT_NAME << " --url <url> --profile <name> [--proxy <addr>]" << endl;
cout << "Required:" << endl;
cout << " --u, --url <url> websocket server" << endl;
cout << " --p, --profile <name> server profile" << endl;
cout << "Optional:" << endl;
cout << " --x, --proxy <addr> proxy address and port" << endl;
cout << endl;
cout << "Examples:" << endl;
cout << " " << PROJECT_NAME << " --url wss://localhost --profile profile1" << endl;
cout << " " << PROJECT_NAME << " --u wss://localhost --profile profile1 --proxy http://proxy.com:1000" << endl;
cout << " " << PROJECT_NAME << " --u wss://localhost --p profile1 --x http://proxy.com:1000" << endl;
cout << endl;
}

10
version.h.in Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __VERSION_H__
#define __VERSION_H__
#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER "@PROJECT_VERSION@"
#define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@"
#define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@"
#define PTOJECT_VER_PATCH "@PROJECT_VERSION_PATCH@"
#endif // __VERSION_H__