diff --git a/AMTHIClient/Include/GetControlModeCommand.h b/AMTHIClient/Include/GetControlModeCommand.h deleted file mode 100644 index d2caefd..0000000 --- a/AMTHIClient/Include/GetControlModeCommand.h +++ /dev/null @@ -1,76 +0,0 @@ -/* 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::const_iterator& itr, const std::vector::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& buffer); - - std::shared_ptr> 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 SerializeData(); - }; -} // namespace AMTHI_Client -} // namespace MEI_Client -} // namespace Intel - -#endif //__GET_CONTROL_MODE_COMMAND_H__ \ No newline at end of file diff --git a/AMTHIClient/Src/GetControlModeCommand.cpp b/AMTHIClient/Src/GetControlModeCommand.cpp deleted file mode 100644 index 571ff7f..0000000 --- a/AMTHIClient/Src/GetControlModeCommand.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* - * Copyright (C) 2010-2019 Intel Corporation - */ -/*++ - -@file: GetControlModeCommand.cpp - ---*/ - -#include "GetControlModeCommand.h" -#include "StatusCodeDefinitions.h" -#include - -using namespace std; - -using namespace Intel::MEI_Client::AMTHI_Client; - -GetControlModeCommand::GetControlModeCommand() -{ - shared_ptr tmp(new GetControlModeRequest()); - m_request = tmp; - Transact(); -} - -void GetControlModeCommand::reTransact() -{ - shared_ptr tmp(new GetControlModeRequest()); - m_request = tmp; - Transact(); -} - -GET_CONTROL_MODE_RESPONSE GetControlModeCommand::getResponse() -{ - return m_response->getResponse(); -} - -void -GetControlModeCommand::parseResponse(const vector& buffer) -{ - shared_ptr> tmp( - new AMTHICommandResponse(buffer, RESPONSE_COMMAND_NUMBER)); - m_response = tmp; -} - -std::vector -GetControlModeRequest::SerializeData() -{ - vector output; - return output; -} diff --git a/CMakeLists.txt b/CMakeLists.txt index e2e0383..5e14006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ if (UNIX) #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) + add_definitions (-fno-strict-overflow -fno-delete-null-pointer-checks -fwrapv -fpermissive) else (UNIX) add_definitions (/GS /sdl) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase") @@ -39,21 +39,10 @@ else (UNIX) 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) - +# Add MicroLMS directly to our build. This adds +# the following targets: MicroLMS +add_subdirectory(MicroLMS) +#add_dependencies(rpc MicroLMS) if (UNIX) @@ -63,6 +52,7 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) # Find Boost +set(Boost_USE_STATIC_LIBS ON) find_package(Boost COMPONENTS system REQUIRED) # Find OpenSSL @@ -94,17 +84,28 @@ endif (UNIX) # ccu-poc add_executable (rpc - AMTHIClient/Include/GetControlModeCommand.h - AMTHIClient/Src/GetControlModeCommand.cpp + info.h + info.cpp + args.h + args.cpp + usage.h + usage.cpp + port.h + utils.h + utils.cpp + network.h + network.cpp commands.h commands.cpp + activation.h + activation.cpp lms.h lms.cpp main.cpp ) target_include_directories(rpc PUBLIC - "AMTHIClient/Include/" + "MicroLMS/heci" ) if (UNIX) @@ -112,20 +113,33 @@ if (UNIX) add_dependencies(rpc cpprestsdk) target_link_libraries (rpc PRIVATE - LmsMEIClient + MicroLMS cpprest ) else (UNIX) +add_dependencies(rpc MicroLMS ) + target_link_libraries (rpc PRIVATE - LmsMEIClient + MicroLMS iphlpapi cpprestsdk::cpprest cpprestsdk::cpprestsdk_zlib_internal cpprestsdk::cpprestsdk_boost_internal cpprestsdk::cpprestsdk_brotli_internal ${Boost_LIBRARIES} + DbgHelp.lib + Iphlpapi.lib + Setupapi.lib + ws2_32.lib + Psapi.lib + Crypt32.lib + Wintrust.lib + Version.lib + Wtsapi32.lib + Gdiplus.lib + Userenv.lib ) endif (UNIX) diff --git a/MicroLMS/CMakeLists.txt b/MicroLMS/CMakeLists.txt new file mode 100644 index 0000000..263960c --- /dev/null +++ b/MicroLMS/CMakeLists.txt @@ -0,0 +1,198 @@ +cmake_minimum_required (VERSION 3.4) + +########################################################### +# Set project options +# + +# set the project name +project(MicroLMS) + +set (CMAKE_CXX_STANDARD 11) + +set (CMAKE_POSITION_INDEPENDENT_CODE ON) + +# set to ON to build library, OFF to build executable +set (BUILD_LIBRARY ON) + +include_directories( + ${PROJECT_BINARY_DIR} + MicroLMS + core + heci) + +########################################################### +# Compiler / linker options +# + +if (UNIX) +# +# Linux Compiler and Linker Options + +# Find threads [unix it pthreads] +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads REQUIRED) + +if (${BUILD_LIBRARY}) +add_definitions(-D BUILD_LIBRARY) +endif (${BUILD_LIBRARY}) + +add_definitions( -D_POSIX ) + +else (UNIX) +# Windows Compiler and Linker Options +if (${BUILD_LIBRARY}) +add_definitions(/D BUILD_LIBRARY) +endif (${BUILD_LIBRARY}) + +add_definitions (/GS /GL /W3 /Gy /Zc:wchar_t /Zi /Zc:inline /fp:precise /WX- /Zc:forScope /Gd /Oi /FC /EHsc /nologo ) +add_definitions (/D WIN32 /D WIN64 /D NDEBUG /D _CONSOLE /D MICROSTACK_NO_STDAFX /D WINSOCK2 /D MICROSTACK_NOTLS /D _UNICODE /D UNICODE) +add_definitions (/errorReport:prompt /diagnostics:column) + +add_compile_options ($<$:/O1>) +add_compile_options (/MT$<$:d>) +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /Gm-") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /Gm /Od /D _DEBUG /D MEMORY_CHECK /D __STDC__ /D _CRT_SECURE_NO_WARNINGS /D ILibWebServer_SESSION_TRACKING /Gd /MDd") + +set (CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} + /MANIFEST + /LTCG:incremental + /NXCOMPAT + /DynamicBase + /MACHINE:X64 + /OPT:REF + /INCREMENTAL:NO + /SUBSYSTEM:CONSOLE + /MANIFESTUAC:\"level='asInvoker' uiAccess='false'\" + /OPT:ICF + /ERRORREPORT:PROMPT + /NOLOGO + /TLBID:1" +) +endif (UNIX) + +########################################################### +# Create the binary +# + +if (UNIX) +# +# Linux Library and Executable Build +# + +if (BUILD_LIBRARY) +# Create a library +add_library ( + MicroLMS + STATIC + MicroLMS/main.c + core/utils.c + heci/HECILinux.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS ) + +else (BUILD_LIBRARY) +# Create an executable +add_executable ( + MicroLMS + MicroLMS/main.c + core/utils.c + heci/HECILinux.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS + PRIVATE + pthread + ) + +endif (BUILD_LIBRARY) + + +else (UNIX) +# +# Windows Library and Executable Build +# + +if (BUILD_LIBRARY) +# Create a library +add_library ( + MicroLMS + STATIC + MicroLMS/main.c + MicroLMS/stdafx.cpp + core/utils.c + heci/HECIWin.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS ) + +else (BUILD_LIBRARY) +# Create an executable +add_executable ( + MicroLMS + MicroLMS/main.c + MicroLMS/stdafx.cpp + core/utils.c + heci/HECIWin.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS + PRIVATE + DbgHelp.lib + Iphlpapi.lib + Setupapi.lib + ws2_32.lib + Psapi.lib + Crypt32.lib + Wintrust.lib + Version.lib + Wtsapi32.lib + Gdiplus.lib + Userenv.lib +) + +endif (BUILD_LIBRARY) + + + + + + + + + + + + +endif (UNIX) + diff --git a/MicroLMS/MicroLMS/main.c b/MicroLMS/MicroLMS/main.c new file mode 100644 index 0000000..0d20ec6 --- /dev/null +++ b/MicroLMS/MicroLMS/main.c @@ -0,0 +1,148 @@ +/* +Copyright 2006 - 2013 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. +*/ + +#if defined(WIN32) || defined (_WIN32_WCE) +#ifndef MICROSTACK_NO_STDAFX +#include "stdafx.h" +#endif +#else +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#if defined(WIN32) +#define _CRTDBG_MAP_ALLOC +#ifdef _DEBUG +#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) +#define new DEBUG_NEW +#endif +#endif + +#if defined(WINSOCK2) + #include + #include +#elif defined(WINSOCK1) + #include + #include +#endif + +#include +#include "../microstack/ILibParsers.h" +#include "../microstack/ILibLMS.h" + +#if defined(WIN32) & !defined(_CONSOLE) +#include "resource.h" +#endif + +#if defined(WIN32) && defined (_DEBUG) +#include +#endif + +// The following macros set and clear, respectively, given bits +// of the C runtime library debug flag, as specified by a bitmask. +#ifdef _DEBUG +#define SET_CRT_DEBUG_FIELD(a) \ + _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) +#define CLEAR_CRT_DEBUG_FIELD(a) \ + _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) +#else +#define SET_CRT_DEBUG_FIELD(a) ((void) 0) +#define CLEAR_CRT_DEBUG_FIELD(a) ((void) 0) +#endif + +#ifdef MEMORY_CHECK +#ifdef WIN32 +// This routine place comments at the head of a section of debug output +void OutputHeading( const char * explanation ) +{ + _RPT1( _CRT_WARN, "\n\n%s:\n**************************************************************************\n", explanation ); +} +#endif +#endif + +void *Chain = NULL; +struct ILibLMS_StateModule *MicroLMS = NULL; + +extern void ctrl_SendSubscriptionEvent(char *data, int datalen); +void BreakSink(int s) +{ + UNREFERENCED_PARAMETER( s ); + signal(SIGINT, SIG_IGN); // To ignore any more ctrl-c interrupts + ILibStopChain(Chain); +} + +#if defined(_POSIX) || defined (_CONSOLE) + +#if defined (BUILD_LIBRARY) +int main_micro_lms() +#else +int main(int argc, char **argv) +#endif + +#else +DWORD WINAPI GPMain(LPVOID lpParameter) +#endif +{ +// Shutdown on Ctrl + C +signal(SIGINT, BreakSink); + +#ifdef _POSIX + signal(SIGPIPE, SIG_IGN); +#ifdef _DEBUG + //mtrace(); +#endif +#endif + +#ifdef MEMORY_CHECK +#ifdef WIN32 + //SET_CRT_DEBUG_FIELD( _CRTDBG_DELAY_FREE_MEM_DF ); + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF); +#endif +#endif + + Chain = ILibCreateChain(); + MicroLMS = ILibLMS_Create(Chain); + if (MicroLMS != NULL) + { + printf("Starting MicroLMS.\r\n"); + ILibStartChain(Chain); + printf("Stopping MicroLMS.\r\n"); + } + else + { + printf("Unable to launch MicroLMS. Check that Intel ME is present, MEI driver installed and run this executable as administrator.\r\n"); + } + +#ifdef MEMORY_CHECK +#ifdef WIN32 + OutputHeading("Generating the final memory leak report\r\n"); + _CrtCheckMemory(); + _CrtDumpMemoryLeaks(); +#endif +#endif + +#ifdef _POSIX +#ifdef _DEBUG + //muntrace(); +#endif +#endif + +#ifdef _POSIX + exit(EXIT_SUCCESS); +#else + return 0; +#endif +} + diff --git a/MicroLMS/MicroLMS/resource.h b/MicroLMS/MicroLMS/resource.h new file mode 100644 index 0000000..6d31350 --- /dev/null +++ b/MicroLMS/MicroLMS/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by MeshMessenger.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MicroLMS/MicroLMS/stdafx.cpp b/MicroLMS/MicroLMS/stdafx.cpp new file mode 100644 index 0000000..09fe45b --- /dev/null +++ b/MicroLMS/MicroLMS/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// TinyMesh.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/MicroLMS/MicroLMS/stdafx.h b/MicroLMS/MicroLMS/stdafx.h new file mode 100644 index 0000000..b005a83 --- /dev/null +++ b/MicroLMS/MicroLMS/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/MicroLMS/MicroLMS/targetver.h b/MicroLMS/MicroLMS/targetver.h new file mode 100644 index 0000000..6fe8eb7 --- /dev/null +++ b/MicroLMS/MicroLMS/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/MicroLMS/core/utils.c b/MicroLMS/core/utils.c new file mode 100644 index 0000000..1c30a05 --- /dev/null +++ b/MicroLMS/core/utils.c @@ -0,0 +1,78 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif + +#if defined(WINSOCK2) + #include + #include + #include + #include +#elif defined(WINSOCK1) + #include + #include + #include + #include +#endif + +#include "utils.h" +#if defined(_MESHAGENT) +#include "meshcore.h" +#include "meshinfo.h" +#else +#define PB_SESSIONKEY 9 +#define PB_AESCRYPTO 8 +#endif + +#include +#include + +char* __fastcall util_tohex_lower(char* data, int len, char* out) +{ + char utils_HexTable2[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for (i = 0; i < len; i++) + { + *(p++) = utils_HexTable2[((unsigned char)data[i]) >> 4]; + *(p++) = utils_HexTable2[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} + +#ifdef WIN32 +BOOL GetWindowsDllPath(const char* dllName, wchar_t* out) +{ + char buf[MAX_PATH]; +#if _WIN64 + if (SHGetSpecialFolderPathA(NULL, buf, CSIDL_SYSTEM, FALSE) == FALSE) return FALSE; +#else + if (SHGetSpecialFolderPathA(NULL, buf, CSIDL_SYSTEMX86, FALSE) == FALSE) return FALSE; +#endif + + if(strcat_s(buf, MAX_PATH, dllName)!= 0) return FALSE; + int str_len = MultiByteToWideChar(CP_UTF8, 0, buf, -1, out, 0); + if(MultiByteToWideChar(CP_UTF8, 0, buf, -1, out, MAX_PATH) == 0) return FALSE; + + return TRUE; + +} +#endif + +// These are all missing MINCORE function that are re-implemented here. +#ifdef WIN32 +BOOL util_MoveFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName) { return MoveFileA(lpExistingFileName, lpNewFileName); } +BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _In_ BOOL bFailIfExists) { return CopyFileA(lpExistingFileName, lpNewFileName, bFailIfExists); } +#endif + diff --git a/MicroLMS/core/utils.h b/MicroLMS/core/utils.h new file mode 100644 index 0000000..dd03029 --- /dev/null +++ b/MicroLMS/core/utils.h @@ -0,0 +1,43 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if !defined(__MeshUtils__) +#define __MeshUtils__ + +#include "../microstack/ILibParsers.h" + +#if !defined(WIN32) +#define __fastcall +#endif + +// Debugging features +#if defined(_DEBUG) + // Display & log + //char spareDebugMemory[4000]; + //int spareDebugLen; + //#define MSG(x) printf("%s",x);//mdb_addevent(x, (int)strlen(x)); + //#define MSG(...) spareDebugLen = snprintf(spareDebugMemory, 4000, __VA_ARGS__);printf("%s",spareDebugMemory);//mdb_addevent(spareDebugMemory, spareDebugLen); + + // Display only + #define MSG(...) printf(__VA_ARGS__); fflush(NULL) + #define DEBUGSTATEMENT(x) x +#else + #ifndef MSG + #define MSG(...) + #endif + #define DEBUGSTATEMENT(x) +#endif + +char* __fastcall util_tohex_lower(char* data, int len, char* out); + +#ifdef WIN32 +int __fastcall util_crc(unsigned char *buffer, int len, int initial_value); +BOOL util_MoveFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName); +BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _In_ BOOL bFailIfExists); +BOOL GetWindowsDllPath(const char* dllName, wchar_t* out); +#endif + +#endif diff --git a/MicroLMS/heci/HECILinux.c b/MicroLMS/heci/HECILinux.c new file mode 100644 index 0000000..cffcfea --- /dev/null +++ b/MicroLMS/heci/HECILinux.c @@ -0,0 +1,434 @@ +/****************************************************************************** + * Intel Management Engine Interface (Intel MEI) Linux driver + * Intel MEI Interface Header + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation. + * linux-mei@linux.intel.com + * http://www.intel.com + * + * BSD LICENSE + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HECILinux.h" +#include "../core/utils.h" + +/***************************************************************************** + * Intel Management Engine Interface + *****************************************************************************/ + +#ifdef _HECIDEBUG + #define mei_msg(_me, fmt, ARGS...) do { printf(fmt, ##ARGS); } while (0) + #define mei_err(_me, fmt, ARGS...) do { printf(fmt, ##ARGS); } while (0) +#else + #define mei_msg(_me, fmt, ARGS...) + #define mei_err(_me, fmt, ARGS...) +#endif + +static void mei_deinit(struct mei *cl) +{ + // mei_err(cl, "mei_deinit()\n"); + if (cl->initialized == false) return; + cl->initialized = false; + if (cl->fd != -1) close(cl->fd); + cl->fd = -1; + cl->buf_size = 0; + cl->prot_ver = 0; + sem_destroy(&(cl->Lock)); +} + +static bool mei_init(struct mei *me, const uuid_le *guid, unsigned char req_protocol_version, bool verbose) +{ + int result; + struct mei_client *cl; + struct mei_connect_client_data data; + + mei_deinit(me); + + me->verbose = verbose; + + // open me + me->fd = open("/dev/mei0", O_RDWR); + if (me->fd == -1) { + // mei_err(me, "Cannot establish a handle to the Intel MEI driver\n"); + goto err; + } + memcpy(&me->guid, guid, sizeof(*guid)); + memset(&data, 0, sizeof(data)); + me->initialized = true; + + memcpy(&data.in_client_uuid, &me->guid, sizeof(me->guid)); + result = ioctl(me->fd, IOCTL_MEI_CONNECT_CLIENT, &data); + if (result) { + mei_err(me, "IOCTL_MEI_CONNECT_CLIENT receive message. err=%d,%d\n", result, errno); + goto err; + } + cl = &data.out_client_properties; + //mei_msg(me, "max_message_length %d\n", cl->max_msg_length); + //mei_msg(me, "protocol_version %d\n", cl->protocol_version); + + if ((req_protocol_version > 0) && (cl->protocol_version != req_protocol_version)) { + mei_err(me, "Intel MEI protocol version not supported\n"); + goto err; + } + + me->buf_size = cl->max_msg_length; + me->prot_ver = cl->protocol_version; + sem_init(&(me->Lock), 0, 1); + + mei_msg(me, "mei init succ"); + return true; +err: + mei_deinit(me); + return false; +} + +static ssize_t mei_recv_msg(struct mei *me, unsigned char *buffer, ssize_t len, unsigned long timeout) +{ + ssize_t rc; + + mei_msg(me, "call read length = %zd\n", len); + rc = read(me->fd, buffer, len); + if (rc < 0) { + mei_err(me, "read failed with status %zd %s\n", rc, strerror(errno)); + mei_deinit(me); + } else { + mei_msg(me, "read succeeded with result %zd\n", rc); + } + return rc; +} + +static ssize_t mei_send_msg(struct mei *me, const unsigned char *buffer, ssize_t len, unsigned long timeout) +{ + struct timeval tv; + ssize_t written; + ssize_t rc; + fd_set set; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000000; + + mei_msg(me, "call write length = %zd, cmd=%d\n", len, (int)buffer[0]); + + sem_wait(&(me->Lock)); + + written = write(me->fd, buffer, len); + if (written < 0) { + rc = -errno; + mei_err(me, "write failed with status %zd %s\n", written, strerror(errno)); + goto out; + } + + FD_ZERO(&set); + FD_SET(me->fd, &set); + rc = select(me->fd + 1 , NULL, &set, NULL, &tv); + if (rc > 0 && FD_ISSET(me->fd, &set)) { + mei_msg(me, "write success\n"); + } else if (rc == 0) { + mei_err(me, "write failed on timeout with status 0, timeout = %ld, written=%ld, cmd=%d\n", timeout, written, (int)buffer[0]); + goto out; + } else { // rc < 0 + mei_err(me, "write failed on select with status %zd\n", rc); + goto out; + } + + rc = written; +out: + sem_post(&(me->Lock)); + mei_msg(me, "call write written = %zd\n", written); + if (rc < 0) mei_deinit(me); + return rc; +} + +/*************************************************************************** + * Intel Advanced Management Technology Host Interface + ***************************************************************************/ + +struct amt_host_if_msg_header { + struct amt_version version; + uint16_t _reserved; + uint32_t command; + uint32_t length; +} __attribute__((packed)); + +struct amt_host_if_resp_header { + struct amt_host_if_msg_header header; + uint32_t status; + unsigned char data[0]; +} __attribute__((packed)); + +const uuid_le MEI_IAMTHIF = {.b={0x28, 0x00, 0xf8, 0x12, 0xb7, 0xb4, 0x2d, 0x4b, 0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}}; +const uuid_le MEI_LMEIF = {.b={0xdb, 0xa4, 0x33, 0x67, 0x76, 0x04, 0x7b, 0x4e, 0xb3, 0xaf, 0xbc, 0xfc, 0x29, 0xbe, 0xe7, 0xa7}}; + +#define AMT_HOST_IF_CODE_VERSIONS_REQUEST 0x0400001A +#define AMT_HOST_IF_CODE_VERSIONS_RESPONSE 0x0480001A + +const struct amt_host_if_msg_header CODE_VERSION_REQ = { + .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, + ._reserved = 0, + .command = AMT_HOST_IF_CODE_VERSIONS_REQUEST, + .length = 0 +}; + + +static bool amt_host_if_init(struct amt_host_if *acmd, unsigned long send_timeout, bool verbose, int client) +{ + acmd->send_timeout = (send_timeout) ? send_timeout : 20000; + if (client == 0) { acmd->initialized = mei_init(&acmd->mei_cl, &MEI_IAMTHIF, 0, verbose); } + else if (client == 1) { acmd->initialized = mei_init(&acmd->mei_cl, &MEI_LMEIF, 0, verbose); } + return acmd->initialized; +} + +static void amt_host_if_deinit(struct amt_host_if *acmd) +{ + mei_deinit(&acmd->mei_cl); + acmd->initialized = false; +} + +static uint32_t amt_verify_code_versions(const struct amt_host_if_resp_header *resp) +{ + uint32_t status = AMT_STATUS_SUCCESS; + struct amt_code_versions *code_ver; + size_t code_ver_len; + uint32_t ver_type_cnt; + uint32_t len; + uint32_t i; + + code_ver = (struct amt_code_versions *)resp->data; + /* length - sizeof(status) */ + code_ver_len = resp->header.length - sizeof(uint32_t); + ver_type_cnt = code_ver_len - + sizeof(code_ver->bios) - + sizeof(code_ver->count); + if (code_ver->count != ver_type_cnt / sizeof(struct amt_version_type)) { + status = AMT_STATUS_INTERNAL_ERROR; + goto out; + } + + for (i = 0; i < code_ver->count; i++) { + len = code_ver->versions[i].description.length; + + if (len > AMT_UNICODE_STRING_LEN) { + status = AMT_STATUS_INTERNAL_ERROR; + goto out; + } + + len = code_ver->versions[i].version.length; + if (code_ver->versions[i].version.string[len] != '\0' || + len != strlen(code_ver->versions[i].version.string)) { + status = AMT_STATUS_INTERNAL_ERROR; + goto out; + } + } +out: + return status; +} + +static uint32_t amt_verify_response_header(uint32_t command, const struct amt_host_if_msg_header *resp_hdr, uint32_t response_size) +{ + if (response_size < sizeof(struct amt_host_if_resp_header)) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (response_size != (resp_hdr->length + + sizeof(struct amt_host_if_msg_header))) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (resp_hdr->command != command) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (resp_hdr->_reserved != 0) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (resp_hdr->version.major != AMT_MAJOR_VERSION || + resp_hdr->version.minor < AMT_MINOR_VERSION) { + return AMT_STATUS_INTERNAL_ERROR; + } + return AMT_STATUS_SUCCESS; +} + +static uint32_t amt_host_if_call(struct amt_host_if *acmd, const unsigned char *command, ssize_t command_sz, uint8_t **read_buf, uint32_t rcmd, unsigned int expected_sz) +{ + uint32_t in_buf_sz; + uint32_t out_buf_sz; + ssize_t written; + uint32_t status; + struct amt_host_if_resp_header *msg_hdr; + + in_buf_sz = acmd->mei_cl.buf_size; + *read_buf = (uint8_t *)malloc(sizeof(uint8_t) * in_buf_sz); + if (*read_buf == NULL) return AMT_STATUS_SDK_RESOURCES; + memset(*read_buf, 0, in_buf_sz); + msg_hdr = (struct amt_host_if_resp_header *)*read_buf; + + written = mei_send_msg(&acmd->mei_cl, command, command_sz, acmd->send_timeout); + if (written != command_sz) + return AMT_STATUS_INTERNAL_ERROR; + + out_buf_sz = mei_recv_msg(&acmd->mei_cl, *read_buf, in_buf_sz, 2000); + if (out_buf_sz <= 0) + return AMT_STATUS_HOST_IF_EMPTY_RESPONSE; + + status = msg_hdr->status; + if (status != AMT_STATUS_SUCCESS) + return status; + + status = amt_verify_response_header(rcmd, &msg_hdr->header, out_buf_sz); + if (status != AMT_STATUS_SUCCESS) + return status; + + if (expected_sz && expected_sz != out_buf_sz) + return AMT_STATUS_INTERNAL_ERROR; + + return AMT_STATUS_SUCCESS; +} + + +static uint32_t amt_get_code_versions(struct amt_host_if *cmd, struct amt_code_versions *versions) +{ + struct amt_host_if_resp_header *response = NULL; + uint32_t status; + + status = amt_host_if_call(cmd, + (const unsigned char *)&CODE_VERSION_REQ, + sizeof(CODE_VERSION_REQ), + (uint8_t **)&response, + AMT_HOST_IF_CODE_VERSIONS_RESPONSE, 0); + + if (status != AMT_STATUS_SUCCESS) + goto out; + + status = amt_verify_code_versions(response); + if (status != AMT_STATUS_SUCCESS) + goto out; + + memcpy(versions, response->data, sizeof(struct amt_code_versions)); +out: + if (response != NULL) + free(response); + + return status; +} + +/************************** end of amt_host_if_command ***********************/ + +int MEI_globalSetup = 0; +struct MEImodule MEI_global; + +bool heci_Init(struct MEImodule* module, int client) +{ + if (module == NULL && client != 0) return false; + if (module == NULL) { module = &MEI_global; if (MEI_globalSetup == 1) return true; } + memset(module, 0 , sizeof(struct MEImodule)); + if (!amt_host_if_init(&(module->acmd), 5000, module->verbose, client)) return false; + if (module == &MEI_global) MEI_globalSetup = 1; + module->inited = true; + if (client == 0) module->status = amt_get_code_versions(&(module->acmd), &(module->ver)); + return true; +} + +void heci_Deinit(struct MEImodule* module) +{ + if (module == NULL) { module = &MEI_global; MEI_globalSetup = 0; } + amt_host_if_deinit(&(module->acmd)); + memset(module, 0, sizeof(struct MEImodule)); +} + +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + if (module == NULL) module = &MEI_global; + return mei_recv_msg(&(module->acmd.mei_cl), buffer, len, timeout); +} + +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + if (module == NULL) module = &MEI_global; + return mei_send_msg(&(module->acmd.mei_cl), buffer, len, timeout); +} + +unsigned int heci_GetBufferSize(struct MEImodule* module) +{ + if (module == NULL) module = &MEI_global; + if (module->inited) return module->acmd.mei_cl.buf_size; + return -1; +} + +unsigned char heci_GetProtocolVersion(struct MEImodule* module) +{ + if (module == NULL) module = &MEI_global; + if (module->inited) return module->acmd.mei_cl.prot_ver; + return 0; +} + +// Get the version of MEI from the last MEI init. +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version) +{ + version->major = AMT_MAJOR_VERSION; + version->minor = AMT_MINOR_VERSION; + return true; +} + +bool heci_IsInitialized(struct MEImodule* module) +{ + return module->inited; +} + diff --git a/MicroLMS/heci/HECILinux.h b/MicroLMS/heci/HECILinux.h new file mode 100644 index 0000000..9bde522 --- /dev/null +++ b/MicroLMS/heci/HECILinux.h @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (C) 2004-2008 Intel Corp. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corp. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Intel Corp. OR THE CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *******************************************************************************/ + +#ifndef __HECI_LINUX_H__ +#define __HECI_LINUX_H__ + +#include "HECI_if.h" +#include "mei.h" +#include +#include + +#ifndef bool +#define bool int +#endif + +/*************************************************************************** + * Intel Advanced Management Technology ME Client + ***************************************************************************/ + +#define AMT_MAJOR_VERSION 1 +#define AMT_MINOR_VERSION 1 + +#define AMT_STATUS_SUCCESS 0x0 +#define AMT_STATUS_INTERNAL_ERROR 0x1 +#define AMT_STATUS_NOT_READY 0x2 +#define AMT_STATUS_INVALID_AMT_MODE 0x3 +#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4 + +#define AMT_STATUS_HOST_IF_EMPTY_RESPONSE 0x4000 +#define AMT_STATUS_SDK_RESOURCES 0x1004 + + +#define AMT_BIOS_VERSION_LEN 65 +#define AMT_VERSIONS_NUMBER 50 +#define AMT_UNICODE_STRING_LEN 20 + +typedef enum +{ + PTHI_CLIENT, + LMS_CLIENT, + MKHI_CLIENT +} HECI_CLIENTS; + + +struct amt_unicode_string { + uint16_t length; + char string[AMT_UNICODE_STRING_LEN]; +} __attribute__((packed)); + +struct amt_version_type { + struct amt_unicode_string description; + struct amt_unicode_string version; +} __attribute__((packed)); + +struct amt_version { + uint8_t major; + uint8_t minor; +} __attribute__((packed)); + +struct amt_code_versions { + uint8_t bios[AMT_BIOS_VERSION_LEN]; + uint32_t count; + struct amt_version_type versions[AMT_VERSIONS_NUMBER]; +} __attribute__((packed)); + +struct mei { + uuid_le guid; + bool initialized; + bool verbose; + unsigned int buf_size; + unsigned char prot_ver; + int fd; + sem_t Lock; +}; + +struct amt_host_if { + struct mei mei_cl; + unsigned long send_timeout; + bool initialized; +}; + +struct MEImodule +{ + struct amt_code_versions ver; + struct amt_host_if acmd; + unsigned int i; + unsigned int status; + bool verbose; + bool inited; +}; + +bool heci_Init(struct MEImodule* module, int client); +void heci_Deinit(struct MEImodule* module); +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +unsigned int heci_GetBufferSize(struct MEImodule* module); +unsigned char heci_GetProtocolVersion(struct MEImodule* module); +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version); +bool heci_IsInitialized(struct MEImodule* module); + +#endif // __HECI_LINUX_H__ diff --git a/MicroLMS/heci/HECIWin.c b/MicroLMS/heci/HECIWin.c new file mode 100644 index 0000000..f885fa3 --- /dev/null +++ b/MicroLMS/heci/HECIWin.c @@ -0,0 +1,377 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#include +#include +#include +#include +#include "HECIWin.h" +#include "heci_if.h" + +#define false 0 +#define true 1 +#define HECI_MAX_LINE_LEN 300 + +DEFINE_GUID(GUID_DEVINTERFACE_HECI, 0xE2D1FF34, 0x3458, 0x49A9, 0x88, 0xDA, 0x8E, 0x69, 0x15, 0xCE, 0x9B, 0xE5); +DEFINE_GUID(HECI_PTHI_GUID , 0x12F80028, 0xB4B7, 0x4b2d, 0xAC, 0xA8, 0x46, 0xE0, 0xFF, 0x65, 0x81, 0x4c); +DEFINE_GUID(LMS_GUID , 0x6733a4db, 0x0476, 0x4e7b, 0xb3, 0xaf, 0xbc, 0xfc, 0x29, 0xbe, 0xe7, 0xa7); +DEFINE_GUID(MKHI_GUID , 0x8E6A6715, 0x9ABC, 0x4043, 0x88, 0xEF, 0x9E, 0x39, 0xC6, 0xF6, 0x3E, 0x0F); + +//VOID _displayHECIError(UINT32 errorCode,DWORD lastError); +//VOID _displayHECIData(UINT32 messageId); +int heci_doIoctl(struct MEImodule* module, DWORD code, void *inbuf, int inlen, void *outbuf, int outlen); + +struct MEImodule MEI_global; + +/***************************** public functions *****************************/ + +unsigned int heci_GetBufferSize(struct MEImodule* module) { if (module != NULL) return module->_bufSize; else return MEI_global._bufSize; } +unsigned char heci_GetProtocolVersion(struct MEImodule* module) { if (module != NULL) return module->_protocolVersion; else return MEI_global._protocolVersion; } +bool heci_IsInitialized(struct MEImodule* module) { if (module != NULL) return module->_initialized; else return MEI_global._initialized; } + +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version) +{ + if (module == NULL) module = &MEI_global; + + if (module->m_haveHeciVersion) + { + memcpy_s(version, sizeof(HECI_VERSION), &(module->m_heciVersion), sizeof(HECI_VERSION)); + return true; + } + + return false; +} + +bool heci_Init(struct MEImodule* module, HECI_CLIENTS client) +{ + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail = NULL; + HDEVINFO hDeviceInfo; + DWORD bufferSize; + SP_DEVICE_INTERFACE_DATA interfaceData; + LONG ii = 0; + int result; + HECI_CLIENT properties; + GUID guid; + + if (client == PTHI_CLIENT) guid = HECI_PTHI_GUID; + if (client == LMS_CLIENT) guid = LMS_GUID; + if (client == MKHI_CLIENT) guid = MKHI_GUID; + if (module == NULL) module = &MEI_global; + module->_verbose = false; + + if (module->_initialized) { + heci_Deinit(module); + } + + // Find all devices that have our interface + hDeviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_HECI, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hDeviceInfo == INVALID_HANDLE_VALUE) { + if (module->_verbose) { + //_displayHECIError(GET_CLASS_DEVS,GetLastError()); + } + return false; //GET_CLASS_DEVS; + } + + // Setup the interface data struct + interfaceData.cbSize = sizeof(interfaceData); + for (ii = 0; + SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, (LPGUID)&GUID_DEVINTERFACE_HECI, ii, &interfaceData); + ++ii) { + // Found our device instance + if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &bufferSize, NULL)) { + DWORD err = GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) { + if (module->_verbose) { + //_displayHECIError(GET_INTERFACE_DETAIL,err); + } + continue; + } + } + + // Allocate a big enough buffer to get detail data + deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(bufferSize); + if (deviceDetail == NULL) { + if (module->_verbose) { + //_displayHECIError(ALLOCATE_MEMORY_ERROR,0); + } + continue; + } + + // Setup the device interface struct + deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Try again to get the device interface detail info + if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL)) + { + /* + if (_verbose) + { + DWORD err = GetLastError(); + _displayHECIError(GET_INTERFACE_DETAIL,err); + } + */ + free(deviceDetail); + deviceDetail = NULL; + continue; + } + + break; + } + SetupDiDestroyDeviceInfoList(hDeviceInfo); + + if (deviceDetail == NULL) { + if (module->_verbose) { + //_displayHECIError(FIND_HECI_FAILURE,0); + } + return false; //FIND_HECI_FAILURE; + } + + module->_handle = CreateFile(deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + free(deviceDetail); + + if (module->_handle == INVALID_HANDLE_VALUE) { + if (module->_verbose) { + //_displayHECIError(CREATE_HECI_FILE_FAILURE,GetLastError()); + } + return false; //CREATE_HECI_FILE_FAILURE; + } + module->_initialized = true; + + result = heci_doIoctl(module, (DWORD)IOCTL_HECI_GET_VERSION, NULL, 0, &(module->m_heciVersion), sizeof(HECI_VERSION)); + if (result != sizeof(HECI_VERSION)) { + if (module->_verbose) { + //_displayHECIError(GET_HECI_DRIVER_VERSION_FAILURE,0); + } + heci_Deinit(module); + return false; //GET_HECI_DRIVER_VERSION_FAILURE; + } + module->m_haveHeciVersion = true; + + if (module->_verbose) { + //_displayHECIData(HECI_DRIVER_VERSION); + //_ftprintf(stdout,_T("%d.%d.%d.%d\n"), (m_heciVersion).major, (m_heciVersion).minor, (m_heciVersion).hotfix, (m_heciVersion).build); + } + + memset(&properties, 0, sizeof(properties)); + result = heci_doIoctl(module, (DWORD)IOCTL_HECI_CONNECT_CLIENT, (void*)(&guid), sizeof(GUID), &properties, sizeof(properties)); + if (result != sizeof(properties)) + { + if (module->_verbose) { + //_displayHECIError(HECI_CONNECT_TO_PTHI_CLIENT_FAILURE,0); + } + //Deinit(); + return false; //HECI_CONNECT_TO_PTHI_CLIENT_FAILURE; + } + module->_bufSize = properties.MaxMessageLength; + + return true; +} + +void heci_Deinit(struct MEImodule* module) +{ + if (module == NULL) module = &MEI_global; + if (module->_initialized == false) return; + module->_initialized = false; + module->_bufSize = 0; + + if (module->_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(module->_handle); + module->_handle = INVALID_HANDLE_VALUE; + } +} + +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + DWORD bytesRead = 0; + int res; + HANDLE h_event = NULL; + OVERLAPPED overlapped; + DWORD error; + DWORD eventRes; + + if (module == NULL) module = &MEI_global; + if ((h_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == 0) goto out; + overlapped.hEvent = h_event; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + + res = ReadFile(module->_handle, buffer, len, &bytesRead, &overlapped); + error = GetLastError(); + if ((0 == res) && (ERROR_IO_PENDING != error)) { + if (module->_verbose) { + //_displayHECIError(READ_FILE,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + + eventRes = WaitForSingleObject(h_event, timeout); + if (eventRes == WAIT_TIMEOUT) { + bytesRead = 0; + goto out; + } + + res = GetOverlappedResult(module->_handle, &overlapped, &bytesRead, true); + + if (res == 0) { + if (module->_verbose) { + //_displayHECIError(READ_FILE,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + +out: + if (h_event != NULL) CloseHandle(h_event); + if (bytesRead <= 0) heci_Deinit(module); + + return bytesRead; +} + +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + DWORD bytesWritten = 0; + int res; + HANDLE h_event = NULL; + OVERLAPPED overlapped; + DWORD lastError; + DWORD eventRes; + + if (module == NULL) module = &MEI_global; + if ((h_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == 0) goto out; + overlapped.hEvent = h_event; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + + res = WriteFile(module->_handle, buffer, len, &bytesWritten, &overlapped); + + lastError = GetLastError(); + if ((0 == res) && (ERROR_IO_PENDING !=lastError )) { + if (module->_verbose) { + //_displayHECIError(WRITE_FILE,GetLastError()); + } + bytesWritten = (DWORD)-1; + goto out; + } + + eventRes = WaitForSingleObject(h_event, timeout); + if (eventRes == WAIT_TIMEOUT) { + if (module->_verbose) { + //_displayHECIError(WRITE_FILE_TIME_OUT,0); + } + bytesWritten = 0; + goto out; + } + + res = GetOverlappedResult(module->_handle, &overlapped, &bytesWritten, false); + + if (res == 0) { + if (module->_verbose) { + //_displayHECIError(WRITE_FILE,GetLastError()); + } + bytesWritten = (DWORD)-1; + goto out; + } + +out: + if (h_event != NULL) CloseHandle(h_event); + if (bytesWritten <= 0) heci_Deinit(module); + + return bytesWritten; +} + +int heci_doIoctl(struct MEImodule* module, DWORD code, void *inbuf, int inlen, void *outbuf, int outlen) +{ + int res; + DWORD bytesRead = 0; + HANDLE h_event = NULL; + OVERLAPPED overlapped; + + if (module == NULL) module = &MEI_global; + if (!module->_initialized) return -1; + + if ((h_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == 0) goto out; + overlapped.hEvent = h_event; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + + res = DeviceIoControl(module->_handle, code, inbuf, inlen, outbuf, outlen, &bytesRead, &overlapped); + + if ((0 == res) && (ERROR_IO_PENDING != GetLastError())) { + if (module->_verbose) { + //_displayHECIError(IOCTL_COMMAND,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + + WaitForSingleObject(h_event, INFINITE); + + res = GetOverlappedResult(module->_handle, &overlapped, &bytesRead, true); + if (res == 0) { + if (module->_verbose) { + //_displayHECIError(IOCTL_COMMAND,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + +out: + if (h_event != NULL) CloseHandle(h_event); + if (bytesRead == (DWORD)-1) heci_Deinit(module); + + return bytesRead; +} + +TCHAR *_getErrMsg(DWORD err) +{ + static TCHAR buffer[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + 0, + buffer, + sizeof(buffer) - 1, + 0); + + return buffer; +} + +/* +// Display a HECI error message +void _displayHECIError(UINT32 errorCode, DWORD lastError) +{ + TCHAR str[HECI_MAX_LINE_LEN]; + TCHAR *msg; + LoadString(GetModuleHandle(NULL), HECI_ERROR_MESSAGE, str, sizeof(str)/sizeof(TCHAR)); + _ftprintf(stderr, _T("%s"), str); + _ftprintf(stderr, _T("%s"), L" "); + LoadString(GetModuleHandle(NULL), errorCode , str, sizeof(str)/sizeof(TCHAR)); + if(0!= lastError) + { + msg = _getErrMsg(lastError); + _ftprintf(stderr, _T("%s (%d): %s\n"),str, lastError, msg); + } + else + { + _ftprintf(stderr, _T("%s\n"),str); + } +} + +// Display a HECI data message +void _displayHECIData(UINT32 messageId) +{ + TCHAR str[HECI_MAX_LINE_LEN]; + LoadString(GetModuleHandle(NULL), messageId , str, sizeof(str)/sizeof(TCHAR)); + _ftprintf(stdout,_T("%s"),str); + _ftprintf(stdout,_T("%s"),L" "); +} +*/ + +#endif diff --git a/MicroLMS/heci/HECIWin.h b/MicroLMS/heci/HECIWin.h new file mode 100644 index 0000000..457ffc9 --- /dev/null +++ b/MicroLMS/heci/HECIWin.h @@ -0,0 +1,49 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef __HECI_WIN_H__ +#define __HECI_WIN_H__ + +#include "HECI_if.h" + +#include +#include +#define bool int + +typedef enum +{ + PTHI_CLIENT, + LMS_CLIENT, + MKHI_CLIENT +} HECI_CLIENTS; + +struct MEImodule +{ + bool _initialized; + bool _verbose; + unsigned int _bufSize; + unsigned char _protocolVersion; + int _fd; + bool m_haveHeciVersion; + HECI_VERSION m_heciVersion; + HANDLE _handle; + OVERLAPPED overlapped; +}; + +bool heci_Init(struct MEImodule* module, HECI_CLIENTS client); +void heci_Deinit(struct MEImodule* module); +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +unsigned int heci_GetBufferSize(struct MEImodule* module); +unsigned char heci_GetProtocolVersion(struct MEImodule* module); +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version); +bool heci_IsInitialized(struct MEImodule* module); + +#endif // __HECI_WIN_H__ + +#endif diff --git a/MicroLMS/heci/HECI_if.h b/MicroLMS/heci/HECI_if.h new file mode 100644 index 0000000..9b862e0 --- /dev/null +++ b/MicroLMS/heci/HECI_if.h @@ -0,0 +1,199 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef __HECI_INTRFACE_H__ +#define __HECI_INTRFACE_H__ + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef char CHAR; +typedef unsigned long ULONG; +typedef UINT32 AMT_STATUS; +typedef UINT32 AMT_BOOLEAN; + +typedef enum _HECI_STATUS { + HECI_STATUS_OK = 0x0, + HECI_STATUS_GENERAL_ERROR = 0x2000, + HECI_STATUS_LOCATE_DEVICE_ERROR, + HECI_STATUS_MEMORY_ACCESS_ERROR, + HECI_STATUS_WRITE_REGISTER_ERROR, + HECI_STATUS_MEMORY_ALLOCATION_ERROR, + HECI_STATUS_BUFFER_OVEREFLOW_ERROR, + HECI_STATUS_NOT_ENOUGH_MEMORY, + HECI_STATUS_MSG_TRANSMISSION_ERROR, + HECI_STATUS_VERSION_MISMATCH, + HECI_STATUS_UNEXPECTED_INTERRUPT_REASON, + HECI_STATUS_TIMEOUT_ERROR, + HECI_STATUS_UNEXPECTED_RESPONSE, + HECI_STATUS_UNKNOWN_MESSAGE, + HECI_STATUS_CANNOT_FOUND_HOST_CLIENT, + HECI_STATUS_CANNOT_FOUND_ME_CLIENT, + HECI_STATUS_CLIENT_ALREADY_CONNECTED, + HECI_STATUS_NO_FREE_CONNECTION, + HECI_STATUS_ILLEGAL_PARAMETER, + HECI_STATUS_FLOW_CONTROL_ERROR, + HECI_STATUS_NO_MESSAGE, + HECI_STATUS_BUFFER_TOO_LARGE, + HECI_STATUS_BUFFER_TOO_SMALL, + HECI_STATUS_BUFFER_NOT_EMPTY, + NUM_OF_HECI_STATUSES +} HECI_STATUS; + +#ifdef WIN32 + // Win32 code + + #define AMT_LOCAL_AGENT_STATUS_SUCCESS 0 + #define USAGE 1 + #define ERROR_MESSAGE 2 + #define VERSION_MESSAGE 3 + #define HECI_ERROR_MESSAGE 4 + #define UNKNOWN 5 + #define HECI_CONNECT_TO_FWU_CLIENT_FAILURE 6 + #define WRITE_FILE_TIME_OUT 7 + #define ME_FW_INFO 8 + #define ME_MODE 9 + #define IOCTL_COMMAND 10 + #define WRITE_FILE 11 + #define READ_FILE 12 + #define GET_CLASS_DEVS 13 + #define GET_INTERFACE_DETAIL 14 + #define ICH_VERSION 15 + #define FIND_HECI_FAILURE 16 + #define CREATE_HECI_FILE 17 + #define CREATE_HECI_FILE_FAILURE 17 + #define GET_HECI_DRIVER_VERSION_FAILURE 18 + #define LA_STATUS_INTERNAL_ERROR 19 + #define HECI_CONNECT_TO_PTHI_CLIENT_FAILURE 20 + #define LA_HECI_ERROR 21 + #define ALLOCATE_MEMORY_ERROR 22 + #define LA_HECI_NOT_INSTALLED_ERROR 23 + #define FW_BUFFER_IS_TO_SMALL 24 + #define SEND_DATA_TO_FW_FAILURE 25 + #define RECEIVE_DATA_FROM_FW_FAILURE 26 + #define GET_INFO_FROM_HECI_FAILURE 27 + #define MCH_VERSION 28 + #define OEM_VENDOR 29 + #define HECI_DRIVER_VERSION 30 + #define CODE_MAJOR_VERSION 31 + #define CODE_MINOR_VERSION 32 + #define CODE_HOTFIX_VERSION 33 + #define CODE_BUID_VERSION 34 + #define BIOS_VERSION 35 + #define AMT_CODE_VERSION 36 + #define AMT_MODE 37 + #define AMT_MODE_1 38 + #define IDS_STRING39 39 + #define AMT_MODE_2 39 + #define PROVISIONING_STATE 40 + #define STATE_PRE 41 + #define STATE_IN 42 + #define STATE_POST 43 + #define IDS_STRING44 44 + #define PARSE_KEYWORD_DISCOVERY_TEST 44 + #define DISCOVERY_PASS 45 + #define IDS_STRING46 46 + #define DISCOVERY_FAILED 46 + #define IDS_STRING47 47 + #define PARSE_KEYWORD_ACTIVATE 47 + #define PARSE_KEYWORD_OTP 48 + #define PARSE_KEYWORD_DNS 49 + #define PARSE_KEYWORD_VERBOSE 50 + #define INVALID_PARAM_INPUT 51 + #define USAGE_LOCAL_AGENT 52 + #define USAGE_OPTIONS 53 + #define USAGE_OPTIONS_OTP 54 + #define USAGE_OPTIONS_DNS 55 + #define USAGE_OPTIONS_DISCOVERY 56 + #define USAGE_OPTIONS_ACTIVATE 57 + #define USAGE_OPTIONS_VERBOSE 58 + #define WORD_ZTC 59 + #define WORD_ENABLED 60 + #define WORD_DISABLED 61 + #define WORD_PROVISIONING_TLS_MODE 62 + #define WORD_PKI 63 + #define WORD_PSK 64 + #define WORD_RNG_SEED_STATUS 65 + #define WORD_EXIST 66 + #define WORD_IN_PROGRESS 67 + #define WORD_NOT_EXIST 68 + #define WORD_AMT_CONFIG_ACTIVATE 69 + #define WORD_SUCCESS 70 + #define IDS_STRING71 71 + #define WORD_FAILURE 71 + #define WORD_NOT_READY 72 + #define IDS_STRING73 73 + #define HASH_ENTRY 73 + #define HECI_CONNECT_TO_WD_CLIENT_FAILURE 74 + #define CHANGE_TO_AMT_FAILURE 75 + #define IDS_STRING76 76 + #define WORD_CHANGE_TO_AMT 76 + #define FOUND 77 + #define CERT_HASHES_IN_FW 78 + #define NO_HANDLES_FOUND 79 + #define CERT_HASH 80 + #define FRIENDLY_NAME 81 + + #define FILE_DEVICE_HECI 0x8000 + #define HECI_IOCTL(index) CTL_CODE(FILE_DEVICE_HECI, index, METHOD_BUFFERED, FILE_READ_DATA) + #define IOCTL_HECI_GET_VERSION CTL_CODE(FILE_DEVICE_HECI, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + #define IOCTL_HECI_CONNECT_CLIENT CTL_CODE(FILE_DEVICE_HECI, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + #define IOCTL_HECI_WD CTL_CODE(FILE_DEVICE_HECI, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +#endif + +#pragma pack(1) + +typedef struct _HECI_VERSION { + UINT8 major; + UINT8 minor; + UINT8 hotfix; + UINT16 build; +} HECI_VERSION; + +typedef struct _HECI_CLIENT { + UINT32 MaxMessageLength; + UINT8 ProtocolVersion; +} HECI_CLIENT; + +typedef union _MEFWCAPS_SKU +{ + UINT32 Data; + struct { + UINT32 Reserved :1; //Legacy + UINT32 Qst :1; //QST + UINT32 Asf :1; //ASF2 + UINT32 Amt :1; //AMT Professional + UINT32 AmtFund :1; //AMT Fundamental + UINT32 Tpm :1; //TPM + UINT32 Dt :1; + UINT32 Fps :1; //Fingerprint Sensor + UINT32 HomeIT :1; //Home IT + UINT32 Mctp :1; //MCTP + UINT32 WoX :1; + UINT32 PmcPatch :1; //PMC Patch + UINT32 Ve :1; //VE + UINT32 Tdt :1; //Theft Deterrent Technology + UINT32 Corp :1; //Corporate + UINT32 Reserved2 :17; + } Fields; +} MEFWCAPS_SKU; + +typedef enum _MEFWCAPS_MANAGEABILITY_SUPP +{ + MEFWCAPS_MANAGEABILITY_SUPP_NONE = 0, + MEFWCAPS_MANAGEABILITY_SUPP_AMT, + MEFWCAPS_MANAGEABILITY_SUPP_ASF, + MEFWCAPS_MANAGEABILITY_SUPP_CP +} MEFWCAPS_MANAGEABILITY_SUPP; + + +#pragma pack() + +#endif // __HECI_INTRFACE_H__ + +#endif diff --git a/MicroLMS/heci/LMEConnection.c b/MicroLMS/heci/LMEConnection.c new file mode 100644 index 0000000..1bd4129 --- /dev/null +++ b/MicroLMS/heci/LMEConnection.c @@ -0,0 +1,554 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "LMEConnection.h" +#include "LMS_if.h" +#include "../microstack/ILibParsers.h" + +#ifdef _POSIX +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#define MEI_IO_TIMEOUT 1000 + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +const unsigned int LME_RX_WINDOW_SIZE = 4096; +int LME_sendMessage(struct LMEConnection* module, unsigned char *buffer, int len); +void LME_doRX(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead); +void LME_apfGlobalRequest(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, int *status); +void LME_Deinit(struct LMEConnection* module); + +bool LME_IsInitialized(struct LMEConnection* module) +{ + // Lock il(_initLock); + bool ret = (bool)(module->initState == INIT_STATE_CONNECTED); + return ret; +} + +#ifdef WIN32 +DWORD WINAPI LME_Thread(void* obj) +#else +void LME_Thread(void* obj) +#endif +{ +#ifdef WIN32 + HECI_VERSION version; +#endif + int len = 0; + int bufsize = 0; + unsigned char* data = NULL; + struct LMEConnection* module = (struct LMEConnection*)obj; + + //printf("LMS THREAD START\r\n"); + + do { + bufsize = heci_GetBufferSize(&(module->mei)); + if ((data = (unsigned char*)malloc(bufsize)) == NULL) ILIBCRITICALEXIT(254); + + do { + len = heci_ReceiveMessage(&(module->mei), data, bufsize, 0xFFFFFFFF); + //printf("LMS THREAD READ LEN=%d\r\n", len); + if (len > 0 && data != NULL) LME_doRX(module, data, len); + } while (len >= 0); + + module->cb(module, module->cbParam, NULL, 0); + module->initState = INIT_STATE_DISCONNECTED; + free(data); + + // printf("LMS TRYING RECONNECT\r\n"); + + while (module->exit == 0 && module->initState == INIT_STATE_DISCONNECTED) + { + // Setup the MEI interface with the LME GUID + #ifdef WIN32 + if (heci_Init(&(module->mei), LMS_CLIENT) == TRUE && heci_GetHeciVersion(&(module->mei), &version) == TRUE && version.major >= LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_CONNECTED; } + #else + if (heci_Init(&(module->mei), 1) == TRUE && heci_GetProtocolVersion(&(module->mei)) >= LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_CONNECTED; } + #endif + + if (module->exit == 0 && module->initState == INIT_STATE_DISCONNECTED) + { + // printf("LMS THREAD SLEEP\r\n"); +#ifdef WIN32 + Sleep(2000); +#else + sleep(2); +#endif + } + //if (module->initState == INIT_STATE_CONNECTED) printf("LMS THREAD RECONNECT\r\n"); + } + } + while (module->exit == 0); + + // printf("LMS THREAD QUIT\r\n"); + module->exit = 2; + +#ifdef WIN32 + return 0; +#endif +} + +// Setup the LME connection +bool LME_Init(struct LMEConnection* module, MEICallback cb, void *param) +{ +#ifdef WIN32 + HECI_VERSION version; +#endif + + // Setup the state object + memset(module, 0, sizeof(struct LMEConnection)); + module->initState = INIT_STATE_DISCONNECTED; + module->cb = cb; + module->cbParam = param; + + // Setup the MEI interface with the LME GUID +#ifdef WIN32 + if (heci_Init(&(module->mei), LMS_CLIENT) == FALSE || heci_GetHeciVersion(&(module->mei), &version) == FALSE || version.major < LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_DISCONNECTED; return FALSE; } +#else + if (heci_Init(&(module->mei), 1) == FALSE || heci_GetProtocolVersion(&(module->mei)) < LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_DISCONNECTED; return FALSE; } +#endif + + module->initState = INIT_STATE_CONNECTED; + if ((module->txBuffer = (unsigned char*)malloc(LME_GetMeiBufferSize(module))) == NULL) ILIBCRITICALEXIT(254); + + // Create the thread that will read the MEI/LME stream + ILibSpawnNormalThread((voidfp)(&LME_Thread), module); + + return TRUE; +} + +// Disconnect the LME connection +void LME_Deinit(struct LMEConnection* module) +{ + //printf("LME_Deinit()\r\n"); + if (module == NULL) return; + if (module->initState == INIT_STATE_CONNECTED) { heci_Deinit(&(module->mei)); } + module->initState = INIT_STATE_DISCONNECTED; +} + +// Exit LME +void LME_Exit(struct LMEConnection* module) +{ + int l = 0; + + //printf("LME_Exit()\r\n"); + if (module == NULL) return; + if (module->exit == 0) module->exit = 1; + LME_Deinit(module); +#ifdef WIN32 + while (module->exit != 2 && l < 40) { Sleep(100); l++; } +#else + /* + while (module->exit != 2 && l < 4) + { + printf("LME_Holding %d\r\n", l); + Sleep(1); + l++; + } + */ +#endif + if (module->txBuffer != NULL && l < 40) { free(module->txBuffer); module->txBuffer = NULL; } +} + +// Send the APF disconnect message to the MEI +bool LME_Disconnect(struct LMEConnection* module, APF_DISCONNECT_REASON_CODE reasonCode) +{ + unsigned char buf[sizeof(APF_DISCONNECT_MESSAGE)]; + APF_DISCONNECT_MESSAGE *disconnectMessage = (APF_DISCONNECT_MESSAGE *)buf; + memset(disconnectMessage, 0, sizeof(buf)); + disconnectMessage->MessageType = APF_DISCONNECT; + disconnectMessage->ReasonCode = htonl(reasonCode); + return (LME_sendMessage(module, buf, sizeof(buf)) == sizeof(buf)); +} + +// Send the AFP service accept message to the MEI +bool LME_ServiceAccept(struct LMEConnection* module, char* serviceName) +{ + int len; + int res; + int servicenamelen = (int)strnlen_s(serviceName, _MAX_PATH); + unsigned char *buf; + unsigned char *pCurrent; + + if (!LME_IsInitialized(module)) return FALSE; + if ((buf = (unsigned char*)malloc(sizeof(APF_SERVICE_ACCEPT_MESSAGE) + servicenamelen)) == NULL) ILIBCRITICALEXIT(254); + pCurrent = buf; + + *pCurrent = APF_SERVICE_ACCEPT; + ++pCurrent; + *((unsigned int *)pCurrent) = htonl(servicenamelen); + pCurrent += 4; + + memcpy_s(pCurrent, sizeof(APF_SERVICE_ACCEPT_MESSAGE) + servicenamelen, serviceName, servicenamelen); + pCurrent += servicenamelen; + + len = (int)(pCurrent - buf); + res = LME_sendMessage(module, buf, len); + free(buf); + + return (res == len); +} + +bool LME_ProtocolVersion(struct LMEConnection* module, unsigned int majorversion, unsigned int minorversion, unsigned int triggerreason) +{ + APF_PROTOCOL_VERSION_MESSAGE protVersion; + memset(&protVersion, 0, sizeof(protVersion)); + protVersion.MessageType = APF_PROTOCOLVERSION; + protVersion.MajorVersion = htonl(majorversion); + protVersion.MinorVersion = htonl(minorversion); + protVersion.TriggerReason = htonl(triggerreason); + return (LME_sendMessage(module, (unsigned char *)&protVersion, sizeof(protVersion)) == sizeof(protVersion)); +} + +bool LME_TcpForwardReplySuccess(struct LMEConnection* module, unsigned int port) +{ + APF_TCP_FORWARD_REPLY_MESSAGE message; + memset(&message, 0, sizeof(message)); + message.MessageType = APF_REQUEST_SUCCESS; + message.PortBound = htonl(port); + return (LME_sendMessage(module, (unsigned char *)&message, sizeof(message)) == sizeof(message)); +} + +bool LME_SendShortMessage(struct LMEConnection* module, unsigned char buf) +{ + return (LME_sendMessage(module, &buf, sizeof(buf)) == sizeof(buf)); +} +#define pCurrentOffset ((int)(pCurrent - buf)) +bool LME_ChannelOpenForwardedRequest(struct LMEConnection* module, unsigned int senderChannel, char* connectedIP, unsigned int connectedPort, char* originatorIP, unsigned int originatorPort) +{ + int res; + int connectedIPlen = (int)strnlen_s(connectedIP, _MAX_PATH); + int originatorIPlen = (int)strnlen_s(originatorIP, _MAX_PATH); + unsigned char *buf; + unsigned char *pCurrent; + int mallocSize = 5 + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED) + 16 + connectedIPlen + 8 + originatorIPlen + 4; + if (!LME_IsInitialized(module)) return FALSE; + + if ((buf = (unsigned char*)malloc(mallocSize)) == NULL) ILIBCRITICALEXIT(254); + pCurrent = buf; + + if (strnlen_s(originatorIP, _MAX_PATH) > 63) { free(buf); return FALSE; } + *pCurrent = APF_CHANNEL_OPEN; + ++pCurrent; + + *((unsigned int *)pCurrent) = htonl(APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED)); + pCurrent += sizeof(unsigned int); + + memcpy_s(pCurrent, mallocSize - pCurrentOffset, APF_OPEN_CHANNEL_REQUEST_FORWARDED, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED)); + pCurrent += APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED); + + *((unsigned int *)pCurrent) = htonl(senderChannel); + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = htonl(LME_RX_WINDOW_SIZE); + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = 0xFFFFFFFF; + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = htonl(connectedIPlen); + pCurrent += sizeof(unsigned int); + + memcpy_s(pCurrent, mallocSize - pCurrentOffset, connectedIP, connectedIPlen); + pCurrent += connectedIPlen; + + *((unsigned int *)pCurrent) = htonl(connectedPort); + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = htonl((unsigned int)originatorIPlen); + pCurrent += sizeof(unsigned int); + + memcpy_s(pCurrent, mallocSize - pCurrentOffset, originatorIP, originatorIPlen); + pCurrent += originatorIPlen; + + *((unsigned int *)pCurrent) = htonl(originatorPort); + pCurrent += sizeof(unsigned int); + + res = LME_sendMessage(module, buf, (int)(pCurrent - buf)); + free(buf); + + return (res == pCurrent - buf); +} + +bool LME_ChannelOpenReplySuccess(struct LMEConnection* module, unsigned int recipientChannel, unsigned int senderChannel) +{ + APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE message; + message.MessageType = APF_CHANNEL_OPEN_CONFIRMATION; + message.RecipientChannel = htonl(recipientChannel); + message.SenderChannel = htonl(senderChannel); + message.InitialWindowSize = htonl(LME_RX_WINDOW_SIZE); + message.Reserved = 0xFFFFFFFF; + return (LME_sendMessage(module, (unsigned char*)&message, sizeof(message)) == sizeof(message)); +} + +bool LME_ChannelOpenReplyFailure(struct LMEConnection* module, unsigned int recipientChannel, unsigned int reason) +{ + APF_CHANNEL_OPEN_FAILURE_MESSAGE message; + message.MessageType = APF_CHANNEL_OPEN_FAILURE; + message.RecipientChannel = htonl(recipientChannel); + message.ReasonCode = htonl(reason); + message.Reserved = 0x00000000; + message.Reserved2 = 0x00000000; + return (LME_sendMessage(module, (unsigned char*)&message, sizeof(message)) == sizeof(message)); +} + +bool LME_ChannelClose(struct LMEConnection* module, unsigned int recipientChannel, unsigned int senderChannel ) +{ + APF_CHANNEL_CLOSE_MESSAGE message; + UNREFERENCED_PARAMETER( senderChannel ); + + message.MessageType = APF_CHANNEL_CLOSE; + message.RecipientChannel = htonl(recipientChannel); + return (LME_sendMessage(module, (unsigned char*)&message, sizeof(message)) == sizeof(message)); +} + +int LME_ChannelData(struct LMEConnection* module, unsigned int recipientChannel, unsigned int len, unsigned char *buffer) +{ + APF_CHANNEL_DATA_MESSAGE *message; + if (len > (LME_GetMeiBufferSize(module) - sizeof(APF_CHANNEL_DATA_MESSAGE)) || module->txBuffer == NULL) return -1; + message = (APF_CHANNEL_DATA_MESSAGE*)module->txBuffer; + message->MessageType = APF_CHANNEL_DATA; + message->RecipientChannel = htonl(recipientChannel); + message->DataLength = htonl(len); + memcpy_s(module->txBuffer + sizeof(APF_CHANNEL_DATA_MESSAGE), LME_GetMeiBufferSize(module) - sizeof(APF_CHANNEL_DATA_MESSAGE), buffer, len); + return LME_sendMessage(module, (unsigned char *)message, sizeof(APF_CHANNEL_DATA_MESSAGE) + len) - sizeof(APF_CHANNEL_DATA_MESSAGE); +} + +bool LME_ChannelWindowAdjust(struct LMEConnection* module, unsigned int recipientChannel, unsigned int len) +{ + APF_WINDOW_ADJUST_MESSAGE message; + message.MessageType = APF_CHANNEL_WINDOW_ADJUST; + message.RecipientChannel = htonl(recipientChannel); + message.BytesToAdd = htonl(len); + return (LME_sendMessage(module, (unsigned char *)&message, sizeof(message)) == sizeof(message)); +} + +int LME_sendMessage(struct LMEConnection* module, unsigned char *buffer, int len) +{ + int result; + if (!LME_IsInitialized(module)) { return -1; } + result = heci_SendMessage(&(module->mei), buffer, len, MEI_IO_TIMEOUT); + if (result < 0) LME_Deinit(module); + return result; +} + +bool LME_checkMinMsgSize(unsigned char *buf, unsigned int bytesRead) +{ + switch (buf[0]) { + case APF_DISCONNECT: + if (bytesRead < sizeof(APF_DISCONNECT_MESSAGE)) { return FALSE; } + break; + case APF_SERVICE_REQUEST: + if (bytesRead < sizeof(APF_SERVICE_REQUEST_MESSAGE)) { return FALSE; } + if (bytesRead < (sizeof(APF_SERVICE_REQUEST_MESSAGE) + ntohl(((APF_SERVICE_REQUEST_MESSAGE *)buf)->ServiceNameLength))) { return FALSE; } + break; + case APF_USERAUTH_REQUEST: + if (bytesRead < (3 * sizeof(unsigned int))) { return FALSE; } + break; + case APF_GLOBAL_REQUEST: + if (bytesRead < (sizeof(APF_GENERIC_HEADER) + sizeof(UINT8))) { return FALSE; } + if (bytesRead < (sizeof(APF_GENERIC_HEADER) + sizeof(UINT8) + ntohl(((APF_GENERIC_HEADER *)buf)->StringLength))) { return FALSE; } + break; + case APF_CHANNEL_OPEN: + if (bytesRead < sizeof(APF_GENERIC_HEADER)) { return FALSE; } + if (bytesRead < (sizeof(APF_GENERIC_HEADER) + ntohl(((APF_GENERIC_HEADER *)buf)->StringLength))) { return FALSE; } + break; + case APF_CHANNEL_OPEN_CONFIRMATION: + if (bytesRead < sizeof(APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE)) { return FALSE; } + break; + case APF_CHANNEL_OPEN_FAILURE: + if (bytesRead < sizeof(APF_CHANNEL_OPEN_FAILURE_MESSAGE)) { return FALSE; } + break; + case APF_CHANNEL_CLOSE: + if (bytesRead < sizeof(APF_CHANNEL_CLOSE_MESSAGE)) { return FALSE; } + break; + case APF_CHANNEL_DATA: + if (bytesRead < sizeof(APF_CHANNEL_DATA_MESSAGE)) { return FALSE; } + if (bytesRead < (sizeof(APF_CHANNEL_DATA_MESSAGE) + ntohl(((APF_CHANNEL_DATA_MESSAGE *)buf)->DataLength))) { return FALSE; } + break; + case APF_CHANNEL_WINDOW_ADJUST: + if (bytesRead < sizeof(APF_WINDOW_ADJUST_MESSAGE)) { return FALSE; } + break; + case APF_PROTOCOLVERSION: + if (bytesRead < sizeof(APF_PROTOCOL_VERSION_MESSAGE)) { return FALSE; } + break; + default: + return FALSE; + } + return TRUE; +} + +void LME_doRX(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead) +{ + if (bytesRead == 0) return; + if (!LME_checkMinMsgSize(rxBuffer, bytesRead)) { LME_Deinit(module); return; } + module->cb(module, module->cbParam, rxBuffer, bytesRead); +} + +/* +void LME_apfChannelOpen(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, int *status) +{ + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + if (_strnicmp((char *)pHeader->String, APF_OPEN_CHANNEL_REQUEST_DIRECT, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT)) == 0) + { + unsigned int senderChannel = 0; + + LME_apfChannelOpenDirect(module, rxBuffer, bytesRead, &senderChannel, status); + if (LME_IsInitialized(module) && (*status == 1)) { + if (plugin.retry(rxBuffer, bytesRead) != LMS_DROPPED) { LME_apfChannelOpenDirect(module, rxBuffer, bytesRead, NULL, status); } + } + + if (LME_IsInitialized(module) && (*status == 1)) { + LME_ChannelOpenReplyFailure(module, senderChannel, OPEN_FAILURE_REASON_CONNECT_FAILED); + } + } +} + +void LME_apfChannelOpenDirect(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, unsigned int *senderChannel, int *status) +{ + unsigned char *pCurrent; + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + if (bytesRead < sizeof(APF_GENERIC_HEADER) + + ntohl(pHeader->StringLength) + + 7 + (5 * sizeof(unsigned int))) { + ILIBMESSAGE("apfChannelOpenDirect: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + pCurrent = rxBuffer + sizeof(APF_GENERIC_HEADER) + + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT); + + LMEChannelOpenRequestMessage channelOpenRequest; + channelOpenRequest.ChannelType = APF_CHANNEL_DIRECT; + + channelOpenRequest.SenderChannel = ntohl(*((unsigned int *)pCurrent)); + if (senderChannel) { + *senderChannel = channelOpenRequest.SenderChannel; + } + pCurrent += sizeof(unsigned int); + channelOpenRequest.InitialWindow = ntohl(*((unsigned int *)pCurrent)); + pCurrent += 2 * sizeof(unsigned int); + + unsigned int len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + channelOpenRequest.Address.append((char *)pCurrent, len); + pCurrent += len; + channelOpenRequest.Port = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + module->_cb(module, module->_cbParam, &channelOpenRequest, sizeof(channelOpenRequest), status); +} +*/ + +/* +void LME_apfUserAuthRequest(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, int *status) +{ + unsigned char *pCurrent = rxBuffer; + + ++pCurrent; + + LMEUserAuthRequestMessage userAuthRequest; + + unsigned int len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest1: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + userAuthRequest.Username.append((char *)pCurrent, len); + pCurrent += len; + + if ((unsigned int)(bytesRead - (pCurrent - rxBuffer)) < sizeof(unsigned int)) { + ILIBMESSAGE("_apfUserAuthRequest2: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest3: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + userAuthRequest.ServiceName.append((char *)pCurrent, len); + pCurrent += len; + + if ((unsigned int)(bytesRead - (pCurrent - rxBuffer)) < sizeof(unsigned int)) { + ILIBMESSAGE("_apfUserAuthRequest4: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest5: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + userAuthRequest.MethodName.append((char *)pCurrent, len); + pCurrent += len; + + if (_strnicmp(userAuthRequest.MethodName.c_str(), APF_AUTH_PASSWORD, + userAuthRequest.MethodName.size()) == 0) { + + if ((unsigned int)(bytesRead - (pCurrent - rxBuffer)) < sizeof(unsigned int) + 1) { + ILIBMESSAGE("_apfUserAuthRequest6: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + ++pCurrent; + + len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest7: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + AuthPasswordData authData; + authData.Password.append((char *)pCurrent, len); + pCurrent += len; + + userAuthRequest.MethodData = &authData; + } + + module->_cb(module, module->_cbParam, &userAuthRequest, sizeof(userAuthRequest), status); +} +*/ + +unsigned int LME_GetMeiBufferSize(struct LMEConnection* module) +{ + return heci_GetBufferSize(&(module->mei)); +} + +#endif diff --git a/MicroLMS/heci/LMEConnection.h b/MicroLMS/heci/LMEConnection.h new file mode 100644 index 0000000..9f51466 --- /dev/null +++ b/MicroLMS/heci/LMEConnection.h @@ -0,0 +1,185 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef __LME_CONNECTION_H__ +#define __LME_CONNECTION_H__ + +#ifdef WIN32 +#include "HECIWin.h" +#else +#include "HECILinux.h" +#endif + +#include "LMS_if.h" + +enum INIT_STATES { + INIT_STATE_DISCONNECTED = 0, + INIT_STATE_CONNECTING, + INIT_STATE_CONNECTED +}; + +struct LMEConnection; + +typedef void (*MEICallback)(struct LMEConnection* module, void *param, void *buffer, unsigned int len); + +struct LMEConnection +{ + unsigned char reqID; + unsigned char *txBuffer; + MEICallback cb; + void* cbParam; + enum INIT_STATES initState; + unsigned char protocolVer; + struct MEImodule mei; + unsigned char exit; +}; + +struct LMEDisconnectMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_DISCONNECT_REASON_CODE ReasonCode; +}; + +struct LMEServiceRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + char* ServiceName; +}; + +typedef enum APF_REQUEST_ENUM { + TCP_FORWARD_REQUEST, + TCP_FORWARD_CANCEL_REQUEST, + UDP_SEND_TO +} APF_REQUEST_TYPE; + +struct LMEGlobalRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; +}; + +struct LMEProtocolVersionMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int MajorVersion; + unsigned int MinorVersion; + APF_TRIGGER_REASON TriggerReason; +}; + +struct LMEUserAuthRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + char* Username; + char* ServiceName; + char* MethodName; + char* MethodData; +}; + +struct LMETcpForwardRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; + char* Address; + unsigned int Port; +}; + +struct LMETcpForwardCancelRequestMessage { + + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; + char* Address; + unsigned int Port; +}; + +struct LMEUdpSendToMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; + char* Address; + unsigned int Port; + unsigned int DataLength; + unsigned char *Data; +}; + +typedef enum APF_CHANNEL_ENUM { + APF_CHANNEL_FORWARDED, + APF_CHANNEL_DIRECT +} APF_CHANNEL_TYPE; + +struct LMEChannelOpenRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_CHANNEL_TYPE ChannelType; + unsigned int SenderChannel; + unsigned int InitialWindow; + char* Address; + unsigned int Port; +}; + +struct LMEChannelOpenReplySuccessMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + unsigned int SenderChannel; + unsigned int InitialWindow; +}; + +struct LMEChannelOpenReplyFailureMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + OPEN_FAILURE_REASON ReasonCode; +}; + +struct LMEChannelCloseMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; +}; + +struct LMEChannelDataMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + unsigned int DataLength; + unsigned char *Data; +}; + +struct LMEChannelWindowAdjustMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + unsigned int BytesToAdd; +}; + +bool LME_Init(struct LMEConnection* module, MEICallback cb, void *param); +void LME_Deinit(struct LMEConnection* module); +bool LME_IsInitialized(struct LMEConnection* module); +bool LME_Disconnect(struct LMEConnection* module, APF_DISCONNECT_REASON_CODE reasonCode); +bool LME_ServiceAccept(struct LMEConnection* module, char* serviceName); +bool LME_ProtocolVersion(struct LMEConnection* module, unsigned int majorversion, unsigned int minorversion, unsigned int triggerreason); +bool LME_TcpForwardReplySuccess(struct LMEConnection* module, unsigned int port); +bool LME_SendShortMessage(struct LMEConnection* module, unsigned char buf); +bool LME_ChannelOpenForwardedRequest(struct LMEConnection* module, unsigned int sender, char* connectedIP, unsigned int connectedPort, char* originatorIP, unsigned int originatorPort); +bool LME_ChannelOpenReplySuccess(struct LMEConnection* module, unsigned int recipient, unsigned int sender); +bool LME_ChannelOpenReplyFailure(struct LMEConnection* module, unsigned int recipient, unsigned int reason); +bool LME_ChannelClose(struct LMEConnection* module, unsigned int recipient, unsigned int sender); +int LME_ChannelData(struct LMEConnection* module, unsigned int recipient, unsigned int len, unsigned char *buffer); +bool LME_ChannelWindowAdjust(struct LMEConnection* module, unsigned int recipient, unsigned int len); +void LME_Deinit(struct LMEConnection* module); +void LME_Exit(struct LMEConnection* module); +unsigned int LME_GetMeiBufferSize(struct LMEConnection* module); + +#define LME_UserAuthSuccess(module) LME_SendShortMessage(module, APF_USERAUTH_SUCCESS) +#define LME_TcpForwardReplyFailure(module) LME_SendShortMessage(module, APF_REQUEST_FAILURE) +#define LME_TcpForwardCancelReplySuccess(module) LME_SendShortMessage(module, APF_REQUEST_SUCCESS) +#define LME_TcpForwardCancelReplyFailure(module) LME_SendShortMessage(module, APF_REQUEST_FAILURE) + +#endif + +#endif diff --git a/MicroLMS/heci/LMS_if.h b/MicroLMS/heci/LMS_if.h new file mode 100644 index 0000000..a3fbd2c --- /dev/null +++ b/MicroLMS/heci/LMS_if.h @@ -0,0 +1,173 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef _LMS_IF_H_ +#define _LMS_IF_H_ + +#include "LMS_if_constants.h" + +#pragma pack(1) + +typedef struct { + unsigned char MessageType; +} APF_MESSAGE_HEADER; + + +/** + * APF_GENERIC_HEADER - generic request header (note that its not complete header per protocol (missing WantReply) + * + * @MessageType: + * @RequestStringLength: length of the string identifies the request + * @RequestString: the string that identifies the request + **/ + +typedef struct { + unsigned char MessageType; + unsigned int StringLength; + unsigned char String[0]; +} APF_GENERIC_HEADER; + +/** + * TCP forward reply message + * @MessageType - Protocol's Major version + * @PortBound - the TCP port was bound on the server + **/ +typedef struct { + unsigned char MessageType; + unsigned int PortBound; +} APF_TCP_FORWARD_REPLY_MESSAGE; + +/** + * response to ChannelOpen when channel open succeed + * @MessageType - APF_CHANNEL_OPEN_CONFIRMATION + * @RecipientChannel - channel number given in the open request + * @SenderChannel - channel number assigned by the sender + * @InitialWindowSize - Number of bytes in the window + * @Reserved - Reserved + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int SenderChannel; + unsigned int InitialWindowSize; + unsigned int Reserved; +} APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE; + +/** + * response to ChannelOpen when a channel open failed + * @MessageType - APF_CHANNEL_OPEN_FAILURE + * @RecipientChannel - channel number given in the open request + * @ReasonCode - code for the reason channel could not be open + * @Reserved - Reserved + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int ReasonCode; + unsigned int Reserved; + unsigned int Reserved2; +} APF_CHANNEL_OPEN_FAILURE_MESSAGE; + +/** + * close channel message + * @MessageType - APF_CHANNEL_CLOSE + * @RecipientChannel - channel number given in the open request + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; +} APF_CHANNEL_CLOSE_MESSAGE; + +/** + * used to send/receive data. + * @MessageType - APF_CHANNEL_DATA + * @RecipientChannel - channel number given in the open request + * @Length - Length of the data in the message + * @Data - The data in the message + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int DataLength; + // unsigned char Data[0]; +} APF_CHANNEL_DATA_MESSAGE; + +/** + * used to adjust receive window size. + * @MessageType - APF_WINDOW_ADJUST + * @RecipientChannel - channel number given in the open request + * @BytesToAdd - number of bytes to add to current window size value + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int BytesToAdd; +} APF_WINDOW_ADJUST_MESSAGE; + +/** + * This message causes immediate termination of the connection with AMT. + * @ReasonCode - A Reason code for the disconnection event + * @Reserved - Reserved must be set to 0 + **/ +typedef struct { + unsigned char MessageType; + unsigned int ReasonCode; + unsigned short Reserved; +} APF_DISCONNECT_MESSAGE; + +/** + * Used to request a service identified by name + * @ServiceNameLength - The length of the service name string. + * @ServiceName - The name of the service being requested. + **/ +typedef struct { + unsigned char MessageType; + unsigned int ServiceNameLength; + unsigned char ServiceName[0]; +} APF_SERVICE_REQUEST_MESSAGE; + +/** + * Used to send a service accept identified by name + * @ServiceNameLength - The length of the service name string. + * @ServiceName - The name of the service being requested. + **/ +typedef struct { + unsigned char MessageType; + unsigned int ServiceNameLength; + unsigned char ServiceName[0]; +} APF_SERVICE_ACCEPT_MESSAGE; + +/** + * holds the protocl major and minor version implemented by AMT. + * @MajorVersion - Protocol's Major version + * @MinorVersion - Protocol's Minor version + * @Trigger - The open session reason + * @UUID - System Id + **/ +typedef struct { + unsigned char MessageType; + unsigned int MajorVersion; + unsigned int MinorVersion; + unsigned int TriggerReason; + unsigned char UUID[16]; + unsigned char Reserved[64]; +} APF_PROTOCOL_VERSION_MESSAGE; + + +/** + * holds the user authentication request success reponse. + **/ +typedef struct { + unsigned char MessageType; +} APF_USERAUTH_SUCCESS_MESSAGE; + +#pragma pack() + +#endif + +#endif diff --git a/MicroLMS/heci/LMS_if_constants.h b/MicroLMS/heci/LMS_if_constants.h new file mode 100644 index 0000000..0b1dda8 --- /dev/null +++ b/MicroLMS/heci/LMS_if_constants.h @@ -0,0 +1,92 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef _LMS_IF_CONSTANTS_H_ +#define _LMS_IF_CONSTANTS_H_ + +#define LMS_PROTOCOL_VERSION 4 + +// +// messages opcodes +// +typedef enum { + APF_DISCONNECT = 1, + APF_SERVICE_REQUEST = 5, + APF_SERVICE_ACCEPT = 6, + APF_USERAUTH_REQUEST = 50, + APF_USERAUTH_FAILURE = 51, + APF_USERAUTH_SUCCESS = 52, + APF_GLOBAL_REQUEST = 80, + APF_REQUEST_SUCCESS = 81, + APF_REQUEST_FAILURE = 82, + APF_CHANNEL_OPEN = 90, + APF_CHANNEL_OPEN_CONFIRMATION = 91, + APF_CHANNEL_OPEN_FAILURE = 92, + APF_CHANNEL_WINDOW_ADJUST = 93, + APF_CHANNEL_DATA = 94, + APF_CHANNEL_CLOSE = 97, + APF_PROTOCOLVERSION = 192 +} APF_MESSAGE_TYPE; + +typedef enum { + APF_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, + APF_DISCONNECT_PROTOCOL_ERROR = 2, + APF_DISCONNECT_KEY_EXCHANGE_FAILED = 3, + APF_DISCONNECT_RESERVED = 4, + APF_DISCONNECT_MAC_ERROR = 5, + APF_DISCONNECT_COMPRESSION_ERROR = 6, + APF_DISCONNECT_SERVICE_NOT_AVAILABLE = 7, + APF_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8, + APF_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9, + APF_DISCONNECT_CONNECTION_LOST = 10, + APF_DISCONNECT_BY_APPLICATION = 11, + APF_DISCONNECT_TOO_MANY_CONNECTIONS = 12, + APF_DISCONNECT_AUTH_CANCELLED_BY_USER = 13, + APF_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14, + APF_DISCONNECT_ILLEGAL_USER_NAME = 15 +} APF_DISCONNECT_REASON_CODE; + +// +//strings used in global messages +// +#define APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST "tcpip-forward" +#define APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST "cancel-tcpip-forward" +#define APF_GLOBAL_REQUEST_STR_UDP_SEND_TO "udp-send-to@amt.intel.com" +#define APF_OPEN_CHANNEL_REQUEST_FORWARDED "forwarded-tcpip" +#define APF_OPEN_CHANNEL_REQUEST_DIRECT "direct-tcpip" + +// APF service names +#define APF_SERVICE_PFWD "pfwd@amt.intel.com" +#define APF_SERVICE_AUTH "auth@amt.intel.com" + +// APF Authentication method +#define APF_AUTH_NONE "none" +#define APF_AUTH_PASSWORD "password" + +//calculate string length without the NULL terminator +#define APF_STR_SIZE_OF(s) (sizeof(s)-1) + +// Trigger reason code +typedef enum { + USER_INITIATED_REQUEST = 1, + ALERT_REQUEST = 2, + HIT_PROVISIONING_REQUEST = 3, + PERIODIC_REQUEST = 4, + LME_REQUEST = 254 +} APF_TRIGGER_REASON; + +typedef enum { + OPEN_FAILURE_REASON_ADMINISTRATIVELY_PROHIBITED = 1, + OPEN_FAILURE_REASON_CONNECT_FAILED = 2, + OPEN_FAILURE_REASON_UNKNOWN_CHANNEL_TYPE = 3, + OPEN_FAILURE_REASON_RESOURCE_SHORTAGE = 4 +} OPEN_FAILURE_REASON; + +#endif + +#endif diff --git a/MicroLMS/heci/PTHICommand.c b/MicroLMS/heci/PTHICommand.c new file mode 100644 index 0000000..0ab42be --- /dev/null +++ b/MicroLMS/heci/PTHICommand.c @@ -0,0 +1,1457 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +//---------------------------------------------------------------------------- +// +// File: PTHICommand.cpp +// +//---------------------------------------------------------------------------- + +#include "PTHICommand.h" + +#if defined(WIN32) && defined (_DEBUG) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#include +#endif + +#include +#include "../microstack/ILibParsers.h" + + +AMT_STATUS _call(const unsigned char *command, UINT32 command_size, UINT8 **readBuffer, UINT32 rcmd, unsigned int expSize); // expSize default is 0 +AMT_STATUS _verifyResponseHeader(const UINT32 command, const PTHI_MESSAGE_HEADER *response_header, UINT32 response_size); +AMT_STATUS _verifyCodeVersions(const CFG_GET_CODE_VERSIONS_RESPONSE *response); +AMT_STATUS _verifyCurrentPowerPolicy(const CFG_GET_CURRENT_POWER_POLICY_RESPONSE *response); +AMT_STATUS _verifyGetDNSSuffixList(const CFG_GET_DNS_SUFFIX_LIST_RESPONSE *response); +AMT_STATUS _verifyRemoteAccessConnectionStatus(const CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *response); +AMT_STATUS _verifyHashHandles(const CFG_GET_HASH_HANDLES_RESPONSE *response); +AMT_STATUS _verifyGetCertificateHashEntry(const CFG_GET_CERTHASH_ENTRY_RESPONSE *response); +AMT_STATUS _verifyGetDnsSuffix(const CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *response); + +unsigned long m_sendTimeout = 2000; + + +const AMT_UUID AMT_UUID_LINK_STATE = + {0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01}; + + + +const PTHI_MESSAGE_HEADER GET_CODE_VERSION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{CODE_VERSIONS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_PROVISIONING_MODE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{PROVISIONING_MODE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_PROVISIONING_STATE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{PROVISIONING_STATE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_MAC_ADDRESSES_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_MAC_ADDRESSES_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_FEATURES_STATE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_FEATURES_STATE_REQUEST}}, (sizeof(CFG_GET_FEATURES_STATE_REQUEST) - sizeof(PTHI_MESSAGE_HEADER)) +}; + +const PTHI_MESSAGE_HEADER GET_CURRENT_POWER_POLICY_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_CURRENT_POWER_POLICY_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_LAST_HOST_RESET_REASON_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_LAST_HOST_RESET_REASON_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_LAN_INTERFACE_SETTINGS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_LAN_INTERFACE_SETTINGS_REQUEST}}, (sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST) - sizeof(PTHI_MESSAGE_HEADER)) +}; + +const PTHI_MESSAGE_HEADER GET_SECURITY_PARAMETERS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_SECURITY_PARAMETERS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_DNS_SUFFIX_LIST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_DNS_SUFFIX_LIST_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER SET_ENTERPRISE_ACCESS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{SET_ENTERPRISE_ACCESS_REQUEST}}, (sizeof(CFG_SET_ENTERPRISE_ACCESS_REQUEST) - sizeof(PTHI_MESSAGE_HEADER)) +}; + +const PTHI_MESSAGE_HEADER OPEN_USER_INITIATED_CONNECTION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{OPEN_USER_INITIATED_CONNECTION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER CLOSE_USER_INITIATED_CONNECTION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{CLOSE_USER_INITIATED_CONNECTION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_AMT_STATE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_AMT_STATE_REQUEST}}, sizeof(AMT_UUID) +}; + +const PTHI_MESSAGE_HEADER GENERATE_RNG_SEED_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GENERATE_RNG_SEED_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_RNG_SEED_STATUS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_RNG_SEED_STATUS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_ZERO_TOUCH_ENABLED_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_ZERO_TOUCH_ENABLED_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_PROVISIONING_TLS_MODE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_PROVISIONING_TLS_MODE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER START_CONFIGURATION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{START_CONFIGURATION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER STOP_CONFIGURATION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{STOP_CONFIGURATION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER ENUMERATE_HASH_HANDLES_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{ENUMERATE_HASH_HANDLES_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_CERTHASH_ENTRY_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_CERTHASH_ENTRY_REQUEST}}, sizeof(UINT32) +}; + +const PTHI_MESSAGE_HEADER GET_PKI_FQDN_SUFFIX_HEADER = { + {AMT_MAJOR_VERSION, AMT_MAJOR_VERSION}, 0, {{GET_PKI_FQDN_SUFFIX_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER SET_HOST_FQDN_HEADER = { + {AMT_MAJOR_VERSION, AMT_MAJOR_VERSION}, 0, {{SET_HOST_FQDN_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_LOCAL_SYSTEM_ACCOUNT_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_LOCAL_SYSTEM_ACCOUNT_REQUEST}}, 40 +}; + +const PTHI_MESSAGE_HEADER UNPROVISION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{UNPROVISION_REQUEST}}, 4 +}; + +const PTHI_MESSAGE_HEADER GET_EHBC_STATE_REQUEST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_EHBC_STATE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_CONTROL_MODE_REQUEST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_CONTROL_MODE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_UUID_REQUEST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_UUID_REQUEST}}, 0 +}; + +AMT_STATUS _call(const unsigned char *command, UINT32 command_size, UINT8 **readBuffer, UINT32 rcmd, unsigned int expSize) +{ + UINT32 inBuffSize; + UINT32 outBuffSize = 0; + int bytesWritten; + AMT_STATUS status; + + inBuffSize = heci_GetBufferSize(NULL); + *readBuffer = (UINT8 *)malloc(sizeof(UINT8) * inBuffSize); + if (NULL == *readBuffer) + { + return PTSDK_STATUS_RESOURCES; + } + memset(*readBuffer, 0, inBuffSize); + + bytesWritten = heci_SendMessage(NULL, command, command_size, m_sendTimeout); + if ((UINT32)bytesWritten != command_size) + { + return AMT_STATUS_INTERNAL_ERROR; + } + outBuffSize = heci_ReceiveMessage(NULL, *readBuffer, inBuffSize, 2000); + if (0 == outBuffSize) + { + return 0xFFFF; //PTHI_STATUS_EMPTY_RESPONSE; + } + status = ((PTHI_RESPONSE_MESSAGE_HEADER *)*readBuffer)->Status; + if (status != AMT_STATUS_SUCCESS) + { + return status; + } + status = _verifyResponseHeader(rcmd, &(((PTHI_RESPONSE_MESSAGE_HEADER *)*readBuffer)->Header), outBuffSize); + if (status != AMT_STATUS_SUCCESS) + { + return status; + } + if ((expSize != 0) && (expSize != outBuffSize)) + { + return PTSDK_STATUS_INTERNAL_ERROR; + } + return AMT_STATUS_SUCCESS; +} + +/* +* Confirms the correctness of the response message header +* and the response message size +* Arguments: +* command - appropriate Host interface command +* response_header - reference to the response message header +* response_size - value that holds the actual size of the +* response message +* expected_size - value that holds the expected size of the +* response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyResponseHeader( + const UINT32 command, const PTHI_MESSAGE_HEADER *response_header, + UINT32 response_size) +{ + AMT_STATUS status = AMT_STATUS_SUCCESS; + + if (response_size < sizeof(PTHI_RESPONSE_MESSAGE_HEADER)) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_size != (response_header->Length + sizeof(PTHI_MESSAGE_HEADER))) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_header->Command.cmd.val != command) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_header->Reserved != 0) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_header->Version.MajorNumber != AMT_MAJOR_VERSION + || response_header->Version.MinorNumber < AMT_MINOR_VERSION) { + status = AMT_STATUS_INTERNAL_ERROR; + } + + return status; +} + +/* +* Confirms the correctness of the GetCodeVersions response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyCodeVersions(const CFG_GET_CODE_VERSIONS_RESPONSE *response) +{ + AMT_STATUS status = AMT_STATUS_SUCCESS; + UINT32 codeVerLen; + UINT32 ptVerTypeCount; + UINT32 len = 0; + UINT32 i; + + do { + codeVerLen = response->Header.Header.Length - sizeof(AMT_STATUS); + ptVerTypeCount = codeVerLen - sizeof(response->CodeVersions.BiosVersion)- sizeof(response->CodeVersions.VersionsCount); + if (response->CodeVersions.VersionsCount != (ptVerTypeCount/sizeof(AMT_VERSION_TYPE))) + { + status = AMT_STATUS_INTERNAL_ERROR; + break; + } + + for (i = 0; i < (response->CodeVersions.VersionsCount); i ++) + { + len = response->CodeVersions.Versions[i].Description.Length; + + if (len > UNICODE_STRING_LEN) + { + status = AMT_STATUS_INTERNAL_ERROR; + break; + } + + len = response->CodeVersions.Versions[i].Version.Length; + if (response->CodeVersions.Versions[i].Version.String[len] != '\0' || + (len != strnlen_s((CHAR *)(response->CodeVersions.Versions[i].Version.String), len))) + { + status = AMT_STATUS_INTERNAL_ERROR; + break; + } + } + } while (0); + + return status; +} + +/* +* GetVersions response message PTHI command +* Arguments: +* response - reference to the CODE_VERSIONS struct +* Return values: +* AMT_STATUS_SUCCESS - on success +* AMT_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS pthi_GetCodeVersions(CODE_VERSIONS *codeVersions) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_CODE_VERSION_HEADER); + unsigned char command[sizeof(GET_CODE_VERSION_HEADER)]; + AMT_STATUS status; + CFG_GET_CODE_VERSIONS_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_CODE_VERSION_HEADER), sizeof(GET_CODE_VERSION_HEADER)); + + status = _call(command, command_size, &readBuffer, CODE_VERSIONS_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_CODE_VERSIONS_RESPONSE *)readBuffer; + status = _verifyCodeVersions(tmp_response); + if (status != AMT_STATUS_SUCCESS) + { + break; + } + + memcpy_s(codeVersions, sizeof(CODE_VERSIONS), (char*)&(tmp_response->CodeVersions), sizeof(CODE_VERSIONS)); + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Calls to GetProvisioningMode Host interface command +* Arguments: +* mode - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetProvisioningMode(CFG_PROVISIONING_MODE *mode, AMT_BOOLEAN *legacy) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PROVISIONING_MODE_HEADER); + unsigned char command[sizeof(GET_PROVISIONING_MODE_HEADER)]; + AMT_STATUS status; + CFG_GET_PROVISIONING_MODE_RESPONSE* tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PROVISIONING_MODE_HEADER), sizeof(GET_PROVISIONING_MODE_HEADER)); + + status = _call(command, command_size, &readBuffer, PROVISIONING_MODE_RESPONSE, sizeof(CFG_GET_PROVISIONING_MODE_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_PROVISIONING_MODE_RESPONSE *)readBuffer; + + *mode = tmp_response->ProvisioningMode; + *legacy = tmp_response->LegacyMode; + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + + +/* +* Calls to GetProvisioningState Host interface command +* Arguments: +* state - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetProvisioningState(AMT_PROVISIONING_STATE *state) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PROVISIONING_STATE_HEADER); + unsigned char command[sizeof(GET_PROVISIONING_STATE_HEADER)]; + AMT_STATUS status; + CFG_GET_PROVISIONING_STATE_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PROVISIONING_STATE_HEADER), sizeof(GET_PROVISIONING_STATE_HEADER)); + + status = _call(command, command_size, &readBuffer, PROVISIONING_STATE_RESPONSE, sizeof(CFG_GET_PROVISIONING_STATE_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_PROVISIONING_STATE_RESPONSE *)readBuffer; + *state = tmp_response->ProvisioningState; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetProvisioningState Host interface command +* Arguments: +* state - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetMacAddresses(UINT8 DedicatedMac[6], UINT8 HostMac[6]) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_MAC_ADDRESSES_HEADER); + unsigned char command[sizeof(GET_MAC_ADDRESSES_HEADER)]; + AMT_STATUS status; + CFG_GET_MAC_ADDRESSES_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_MAC_ADDRESSES_HEADER), sizeof(GET_MAC_ADDRESSES_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_MAC_ADDRESSES_RESPONSE, sizeof(CFG_GET_MAC_ADDRESSES_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_MAC_ADDRESSES_RESPONSE *)readBuffer; + memcpy_s(DedicatedMac, 6, (char*)tmp_response->DedicatedMac, 6); + memcpy_s(HostMac, 6, (char*)tmp_response->HostMac, 6); + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetFeatureState Host interface command +* Arguments: +* requestID Indicates what feature status to query: +* 0 Redirection Sessions Status +* 1 System Defense Status +* 2 WebUI Status +* requestStatus The requested feature state(the size depand on the requestID).(OUT) +* +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetFeaturesState(UINT32 requestID, AMT_BOOLEAN (*requestStatus)[2]) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_FEATURES_STATE_REQUEST); + unsigned char command[sizeof(CFG_GET_FEATURES_STATE_REQUEST)]; + AMT_STATUS status; + CFG_GET_FEATURES_STATE_RESPONSE *tmp_response; + GET_FEATURES_REDIRECTION_SESSION_STATUS redirectionState; + GET_FEATURES_SYSTEM_DEFENSE_STATUS_RESPONSE systemDefenseState; + GET_FEATURES_WEB_UI_STATUS_RESPONSE webUIState; + + memcpy_s(command, sizeof(command), (char*)&GET_FEATURES_STATE_HEADER, sizeof(GET_FEATURES_STATE_HEADER)); + memcpy_s(command + sizeof(GET_FEATURES_STATE_HEADER), sizeof(command) - sizeof(GET_FEATURES_STATE_HEADER), (char*)&(requestID), sizeof(UINT32)); + + status = _call(command, command_size, &readBuffer, GET_FEATURES_STATE_RESPONSE, sizeof(CFG_GET_FEATURES_STATE_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_FEATURES_STATE_RESPONSE *)readBuffer; + + switch (requestID) + { + case REDIRECTION_SESSION: + redirectionState = tmp_response->Data.rs; + (*requestStatus)[0] = redirectionState.SolOpen; + (*requestStatus)[1] = redirectionState.IderOpen; + break; + + case SYSTEM_DEFENSE: + systemDefenseState = tmp_response->Data.sd; + (*requestStatus)[0] = systemDefenseState.SystemDefenseActivated; + break; + + case WEB_UI: + webUIState = tmp_response->Data.webUI; + (*requestStatus)[0] = webUIState.WebUiEnabled; + break; + } + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Calls to GetLastHostResetReason Host interface command +* Arguments: +* reason Indicates whether the last host reason was because of remote control operation(0) +* or other reason(1). (OUT) +* remoteControlTimeStamp In case the reason was due to remote control then this field +* indicates the timestamp of when the remote control command has been executed. +* (The timestamp is the number of seconds since 1/1/1970) +* +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetLastHostResetReason(UINT32 *reason, UINT32 *remoteControlTimeStamp) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_LAST_HOST_RESET_REASON_HEADER); + unsigned char command[sizeof(GET_LAST_HOST_RESET_REASON_HEADER)]; + AMT_STATUS status; + CFG_GET_LAST_HOST_RESET_REASON_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_LAST_HOST_RESET_REASON_HEADER), sizeof(GET_LAST_HOST_RESET_REASON_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_LAST_HOST_RESET_REASON_RESPONSE, sizeof(CFG_GET_LAST_HOST_RESET_REASON_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_LAST_HOST_RESET_REASON_RESPONSE *)readBuffer; + + *reason = tmp_response->Reason; + *remoteControlTimeStamp = tmp_response->RemoteControlTimeStamp; + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Calls to GetCurrentPowerPolicy Host interface command +* Arguments: +* policyName The power policy name. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetCurrentPowerPolicy(AMT_ANSI_STRING *policyName) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_CURRENT_POWER_POLICY_HEADER); + unsigned char command[sizeof(GET_CURRENT_POWER_POLICY_HEADER)]; + AMT_STATUS status; + CFG_GET_CURRENT_POWER_POLICY_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_CURRENT_POWER_POLICY_HEADER), sizeof(GET_CURRENT_POWER_POLICY_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_CURRENT_POWER_POLICY_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_CURRENT_POWER_POLICY_RESPONSE *)readBuffer; + status = _verifyCurrentPowerPolicy(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + policyName->Length = tmp_response->PolicyName.Length; + policyName->Buffer = (CHAR *)malloc(policyName->Length * sizeof(CHAR)); + if (NULL == policyName->Buffer) { + status = AMT_STATUS_INTERNAL_ERROR; + } else { + memcpy_s(policyName->Buffer, policyName->Length * sizeof(CHAR), (char*)&(tmp_response->PolicyName.Buffer), policyName->Length * sizeof(CHAR)); + } + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Confirms the correctness of the GetCurrentPowerPolicy response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyCurrentPowerPolicy(const CFG_GET_CURRENT_POWER_POLICY_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_CURRENT_POWER_POLICY_RESPONSE) + - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + + response->PolicyName.Length)) + { + return PTSDK_STATUS_INTERNAL_ERROR; + } + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to GetLanInterfaceSttings Host interface command +* Arguments: +* interfaceSettings The interface to get the settings for. +* lanSettings reference to a pre allocated struct which will hold the lan settings. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetLanInterfaceSettings(UINT32 interfaceSettings, LAN_SETTINGS *lanSettings) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST); + unsigned char command[sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST)]; + AMT_STATUS status; + CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_LAN_INTERFACE_SETTINGS_HEADER), sizeof(GET_LAN_INTERFACE_SETTINGS_HEADER)); + memcpy_s(command + sizeof(GET_LAN_INTERFACE_SETTINGS_HEADER), sizeof(command) - sizeof(GET_LAN_INTERFACE_SETTINGS_HEADER), (char*)&(interfaceSettings), sizeof(UINT32)); + + status = _call(command, command_size, &readBuffer, GET_LAN_INTERFACE_SETTINGS_RESPONSE, sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE *)readBuffer; + + lanSettings->Enabled = tmp_response->Enabled; + lanSettings->Ipv4Address = tmp_response->Ipv4Address; + lanSettings->DhcpEnabled = tmp_response->DhcpEnabled; + lanSettings->DhcpIpMode = tmp_response->DhcpIpMode; + lanSettings->LinkStatus = tmp_response->LinkStatus; + memcpy_s(lanSettings->MacAddress, sizeof(lanSettings->MacAddress), (char*)tmp_response->MacAddress, sizeof(tmp_response->MacAddress)); + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/** +* Gets the HECI driver version +* Arguments: +* heciVersion - pointewr to HECI_VERSION struct (out) +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INVALID_PARAM - on failure +*/ +AMT_STATUS pthi_GetHeciVersion(HECI_VERSION *heciVersion) +{ + if (heci_GetHeciVersion(NULL, heciVersion)) return AMT_STATUS_SUCCESS; + return AMT_STATUS_INTERNAL_ERROR; +} + +/* +* Calls to GetSecurityParameters Host interface command +* Arguments: +* tlsEnabled true if AMT on TLS mode. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetTLSEnabled(AMT_BOOLEAN *tlsEnabled) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_SECURITY_PARAMETERS_HEADER); + unsigned char command[sizeof(GET_SECURITY_PARAMETERS_HEADER)]; + AMT_STATUS status; + CFG_GET_SECURITY_PARAMETERS_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_SECURITY_PARAMETERS_HEADER), sizeof(GET_SECURITY_PARAMETERS_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_SECURITY_PARAMETERS_RESPONSE, sizeof(CFG_GET_SECURITY_PARAMETERS_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_SECURITY_PARAMETERS_RESPONSE *)readBuffer; + *tlsEnabled = tmp_response->TLSEnabled; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetDNSSuffixList Host interface command +* Arguments: +* dnsSuffixList reference to list of DNS suffix strings. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +/* +AMT_STATUS pthi_GetDNSSuffixList(std::list &dnsSuffixList) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_DNS_SUFFIX_LIST_HEADER); + unsigned char command[command_size]; + memcpy(command, &(GET_DNS_SUFFIX_LIST_HEADER), sizeof(GET_DNS_SUFFIX_LIST_HEADER)); + + AMT_STATUS status = _call(command, command_size, &readBuffer, GET_DNS_SUFFIX_LIST_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + CFG_GET_DNS_SUFFIX_LIST_RESPONSE *tmp_response = (CFG_GET_DNS_SUFFIX_LIST_RESPONSE *)readBuffer; + status = _verifyGetDNSSuffixList(*tmp_response); + if (status != AMT_STATUS_SUCCESS) + { + break; + } + + char *current = (char *)tmp_response->Data; + while (current < (char *)tmp_response->Data + tmp_response->DataLength) + { + std::string dnsSuffix = current; + if (dnsSuffix.length() > tmp_response->DataLength) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + if (!dnsSuffix.empty()) + { + dnsSuffixList.push_back(dnsSuffix); + } + current += dnsSuffix.length() + 1; + } + } while (0); + + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} +*/ + +/* +* Confirms the correctness of the GetDNSSuffixList response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyGetDNSSuffixList(const CFG_GET_DNS_SUFFIX_LIST_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_DNS_SUFFIX_LIST_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) + response->DataLength)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to SetEnterpriseAccess Host interface command +* Arguments: +* Flags flags +* HostIPAddress host IP address for enterprise access +* EnterpriseAccess enterprise access mode +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_SetEnterpriseAccess(UINT8 Flags, UINT8 HostIPAddress[16], UINT8 EnterpriseAccess) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_SET_ENTERPRISE_ACCESS_REQUEST); + unsigned char command[sizeof(CFG_SET_ENTERPRISE_ACCESS_REQUEST)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(SET_ENTERPRISE_ACCESS_HEADER), sizeof(SET_ENTERPRISE_ACCESS_HEADER)); + memcpy_s(command + sizeof(SET_ENTERPRISE_ACCESS_HEADER), sizeof(command) - sizeof(SET_ENTERPRISE_ACCESS_HEADER), (char*)&(Flags), sizeof(UINT8)); + memcpy_s(command + sizeof(SET_ENTERPRISE_ACCESS_HEADER) + sizeof(UINT8), sizeof(command) - sizeof(SET_ENTERPRISE_ACCESS_HEADER) - sizeof(UINT8), (char*)HostIPAddress, (int)sizeof(HostIPAddress)); + memcpy_s(command + sizeof(SET_ENTERPRISE_ACCESS_HEADER) + sizeof(UINT8) + sizeof(HostIPAddress), sizeof(command) - sizeof(SET_ENTERPRISE_ACCESS_HEADER) - sizeof(UINT8) - sizeof(HostIPAddress), (char*)&(EnterpriseAccess), (int)sizeof(UINT8)); + + status = _call(command, command_size, &readBuffer, SET_ENTERPRISE_ACCESS_RESPONSE, sizeof(CFG_SET_ENTERPRISE_ACCESS_RESPONSE)); + + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Get FW last reset reason +* Arguments: +* reason - last FW reason +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetFWResetReason(UINT8 *MEResetReason) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(STATE_GET_AMT_STATE_REQUEST); + unsigned char command[sizeof(STATE_GET_AMT_STATE_REQUEST)]; + AMT_STATUS status; + STATE_GET_AMT_STATE_RESPONSE *tmp_response = NULL; + + memcpy_s(command, sizeof(command), (char*)&(GET_AMT_STATE_HEADER), sizeof(GET_AMT_STATE_HEADER)); + memcpy_s(command + sizeof(GET_AMT_STATE_HEADER), sizeof(command) - sizeof(GET_AMT_STATE_HEADER), (char*)&(AMT_UUID_LINK_STATE), sizeof(AMT_UUID)); + + status = _call(command, command_size, &readBuffer, GET_AMT_STATE_RESPONSE, sizeof(STATE_GET_AMT_STATE_RESPONSE)); + if (status != AMT_STATUS_SUCCESS) + { + if (readBuffer == NULL) return AMT_STATUS_INTERNAL_ERROR; + tmp_response = (STATE_GET_AMT_STATE_RESPONSE *)readBuffer; + *MEResetReason = tmp_response->StateData.LastMEResetReason; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* Calls to OpenUserInitiatedConnection Host interface command +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_OpenUserInitiatedConnection() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(OPEN_USER_INITIATED_CONNECTION_HEADER); + unsigned char command[sizeof(OPEN_USER_INITIATED_CONNECTION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(OPEN_USER_INITIATED_CONNECTION_HEADER), sizeof(OPEN_USER_INITIATED_CONNECTION_HEADER)); + status = _call(command, command_size, &readBuffer, OPEN_USER_INITIATED_CONNECTION_RESPONSE, sizeof(CFG_OPEN_USER_INITIATED_CONNECTION_RESPONSE)); + + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* Calls to CloseUserInitiatedConnection Host interface command +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_CloseUserInitiatedConnection() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CLOSE_USER_INITIATED_CONNECTION_HEADER); + unsigned char command[sizeof(CLOSE_USER_INITIATED_CONNECTION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), &(CLOSE_USER_INITIATED_CONNECTION_HEADER), sizeof(CLOSE_USER_INITIATED_CONNECTION_HEADER)); + status = _call(command, command_size, &readBuffer, CLOSE_USER_INITIATED_CONNECTION_RESPONSE, sizeof(CFG_CLOSE_USER_INITIATED_CONNECTION_RESPONSE)); + + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* Calls to GetRemoteAccessConnectionStatus Host interface command +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetRemoteAccessConnectionStatus(REMOTE_ACCESS_STATUS *remoteAccessStatus) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER); + unsigned char command[sizeof(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER)]; + CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *tmp_response; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER), sizeof(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *)readBuffer; + status = _verifyRemoteAccessConnectionStatus(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + remoteAccessStatus->AmtNetworkConnectionStatus = tmp_response->AmtNetworkConnectionStatus; + remoteAccessStatus->RemoteAccessConnectionStatus = tmp_response->RemoteAccessConnectionStatus; + remoteAccessStatus->RemoteAccessConnectionTrigger = tmp_response->RemoteAccessConnectionTrigger; + + remoteAccessStatus->MpsHostname.Length = tmp_response->MpsHostname.Length; + remoteAccessStatus->MpsHostname.Buffer = (CHAR *)malloc(remoteAccessStatus->MpsHostname.Length * sizeof(CHAR)); + if (NULL == remoteAccessStatus->MpsHostname.Buffer) { + status = AMT_STATUS_INTERNAL_ERROR; + } else { + memcpy_s(remoteAccessStatus->MpsHostname.Buffer, + remoteAccessStatus->MpsHostname.Length * sizeof(CHAR), + &(tmp_response->MpsHostname.Buffer), + tmp_response->MpsHostname.Length * sizeof(CHAR)); + } + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + + +/* +* Confirms the correctness of the GetRemoteAccessConnectionStatus response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyRemoteAccessConnectionStatus(const CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + response->MpsHostname.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to GenerateRngKey Host interface command +* Arguments: +* None +* Return values: +* AMT_STATUS_SUCCESS - or AMT_STATUS_IN_PROGRESS on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GenerateRngKey() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GENERATE_RNG_SEED_HEADER); + unsigned char command[sizeof(GENERATE_RNG_SEED_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(GENERATE_RNG_SEED_HEADER), sizeof(GENERATE_RNG_SEED_HEADER)); + + status = _call(command, command_size, &readBuffer, GENERATE_RNG_SEED_RESPONSE, sizeof(CFG_GENERATE_RNG_SEED_RESPONSE)); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetRngSeedStatus Host interface command +* Arguments: +* rngStatus - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetRngSeedStatus(AMT_RNG_STATUS *rngStatus) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_RNG_SEED_STATUS_HEADER); + unsigned char command[sizeof(GET_RNG_SEED_STATUS_HEADER)]; + CFG_GET_RNG_SEED_STATUS_RESPONSE *tmp_response; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(GET_RNG_SEED_STATUS_HEADER), sizeof(GET_RNG_SEED_STATUS_HEADER)); + status = _call(command, command_size, &readBuffer, GET_RNG_SEED_STATUS_RESPONSE, sizeof(CFG_GET_RNG_SEED_STATUS_RESPONSE)); + if ((tmp_response = (CFG_GET_RNG_SEED_STATUS_RESPONSE *)readBuffer) == NULL) exit(254); + *rngStatus = tmp_response->RngStatus; + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to ZeroTouchEnabled Host interface command +* Arguments: +* zeroTouchEnabled - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetZeroTouchEnabled(AMT_BOOLEAN *zeroTouchEnabled) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_ZERO_TOUCH_ENABLED_HEADER); + unsigned char command[sizeof(GET_ZERO_TOUCH_ENABLED_HEADER)]; + AMT_STATUS status; + CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*) &(GET_ZERO_TOUCH_ENABLED_HEADER), sizeof(GET_ZERO_TOUCH_ENABLED_HEADER)); + status = _call(command, command_size, &readBuffer, GET_ZERO_TOUCH_ENABLED_RESPONSE, sizeof(CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE)); + if ((tmp_response = (CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE *)readBuffer) == NULL) exit(254); + *zeroTouchEnabled = tmp_response->ZeroTouchEnabled; + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetProvisioningTlsMode Host interface command +* Arguments: +* provisioningTlsMode - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetProvisioningTlsMode(AMT_PROVISIONING_TLS_MODE *provisioningTlsMode) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PROVISIONING_TLS_MODE_HEADER); + unsigned char command[sizeof(GET_PROVISIONING_TLS_MODE_HEADER)]; + AMT_STATUS status; + CFG_GET_PROVISIONING_TLS_MODE_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PROVISIONING_TLS_MODE_HEADER), sizeof(GET_PROVISIONING_TLS_MODE_HEADER)); + status = _call(command, command_size, &readBuffer, GET_PROVISIONING_TLS_MODE_RESPONSE, sizeof(CFG_GET_PROVISIONING_TLS_MODE_RESPONSE)); + if ((tmp_response = (CFG_GET_PROVISIONING_TLS_MODE_RESPONSE *)readBuffer) == NULL) exit(254); + *provisioningTlsMode = tmp_response->ProvisioningTlsMode; + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to StartConfiguration Host interface command +* Arguments: +* None +* Return values: +* AMT_STATUS_SUCCESS - or AMT_STATUS_CERTIFICATE_NOT_READY on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_StartConfiguration() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(START_CONFIGURATION_HEADER); + unsigned char command[sizeof(START_CONFIGURATION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*) &(START_CONFIGURATION_HEADER), sizeof(START_CONFIGURATION_HEADER)); + status = _call(command, command_size, &readBuffer, START_CONFIGURATION_RESPONSE, sizeof(CFG_START_CONFIGURATION_RESPONSE)); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to StopConfiguration Host interface command +* Arguments: +* None +* Return values: +* A status code that indicates the success or specific reason for failure of the operation is returned in the Status field of the response message. The following table lists the possible Status values. +* +* Status Description +* AMT_STATUS_INVALID_AMT_MODE Returned when FW not in in- provision state or when provisioning period expired +* AMT_STATUS_SUCCESS Request succeeded. +* AMT_STATUS_INTERNAL_ERROR An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error +* AMT_STATUS_INVALID_MESSAGE_LENGTH Length field of header is invalid. +* AMT_STATUS_NOT_READY Management controller has not progressed far enough in its initialization to process the command. +*/ +AMT_STATUS pthi_StopConfiguration() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(STOP_CONFIGURATION_HEADER); + unsigned char command[sizeof(STOP_CONFIGURATION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(STOP_CONFIGURATION_HEADER), sizeof(STOP_CONFIGURATION_HEADER)); + status = _call(command, command_size, &readBuffer, STOP_CONFIGURATION_RESPONSE, sizeof(CFG_STOP_CONFIGURATION_RESPONSE)); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to SetProvisioningServerOTP Host interface command +* Arguments: +* passwordOTP AMT_ANSI_STRING structure of OTP password +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_SetProvisioningServerOTP(AMT_ANSI_STRING passwordOTP) +{ + UINT8 *readBuffer = NULL; + UINT32 msgLength = sizeof(passwordOTP.Length) + (passwordOTP.Length * sizeof(CHAR)); + PTHI_MESSAGE_HEADER SET_PROVISIONING_SERVER_OTP_HEADER = {{AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{SET_PROVISIONING_SERVER_OTP_REQUEST}}, msgLength}; + UINT32 command_size; + unsigned char *command; + AMT_STATUS status; + + if (NULL == passwordOTP.Buffer) return PTSDK_STATUS_INVALID_PARAM; + + command_size = sizeof(SET_PROVISIONING_SERVER_OTP_HEADER) + msgLength; + command = (unsigned char *)malloc(command_size); + if (command == NULL) return PTSDK_STATUS_INTERNAL_ERROR; + memcpy_s(command, command_size, (char*)&SET_PROVISIONING_SERVER_OTP_HEADER, sizeof(SET_PROVISIONING_SERVER_OTP_HEADER)); + memcpy_s(command + sizeof(SET_PROVISIONING_SERVER_OTP_HEADER), command_size - sizeof(SET_PROVISIONING_SERVER_OTP_HEADER), (char*)&(passwordOTP.Length), sizeof(passwordOTP.Length)); + memcpy_s(command + sizeof(SET_PROVISIONING_SERVER_OTP_HEADER) + sizeof(passwordOTP.Length), command_size - sizeof(SET_PROVISIONING_SERVER_OTP_HEADER) - sizeof(passwordOTP.Length), passwordOTP.Buffer, passwordOTP.Length); + + status = _call(command, command_size, &readBuffer, SET_PROVISIONING_SERVER_OTP_RESPONSE, sizeof(CFG_SET_PROVISIONING_SERVER_OTP_RESPONSE)); + + if (NULL != command) free(command); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to SetDnsSuffix Host interface command +* Arguments: +* dnsSuffix AMT_ANSI_STRING structure of DNS suffix +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_SetDnsSuffix(AMT_ANSI_STRING dnsSuffix) +{ + UINT8 *readBuffer = NULL; + UINT32 msgLength = sizeof(dnsSuffix.Length) + (dnsSuffix.Length * sizeof(CHAR)); + PTHI_MESSAGE_HEADER SET_DNS_SUFFIX_HEADER = {{AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{SET_DNS_SUFFIX_REQUEST}}, msgLength}; + UINT32 command_size; + unsigned char *command; + AMT_STATUS status; + + if (dnsSuffix.Buffer == NULL) return PTSDK_STATUS_INVALID_PARAM; + command_size = sizeof(SET_DNS_SUFFIX_HEADER) + msgLength; + command = (unsigned char *)malloc(command_size); + if (command == NULL) return PTSDK_STATUS_INTERNAL_ERROR; + memcpy_s(command, command_size, (char*)&SET_DNS_SUFFIX_HEADER, sizeof(SET_DNS_SUFFIX_HEADER)); + memcpy_s(command + sizeof(SET_DNS_SUFFIX_HEADER), command_size - sizeof(SET_DNS_SUFFIX_HEADER), (char*)&(dnsSuffix.Length), sizeof(dnsSuffix.Length)); + memcpy_s(command + sizeof(SET_DNS_SUFFIX_HEADER) + sizeof(dnsSuffix.Length), command_size - sizeof(SET_DNS_SUFFIX_HEADER) - sizeof(dnsSuffix.Length), dnsSuffix.Buffer, dnsSuffix.Length); + + status = _call(command, command_size, &readBuffer, SET_DNS_SUFFIX_RESPONSE, sizeof(CFG_SET_DNS_SUFFIX_RESPONSE)); + + if (command != NULL) free(command); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to EnumerateHashHandles Host interface command +* Arguments: +* hashHandles - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_EnumerateHashHandles(AMT_HASH_HANDLES *hashHandles) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(ENUMERATE_HASH_HANDLES_HEADER); + unsigned char command[sizeof(ENUMERATE_HASH_HANDLES_HEADER)]; + AMT_STATUS status; + CFG_GET_HASH_HANDLES_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(ENUMERATE_HASH_HANDLES_HEADER), sizeof(ENUMERATE_HASH_HANDLES_HEADER)); + + status = _call(command, command_size, &readBuffer, ENUMERATE_HASH_HANDLES_RESPONSE, 0); + do + { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_HASH_HANDLES_RESPONSE *)readBuffer; + status = _verifyHashHandles(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + memset(hashHandles->Handles, 0, sizeof(UINT32) * CERT_HASH_MAX_NUMBER); + hashHandles->Length = tmp_response->HashHandles.Length; + if (CERT_HASH_MAX_NUMBER < hashHandles->Length) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + + memcpy_s(hashHandles->Handles, sizeof(hashHandles->Handles), (char*)tmp_response->HashHandles.Handles, sizeof(UINT32) * hashHandles->Length); + + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Confirms the correctness of the EnumerateHashHandles response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyHashHandles(const CFG_GET_HASH_HANDLES_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != sizeof(AMT_STATUS) + sizeof(response->HashHandles.Length) + (sizeof(UINT32) * response->HashHandles.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + + +/* +* Calls to GetCertificateHashEntry Host interface command +* Arguments: +* passwordOTP AMT_ANSI_STRING structure of DNS suffix +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetCertificateHashEntry(UINT32 hashHandle, CERTHASH_ENTRY *hashEntry) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_CERTHASH_ENTRY_REQUEST); + unsigned char command[sizeof(CFG_GET_CERTHASH_ENTRY_REQUEST)]; + AMT_STATUS status; + CFG_GET_CERTHASH_ENTRY_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_CERTHASH_ENTRY_HEADER), sizeof(GET_CERTHASH_ENTRY_HEADER)); + memcpy_s(command + sizeof(GET_CERTHASH_ENTRY_HEADER), sizeof(command) - sizeof(GET_CERTHASH_ENTRY_HEADER), (char*)&(hashHandle), sizeof(hashHandle)); + + status = _call(command, command_size, &readBuffer, GET_CERTHASH_ENTRY_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_CERTHASH_ENTRY_RESPONSE *)readBuffer; + status = _verifyGetCertificateHashEntry(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + hashEntry->IsActive = tmp_response->Hash.IsActive; + hashEntry->IsDefault = tmp_response->Hash.IsDefault; + hashEntry->Name.Length = tmp_response->Hash.Name.Length; + hashEntry->HashAlgorithm = tmp_response->Hash.HashAlgorithm; + memcpy_s(hashEntry->CertificateHash, sizeof(hashEntry->CertificateHash), tmp_response->Hash.CertificateHash, sizeof(tmp_response->Hash.CertificateHash)); + hashEntry->Name.Buffer = (CHAR *)malloc(hashEntry->Name.Length * sizeof(CHAR)); + if (NULL == hashEntry->Name.Buffer) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + memcpy_s(hashEntry->Name.Buffer, hashEntry->Name.Length * sizeof(CHAR), &(tmp_response->Hash.Name.Buffer), hashEntry->Name.Length * sizeof(CHAR)); + + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} +/* +* Confirms the correctness of the GetCertificateHashEntry response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyGetCertificateHashEntry(const CFG_GET_CERTHASH_ENTRY_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_CERTHASH_ENTRY_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + response->Hash.Name.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to GetDnsSuffix Host interface command +* Arguments: +* dnsSuffix - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetDnsSuffix(AMT_ANSI_STRING *dnsSuffix) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PKI_FQDN_SUFFIX_HEADER); + unsigned char command[sizeof(GET_PKI_FQDN_SUFFIX_HEADER)]; + AMT_STATUS status; + CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PKI_FQDN_SUFFIX_HEADER), sizeof(GET_PKI_FQDN_SUFFIX_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_PKI_FQDN_SUFFIX_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *)readBuffer; + status = _verifyGetDnsSuffix(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + dnsSuffix->Length = tmp_response->Suffix.Length; + dnsSuffix->Buffer = (CHAR *)malloc(dnsSuffix->Length * sizeof(CHAR)); + if (NULL == dnsSuffix->Buffer) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + memcpy_s(dnsSuffix->Buffer, dnsSuffix->Length * sizeof(CHAR), &(tmp_response->Suffix.Buffer), dnsSuffix->Length * sizeof(CHAR)); + + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} +/* +* Confirms the correctness of the GetDnsSuffix response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyGetDnsSuffix(const CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != sizeof(AMT_STATUS) + sizeof(response->Suffix.Length) + response->Suffix.Length * sizeof(CHAR)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +AMT_STATUS pthi_GetLocalSystemAccount(LOCAL_SYSTEM_ACCOUNT *localAccount) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST); + unsigned char command[sizeof(CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST)]; + AMT_STATUS status; + CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_LOCAL_SYSTEM_ACCOUNT_HEADER), sizeof(GET_LOCAL_SYSTEM_ACCOUNT_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE *)readBuffer; + status = tmp_response->Status; + if( status != AMT_STATUS_SUCCESS ) break; + memcpy_s(localAccount, sizeof(LOCAL_SYSTEM_ACCOUNT), (char*)&(tmp_response->Account), sizeof(LOCAL_SYSTEM_ACCOUNT)); + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* + * Unprovision AMTHI command + * Arguments: + * provisionMode - indicates the provisioining mode of the device upon unprovisioining + * Return values: + * AMT_LOCAL_AGENT_STATUS_SUCCESS - on success + * AMT_STATUS_INTERNAL_ERROR - on failure + * AMT_STATUS_NOT_READY - Management controller has not progressed far enough in its + * initialization to process the command. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + * AMT_STATUS_BLOCKING_COMPONENT - One of the ME components is not ready for unprovisioning. + */ +AMT_STATUS pthi_Unprovision(CFG_PROVISIONING_MODE provisionMode) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_UNPROVISION_REQUEST); + unsigned char command[sizeof(CFG_UNPROVISION_REQUEST)]; + AMT_STATUS status; + + if ((CFG_PROVISIONING_MODE_NONE != provisionMode) && (CFG_PROVISIONING_MODE_ENTERPRISE != provisionMode)) { ILIBMESSAGE("Unprovision PTSDK_STATUS_INVALID_PARAM"); return PTSDK_STATUS_INVALID_PARAM; } + memset(command, 0, sizeof(CFG_UNPROVISION_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(UNPROVISION_HEADER), sizeof(UNPROVISION_HEADER)); + status = _call(command, command_size, &readBuffer, UNPROVISION_RESPONSE, 0); + if (readBuffer != NULL) free(readBuffer); + return status; +} + + +/* +* Sets host FQDN in AMT Calls to CFG_SET_HOST_FQDN_REQUEST command +* Arguments: +* host - host FQDN +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS pthi_SetHostFQDN(char* str) +{ + //send requeast message to FW: + AMT_STATUS status; + UINT8 *readBuffer = NULL; + CFG_SET_HOST_FQDN_REQUEST command; + int len = (int)strnlen_s(str, _MAX_PATH); + + memset(&command, 0, sizeof(CFG_SET_HOST_FQDN_REQUEST)); // Fix the valgrind warning + command.Header = SET_HOST_FQDN_HEADER; + command.Header.Length = sizeof(UINT16) + len; + command.FQDNL = len; + memcpy_s(&command.FQDN, sizeof(command.FQDN), str, len); + status = _call((UINT8 *)&command, sizeof(command), &readBuffer, SET_HOST_FQDN_RESPONSE, sizeof(CFG_SET_HOST_FQDN_RESPONSE)); + if (readBuffer != NULL) { free(readBuffer); } + return status; +} + + + +/* + * Get EHBC state AMTHI command + * This command returns EHBC enable state. Note: This command is supported from MR 8.1.20 + * Arguments: + * None + * Return values: (A status code returned in a response message that indicates whether the operation specified in the corresponding request message succeeded or failed. If the operation failed, this code indicates the specific reason for failure. Possible values described below.) + * AMT_STATUS_SUCCESS - Request succeeded. + * AMT_STATUS_INTERNAL_ERROR - An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + */ +AMT_STATUS pthi_GetStateEHBC(AMT_EHBC_STATE *state) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GETEHBPSTATE_REQUEST); + unsigned char command[sizeof(CFG_GETEHBPSTATE_REQUEST)]; + AMT_STATUS status; + CFG_GETEHBPSTATE_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GETEHBPSTATE_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_EHBC_STATE_REQUEST_HEADER), sizeof(GET_EHBC_STATE_REQUEST_HEADER)); + status = _call(command, command_size, &readBuffer, GET_EHBC_STATE_RESPONSE, sizeof(CFG_GETEHBPSTATE_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GETEHBPSTATE_RESPONSE*)readBuffer; + *state = tmp_response->EHBCState; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + + +/* + * Get control mode AMTHI command + * gets the current control mode of AMT (client or admin control mode). The machine will be in client control mode if it was configured this way using host based configuration. + * If the machine was configured using RCFG or using host based provisioning to admin control mode in admin control mode. If the machine is not provisioned this command will return the value None. + * Arguments: + * None + * Return values: (A status code returned in a response message that indicates whether the operation specified in the corresponding request message succeeded or failed. If the operation failed, this code indicates the specific reason for failure. Possible values described below.) + * AMT_STATUS_SUCCESS - Request succeeded. + * AMT_STATUS_INTERNAL_ERROR - An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + */ +AMT_STATUS pthi_GetControlMode(int *state) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_CONTROL_MODE_REQUEST); + unsigned char command[sizeof(CFG_GET_CONTROL_MODE_REQUEST)]; + AMT_STATUS status; + CFG_GET_CONTROL_MODE_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GET_CONTROL_MODE_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_CONTROL_MODE_REQUEST_HEADER), sizeof(GET_CONTROL_MODE_REQUEST_HEADER)); + status = _call(command, command_size, &readBuffer, GET_CONTROL_MODE_RESPONSE, sizeof(CFG_GET_CONTROL_MODE_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_CONTROL_MODE_RESPONSE*)readBuffer; + *state = tmp_response->state; // 0 - None (or RPAT for 6.x) 1 - Client 2 - Admin + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* + * Get UID AMTHI command + * gets the UUID of AMT. + * Arguments: + * None + * Return values: (A status code returned in a response message that indicates whether the operation specified in the corresponding request message succeeded or failed. If the operation failed, this code indicates the specific reason for failure. Possible values described below.) + * AMT_STATUS_SUCCESS - Request succeeded. + * AMT_STATUS_INTERNAL_ERROR - An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + */ +AMT_STATUS pthi_GetUUID(AMT_UUID *uuid) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_UUID_REQUEST); + unsigned char command[sizeof(CFG_GET_UUID_REQUEST)]; + AMT_STATUS status; + CFG_GET_UUID_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GET_UUID_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_UUID_REQUEST_HEADER), sizeof(GET_UUID_REQUEST_HEADER)); + status = _call(command, command_size, &readBuffer, GET_UUID_RESPONSE, sizeof(CFG_GET_UUID_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_UUID_RESPONSE*)readBuffer; + memcpy_s(uuid, sizeof(AMT_UUID), (char*)&(tmp_response->UUID), sizeof(AMT_UUID)); // uuid + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +#endif diff --git a/MicroLMS/heci/PTHICommand.h b/MicroLMS/heci/PTHICommand.h new file mode 100644 index 0000000..3039093 --- /dev/null +++ b/MicroLMS/heci/PTHICommand.h @@ -0,0 +1,815 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +//---------------------------------------------------------------------------- +// +// File: PTHICommand.h +// +// Contents: header file of PTHICommand class +// +//---------------------------------------------------------------------------- +#ifndef __PTHI_COMMAND_H__ +#define __PTHI_COMMAND_H__ + +#include "StatusCodeDefinitions.h" + +#ifdef WIN32 +#include +#include "HECIwin.h" +#endif + +#ifdef _POSIX +#include "HECILinux.h" +#endif +#define CERT_HASH_MAX_LENGTH 64 +#define CERT_HASH_MAX_NUMBER 23 +#define NET_TLS_CERT_PKI_MAX_SERIAL_NUMS 3 +#define NET_TLS_CERT_PKI_MAX_SERIAL_NUM_LENGTH 16 +#define MPS_HOSTNAME_LENGTH 256 + +#define CFG_MAX_ACL_USER_LENGTH 33 +#define CFG_MAX_ACL_PWD_LENGTH 33 + +#pragma pack(1) +typedef struct _LOCAL_SYSTEM_ACCOUNT +{ + // contain null terminated string + char username[CFG_MAX_ACL_USER_LENGTH]; + // contain null terminated string + char password[CFG_MAX_ACL_PWD_LENGTH]; +}LOCAL_SYSTEM_ACCOUNT; +#pragma pack() + +/* +* Constants +*/ + +/* +static #define IDER_LOG_ENTRIES = 16; + +const UINT8 MAJOR_VERSION = 1; +const UINT8 MINOR_VERSION = 1; +const UINT8 AMT_MAJOR_VERSION = 1; +const UINT8 AMT_MINOR_VERSION = 1; +*/ + +#define IDER_LOG_ENTRIES 16 + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 1 +#define AMT_MAJOR_VERSION 1 +#define AMT_MINOR_VERSION 1 + +typedef enum _CFG_PROVISIONING_MODE +{ + CFG_PROVISIONING_MODE_NONE = 0, + CFG_PROVISIONING_MODE_ENTERPRISE, + CFG_PROVISIONING_MODE_SMALL_BUSINESS, + CFG_PROVISIONING_MODE_REMOTE_ASSISTANCE +} CFG_PROVISIONING_MODE; + +typedef enum _AMT_PROVISIONING_STATE +{ + PROVISIONING_STATE_PRE = 0, + PROVISIONING_STATE_IN = 1, + PROVISIONING_STATE_POST = 2 +} AMT_PROVISIONING_STATE; + +typedef enum _AMT_EHBC_STATE +{ + EHBC_STATE_DISABLED = 0, + EHBC_STATE_ENABLED = 1 +} AMT_EHBC_STATE; + +typedef enum _AMT_FEATURE_STATE_REQUEST +{ + REDIRECTION_SESSION = 0, + SYSTEM_DEFENSE = 1, + WEB_UI = 2 +} AMT_FEATURE_STATE_REQUEST; + +typedef enum _AMT_LAST_HOST_RESET_REASON +{ + RemoteControl = 0, + Other = 1 +} AMT_LAST_HOST_RESET_REASON; + +typedef enum _AMT_INTERFACE_INDEX +{ + WIRED = 0, + WIRELESS = 1 +} AMT_INTERFACE_INDEX; + +typedef enum _AMT_DHCP_IP_ADDRESS +{ + ACTIVE = 1, + PASSIVE = 2 +} AMT_DHCP_IP_MODE; + +//typedef UINT32 CFG_IPv4_ADDRESS + +#define CFG_IPv4_ADDRESS UINT32 + +/* +static #define BIOS_VERSION_LEN = 65; +static #define VERSIONS_NUMBER = 50; +static #define UNICODE_STRING_LEN = 20; +*/ + +#define BIOS_VERSION_LEN 65 +#define VERSIONS_NUMBER 50 +#define UNICODE_STRING_LEN 20 + +typedef enum _AMT_PROVISIONING_TLS_MODE +{ + NOT_READY = 0, + PSK = 1, + PKI = 2 +} AMT_PROVISIONING_TLS_MODE; + +typedef enum _AMT_RNG_STATUS +{ + RNG_STATUS_EXIST = 0, + RNG_STATUS_IN_PROGRESS = 1, + RNG_STATUS_NOT_EXIST = 2 +} AMT_RNG_STATUS; + +#pragma pack(1) + +typedef struct _AMT_UNICODE_STRING +{ + UINT16 Length; + UINT8 String[UNICODE_STRING_LEN]; +} AMT_UNICODE_STRING; + +typedef struct _AMT_VERSION_TYPE +{ + AMT_UNICODE_STRING Description; + AMT_UNICODE_STRING Version; +} AMT_VERSION_TYPE; + +typedef struct _PTHI_VERSION +{ + UINT8 MajorNumber; + UINT8 MinorNumber; +} PTHI_VERSION; + +typedef struct _CODE_VERSIONS +{ + UINT8 BiosVersion[BIOS_VERSION_LEN]; + UINT32 VersionsCount; + AMT_VERSION_TYPE Versions[VERSIONS_NUMBER]; +} CODE_VERSIONS; + +typedef struct _COMMAND_FMT +{ + union + { + UINT32 val; + struct + { + UINT32 Operation : 23; + UINT32 IsResponse : 1; + UINT32 Class : 8; + } fields; + } cmd; + +} COMMAND_FMT; + +typedef struct _AMT_ANSI_STRING +{ + UINT16 Length; + CHAR *Buffer; +} AMT_ANSI_STRING; + +typedef struct _PTHI_MESSAGE_HEADER +{ + PTHI_VERSION Version; + UINT16 Reserved; + COMMAND_FMT Command; + UINT32 Length; +} PTHI_MESSAGE_HEADER; + +typedef struct _PTHI_RESPONSE_MESSAGE_HEADER +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; +} PTHI_RESPONSE_MESSAGE_HEADER; + +typedef struct _CFG_GET_CODE_VERSIONS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + CODE_VERSIONS CodeVersions; +} CFG_GET_CODE_VERSIONS_RESPONSE; + +typedef struct _CFG_GET_PROVISIONING_MODE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + CFG_PROVISIONING_MODE ProvisioningMode; + AMT_BOOLEAN LegacyMode; +} CFG_GET_PROVISIONING_MODE_RESPONSE; + +typedef struct _CFG_GET_PROVISIONING_STATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_PROVISIONING_STATE ProvisioningState; +} CFG_GET_PROVISIONING_STATE_RESPONSE; + +typedef struct _CFG_GET_MAC_ADDRESSES_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT8 DedicatedMac[6]; + UINT8 HostMac[6]; +} CFG_GET_MAC_ADDRESSES_RESPONSE; + +typedef struct _CFG_GET_FEATURES_STATE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 RequestID; +} CFG_GET_FEATURES_STATE_REQUEST; + +typedef struct _GET_FEATURES_REDIRECTION_SESSION_STATUS +{ + AMT_BOOLEAN IderOpen; + AMT_BOOLEAN SolOpen; + AMT_BOOLEAN Reserved; +} GET_FEATURES_REDIRECTION_SESSION_STATUS; + +typedef struct _GET_FEATURES_SYSTEM_DEFENSE_STATE_RESPONSE +{ + AMT_BOOLEAN SystemDefenseActivated; +} GET_FEATURES_SYSTEM_DEFENSE_STATUS_RESPONSE; + +typedef struct _GET_FEATURES_WEB_UI_STATE_RESPONSE +{ + AMT_BOOLEAN WebUiEnabled; +} GET_FEATURES_WEB_UI_STATUS_RESPONSE; + +typedef union _FEATURES_STATUS_DATA +{ + GET_FEATURES_REDIRECTION_SESSION_STATUS rs; + GET_FEATURES_SYSTEM_DEFENSE_STATUS_RESPONSE sd; + GET_FEATURES_WEB_UI_STATUS_RESPONSE webUI; +} FEATURES_STATUS_DATA; + +typedef struct _CFG_GET_FEATURES_STATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT32 RequestID; + FEATURES_STATUS_DATA Data; +} CFG_GET_FEATURES_STATE_RESPONSE; + +typedef struct _CFG_GET_CURRENT_POWER_POLICY_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_CURRENT_POWER_POLICY_REQUEST; + +typedef struct _CFG_GET_CURRENT_POWER_POLICY_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_ANSI_STRING PolicyName; +} CFG_GET_CURRENT_POWER_POLICY_RESPONSE; + +typedef struct _CFG_GET_LAST_HOST_RESET_REASON_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_LAST_HOST_RESET_REASON_REQUEST; + +typedef struct _CFG_GET_LAST_HOST_RESET_REASON_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT32 Reason; + UINT32 RemoteControlTimeStamp; +} CFG_GET_LAST_HOST_RESET_REASON_RESPONSE; + +typedef struct _LAN_SETTINGS +{ + AMT_BOOLEAN Enabled; + CFG_IPv4_ADDRESS Ipv4Address; + AMT_BOOLEAN DhcpEnabled; + UINT8 DhcpIpMode; + UINT8 LinkStatus; + UINT8 MacAddress[6]; +} LAN_SETTINGS; + +typedef struct _CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 InterfaceIndex; +} CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST; + +typedef struct _CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_BOOLEAN Enabled; + CFG_IPv4_ADDRESS Ipv4Address; + AMT_BOOLEAN DhcpEnabled; + UINT8 DhcpIpMode; + UINT8 LinkStatus; + UINT8 MacAddress[6]; +} CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE; + +typedef struct _CFG_GET_SECURITY_PARAMETERS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_SECURITY_PARAMETERS_REQUEST; + +typedef struct _CFG_GET_SECURITY_PARAMETERS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_BOOLEAN EnterpriseMode; + AMT_BOOLEAN TLSEnabled; + AMT_BOOLEAN HWCryptoEnabled; + AMT_PROVISIONING_STATE ProvisioningState; + AMT_BOOLEAN NetworkInterfaceEnabled; + AMT_BOOLEAN SOLEnabled; + AMT_BOOLEAN IDEREnabled; + AMT_BOOLEAN FWUpdateEnabled; + AMT_BOOLEAN LinkIsUp; + AMT_BOOLEAN Reserved[8]; +} CFG_GET_SECURITY_PARAMETERS_RESPONSE; + +typedef struct _CFG_GET_DNS_SUFFIX_LIST_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_DNS_SUFFIX_LIST_REQUEST; + +typedef struct _CFG_GET_DNS_SUFFIX_LIST_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT16 DataLength; + UINT8 Data[0]; +} CFG_GET_DNS_SUFFIX_LIST_RESPONSE; + +/** + * CFG_SET_ENTERPRISE_ACCESS_REQUEST + * + * Flags Bit 0 - If this bit is set then HostIPAddress is IPv6, otherwise HostIPAddress is IPv4 address. + * Bits 1..7 - Reserved, should be zero. + * HostIPAddress IPv4 / IPv6 address + * EnterpriseAccess 1 if LMS has access to enterprise network, otherwise 0. + */ +typedef struct _CFG_SET_ENTERPRISE_ACCESS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT8 Flags; + UINT8 HostIPAddress[16]; + UINT8 EnterpriseAccess; +} CFG_SET_ENTERPRISE_ACCESS_REQUEST; + +typedef struct _CFG_SET_ENTERPRISE_ACCESS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_SET_ENTERPRISE_ACCESS_RESPONSE; + +typedef struct _CFG_OPEN_USER_INITIATED_CONNECTION_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_OPEN_USER_INITIATED_CONNECTION_REQUEST; + +typedef struct _CFG_OPEN_USER_INITIATED_CONNECTION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_OPEN_USER_INITIATED_CONNECTION_RESPONSE; + +typedef struct _CFG_CLOSE_USER_INITIATED_CONNECTION_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_CLOSE_USER_INITIATED_CONNECTION_REQUEST; + +typedef struct _CFG_CLOSE_USER_INITIATED_CONNECTION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_CLOSE_USER_INITIATED_CONNECTION_RESPONSE; + +typedef enum +{ + AMT_NETWORK_CONNECTION_DIRECT = 0, + AMT_NETWORK_CONNECTION_VPN, + AMT_NETWORK_CONNECTION_OUTSIDE_ENTERPRISE +} AMT_NETWORK_CONNECTION_STATUS; + +typedef enum +{ + REMOTE_ACCESS_CONNECTION_STATUS_NOT_CONNECTED = 0, + REMOTE_ACCESS_CONNECTION_STATUS_CONNECTING, + REMOTE_ACCESS_CONNECTION_STATUS_CONNECTED +} REMOTE_ACCESS_CONNECTION_STATUS; + +typedef enum +{ + REMOTE_ACCESS_CONNECTION_TRIGGER_USER_INITIATED = 0, + REMOTE_ACCESS_CONNECTION_TRIGGER_ALERT, + REMOTE_ACCESS_CONNECTION_TRIGGER_PROVISIONING, + REMOTE_ACCESS_CONNECTION_TRIGGER_PERIODIC +} REMOTE_ACCESS_CONNECTION_TRIGGER; + +typedef struct _CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST; + +typedef struct _CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_NETWORK_CONNECTION_STATUS AmtNetworkConnectionStatus; + REMOTE_ACCESS_CONNECTION_STATUS RemoteAccessConnectionStatus; + REMOTE_ACCESS_CONNECTION_TRIGGER RemoteAccessConnectionTrigger; + AMT_ANSI_STRING MpsHostname; +} CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE; + +typedef struct _REMOTE_ACCESS_STATUS +{ + AMT_NETWORK_CONNECTION_STATUS AmtNetworkConnectionStatus; + REMOTE_ACCESS_CONNECTION_STATUS RemoteAccessConnectionStatus; + REMOTE_ACCESS_CONNECTION_TRIGGER RemoteAccessConnectionTrigger; + AMT_ANSI_STRING MpsHostname; +} REMOTE_ACCESS_STATUS; + + +typedef UINT8 AMT_UUID[16]; + +//const AMT_UUID AMT_UUID_LINK_STATE; + +typedef struct _STATE_DATA +{ + UINT8 LinkStatus; // (0 - down; 1 - up) + UINT8 HardSKU; + UINT8 CryptoFuse; // (0 - disabled; 1 - enabled) + UINT8 FlashProtaction; // (0 - disabled; 1 - enabled) + UINT8 LastMEResetReason; +} STATE_DATA; + +typedef struct _STATE_GET_AMT_STATE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + AMT_UUID StateVariableIdentifier; +} STATE_GET_AMT_STATE_REQUEST; + +typedef struct _STATE_GET_AMT_STATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_UUID StateDataIdentifier; + UINT32 ByteCount; + STATE_DATA StateData; +} STATE_GET_AMT_STATE_RESPONSE; + + +typedef struct _AMT_HASH_HANDLES +{ + UINT32 Length; + UINT32 Handles[CERT_HASH_MAX_NUMBER]; +} AMT_HASH_HANDLES; + +typedef struct _CERTHASH_ENTRY +{ + AMT_BOOLEAN IsDefault; + AMT_BOOLEAN IsActive; + UINT8 CertificateHash[CERT_HASH_MAX_LENGTH]; + UINT8 HashAlgorithm; + AMT_ANSI_STRING Name; +} CERTHASH_ENTRY; + +typedef enum +{ + CERT_HASH_ALGORITHM_MD5 = 0, // 16 bytes + CERT_HASH_ALGORITHM_SHA1, // 20 bytes + CERT_HASH_ALGORITHM_SHA256, // 32 bytes + CERT_HASH_ALGORITHM_SHA512, // 64 bytes +} CERT_HASH_ALGORITHM; + +typedef struct +{ + UINT16 Year; + UINT16 Month; + UINT16 DayOfWeek; + UINT16 Day; + UINT16 Hour; + UINT16 Minute; + UINT16 Second; +} TIME_DATE; + +typedef struct _AMT_PROV_AUDIT_RECORD +{ + UINT8 ProvisioningTLSMode; + AMT_BOOLEAN SecureDNS; + AMT_BOOLEAN HostInitiated; + CERT_HASH_ALGORITHM SelectedHashType; + UINT8 SelectedHashData[CERT_HASH_MAX_LENGTH]; + UINT8 CaCertificateSerials[NET_TLS_CERT_PKI_MAX_SERIAL_NUMS*NET_TLS_CERT_PKI_MAX_SERIAL_NUM_LENGTH]; + AMT_BOOLEAN AdditionalCaSerialNums; + AMT_BOOLEAN IsOemDefault; + AMT_BOOLEAN IsTimeValid; + UINT32 ProvServerIP; + TIME_DATE TlsStartTime; + AMT_ANSI_STRING ProvServerFQDN; +} AMT_PROV_AUDIT_RECORD; + +typedef struct _CFG_GENERATE_RNG_SEED_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_GENERATE_RNG_SEED_RESPONSE; + +typedef struct _CFG_GET_RNG_SEED_STATUS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_RNG_STATUS RngStatus; +} CFG_GET_RNG_SEED_STATUS_RESPONSE; + +typedef struct _CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_BOOLEAN ZeroTouchEnabled; +} CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE; + +typedef struct _CFG_GET_PROVISIONING_TLS_MODE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_PROVISIONING_TLS_MODE ProvisioningTlsMode; +} CFG_GET_PROVISIONING_TLS_MODE_RESPONSE; + +typedef struct _CFG_START_CONFIGURATION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_START_CONFIGURATION_RESPONSE; + +typedef struct _CFG_STOP_CONFIGURATION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_STOP_CONFIGURATION_RESPONSE; + +typedef struct _CFG_SET_PROVISIONING_SERVER_OTP_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_SET_PROVISIONING_SERVER_OTP_RESPONSE; + +typedef struct _CFG_SET_DNS_SUFFIX_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_SET_DNS_SUFFIX_RESPONSE; + +typedef struct _CFG_GET_HASH_HANDLES_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_HASH_HANDLES HashHandles; +} CFG_GET_HASH_HANDLES_RESPONSE; + +typedef struct _CFG_GET_CERTHASH_ENTRY_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 HashHandle; +} CFG_GET_CERTHASH_ENTRY_REQUEST; + +typedef struct _CFG_GET_CERTHASH_ENTRY_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + CERTHASH_ENTRY Hash; +} CFG_GET_CERTHASH_ENTRY_RESPONSE; + +typedef struct _CFG_GET_PKI_FQDN_SUFFIX_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_ANSI_STRING Suffix; +} CFG_GET_PKI_FQDN_SUFFIX_RESPONSE; + +typedef struct CFG_SET_HOST_FQDN_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT16 FQDNL; + char FQDN[256]; +}CFG_SET_HOST_FQDN_REQUEST; + +typedef struct _CFG_SET_HOST_FQDN_RESPONSE +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; +} CFG_SET_HOST_FQDN_RESPONSE; + +typedef struct _CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; + LOCAL_SYSTEM_ACCOUNT Account; + } CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE; + +typedef struct _CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT8 Reserved[40]; +} CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST; + +typedef struct _CFG_UNPROVISION_RESPONSE +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; + } CFG_UNPROVISION_RESPONSE; + +typedef struct _CFG_UNPROVISION_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 Mode; +} CFG_UNPROVISION_REQUEST; + +typedef struct _CFG_GETEHBPSTATE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GETEHBPSTATE_REQUEST; + +typedef struct _CFG_GETEHBPSTATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_EHBC_STATE EHBCState; +} CFG_GETEHBPSTATE_RESPONSE; + +typedef struct _CFG_GET_CONTROL_MODE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_CONTROL_MODE_REQUEST; + +typedef struct _CFG_GET_CONTROL_MODE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + int state; +} CFG_GET_CONTROL_MODE_RESPONSE; + +typedef struct _CFG_GET_UUID_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_UUID_REQUEST; + +typedef struct _CFG_GET_UUID_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_UUID UUID; +} CFG_GET_UUID_RESPONSE; + +#pragma pack() + + +AMT_STATUS pthi_GetCodeVersions(CODE_VERSIONS *codeVersions); +AMT_STATUS pthi_GetProvisioningMode(CFG_PROVISIONING_MODE *provisioningMode, AMT_BOOLEAN *legacy); +AMT_STATUS pthi_GetProvisioningState(AMT_PROVISIONING_STATE *state); +AMT_STATUS pthi_GetMacAddresses(UINT8 DedicatedMac[6], UINT8 HostMac[6]); +AMT_STATUS pthi_GetFeaturesState(UINT32 requestID, AMT_BOOLEAN (*requestStatus)[2]); +AMT_STATUS pthi_GetLastHostResetReason(UINT32 *Reason, UINT32 *RemoteControlTimeStamp); +AMT_STATUS pthi_GetCurrentPowerPolicy(AMT_ANSI_STRING *policyName); +AMT_STATUS pthi_GetLanInterfaceSettings(UINT32 interfaceSettings, LAN_SETTINGS *lanSettings); +AMT_STATUS pthi_GetHeciVersion(HECI_VERSION *hecVersion); +AMT_STATUS pthi_GetTLSEnabled(AMT_BOOLEAN *tlsEnabled); +//AMT_STATUS pthi_GetDNSSuffixList(std::list *dnsSuffixList); +AMT_STATUS pthi_SetEnterpriseAccess(UINT8 Flags, UINT8 HostIPAddress[16], UINT8 EnterpriseAccess); +AMT_STATUS pthi_GetFWResetReason(UINT8 *MEResetReason); +AMT_STATUS pthi_OpenUserInitiatedConnection(); +AMT_STATUS pthi_CloseUserInitiatedConnection(); +AMT_STATUS pthi_GetRemoteAccessConnectionStatus(REMOTE_ACCESS_STATUS *remoteAccessStatus); +AMT_STATUS pthi_GenerateRngKey(); +AMT_STATUS pthi_GetRngSeedStatus(AMT_RNG_STATUS *rngStatus); +AMT_STATUS pthi_GetZeroTouchEnabled(AMT_BOOLEAN *zeroTouchEnabled); +AMT_STATUS pthi_GetProvisioningTlsMode(AMT_PROVISIONING_TLS_MODE *provisioningTlsMode); +AMT_STATUS pthi_StartConfiguration(); +AMT_STATUS pthi_StopConfiguration(); +AMT_STATUS pthi_SetProvisioningServerOTP(AMT_ANSI_STRING passwordOTP); +AMT_STATUS pthi_SetDnsSuffix(AMT_ANSI_STRING dnsSuffix); +AMT_STATUS pthi_EnumerateHashHandles(AMT_HASH_HANDLES *hashHandles); +AMT_STATUS pthi_GetCertificateHashEntry(UINT32 hashHandle, CERTHASH_ENTRY *hashEntry); +AMT_STATUS pthi_GetDnsSuffix(AMT_ANSI_STRING *dnsSuffix); +AMT_STATUS pthi_SetHostFQDN(char* str); +AMT_STATUS pthi_GetLocalSystemAccount(LOCAL_SYSTEM_ACCOUNT *localAccount); +AMT_STATUS pthi_Unprovision(CFG_PROVISIONING_MODE provisionMode); +AMT_STATUS pthi_GetStateEHBC(AMT_EHBC_STATE *state); +AMT_STATUS pthi_GetControlMode(int *state); +AMT_STATUS pthi_GetUUID(AMT_UUID *uuid); + +#define PROVISIONING_MODE_REQUEST 0x04000008 +#define PROVISIONING_MODE_RESPONSE 0x04800008 +const PTHI_MESSAGE_HEADER GET_PROVISIONING_MODE_HEADER; + +#define UNPROVISION_REQUEST 0x04000010 +#define UNPROVISION_RESPONSE 0x04800010 +const PTHI_MESSAGE_HEADER UNPROVISION_HEADER; + +#define PROVISIONING_STATE_REQUEST 0x04000011 +#define PROVISIONING_STATE_RESPONSE 0x04800011 +const PTHI_MESSAGE_HEADER GET_PROVISIONING_STATE_HEADER; + +#define CODE_VERSIONS_REQUEST 0x0400001A +#define CODE_VERSIONS_RESPONSE 0x0480001A +const PTHI_MESSAGE_HEADER GET_CODE_VERSION_HEADER; + +#define GET_SECURITY_PARAMETERS_REQUEST 0x0400001B +#define GET_SECURITY_PARAMETERS_RESPONSE 0x0480001B +//const PTHI_MESSAGE_HEADER GET_SECURITY_PARAMETERS_HEADER; + +#define GET_MAC_ADDRESSES_REQUEST 0x04000025 +#define GET_MAC_ADDRESSES_RESPONSE 0x04800025 +const PTHI_MESSAGE_HEADER GET_MAC_ADDRESSES_HEADER; + +#define GENERATE_RNG_SEED_REQUEST 0x04000028 +#define GENERATE_RNG_SEED_RESPONSE 0x04800028 +//const PTHI_MESSAGE_HEADER GENERATE_RNG_SEED_HEADER; + +#define SET_PROVISIONING_SERVER_OTP_REQUEST 0x0400002A +#define SET_PROVISIONING_SERVER_OTP_RESPONSE 0x0480002A + +#define SET_DNS_SUFFIX_REQUEST 0x0400002F +#define SET_DNS_SUFFIX_RESPONSE 0x0480002F + +#define ENUMERATE_HASH_HANDLES_REQUEST 0x0400002C +#define ENUMERATE_HASH_HANDLES_RESPONSE 0x0480002C +//const PTHI_MESSAGE_HEADER ENUMERATE_HASH_HANDLES_HEADER; + +#define GET_RNG_SEED_STATUS_REQUEST 0x0400002E +#define GET_RNG_SEED_STATUS_RESPONSE 0x0480002E +//const PTHI_MESSAGE_HEADER GET_RNG_SEED_STATUS_HEADER; + +#define GET_DNS_SUFFIX_LIST_REQUEST 0x0400003E +#define GET_DNS_SUFFIX_LIST_RESPONSE 0x0480003E +//const PTHI_MESSAGE_HEADER GET_DNS_SUFFIX_LIST_HEADER; + +#define SET_ENTERPRISE_ACCESS_REQUEST 0x0400003F +#define SET_ENTERPRISE_ACCESS_RESPONSE 0x0480003F +//const PTHI_MESSAGE_HEADER SET_ENTERPRISE_ACCESS_HEADER; + +#define OPEN_USER_INITIATED_CONNECTION_REQUEST 0x04000044 +#define OPEN_USER_INITIATED_CONNECTION_RESPONSE 0x04800044 +//const PTHI_MESSAGE_HEADER OPEN_USER_INITIATED_CONNECTION_HEADER; + +#define CLOSE_USER_INITIATED_CONNECTION_REQUEST 0x04000045 +#define CLOSE_USER_INITIATED_CONNECTION_RESPONSE 0x04800045 +//const PTHI_MESSAGE_HEADER CLOSE_USER_INITIATED_CONNECTION_HEADER; + +#define GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST 0x04000046 +#define GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE 0x04800046 +//const PTHI_MESSAGE_HEADER GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER; + +#define GET_CURRENT_POWER_POLICY_REQUEST 0x04000047 +#define GET_CURRENT_POWER_POLICY_RESPONSE 0x04800047 +const PTHI_MESSAGE_HEADER GET_CURRENT_POWER_POLICY_HEADER; + +#define GET_LAN_INTERFACE_SETTINGS_REQUEST 0x04000048 +#define GET_LAN_INTERFACE_SETTINGS_RESPONSE 0x04800048 +//const PTHI_MESSAGE_HEADER GET_LAN_INTERFACE_SETTINGS_HEADER; + +#define GET_FEATURES_STATE_REQUEST 0x04000049 +#define GET_FEATURES_STATE_RESPONSE 0x04800049 +const PTHI_MESSAGE_HEADER GET_FEATURES_STATE_HEADER; + +#define GET_LAST_HOST_RESET_REASON_REQUEST 0x0400004A +#define GET_LAST_HOST_RESET_REASON_RESPONSE 0x0480004A +//const PTHI_MESSAGE_HEADER GET_LAST_HOST_RESET_REASON_HEADER; + +#define GET_AMT_STATE_REQUEST 0x01000001 +#define GET_AMT_STATE_RESPONSE 0x01800001 +//const PTHI_MESSAGE_HEADER GET_AMT_STATE_HEADER; + +#define GET_ZERO_TOUCH_ENABLED_REQUEST 0x04000030 +#define GET_ZERO_TOUCH_ENABLED_RESPONSE 0x04800030 +//const PTHI_MESSAGE_HEADER GET_ZERO_TOUCH_ENABLED_HEADER; + +#define GET_PROVISIONING_TLS_MODE_REQUEST 0x0400002B +#define GET_PROVISIONING_TLS_MODE_RESPONSE 0x0480002B +//const PTHI_MESSAGE_HEADER GET_PROVISIONING_TLS_MODE_HEADER; + +#define START_CONFIGURATION_REQUEST 0x04000029 +#define START_CONFIGURATION_RESPONSE 0x04800029 +//const PTHI_MESSAGE_HEADER START_CONFIGURATION_HEADER; + +#define GET_CERTHASH_ENTRY_REQUEST 0x0400002D +#define GET_CERTHASH_ENTRY_RESPONSE 0x0480002D +//const PTHI_MESSAGE_HEADER GET_CERTHASH_ENTRY_HEADER; + +#define GET_PKI_FQDN_SUFFIX_REQUEST 0x04000036 +#define GET_PKI_FQDN_SUFFIX_RESPONSE 0x04800036 +// + +#define SET_HOST_FQDN_REQUEST 0x0400005b +#define SET_HOST_FQDN_RESPONSE 0x0480005b +//const PTHI_MESSAGE_HEADER GET_PKI_FQDN_SUFFIX_HEADER; + +#define GET_LOCAL_SYSTEM_ACCOUNT_REQUEST 0x04000067 +#define GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE 0x04800067 +//const PTHI_MESSAGE_HEADER GET_LOCAL_SYSTEM_ACCOUNT_HEADER; + +#define GET_EHBC_STATE_REQUEST 0x4000084 +#define GET_EHBC_STATE_RESPONSE 0x4800084 + +const PTHI_MESSAGE_HEADER GET_EHBC_STATE_HEADER; + +#define GET_CONTROL_MODE_REQUEST 0x400006b +#define GET_CONTROL_MODE_RESPONSE 0x480006b +const PTHI_MESSAGE_HEADER GET_CONTROL_MODE_HEADER; + +#define STOP_CONFIGURATION_REQUEST 0x400005e +#define STOP_CONFIGURATION_RESPONSE 0x480005e + +#define GET_UUID_REQUEST 0x400005c +#define GET_UUID_RESPONSE 0x480005c + +#endif + +#endif diff --git a/MicroLMS/heci/StatusCodeDefinitions.h b/MicroLMS/heci/StatusCodeDefinitions.h new file mode 100644 index 0000000..c903f97 --- /dev/null +++ b/MicroLMS/heci/StatusCodeDefinitions.h @@ -0,0 +1,403 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +//---------------------------------------------------------------------------- +// +// File: StatusCodeDefinitions.h +// +// Notes: This file contains the definitions of the status codes +// as defined in the Intel® AMT Network Design Guide. +// +//---------------------------------------------------------------------------- + +#ifndef STATUS_CODE_DEFINITIONS_H +#define STATUS_CODE_DEFINITIONS_H + +typedef unsigned int PT_STATUS; +//typedef unsigned int AMT_STATUS; + +//Request succeeded +#define PT_STATUS_SUCCESS 0x0 +#define AMT_STATUS_SUCCESS 0x0 + +//An internal error in the Intel® AMT device has occurred +#define PT_STATUS_INTERNAL_ERROR 0x1 +#define AMT_STATUS_INTERNAL_ERROR 0x1 + +//Intel® AMT device has not progressed far enough in its +//initialization to process the command. +#define PT_STATUS_NOT_READY 0x2 +#define AMT_STATUS_NOT_READY 0x2 + +//Command is not permitted in current operating mode. +#define PT_STATUS_INVALID_PT_MODE 0x3 +#define AMT_STATUS_INVALID_PT_MODE 0x3 + +//Length field of header is invalid. +#define PT_STATUS_INVALID_MESSAGE_LENGTH 0x4 +#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4 + +//The requested hardware asset inventory table +//checksum is not available. +#define PT_STATUS_TABLE_FINGERPRINT_NOT_AVAILABLE 0x5 +#define AMT_STATUS_TABLE_FINGERPRINT_NOT_AVAILABLE 0x5 + +//The Integrity Check Value field of the request +//message sent by Intel® AMT enabled device is invalid. +#define PT_STATUS_INTEGRITY_CHECK_FAILED 0x6 +#define AMT_STATUS_INTEGRITY_CHECK_FAILED 0x6 + +//The specified ISV version is not supported +#define PT_STATUS_UNSUPPORTED_ISVS_VERSION 0x7 +#define AMT_STATUS_UNSUPPORTED_ISVS_VERSION 0x7 + +//The specified queried application is not registered. +#define PT_STATUS_APPLICATION_NOT_REGISTERED 0x8 +#define AMT_STATUS_APPLICATION_NOT_REGISTERED 0x8 + +//Either an invalid name or a not previously registered +//“Enterprise” name was specified +#define PT_STATUS_INVALID_REGISTRATION_DATA 0x9 +#define AMT_STATUS_INVALID_REGISTRATION_DATA 0x9 + +//The application handle provided in the request +//message has never been allocated. +#define PT_STATUS_APPLICATION_DOES_NOT_EXIST 0xA +#define AMT_STATUS_APPLICATION_DOES_NOT_EXIST 0xA + +//The requested number of bytes cannot be allocated in ISV storage. +#define PT_STATUS_NOT_ENOUGH_STORAGE 0xB +#define AMT_STATUS_NOT_ENOUGH_STORAGE 0xB + +//The specified name is invalid. +#define PT_STATUS_INVALID_NAME 0xC +#define AMT_STATUS_INVALID_NAME 0xC + +//The specified block does not exist. +#define PT_STATUS_BLOCK_DOES_NOT_EXIST 0xD +#define AMT_STATUS_BLOCK_DOES_NOT_EXIST 0xD + +//The specified byte offset is invalid. +#define PT_STATUS_INVALID_BYTE_OFFSET 0xE +#define AMT_STATUS_INVALID_BYTE_OFFSET 0xE + +//The specified byte count is invalid. +#define PT_STATUS_INVALID_BYTE_COUNT 0xF +#define AMT_STATUS_INVALID_BYTE_COUNT 0xF + +//The requesting application is not +//permitted to request execution of the specified operation. +#define PT_STATUS_NOT_PERMITTED 0x10 +#define AMT_STATUS_NOT_PERMITTED 0x10 + +//The requesting application is not the owner of the block +//as required for the requested operation. +#define PT_STATUS_NOT_OWNER 0x11 +#define AMT_STATUS_NOT_OWNER 0x11 + +//The specified block is locked by another application. +#define PT_STATUS_BLOCK_LOCKED_BY_OTHER 0x12 +#define AMT_STATUS_BLOCK_LOCKED_BY_OTHER 0x12 + +//The specified block is not locked. +#define PT_STATUS_BLOCK_NOT_LOCKED 0x13 +#define AMT_STATUS_BLOCK_NOT_LOCKED 0x13 + +//The specified group permission bits are invalid. +#define PT_STATUS_INVALID_GROUP_PERMISSIONS 0x14 +#define AMT_STATUS_INVALID_GROUP_PERMISSIONS 0x14 + +//The specified group does not exist. +#define PT_STATUS_GROUP_DOES_NOT_EXIST 0x15 +#define AMT_STATUS_GROUP_DOES_NOT_EXIST 0x15 + +//The specified member count is invalid. +#define PT_STATUS_INVALID_MEMBER_COUNT 0x16 +#define AMT_STATUS_INVALID_MEMBER_COUNT 0x16 + +//The request cannot be satisfied because a maximum +//limit associated with the request has been reached. +#define PT_STATUS_MAX_LIMIT_REACHED 0x17 +#define AMT_STATUS_MAX_LIMIT_REACHED 0x17 + +//specified key algorithm is invalid. +#define PT_STATUS_INVALID_AUTH_TYPE 0x18 +#define AMT_STATUS_INVALID_AUTH_TYPE 0x18 + +//Not Used +#define PT_STATUS_AUTHENTICATION_FAILED 0x19 +#define AMT_STATUS_AUTHENTICATION_FAILED 0x19 + +//The specified DHCP mode is invalid. +#define PT_STATUS_INVALID_DHCP_MODE 0x1A +#define AMT_STATUS_INVALID_DHCP_MODE 0x1A + +//The specified IP address is not a valid IP unicast address. +#define PT_STATUS_INVALID_IP_ADDRESS 0x1B +#define AMT_STATUS_INVALID_IP_ADDRESS 0x1B + +//The specified domain name is not a valid domain name. +#define PT_STATUS_INVALID_DOMAIN_NAME 0x1C +#define AMT_STATUS_INVALID_DOMAIN_NAME 0x1C + +//Not Used +#define PT_STATUS_UNSUPPORTED_VERSION 0x1D +#define AMT_STATUS_UNSUPPORTED_VERSION 0x1D + +//The requested operation cannot be performed because a +//prerequisite request message has not been received. +#define PT_STATUS_REQUEST_UNEXPECTED 0x1E +#define AMT_STATUS_REQUEST_UNEXPECTED 0x1E + +//Not Used +#define PT_STATUS_INVALID_TABLE_TYPE 0x1F +#define AMT_STATUS_INVALID_TABLE_TYPE 0x1F + +//The specified provisioning mode code is undefined. +#define PT_STATUS_INVALID_PROVISIONING_STATE 0x20 +#define AMT_STATUS_INVALID_PROVISIONING_STATE 0x20 + +//Not Used +#define PT_STATUS_UNSUPPORTED_OBJECT 0x21 +#define AMT_STATUS_UNSUPPORTED_OBJECT 0x21 + +//The specified time was not accepted by the Intel® AMT device +//since it is earlier than the baseline time set for the device. +#define PT_STATUS_INVALID_TIME 0x22 +#define AMT_STATUS_INVALID_TIME 0x22 + +//StartingIndex is invalid. +#define PT_STATUS_INVALID_INDEX 0x23 +#define AMT_STATUS_INVALID_INDEX 0x23 + +//A parameter is invalid. +#define PT_STATUS_INVALID_PARAMETER 0x24 +#define AMT_STATUS_INVALID_PARAMETER 0x24 + +//An invalid netmask was supplied +//(a valid netmask is an IP address in which all ‘1’s are before +//the ‘0’ – e.g. FFFC0000h is valid, FF0C0000h is invalid). +#define PT_STATUS_INVALID_NETMASK 0x25 +#define AMT_STATUS_INVALID_NETMASK 0x25 + +//The operation failed because the Flash wear-out +//protection mechanism prevented a write to an NVRAM sector. +#define PT_STATUS_FLASH_WRITE_LIMIT_EXCEEDED 0x26 +#define AMT_STATUS_FLASH_WRITE_LIMIT_EXCEEDED 0x26 + +//ME FW did not receive the entire image file. +#define PT_STATUS_INVALID_IMAGE_LENGTH 0x27 +#define AMT_STATUS_INVALID_IMAGE_LENGTH 0x27 + +//ME FW received an image file with an invalid signature. +#define PT_STATUS_INVALID_IMAGE_SIGNATURE 0x28 +#define AMT_STATUS_INVALID_IMAGE_SIGNATURE 0x28 + +//LME can not support the requested version. +#define PT_STATUS_PROPOSE_ANOTHER_VERSION 0x29 +#define AMT_STATUS_PROPOSE_ANOTHER_VERSION 0x29 + +//The PID must be a 64 bit quantity made up of ASCII codes +//of some combination of 8 characters – +//capital alphabets (A–Z), and numbers (0–9). +#define PT_STATUS_INVALID_PID_FORMAT 0x2A +#define AMT_STATUS_INVALID_PID_FORMAT 0x2A + +//The PPS must be a 256 bit quantity made up of ASCII codes +//of some combination of 32 characters – +//capital alphabets (A–Z), and numbers (0–9). +#define PT_STATUS_INVALID_PPS_FORMAT 0x2B +#define AMT_STATUS_INVALID_PPS_FORMAT 0x2B + +//Full BIST test has been blocked +#define PT_STATUS_BIST_COMMAND_BLOCKED 0x2C +#define AMT_STATUS_BIST_COMMAND_BLOCKED 0x2C + +//A TCP/IP connection could not be opened on with the selected port. +#define PT_STATUS_CONNECTION_FAILED 0x2D +#define AMT_STATUS_CONNECTION_FAILED 0x2D + +//Max number of connection reached. +//LME can not open the requested connection. +#define PT_STATUS_CONNECTION_TOO_MANY 0x2E +#define AMT_STATUS_CONNECTION_TOO_MANY 0x2E + +//Random key generation is in progress. +#define PT_STATUS_RNG_GENERATION_IN_PROGRESS 0x2F +#define AMT_STATUS_RNG_GENERATION_IN_PROGRESS 0x2F + +//A randomly generated key does not exist. +#define PT_STATUS_RNG_NOT_READY 0x30 +#define AMT_STATUS_RNG_NOT_READY 0x30 + +//Self-generated AMT certificate does not exist. +#define PT_STATUS_CERTIFICATE_NOT_READY 0x31 +#define AMT_STATUS_CERTIFICATE_NOT_READY 0x31 + +//This code establishes a dividing line between +//status codes which are common to host interface and +//network interface and status codes which are used by +//network interface only. +#define PT_STATUS_NETWORK_IF_ERROR_BASE 0x800 +#define AMT_STATUS_NETWORK_IF_ERROR_BASE 0x800 + +//The OEM number specified in the remote control +//command is not supported by the Intel® AMT device +#define PT_STATUS_UNSUPPORTED_OEM_NUMBER 0x801 +#define AMT_STATUS_UNSUPPORTED_OEM_NUMBER 0x801 + +//The boot option specified in the remote control command +//is not supported by the Intel® AMT device +#define PT_STATUS_UNSUPPORTED_BOOT_OPTION 0x802 +#define AMT_STATUS_UNSUPPORTED_BOOT_OPTION 0x802 + +//The command specified in the remote control command +//is not supported by the Intel® AMT device +#define PT_STATUS_INVALID_COMMAND 0x803 +#define AMT_STATUS_INVALID_COMMAND 0x803 + +//The special command specified in the remote control command +//is not supported by the Intel® AMT device +#define PT_STATUS_INVALID_SPECIAL_COMMAND 0x804 +#define AMT_STATUS_INVALID_SPECIAL_COMMAND 0x804 + +//The handle specified in the command is invalid +#define PT_STATUS_INVALID_HANDLE 0x805 +#define AMT_STATUS_INVALID_HANDLE 0x805 + +//The password specified in the User ACL is invalid +#define PT_STATUS_INVALID_PASSWORD 0x806 +#define AMT_STATUS_INVALID_PASSWORD 0x806 + +//The realm specified in the User ACL is invalid +#define PT_STATUS_INVALID_REALM 0x807 +#define AMT_STATUS_INVALID_REALM 0x807 + +//The FPACL or EACL entry is used by an active +//registration and cannot be removed or modified. +#define PT_STATUS_STORAGE_ACL_ENTRY_IN_USE 0x808 +#define AMT_STATUS_STORAGE_ACL_ENTRY_IN_USE 0x808 + +//Essential data is missing on CommitChanges command. +#define PT_STATUS_DATA_MISSING 0x809 +#define AMT_STATUS_DATA_MISSING 0x809 + +//The parameter specified is a duplicate of an existing value. +//Returned for a case where duplicate entries are added to FPACL +//(Factory Partner Allocation Control List) or EACL +//(Enterprise Access Control List) lists. +#define PT_STATUS_DUPLICATE 0x80A +#define AMT_STATUS_DUPLICATE 0x80A + +//Event Log operation failed due to the current freeze status of the log. +#define PT_STATUS_EVENTLOG_FROZEN 0x80B +#define AMT_STATUS_EVENTLOG_FROZEN 0x80B + +//The device is missing private key material. +#define PT_STATUS_PKI_MISSING_KEYS 0x80C +#define AMT_STATUS_PKI_MISSING_KEYS 0x80C + +//The device is currently generating a keypair. +//Caller may try repeating this operation at a later time. +#define PT_STATUS_PKI_GENERATING_KEYS 0x80D +#define AMT_STATUS_PKI_GENERATING_KEYS 0x80D + +//An invalid Key was entered. +#define PT_STATUS_INVALID_KEY 0x80E +#define AMT_STATUS_INVALID_KEY 0x80E + +//An invalid X.509 certificate was entered. +#define PT_STATUS_INVALID_CERT 0x80F +#define AMT_STATUS_INVALID_CERT 0x80F + +//Certificate Chain and Private Key do not match. +#define PT_STATUS_CERT_KEY_NOT_MATCH 0x810 +#define AMT_STATUS_CERT_KEY_NOT_MATCH 0x810 + +//The request cannot be satisfied because the maximum +//number of allowed Kerberos domains has been reached. +//(The domain is determined by the first 24 Bytes of the SID.) +#define PT_STATUS_MAX_KERB_DOMAIN_REACHED 0x811 +#define AMT_STATUS_MAX_KERB_DOMAIN_REACHED 0x811 + +// The requested configuration is unsupported +#define PT_STATUS_UNSUPPORTED 0x812 +#define AMT_STATUS_UNSUPPORTED 0x812 + +// A profile with the requested priority already exists +#define PT_STATUS_INVALID_PRIORITY 0x813 +#define AMT_STATUS_INVALID_PRIORITY 0x813 + +// Unable to find specified element +#define PT_STATUS_NOT_FOUND 0x814 +#define AMT_STATUS_NOT_FOUND 0x814 + +// Invalid User credentials +#define PT_STATUS_INVALID_CREDENTIALS 0x815 +#define AMT_STATUS_INVALID_CREDENTIALS 0x815 + +// Passphrase is invalid +#define PT_STATUS_INVALID_PASSPHRASE 0x816 +#define AMT_STATUS_INVALID_PASSPHRASE 0x816 + +// A certificate handle must be chosen before the +// operation can be completed. +#define PT_STATUS_NO_ASSOCIATION 0x818 +#define AMT_STATUS_NO_ASSOCIATION 0x818 + +// The command is defined as Audit Log event and can not be +// logged. +#define PT_STATUS_AUDIT_FAIL 0x81B +#define AMT_STATUS_AUDIT_FAIL 0x81B + +// One of the ME components is not ready for unprovisioning. +#define PT_STATUS_BLOCKING_COMPONENT 0x81C +#define AMT_STATUS_BLOCKING_COMPONENT 0x81C + +//The application has identified an internal error +#define PTSDK_STATUS_INTERNAL_ERROR 0x1000 + +//An ISV operation was called while the library is not +//initialized +#define PTSDK_STATUS_NOT_INITIALIZED 0x1001 + +//The requested library I/F is not supported by the current library +//implementation. +#define PTSDK_STATUS_LIB_VERSION_UNSUPPORTED 0x1002 + +//One of the parameters is invalid (usually indicates a +//NULL pointer or an invalid session handle is specified) +#define PTSDK_STATUS_INVALID_PARAM 0x1003 + +//The SDK could not allocate sufficient resources to complete the operation. +#define PTSDK_STATUS_RESOURCES 0x1004 + +//The Library has identified a HW Internal error. +#define PTSDK_STATUS_HARDWARE_ACCESS_ERROR 0x1005 + +//The application that sent the request message is not registered. +//Usually indicates the registration timeout has elapsed. +//The caller should reregister with the Intel AMT enabled device. +#define PTSDK_STATUS_REQUESTOR_NOT_REGISTERED 0x1006 + +//A network error has occurred while processing the call. +#define PTSDK_STATUS_NETWORK_ERROR 0x1007 + +//Specified container can not hold the requested string +#define PTSDK_STATUS_PARAM_BUFFER_TOO_SHORT 0x1008 + +//For Windows only. +//ISVS_InitializeCOMinThread was not called by the current thread. +#define PTSDK_STATUS_COM_NOT_INITIALIZED_IN_THREAD 0x1009 + +//The URL parameter was not optional in current configuration. +#define PTSDK_STATUS_URL_REQUIRED 0x100A + +#endif + +#endif diff --git a/MicroLMS/heci/mei.h b/MicroLMS/heci/mei.h new file mode 100644 index 0000000..356d7cf --- /dev/null +++ b/MicroLMS/heci/mei.h @@ -0,0 +1,57 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _LINUX_MEI_H +#define _LINUX_MEI_H +// #include +// #include +typedef struct { + unsigned char b[16]; +} uuid_le; +#define IOCTL_MEI_CONNECT_CLIENT _IOWR('H' , 0x01, struct mei_connect_client_data) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct mei_client { + unsigned int max_msg_length; + unsigned char protocol_version; + unsigned char reserved[3]; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +struct mei_connect_client_data { + union { + uuid_le in_client_uuid; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct mei_client out_client_properties; + }; +}; +#define IOCTL_MEI_SETUP_DMA_BUF _IOWR('H' , 0x02, struct mei_client_dma_data) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IOCTL_MEI_UNSET_DMA_BUF _IOW('H' , 0x03, struct mei_client_dma_handle) +struct mei_client_dma_data { + union { + unsigned long userptr; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + }; + unsigned int length; + unsigned int handle; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct mei_client_dma_handle { + unsigned int handle; +}; +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/MicroLMS/microstack/ILibAsyncServerSocket.c b/MicroLMS/microstack/ILibAsyncServerSocket.c new file mode 100644 index 0000000..91520d8 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncServerSocket.c @@ -0,0 +1,614 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#include +#include +#define SOCKET_ERROR (-1) +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#if defined(WINSOCK2) +#include +#include +#elif defined(WINSOCK1) +#include +#include +#endif + +#include "ILibParsers.h" +#include "ILibAsyncServerSocket.h" +#include "ILibAsyncSocket.h" + +#define DEBUGSTATEMENT(x) + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) + +typedef struct ILibAsyncServerSocketModule +{ + ILibChain_Link ChainLink; + + int MaxConnection; + void **AsyncSockets; + ILibServerScope scope; + + SOCKET ListenSocket; + unsigned short portNumber, initialPortNumber; + int listening; + int loopbackFlag; + + ILibAsyncServerSocket_OnReceive OnReceive; + ILibAsyncServerSocket_OnConnect OnConnect; + ILibAsyncServerSocket_OnDisconnect OnDisconnect; + ILibAsyncServerSocket_OnInterrupt OnInterrupt; + ILibAsyncServerSocket_OnSendOK OnSendOK; + + void *Tag; + int Tag2; + +}ILibAsyncServerSocketModule; +struct ILibAsyncServerSocket_Data +{ + struct ILibAsyncServerSocketModule *module; + ILibAsyncServerSocket_BufferReAllocated Callback; + void *user; +}; + +const int ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE = (const int)sizeof(ILibAsyncServerSocketModule); + + +/*! \fn ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +\brief Returns the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to query +\returns The user Tag +*/ +void *ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +{ + return (((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag); +} +/*! \fn ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag) +\brief Sets the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to save the tag to +\param tag The value to save +*/ +void ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag) +{ + ((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag = tag; +} + +/*! \fn ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +\brief Returns the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to query +\returns The user Tag +*/ +int ILibAsyncServerSocket_GetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +{ + return(((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag2); +} +/*! \fn ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag) +\brief Sets the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to save the tag to +\param tag The value to save +*/ +void ILibAsyncServerSocket_SetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, int tag) +{ + ((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag2 = tag; +} + +// +// Internal method called by ILibAsyncSocket, to signal an interrupt condition +// +// The ILibAsyncServerSocket that was interrupted +// The associated user tag +void ILibAsyncServerSocket_OnInterruptSink(ILibAsyncSocket_SocketModule socketModule, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + if (data == NULL) return; + if (data->module->OnInterrupt != NULL) data->module->OnInterrupt(data->module, socketModule, data->user); + free(user); +} +// +// Chain PreSelect handler +// +// +// +// +// +// +void ILibAsyncServerSocket_PreSelect(void* socketModule, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + struct ILibAsyncServerSocketModule *module = (struct ILibAsyncServerSocketModule*)socketModule; + int flags,i; + + UNREFERENCED_PARAMETER( writeset ); + UNREFERENCED_PARAMETER( errorset ); + UNREFERENCED_PARAMETER( blocktime ); + + // + // The socket isn't put in listening mode, until the chain is started. + // If this variable == 0, that means we need to do that. + // + if (module->listening == 0) + { + // + // Set the socket to non-block mode, so we can play nice and share the thread + // +#ifdef WIN32 + flags = 1; + if(ioctlsocket(module->ListenSocket, FIONBIO, (u_long *)(&flags)) == SOCKET_ERROR) ILIBCRITICALEXIT2(246, WSAGetLastError()); +#elif _POSIX + flags = fcntl(module->ListenSocket, F_GETFL,0); + fcntl(module->ListenSocket, F_SETFL, O_NONBLOCK | flags); +#endif + + // + // Put the socket in Listen, and add it to the fdset for the Select loop + // + module->listening = 1; + listen(module->ListenSocket, 4); + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->ListenSocket, readset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + else if (module->ListenSocket != ~0) + { + // Only put the ListenSocket in the readset, if we are able to handle a new socket + for(i = 0; i < module->MaxConnection; ++i) + { + if (ILibAsyncSocket_IsFree(module->AsyncSockets[i]) != 0) + { + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->ListenSocket, readset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + break; + } + } + } +} +/*! \fn ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback) +\brief Set the callback handler for when the internal data buffer has been resized +\param AsyncServerSocketToken The ILibAsyncServerSocket to query +\param ConnectionToken The specific connection to set the callback with +\param Callback The callback handler to set +*/ +void ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)ILibAsyncSocket_GetUser(ConnectionToken); + UNREFERENCED_PARAMETER( AsyncServerSocketToken ); + if (data != NULL) data->Callback = Callback; +} + +// +// Chain PostSelect handler +// +// +// +// +// +// +void ILibAsyncServerSocket_PostSelect(void* socketModule, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset) +{ + struct ILibAsyncServerSocket_Data *data; + struct sockaddr_in6 addr; +#ifdef _POSIX + socklen_t addrlen; +#else + int addrlen; +#endif + + struct ILibAsyncServerSocketModule *module = (struct ILibAsyncServerSocketModule*)socketModule; + int i,flags; +#ifdef WIN32 + SOCKET NewSocket; +#elif defined( _POSIX) + int NewSocket; +#endif + + UNREFERENCED_PARAMETER( slct ); + UNREFERENCED_PARAMETER( writeset ); + UNREFERENCED_PARAMETER( errorset ); + + if (FD_ISSET(module->ListenSocket, readset) != 0) + { + // + // There are pending TCP connection requests + // + for(i = 0; i < module->MaxConnection; ++i) + { + // + // Check to see if we have available resources to handle this connection request + // + if (ILibAsyncSocket_IsFree(module->AsyncSockets[i]) != 0) + { + addrlen = sizeof(addr); + NewSocket = accept(module->ListenSocket, (struct sockaddr*)&addr, &addrlen); // Klocwork claims we could lose the resource acquired fom the declaration, but that is not possible in this case + + if (NewSocket != ~0) + { + //printf("Accepting new connection, socket = %d\r\n", NewSocket); + // + // Set this new socket to non-blocking mode, so we can play nice and share thread + // +#ifdef WIN32 + flags = 1; + if (ioctlsocket(NewSocket, FIONBIO, (u_long *)(&flags)) == SOCKET_ERROR) ILIBCRITICALEXIT2(246, WSAGetLastError()); +#elif _POSIX + flags = fcntl(NewSocket, F_GETFL,0); + fcntl(NewSocket, F_SETFL, O_NONBLOCK|flags); +#endif + // + // Instantiate a module to contain all the data about this connection + // + if ((data = (struct ILibAsyncServerSocket_Data*)malloc(sizeof(struct ILibAsyncServerSocket_Data))) == NULL) ILIBCRITICALEXIT(254); + memset(data, 0, sizeof(struct ILibAsyncServerSocket_Data)); + data->module = (struct ILibAsyncServerSocketModule*)socketModule; + + ILibAsyncSocket_UseThisSocket(module->AsyncSockets[i], NewSocket, &ILibAsyncServerSocket_OnInterruptSink, data); + ILibAsyncSocket_SetRemoteAddress(module->AsyncSockets[i], (struct sockaddr*)&addr); + + if (module->OnConnect != NULL) + { + // Notify the user about this new connection + module->OnConnect(module, module->AsyncSockets[i], &(data->user)); + } + } + else {break;} + } + } + } +} // Klocwork claims that we could lose the resource acquired in the declaration, but that is not possible in this case +// +// Chain Destroy handler +// +// +void ILibAsyncServerSocket_Destroy(void *socketModule) +{ + struct ILibAsyncServerSocketModule *module =(struct ILibAsyncServerSocketModule*)socketModule; + + free(module->AsyncSockets); + if (module->ListenSocket != (SOCKET)~0) + { +#ifdef WIN32 + closesocket(module->ListenSocket); +#elif _POSIX + close(module->ListenSocket); +#endif + module->ListenSocket = (SOCKET)~0; + } +} + +// +// Internal method dispatched by the OnData event of the underlying ILibAsyncSocket +// +// +// +// +// +// +// +// +void ILibAsyncServerSocket_OnData(ILibAsyncSocket_SocketModule socketModule,char* buffer,int *p_beginPointer, int endPointer,void (**OnInterrupt)(void *AsyncSocketMoudle, void *user),void **user, int *PAUSE) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)(*user); + int bpointer = *p_beginPointer; + + UNREFERENCED_PARAMETER( OnInterrupt ); + + // Pass the received data up + if (data != NULL && data->module->OnReceive != NULL) + { + data->module->OnReceive(data->module, socketModule, buffer, &bpointer, endPointer, &(data->module->OnInterrupt), &(data->user), PAUSE); + if (ILibAsyncSocket_IsFree(socketModule)) + { + *p_beginPointer = endPointer; + } + else + { + *p_beginPointer = bpointer; + } + } +} +// +// Internal method dispatched by the OnConnect event of the underlying ILibAsyncSocket. In this case, it will only occur when TLS connection is established. +// +// +// +void ILibAsyncServerSocket_OnConnectSink(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + + if (data == NULL) return; + if (Connected == 0) + { + // Connection Failed, clean up + free(data); + return; + } + + if (data->module->OnConnect != NULL) data->module->OnConnect(data->module, socketModule, &(data->user)); +} +// +// Internal method dispatched by the OnDisconnect event of the underlying ILibAsyncSocket +// +// +// +void ILibAsyncServerSocket_OnDisconnectSink(ILibAsyncSocket_SocketModule socketModule, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + + // Pass this Disconnect event up + if (data == NULL) return; + if (data->module->OnDisconnect != NULL) data->module->OnDisconnect(data->module, socketModule, data->user); + + // If the chain is shutting down, we need to free some resources + if (ILibIsChainBeingDestroyed(data->module->ChainLink.ParentChain) == 0) free(data); +} +// +// Internal method dispatched by the OnSendOK event of the underlying ILibAsyncSocket +// +// +// +void ILibAsyncServerSocket_OnSendOKSink(ILibAsyncSocket_SocketModule socketModule, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + + // Pass the OnSendOK event up + if (data != NULL && data->module->OnSendOK != NULL) data->module->OnSendOK(data->module, socketModule, data->user); +} +// +// Internal method dispatched by ILibAsyncSocket, to signal that the buffers have been reallocated +// +// The ILibAsyncSocket sender +// The ILibAsyncServerSocket_Data object +// The offset to the new buffer location +void ILibAsyncServerSocket_OnBufferReAllocated(ILibAsyncSocket_SocketModule ConnectionToken, void *user, ptrdiff_t offSet) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + if (data!=NULL && data->Callback!=NULL) + { + // + // If someone above us, has registered for this callback, we need to fire it, + // with the correct user object + // + data->Callback(data->module,ConnectionToken,data->user,offSet); + } +} + +void ILibAsyncServerSocket_ResumeListeningSink(void *chain, void* user) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)user; + + int ra = 1; + int off = 0; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct sockaddr_in6 localif; + struct sockaddr_in6 localAddress; + + UNREFERENCED_PARAMETER(chain); + + memset(&localif, 0, sizeof(struct sockaddr_in6)); + if (m->loopbackFlag != 2 && ILibDetectIPv6Support()) + { + // Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack + localif.sin6_family = AF_INET6; + localif.sin6_addr = (m->loopbackFlag != 0 ? in6addr_loopback : in6addr_any); + localif.sin6_port = htons(m->initialPortNumber); + } + else + { + // IPv4-only detected + localif.sin6_family = AF_INET; +#ifdef WINSOCK2 + ((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((m->loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY)); +#else + ((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((m->loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY)); +#endif + ((struct sockaddr_in*)&localif)->sin_port = htons(m->initialPortNumber); + } + + // Get our listening socket + if ((m->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { m->ListenSocket = (SOCKET)~0; return; } + + // Setup the IPv6 & IPv4 support on same socket + if (localif.sin6_family == AF_INET6) if (setsockopt(m->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); + +#ifdef SO_NOSIGPIPE + setsockopt(m->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket +#endif + +#if defined(WIN32) + // On Windows. Lets make sure no one else can bind to this addr/port. This stops socket hijacking (not a problem on Linux). + if (setsockopt(m->ListenSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#else + // On Linux. Setting the re-use on a TCP socket allows reuse of the socket even in timeout state. Allows for fast stop/start (Not a problem on Windows). + if (setsockopt(m->ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#endif + + // Bind the socket +#if defined(WIN32) + if (bind(m->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(m->ListenSocket); m->ListenSocket = (SOCKET)~0; return; } +#else + if (bind(m->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(m->ListenSocket); m->ListenSocket = (SOCKET)~0; return; } +#endif + + // Fetch the local port number +#if defined(WINSOCK2) + getsockname(m->ListenSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength); +#else + getsockname(m->ListenSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength); +#endif + if (localAddress.sin6_family == AF_INET6) m->portNumber = ntohs(localAddress.sin6_port); else m->portNumber = ntohs(((struct sockaddr_in*)&localAddress)->sin_port); + m->listening = 0; +} +void ILibAsyncServerSocket_StopListeningSink(void *chain, void* user) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)user; + UNREFERENCED_PARAMETER(chain); + + if (m->ListenSocket != (SOCKET)~0) + { +#ifdef WIN32 + closesocket(m->ListenSocket); +#else + close(m->ListenSocket); +#endif + m->ListenSocket = (SOCKET)~0; + } +} +//! Take the server socket out of listening mode, rejecting new incoming connection requests +/*! + \param module ILibAsyncServerSocket_ServerModule Server Listening Module +*/ +void ILibAsyncServerSocket_StopListening(ILibAsyncServerSocket_ServerModule module) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)module; + ILibChain_RunOnMicrostackThread(m->ChainLink.ParentChain, ILibAsyncServerSocket_StopListeningSink, m); +} +//! Put the server socket back in listening mode, to allow new incoming connection requests +/*! + \param module ILibAsyncServerSocket_ServerModule Server Listening Module +*/ +void ILibAsyncServerSocket_ResumeListening(ILibAsyncServerSocket_ServerModule module) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)module; + ILibChain_RunOnMicrostackThread(m->ChainLink.ParentChain, ILibAsyncServerSocket_ResumeListeningSink, m); +} + + +/*! \fn ILibCreateAsyncServerSocketModule(void *Chain, int MaxConnections, int PortNumber, int initialBufferSize, ILibAsyncServerSocket_OnConnect OnConnect,ILibAsyncServerSocket_OnDisconnect OnDisconnect,ILibAsyncServerSocket_OnReceive OnReceive,ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK) +\brief Instantiates a new ILibAsyncServerSocket +\param Chain The chain to add this module to. (Chain must not be running) +\param MaxConnections The max number of simultaneous connections that will be allowed +\param PortNumber The port number to bind to. 0 will select a random port +\param initialBufferSize The initial size of the receive buffer +\param loopbackFlag 0 to bind to ANY, 1 to bind to IPv6 loopback first, 2 to bind to IPv4 loopback first. +\param OnConnect Function Pointer that triggers when a connection is established +\param OnDisconnect Function Pointer that triggers when a connection is closed +\param OnReceive Function Pointer that triggers when data is received +\param OnInterrupt Function Pointer that triggers when connection interrupted +\param OnSendOK Function Pointer that triggers when pending sends are complete +\param ServerAutoFreeMemorySize Size of AutoFreeMemory on Server to co-allocate +\param SessionAutoFreeMemorySize Size of AutoFreeMemory on Session to co-allocate +\returns An ILibAsyncServerSocket module +*/ +ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize) +{ + int i; + int ra = 1; + int off = 0; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct sockaddr_in6 localif; + struct sockaddr_in6 localAddress; + struct ILibAsyncServerSocketModule *RetVal; + + memset(&localif, 0, sizeof(struct sockaddr_in6)); + if (loopbackFlag != 2 && ILibDetectIPv6Support()) + { + // Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack + localif.sin6_family = AF_INET6; + localif.sin6_addr = (loopbackFlag != 0?in6addr_loopback:in6addr_any); + localif.sin6_port = htons(PortNumber); + } + else + { + // IPv4-only detected + localif.sin6_family = AF_INET; +#ifdef WINSOCK2 + ((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY)); +#else + ((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY)); +#endif + ((struct sockaddr_in*)&localif)->sin_port = htons(PortNumber); + } + + // Instantiate a new AsyncServer module + RetVal = (struct ILibAsyncServerSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncServerSocketModule), ServerUserMappedMemorySize); + + RetVal->ChainLink.PreSelectHandler = &ILibAsyncServerSocket_PreSelect; + RetVal->ChainLink.PostSelectHandler = &ILibAsyncServerSocket_PostSelect; + RetVal->ChainLink.DestroyHandler = &ILibAsyncServerSocket_Destroy; + RetVal->ChainLink.ParentChain = Chain; + RetVal->OnConnect = OnConnect; + RetVal->OnDisconnect = OnDisconnect; + RetVal->OnInterrupt = OnInterrupt; + RetVal->OnSendOK = OnSendOK; + RetVal->OnReceive = OnReceive; + RetVal->MaxConnection = MaxConnections; + RetVal->AsyncSockets = (void**)malloc(MaxConnections * sizeof(void*)); + if (RetVal->AsyncSockets == NULL) { free(RetVal); ILIBMARKPOSITION(253); return NULL; } + RetVal->portNumber = (unsigned short)PortNumber; + RetVal->loopbackFlag = loopbackFlag; + RetVal->initialPortNumber = PortNumber; + + // Get our listening socket + if ((RetVal->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { free(RetVal->AsyncSockets); free(RetVal); return 0; } + + // Setup the IPv6 & IPv4 support on same socket + if (localif.sin6_family == AF_INET6) if (setsockopt(RetVal->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); + +#ifdef SO_NOSIGPIPE + setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket +#endif + +#if defined(WIN32) + // On Windows. Lets make sure no one else can bind to this addr/port. This stops socket hijacking (not a problem on Linux). + if (setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#else + // On Linux. Setting the re-use on a TCP socket allows reuse of the socket even in timeout state. Allows for fast stop/start (Not a problem on Windows). + if (setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#endif + + // Bind the socket +#if defined(WIN32) + if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } +#else + if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } +#endif + + // Fetch the local port number +#if defined(WINSOCK2) + getsockname(RetVal->ListenSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength); +#else + getsockname(RetVal->ListenSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength); +#endif + if (localAddress.sin6_family == AF_INET6) RetVal->portNumber = ntohs(localAddress.sin6_port); else RetVal->portNumber = ntohs(((struct sockaddr_in*)&localAddress)->sin_port); + + // Create our socket pool + for(i = 0; i < MaxConnections; ++i) + { + RetVal->AsyncSockets[i] = ILibCreateAsyncSocketModuleWithMemory(Chain, initialBufferSize, &ILibAsyncServerSocket_OnData, &ILibAsyncServerSocket_OnConnectSink, &ILibAsyncServerSocket_OnDisconnectSink, &ILibAsyncServerSocket_OnSendOKSink, SessionUserMappedMemorySize); + // + // We want to know about any buffer reallocations, because anything above us may want to know + // + ILibAsyncSocket_SetReAllocateNotificationCallback(RetVal->AsyncSockets[i], &ILibAsyncServerSocket_OnBufferReAllocated); + } + ILibAddToChain(Chain, RetVal); + + return(RetVal); +} + +/*! \fn ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule) +\brief Returns the port number the server is bound to +\param ServerSocketModule The ILibAsyncServer to query +\returns The listening port number +*/ +unsigned short ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule) +{ + return(((struct ILibAsyncServerSocketModule*)ServerSocketModule)->portNumber); +} diff --git a/MicroLMS/microstack/ILibAsyncServerSocket.h b/MicroLMS/microstack/ILibAsyncServerSocket.h new file mode 100644 index 0000000..60c5aa9 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncServerSocket.h @@ -0,0 +1,155 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef ___ILibAsyncServerSocket___ +#define ___ILibAsyncServerSocket___ + +/*! \file ILibAsyncServerSocket.h + \brief MicroStack APIs for TCP Server Functionality +*/ + +/*! \defgroup ILibAsyncServerSocket ILibAsyncServerSocket Module + \{ +*/ + +#if defined(WIN32) +#include +#elif defined(_POSIX) +#include +#endif + +#include "ILibAsyncSocket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \typedef ILibAsyncServerSocket_ServerModule + \brief The handle for an ILibAsyncServerSocket module +*/ +typedef void* ILibAsyncServerSocket_ServerModule; + +/*! \typedef ILibAsyncServerSocket_ConnectionToken + \brief Connection state, for a connected session +*/ +typedef ILibAsyncSocket_SocketModule ILibAsyncServerSocket_ConnectionToken; + +/*! \typedef ILibAsyncServerSocket_BufferReAllocated + \brief BufferReAllocation Handler + \param AsyncServerSocketToken The ILibAsyncServerSocket token + \param ConnectionToken The ILibAsyncServerSocket_Connection token + \param user The User object + \param newOffset The buffer has shifted by this offset +*/ +typedef void (*ILibAsyncServerSocket_BufferReAllocated)(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user, ptrdiff_t newOffset); +void ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback); + +/*! \typedef ILibAsyncServerSocket_OnInterrupt + \brief Handler for when a session was interrupted by a call to ILibStopChain + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param user +*/ +typedef void (*ILibAsyncServerSocket_OnInterrupt)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user); +/*! \typedef ILibAsyncServerSocket_OnReceive + \brief Handler for when data is received + \par + Note on memory handling: + When you process the received buffer, you must advance \a p_beginPointer the number of bytes that you + have processed. If \a p_beginPointer does not equal \a endPointer when this method completes, + the system will continue to reclaim any memory that has already been processed, and call this method again + until no more memory has been processed. If no memory has been processed, and more data has been received + on the network, the buffer will be automatically grown (according to a specific alogrythm), to accomodate any new data. + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param buffer The data that was received + \param[in,out] p_beginPointer The start index of the data that was received + \param endPointer The end index of the data that was received + \param[in,out] OnInterrupt Set this pointer to receive notification if this session is interrupted + \param[in,out] user Set a custom user object + \param[out] PAUSE Flag to indicate if the system should continue reading data off the network +*/ +typedef void (*ILibAsyncServerSocket_OnReceive)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncServerSocket_OnInterrupt *OnInterrupt, void **user, int *PAUSE); +/*! \typedef ILibAsyncServerSocket_OnConnect + \brief Handler for when a connection is made + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param[in,out] user Set a user object to associate with this connection +*/ +typedef void (*ILibAsyncServerSocket_OnConnect)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void **user); +/*! \typedef ILibAsyncServerSocket_OnDisconnect + \brief Handler for when a connection is terminated normally + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param user User object that was associated with this connection +*/ +typedef void (*ILibAsyncServerSocket_OnDisconnect)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user); +/*! \typedef ILibAsyncServerSocket_OnSendOK + \brief Handler for when pending send operations have completed + \par + This handler will only be called if a call to \a ILibAsyncServerSocket_Send returned a value greater + than 0, which indicates that not all of the data could be sent. + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param user User object that was associated with this connection +*/ +typedef void (*ILibAsyncServerSocket_OnSendOK)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user); + +extern const int ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE; + +#define ILibCreateAsyncServerSocketModule(Chain, MaxConnections, PortNumber, initialBufferSize, loopbackFlag, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK) ILibCreateAsyncServerSocketModuleWithMemory(Chain, MaxConnections, PortNumber, initialBufferSize, loopbackFlag, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK, 0, 0) +ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize); + +void *ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule); +void ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *user); +int ILibAsyncServerSocket_GetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule); +void ILibAsyncServerSocket_SetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, int user); + +void ILibAsyncServerSocket_StopListening(ILibAsyncServerSocket_ServerModule module); +void ILibAsyncServerSocket_ResumeListening(ILibAsyncServerSocket_ServerModule module); + +unsigned short ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule); + +/*! \def ILibAsyncServerSocket_Send + \brief Sends data onto the TCP stream + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param buffer The data to be sent + \param bufferLength The length of \a buffer + \param UserFreeBuffer The \a ILibAsyncSocket_MemoryOwnership enumeration, that identifies how the memory pointer to by \a buffer is to be handled + \returns \a ILibAsyncSocket_SendStatus indicating the send status +*/ +#define ILibAsyncServerSocket_Send(ServerSocketModule, ConnectionToken, buffer, bufferLength, UserFreeBuffer) ILibAsyncSocket_Send(ConnectionToken, buffer, bufferLength, UserFreeBuffer) + +/*! \def ILibAsyncServerSocket_Disconnect + \brief Disconnects a TCP stream + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session +*/ +#define ILibAsyncServerSocket_Disconnect(ServerSocketModule, ConnectionToken) ILibAsyncSocket_Disconnect(ConnectionToken) +/*! \def ILibAsyncServerSocket_GetPendingBytesToSend + \brief Gets the outstanding number of bytes to be sent +*/ +#define ILibAsyncServerSocket_GetPendingBytesToSend(ServerSocketModule, ConnectionToken) ILibAsyncSocket_GetPendingBytesToSend(ConnectionToken) +/*! \def ILibAsyncServerSocket_GetTotalBytesSent + \brief Gets the total number of bytes that have been sent + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session +*/ +#define ILibAsyncServerSocket_GetTotalBytesSent(ServerSocketModule, ConnectionToken) ILibAsyncSocket_GetTotalBytesSent(ConnectionToken) +/*! \def ILibAsyncServerSocket_ResetTotalBytesSent + \brief Resets the total bytes sent counter + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session +*/ +#define ILibAsyncServerSocket_ResetTotalBytesSent(ServerSocketModule, ConnectionToken) ILibAsyncSocket_ResetTotalBytesSent(ConnectionToken) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/MicroLMS/microstack/ILibAsyncSocket.c b/MicroLMS/microstack/ILibAsyncSocket.c new file mode 100644 index 0000000..8a4e340 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncSocket.c @@ -0,0 +1,1677 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifdef MEMORY_CHECK +#include +#define MEMCHECK(x) x +#else +#define MEMCHECK(x) +#endif + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#include +#include +#define SOCKET_ERROR (-1) +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#if defined(WINSOCK2) +#include +#include +#define MSG_NOSIGNAL 0 +#elif defined(WINSOCK1) +#include +#include +#endif + +#include "ILibParsers.h" +#include "ILibAsyncSocket.h" +#include "ILibRemoteLogging.h" + +#include + +//#ifndef WINSOCK2 +//#define SOCKET unsigned int +//#endif + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) + +#ifdef SEMAPHORE_TRACKING +#define SEM_TRACK(x) x +void AsyncSocket_TrackLock(const char* MethodName, int Occurance, void *data) +{ + char v[100]; + wchar_t wv[100]; + size_t l; + + sprintf_s(v, 100, " LOCK[%s, %d] (%x)\r\n",MethodName,Occurance,data); +#ifdef WIN32 + mbstowcs_s(&l, wv, 100, v, 100); + OutputDebugString(wv); +#else + printf(v); +#endif +} +void AsyncSocket_TrackUnLock(const char* MethodName, int Occurance, void *data) +{ + char v[100]; + wchar_t wv[100]; + size_t l; + + sprintf_s(v, 100, "UNLOCK[%s, %d] (%x)\r\n",MethodName,Occurance,data); +#ifdef WIN32 + mbstowcs_s(&l, wv, 100, v, 100); + OutputDebugString(wv); +#else + printf(v); +#endif +} +#else +#define SEM_TRACK(x) +#endif + +struct ILibAsyncSocket_SendData +{ + char* buffer; + int bufferSize; + int bytesSent; + + struct sockaddr_in6 remoteAddress; + + int UserFree; + struct ILibAsyncSocket_SendData *Next; +}; + +typedef struct ILibAsyncSocketModule +{ + ILibTransport Transport; + // DO NOT MODIFY THE ABOVE FIELDS (ILibTransport) + + unsigned int PendingBytesToSend; + unsigned int TotalBytesSent; + +#if defined(WIN32) + SOCKET internalSocket; +#elif defined(_POSIX) + int internalSocket; +#endif + + // The IPv4/IPv6 compliant address of the remote endpoint. We are not going to be using IPv6 all the time, + // but we use the IPv6 structure to allocate the meximum space we need. + struct sockaddr_in6 RemoteAddress; + + // Local interface of a given socket. This module will bind to any interface, but the actual interface used + // is stored here. + struct sockaddr_in6 LocalAddress; + + // Source address. Here is stored the actual source of a packet, usualy used with UDP where the source + // of the traffic changes. + struct sockaddr_in6 SourceAddress; + +#ifdef MICROSTACK_PROXY + // The address and port of a HTTPS proxy + struct sockaddr_in6 ProxyAddress; + char ProxiedHost[255]; + int ProxyState; + char* ProxyUser; + char* ProxyPass; +#endif + + ILibAsyncSocket_OnData OnData; + ILibAsyncSocket_OnConnect OnConnect; + ILibAsyncSocket_OnDisconnect OnDisconnect; + ILibAsyncSocket_OnSendOK OnSendOK; + ILibAsyncSocket_OnInterrupt OnInterrupt; + + ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceeded; + ILibAsyncSocket_OnBufferReAllocated OnBufferReAllocated; + + void *LifeTime; + void *user; + void *user2; + int user3; + int PAUSE; + int FinConnect; + int BeginPointer; + int EndPointer; + char* buffer; + int MallocSize; + int InitialSize; + + struct ILibAsyncSocket_SendData *PendingSend_Head; + struct ILibAsyncSocket_SendData *PendingSend_Tail; + sem_t SendLock; + + int MaxBufferSize; + int MaxBufferSizeExceeded; + void *MaxBufferSizeUserObject; + + // Added for TLS support + long long timeout_lastActivity; + int timeout_milliSeconds; + ILibAsyncSocket_TimeoutHandler timeout_handler; +}ILibAsyncSocketModule; + +void ILibAsyncSocket_PostSelect(void* object,int slct, fd_set *readset, fd_set *writeset, fd_set *errorset); +void ILibAsyncSocket_PreSelect(void* object,fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime); +const int ILibMemory_ASYNCSOCKET_CONTAINERSIZE = (const int)sizeof(ILibAsyncSocketModule); + +typedef enum ILibAsyncSocket_TLSPlainText_ContentType +{ + ILibAsyncSocket_TLSPlainText_ContentType_ChangeCipherSpec = 20, + ILibAsyncSocket_TLSPlainText_ContentType_Alert = 21, + ILibAsyncSocket_TLSPlainText_ContentType_Handshake = 22, + ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData = 23 +}ILibAsyncSocket_TLSPlainText_ContentType; +typedef enum ILibAsyncSocket_TLSHandshakeType +{ + ILibAsyncSocket_TLSHandshakeType_hello = 0, + ILibAsyncSocket_TLSHandshakeType_clienthello = 1, + ILibAsyncSocket_TLSHandshakeType_serverhello = 2, + ILibAsyncSocket_TLSHandshakeType_certificate = 11, + ILibAsyncSocket_TLSHandshakeType_serverkeyexchange = 12, + ILibAsyncSocket_TLSHandshakeType_certificaterequest = 13, + ILibAsyncSocket_TLSHandshakeType_serverhellodone = 14, + ILibAsyncSocket_TLSHandshakeType_certificateverify = 15, + ILibAsyncSocket_TLSHandshakeType_clientkeyexchange = 16, + ILibAsyncSocket_TLSHandshakeType_finished = 20 +}ILibAsyncSocket_TLSHandshakeType; + +// +// An internal method called by Chain as Destroy, to cleanup AsyncSocket +// +// The AsyncSocketModule +void ILibAsyncSocket_Destroy(void *socketModule) +{ + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + struct ILibAsyncSocket_SendData *temp, *current; + + // Call the interrupt event if necessary + if (!ILibAsyncSocket_IsFree(module)) + { + if (module->OnInterrupt != NULL) module->OnInterrupt(module, module->user); + } + + // Close socket if necessary + if (module->internalSocket != ~0) + { +#if defined(WIN32) +#if defined(WINSOCK2) + shutdown(module->internalSocket, SD_BOTH); +#endif + closesocket(module->internalSocket); +#elif defined(_POSIX) + shutdown(module->internalSocket, SHUT_RDWR); + close(module->internalSocket); +#endif + module->internalSocket = (SOCKET)~0; + } + + // Free the buffer if necessary + if (module->buffer != NULL) + { + if (module->buffer != ILibScratchPad2) free(module->buffer); + module->buffer = NULL; + module->MallocSize = 0; + } + + // Clear all the data that is pending to be sent + temp = current = module->PendingSend_Head; + while (current != NULL) + { + temp = current->Next; + if (current->UserFree == 0) free(current->buffer); + free(current); + current = temp; + } + + module->FinConnect = 0; + module->user = NULL; + sem_destroy(&(module->SendLock)); +} +/*! \fn ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback) +\brief Set the callback handler for when the internal data buffer has been resized +\param AsyncSocketToken The specific connection to set the callback with +\param Callback The callback handler to set +*/ +void ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback) +{ + if (AsyncSocketToken != NULL) { ((struct ILibAsyncSocketModule*)AsyncSocketToken)->OnBufferReAllocated = Callback; } +} + +ILibTransport_DoneState ILibAsyncSocket_TransportSend(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done) +{ + UNREFERENCED_PARAMETER(done); + return((ILibTransport_DoneState)ILibAsyncSocket_Send(transport, buffer, bufferLength, (enum ILibAsyncSocket_MemoryOwnership)ownership)); +} + + +/*! \fn ILibCreateAsyncSocketModule(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect,ILibAsyncSocket_OnSendOK OnSendOK) +\brief Creates a new AsyncSocketModule +\param Chain The chain to add this module to. (Chain must not be running) +\param initialBufferSize The initial size of the receive buffer +\param OnData Function Pointer that triggers when Data is received +\param OnConnect Function Pointer that triggers upon successfull connection establishment +\param OnDisconnect Function Pointer that triggers upon disconnect +\param OnSendOK Function Pointer that triggers when pending sends are complete +\param AutoFreeMemorySize Amount of memory to create along side the object, that will be freed when the parent object is freed +\returns An ILibAsyncSocket token +*/ +ILibAsyncSocket_SocketModule ILibCreateAsyncSocketModuleWithMemory(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect, ILibAsyncSocket_OnSendOK OnSendOK, int UserMappedMemorySize) +{ + struct ILibAsyncSocketModule *RetVal = (struct ILibAsyncSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncSocketModule), UserMappedMemorySize); + + RetVal->Transport.IdentifierFlags = ILibTransports_AsyncSocket; + RetVal->Transport.SendPtr = &ILibAsyncSocket_TransportSend; + RetVal->Transport.ClosePtr = &ILibAsyncSocket_Disconnect; + RetVal->Transport.PendingBytesPtr = &ILibAsyncSocket_GetPendingBytesToSend; + RetVal->Transport.ChainLink.ParentChain = Chain; + + if (initialBufferSize != 0) + { + // Use a new buffer + if ((RetVal->buffer = (char*)malloc(initialBufferSize)) == NULL) ILIBCRITICALEXIT(254); + } + else + { + // Use a static buffer, often used for UDP. + initialBufferSize = sizeof(ILibScratchPad2); + RetVal->buffer = ILibScratchPad2; + } + RetVal->Transport.ChainLink.PreSelectHandler = &ILibAsyncSocket_PreSelect; + RetVal->Transport.ChainLink.PostSelectHandler = &ILibAsyncSocket_PostSelect; + RetVal->Transport.ChainLink.DestroyHandler = &ILibAsyncSocket_Destroy; + RetVal->internalSocket = (SOCKET)~0; + RetVal->OnData = OnData; + RetVal->OnConnect = OnConnect; + RetVal->OnDisconnect = OnDisconnect; + RetVal->OnSendOK = OnSendOK; + RetVal->InitialSize = initialBufferSize; + RetVal->MallocSize = initialBufferSize; + RetVal->LifeTime = ILibGetBaseTimer(Chain); //ILibCreateLifeTime(Chain); + + sem_init(&(RetVal->SendLock), 0, 1); + + ILibAddToChain(Chain, RetVal); + + return((void*)RetVal); +} + +/*! \fn ILibAsyncSocket_ClearPendingSend(ILibAsyncSocket_SocketModule socketModule) +\brief Clears all the pending data to be sent for an AsyncSocket +\param socketModule The ILibAsyncSocket to clear +*/ +void ILibAsyncSocket_ClearPendingSend(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + struct ILibAsyncSocket_SendData *data, *temp; + + data = module->PendingSend_Head; + module->PendingSend_Tail = NULL; + module->PendingSend_Head = NULL; + module->PendingBytesToSend = 0; + while (data != NULL) + { + temp = data->Next; + // We only need to free this if we have ownership of this memory + if (data->UserFree == 0) free(data->buffer); + free(data); + data = temp; + } +} + +/*! \fn ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, int remoteAddress, unsigned short remotePort, enum ILibAsyncSocket_MemoryOwnership UserFree) +\brief Sends data on an AsyncSocket module to a specific destination. (Valid only for UDP) +\param socketModule The ILibAsyncSocket module to send data on +\param buffer The buffer to send +\param length The length of the buffer to send +\param remoteAddress The IPAddress of the destination +\param remotePort The Port number of the destination +\param UserFree Flag indicating memory ownership. +\returns \a ILibAsyncSocket_SendStatus indicating the send status +*/ +enum ILibAsyncSocket_SendStatus ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, struct sockaddr *remoteAddress, enum ILibAsyncSocket_MemoryOwnership UserFree) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + struct ILibAsyncSocket_SendData *data; + int bytesSent = 0; + enum ILibAsyncSocket_SendStatus retVal = ILibAsyncSocket_ALL_DATA_SENT; + + // If the socket is empty, return now. + if (socketModule == NULL) return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR; + + // Setup a new send data structure + if ((data = (struct ILibAsyncSocket_SendData*)malloc(sizeof(struct ILibAsyncSocket_SendData))) == NULL) ILIBCRITICALEXIT(254); + memset(data, 0, sizeof(struct ILibAsyncSocket_SendData)); + data->buffer = buffer; + data->bufferSize = length; + data->bytesSent = 0; + data->UserFree = UserFree; + data->Next = NULL; + + // Copy the address to the send data structure + memset(&(data->remoteAddress), 0, sizeof(struct sockaddr_in6)); + if (remoteAddress != NULL) memcpy_s(&(data->remoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_Send", 1, module);) + sem_wait(&(module->SendLock)); + + if (module->internalSocket == ~0) + { +#ifdef WIN32 + ILIBMESSAGE2("internalSocket ERROR", WSAGetLastError()); +#endif + // Too Bad, the socket closed + if (UserFree == 0) free(buffer); + free(data); + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 2, module);) + sem_post(&(module->SendLock)); + return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR; + } + + module->PendingBytesToSend += length; + if (module->PendingSend_Tail != NULL || module->FinConnect == 0) + { + // There are still bytes that are pending to be sent, or pending connection, so we need to queue this up + if (module->PendingSend_Tail == NULL) + { + module->PendingSend_Tail = data; + module->PendingSend_Head = data; + } + else + { + module->PendingSend_Tail->Next = data; + module->PendingSend_Tail = data; + } + + retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET; + + if (UserFree == ILibAsyncSocket_MemoryOwnership_USER) + { + // If we don't own this memory, we need to copy the buffer, + // because the user may free this memory before we have a chance to send it + if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(data->buffer, data->bufferSize, buffer, length); + MEMCHECK(assert(length <= data->bufferSize);) + data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN; + } + } + else + { + // There is no data pending to be sent, so lets go ahead and try to send this data + module->PendingSend_Tail = data; + module->PendingSend_Head = data; + + if (remoteAddress == NULL) + { + // Set MSG_NOSIGNAL since we don't want to get Broken Pipe signals in Linux, ignored if Windows. + bytesSent = send(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize-module->PendingSend_Head->bytesSent, MSG_NOSIGNAL); + } + else + { + bytesSent = sendto(module->internalSocket, module->PendingSend_Head->buffer+module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize-module->PendingSend_Head->bytesSent, MSG_NOSIGNAL, (struct sockaddr*)remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); + } + + module->timeout_lastActivity = ILibGetUptime(); + + if (bytesSent > 0) + { + // We were able to send something, so lets increment the counters + module->PendingSend_Head->bytesSent += bytesSent; + module->PendingBytesToSend -= bytesSent; + module->TotalBytesSent += bytesSent; + } + + if (bytesSent == -1) + { + // Send returned an error, so lets figure out what it was, as it could be normal +#if defined(WIN32) + bytesSent = WSAGetLastError(); + if (bytesSent != WSAEWOULDBLOCK) +#elif defined(_POSIX) + if (errno != EWOULDBLOCK) +#endif + { + // printf("ILibAsyncSocket_Send ERROR %d\r\n", errno); + +#ifdef WIN32 + ILIBMESSAGE2("ILibAsyncSocket_Send ERROR", WSAGetLastError()); +#endif // WIN32 + + // Most likely the socket closed while we tried to send + module->PAUSE = 1; + ILibAsyncSocket_ClearPendingSend(module); // This causes a segfault + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 3, module);) + sem_post(&(module->SendLock)); + + // Ensure Calling On_Disconnect with MicroStackThread + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + + return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR; + } + } + if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize) + { + // All of the data has been sent + if (UserFree == 0) free(module->PendingSend_Head->buffer); + module->PendingSend_Tail = NULL; + free(module->PendingSend_Head); + module->PendingSend_Head = NULL; + } + else + { + // All of the data wasn't sent, so we need to copy the buffer + // if we don't own the memory, because the user may free the + // memory, before we have a chance to complete sending it. + if (UserFree == ILibAsyncSocket_MemoryOwnership_USER) + { + if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(data->buffer, data->bufferSize, buffer, length); + MEMCHECK(assert(length <= data->bufferSize);) + data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN; + } + retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET; + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_3, "AsyncSocket[%p] NOT_ALL_DATA_SENT", (void*)module); + } + + } + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 4, module);) + sem_post(&(module->SendLock)); + if (retVal != ILibAsyncSocket_ALL_DATA_SENT) ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain); + return (retVal); +} + +/*! \fn ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule) +\brief Disconnects an ILibAsyncSocket +\param socketModule The ILibAsyncSocket to disconnect +*/ +void ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule) +{ +#if defined(WIN32) + SOCKET s; +#else + int s; +#endif + + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] << DISCONNECT", (void*)module); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_Disconnect", 1, module);) + sem_wait(&(module->SendLock)); + module->timeout_handler = NULL; + module->timeout_milliSeconds = 0; + + + if (module->internalSocket != ~0) + { + // There is an associated socket that is still valid, so we need to close it + module->PAUSE = 1; + s = module->internalSocket; + module->internalSocket = (SOCKET)~0; + if (s != -1) + { +#if defined(WIN32) +#if defined(WINSOCK2) + shutdown(s, SD_BOTH); +#endif + closesocket(s); +#elif defined(_POSIX) + shutdown(s, SHUT_RDWR); + close(s); +#endif + } + + // Since the socket is closing, we need to clear the data that is pending to be sent + ILibAsyncSocket_ClearPendingSend(socketModule); + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Disconnect", 2, module);) + sem_post(&(module->SendLock)); + + // This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently + if (module->FinConnect <= 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed + if (module->FinConnect > 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected + + module->FinConnect = 0; + module->user = NULL; + } + else + { + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Disconnect", 3, module);) + sem_post(&(module->SendLock)); + } +} + +void ILibProcessAsyncSocket(struct ILibAsyncSocketModule *Reader, int pendingRead); +void ILibAsyncSocket_Callback(ILibAsyncSocket_SocketModule socketModule, int connectDisconnectReadWrite) +{ + if (socketModule != NULL) + { + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + if (connectDisconnectReadWrite == 0) // Connected + { + memset(&(module->LocalAddress), 0, sizeof(struct sockaddr_in6)); + + module->FinConnect = 1; + module->PAUSE = 0; + + // No SSL, tell application we are connected. + module->OnConnect(module, -1, module->user); + } + else if (connectDisconnectReadWrite == 1) // Disconnected + ILibAsyncSocket_Disconnect(module); + else if (connectDisconnectReadWrite == 2) // Data read + ILibProcessAsyncSocket(module, 1); + } +} + + +/*! \fn ILibAsyncSocket_ConnectTo(ILibAsyncSocket_SocketModule socketModule, int localInterface, int remoteInterface, int remotePortNumber, ILibAsyncSocket_OnInterrupt InterruptPtr,void *user) +\brief Attempts to establish a TCP connection +\param socketModule The ILibAsyncSocket to initiate the connection +\param localInterface The interface to use to establish the connection +\param remoteInterface The remote interface to connect to +\param InterruptPtr Function Pointer that triggers if connection attempt is interrupted +\param user User object that will be passed to the \a OnConnect method +*/ +void ILibAsyncSocket_ConnectTo(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteInterface, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +{ + int flags = 1; + char *tmp; + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + struct sockaddr_in6 any; + + // If there is something going on and we try to connect using this socket, fail! This is not supposed to happen. + if (module->internalSocket != -1) + { + ILIBCRITICALEXIT2(253, (int)(module->internalSocket)); + } + + // Clean up + memset(&(module->RemoteAddress), 0, sizeof(struct sockaddr_in6)); + memset(&(module->LocalAddress) , 0, sizeof(struct sockaddr_in6)); + memset(&(module->SourceAddress), 0, sizeof(struct sockaddr_in6)); + + // Setup + memcpy_s(&(module->RemoteAddress), sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sa_family)); + module->PendingBytesToSend = 0; + module->TotalBytesSent = 0; + module->PAUSE = 0; + module->user = user; + module->OnInterrupt = InterruptPtr; + if ((tmp = (char*)realloc(module->buffer, module->InitialSize)) == NULL) ILIBCRITICALEXIT(254); + module->buffer = tmp; + module->MallocSize = module->InitialSize; + + // If localInterface is NULL, we will assume INADDRANY - IPv4/IPv6 based on remote address + if (localInterface == NULL) + { + memset(&any, 0, sizeof(struct sockaddr_in6)); + #ifdef MICROSTACK_PROXY + if (module->ProxyAddress.sin6_family == 0) any.sin6_family = remoteInterface->sa_family; else any.sin6_family = module->ProxyAddress.sin6_family; + #else + any.sin6_family = remoteInterface->sa_family; + #endif + localInterface = (struct sockaddr*)&any; + } + + // The local port should always be zero +#ifdef _DEBUG + if (localInterface->sa_family == AF_INET && ((struct sockaddr_in*)localInterface)->sin_port != 0) { ILIBCRITICALEXIT(253); } + if (localInterface->sa_family == AF_INET6 && ((struct sockaddr_in6*)localInterface)->sin6_port != 0) { ILIBCRITICALEXIT(253); } +#endif + + // Allocate a new socket + if ((module->internalSocket = ILibGetSocket(localInterface, SOCK_STREAM, IPPROTO_TCP)) == 0) { ILIBCRITICALEXIT(253); return; } + + // Initialise the buffer pointers, since no data is in them yet. + module->FinConnect = 0; + module->BeginPointer = 0; + module->EndPointer = 0; + + // Turn on keep-alives for the socket + if (setsockopt(module->internalSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flags, sizeof(flags)) != 0) ILIBCRITICALERREXIT(253); + + // Set the socket to non-blocking mode, because we need to play nice and share the MicroStack thread +#if defined(WIN32) + ioctlsocket(module->internalSocket, FIONBIO, (u_long *)(&flags)); +#elif defined(_POSIX) + flags = fcntl(module->internalSocket, F_GETFL,0); + fcntl(module->internalSocket, F_SETFL, O_NONBLOCK | flags); +#endif + + // Connect the socket, and force the chain to unblock, since the select statement doesn't have us in the fdset yet. +#ifdef MICROSTACK_PROXY + if (module->ProxyAddress.sin6_family != 0) + { + if (connect(module->internalSocket, (struct sockaddr*)&(module->ProxyAddress), INET_SOCKADDR_LENGTH(module->ProxyAddress.sin6_family)) != -1) + { + // Connect failed. Set a short time and call disconnect. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + } + else +#endif + if (connect(module->internalSocket, (struct sockaddr*)remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sa_family)) != -1) + { + // Connect failed. Set a short time and call disconnect. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + +#ifdef _DEBUG + #ifdef _POSIX + if (errno != EINPROGRESS) // The result of the connect should always be "WOULD BLOCK" on Linux. But sometimes this fails. + { + // This happens when the interface is no longer available. Disconnect socket. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + #endif + #ifdef WIN32 + { + if (GetLastError() != WSAEWOULDBLOCK) // The result of the connect should always be "WOULD BLOCK" on Windows. + { + // This happens when the interface is no longer available. Disconnect socket. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + } + #endif +#endif + + ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain); +} + +#ifdef MICROSTACK_PROXY +//! Connect using an HTTPS proxy. If "proxyAddress" is set to NULL, this call acts just to a normal connect call without a proxy. +/*! + \param socketModule ILibAsyncSocket Client to initiate the connection + \param localInterface Local endpoint to originate the connection request from + \param remoteAddress Destination endpoint to connect to + \param proxyAddress Proxy Server to relay the connection thru. + \param proxyUser Proxy Server username (Username is stored by reference, so this memory must remain valid for duration of connection) + \param proxyPass Proxy Server password (Password is stored by reference, so this memory must remain valid for duration of connection) + \param InterruptPtr Event handler triggered if connection request is interrupted + \param user Custom user state data +*/ +void ILibAsyncSocket_ConnectToProxy(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, struct sockaddr *proxyAddress, char* proxyUser, char* proxyPass, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + memset(&(module->ProxyAddress), 0, sizeof(struct sockaddr_in6)); + module->ProxyState = 0; + module->ProxyUser = proxyUser; // Proxy user & password are kept by reference!!! + module->ProxyPass = proxyPass; + + if (proxyAddress != NULL) memcpy_s(&(module->ProxyAddress), sizeof(struct sockaddr_in6), proxyAddress, INET_SOCKADDR_LENGTH(proxyAddress->sa_family)); + ILibAsyncSocket_ConnectTo(socketModule, localInterface, remoteAddress, InterruptPtr, user); +} +#endif + +// +// Internal method called when data is ready to be processed on an ILibAsyncSocket +// +// The ILibAsyncSocket with pending data +void ILibProcessAsyncSocket(struct ILibAsyncSocketModule *Reader, int pendingRead) +{ + int bytesReceived = 0; + int len; + char *temp; + + if (Reader->PAUSE > 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] is PAUSED", (void*)Reader); + return; + } + + + // + // Try to read from the socket + // + if (pendingRead != 0) + { + { + // Read data off the non-SSL, generic socket. + // Set the receive address buffer size and read from the socket. + len = sizeof(struct sockaddr_in6); +#if defined(WINSOCK2) + bytesReceived = recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), (int*)&len); +#else + bytesReceived = (int)recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), (socklen_t*)&len); +#endif +#ifdef WIN32 +#ifdef _DEBUG + if (bytesReceived == SOCKET_ERROR) ILIBMESSAGE2("Failed socket recvfrom function", WSAGetLastError()); +#endif +#endif + ILib6to4((struct sockaddr*)&(Reader->SourceAddress)); + ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] recv (NON-TLS) returned %d", (void*)Reader, bytesReceived); + } + if (bytesReceived > 0) + { + // + // Data was read, so increment our counters + // + Reader->EndPointer += bytesReceived; + } + } + + // + // Event OnData up the stack, to process any data that is available + // + while (Reader->internalSocket != ~0 && Reader->PAUSE <= 0 && Reader->BeginPointer != Reader->EndPointer && Reader->EndPointer != 0) + { + int iPointer = 0; + + if (Reader->OnData != NULL) + { + Reader->OnData(Reader, Reader->buffer + Reader->BeginPointer, &(iPointer), Reader->EndPointer - Reader->BeginPointer, &(Reader->OnInterrupt), &(Reader->user), &(Reader->PAUSE)); + if (iPointer == 0) { break; } + Reader->BeginPointer += iPointer; + } + } + if (Reader->BeginPointer == Reader->EndPointer) { Reader->BeginPointer = Reader->EndPointer = 0; } + + + if (bytesReceived <= 0 && pendingRead != 0) + { + // If a UDP packet is larger than the buffer, drop it. +#if defined(WINSOCK2) + if (bytesReceived == SOCKET_ERROR && WSAGetLastError() == 10040) { ILIBMESSAGE2("Failed bytesReceived", WSAGetLastError()); return; } +#else + // TODO: Linux errno + //if (bytesReceived == -1 && errno != 0) printf("ERROR: errno = %d, %s\r\n", errno, strerror(errno)); +#endif + + // + // This means the socket was gracefully closed by the remote endpoint + // + SEM_TRACK(AsyncSocket_TrackLock("ILibProcessAsyncSocket", 1, Reader);) + ILibAsyncSocket_ClearPendingSend(Reader); + SEM_TRACK(AsyncSocket_TrackUnLock("ILibProcessAsyncSocket", 2, Reader);) + +#if defined(WIN32) +#if defined(WINSOCK2) + shutdown(Reader->internalSocket, SD_BOTH); +#endif + closesocket(Reader->internalSocket); +#elif defined(_POSIX) + shutdown(Reader->internalSocket, SHUT_RDWR); + close(Reader->internalSocket); +#endif + Reader->internalSocket = (SOCKET)~0; + + ILibAsyncSocket_ClearPendingSend(Reader); + + // + // Inform the user the socket has closed + // + Reader->timeout_handler = NULL; + Reader->timeout_milliSeconds = 0; + // This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently + if (Reader->FinConnect <= 0 && Reader->OnConnect != NULL) { Reader->OnConnect(Reader, 0, Reader->user); } // Connection Failed + if (Reader->FinConnect > 0 && Reader->OnDisconnect != NULL) { Reader->OnDisconnect(Reader, Reader->user); } // Socket Disconnected + Reader->FinConnect = 0; + + // + // If we need to free the buffer, do so + // + if (Reader->buffer != NULL) + { + if (Reader->buffer != ILibScratchPad2) free(Reader->buffer); + Reader->buffer = NULL; + Reader->MallocSize = 0; + } + } + else + { + // + // Only do these checks if the socket was not closed, otherwise we're wasting time + // + + // + // Check to see if we need to move any data, to maximize buffer space + // + if (Reader->BeginPointer > 0) + { + // + // We can save some cycles by moving the data back to the top + // of the buffer, instead of just allocating more memory. + // + temp = Reader->buffer + Reader->BeginPointer; + + memmove_s(Reader->buffer, Reader->MallocSize, temp, Reader->EndPointer - Reader->BeginPointer); + Reader->EndPointer -= Reader->BeginPointer; + Reader->BeginPointer = 0; + + // + // Even though we didn't allocate new memory, we still moved data in the buffer, + // so we need to inform people of that, because it might be important + // + if (Reader->OnBufferReAllocated != NULL) Reader->OnBufferReAllocated(Reader, Reader->user, temp - Reader->buffer); + } + + // + // Check to see if we should grow the buffer + // + if (Reader->MallocSize - Reader->EndPointer < 1024 && (Reader->MaxBufferSize == 0 || Reader->MallocSize < Reader->MaxBufferSize)) + { + Reader->MallocSize = (Reader->MallocSize + MEMORYCHUNKSIZE < Reader->MaxBufferSize) ? (Reader->MallocSize + MEMORYCHUNKSIZE) : (Reader->MaxBufferSize == 0 ? (Reader->MallocSize + MEMORYCHUNKSIZE) : Reader->MaxBufferSize); + + temp = Reader->buffer; + if ((Reader->buffer = (char*)realloc(Reader->buffer, Reader->MallocSize)) == NULL) ILIBCRITICALEXIT(254); + // + // If this realloc moved the buffer somewhere, we need to inform people of it + // + if (Reader->buffer != temp && Reader->OnBufferReAllocated != NULL) Reader->OnBufferReAllocated(Reader, Reader->user, Reader->buffer - temp); + } + } +} + +/*! \fn ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the user object +\param socketModule The ILibAsyncSocket token to fetch the user object from +\returns The user object +*/ +void *ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule) +{ + return(socketModule == NULL?NULL:((struct ILibAsyncSocketModule*)socketModule)->user); +} + +void ILibAsyncSocket_SetUser(ILibAsyncSocket_SocketModule socketModule, void* user) +{ + if (socketModule == NULL) return; + ((struct ILibAsyncSocketModule*)socketModule)->user = user; +} + +void *ILibAsyncSocket_GetUser2(ILibAsyncSocket_SocketModule socketModule) +{ + return(socketModule == NULL?NULL:((struct ILibAsyncSocketModule*)socketModule)->user2); +} + +void ILibAsyncSocket_SetUser2(ILibAsyncSocket_SocketModule socketModule, void* user2) +{ + if (socketModule == NULL) return; + ((struct ILibAsyncSocketModule*)socketModule)->user2 = user2; +} + +int ILibAsyncSocket_GetUser3(ILibAsyncSocket_SocketModule socketModule) +{ + return(socketModule == NULL?-1:((struct ILibAsyncSocketModule*)socketModule)->user3); +} + +void ILibAsyncSocket_SetUser3(ILibAsyncSocket_SocketModule socketModule, int user3) +{ + if (socketModule == NULL) return; + ((struct ILibAsyncSocketModule*)socketModule)->user3 = user3; +} + +void ILibAsyncSocket_SetTimeout(ILibAsyncSocket_SocketModule socketModule, int timeoutSeconds, ILibAsyncSocket_TimeoutHandler timeoutHandler) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + module->timeout_milliSeconds = timeoutSeconds * 1000; + module->timeout_handler = timeoutHandler; +} + +// +// Chained PreSelect handler for ILibAsyncSocket +// +// +// +// +// +void ILibAsyncSocket_PreSelect(void* socketModule,fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + if (module->internalSocket == -1) return; // If there is not internal socket, just return now. + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "AsyncSocket[%p] entered PreSelect", (void*)module); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PreSelect", 1, module);) + sem_wait(&(module->SendLock)); + + if (module->internalSocket != -1) + { + if (module->timeout_milliSeconds != 0) + { + // User has set idle timeout, so we need to start checking + if (module->timeout_lastActivity == 0) + { + // No activity yet on socket, so set the idle timeout for the full duration + *blocktime = module->timeout_milliSeconds; + } + else + { + long long activity = ILibGetUptime() - module->timeout_lastActivity; // number of milliseconds since last activity + if (activity >= module->timeout_milliSeconds) + { + // Idle Timeout Occured + ILibAsyncSocket_TimeoutHandler h = module->timeout_handler; + module->timeout_milliSeconds = 0; + module->timeout_handler = NULL; + if (h != NULL) + { + sem_post(&(module->SendLock)); + h(module, module->user); + sem_wait(&(module->SendLock)); + if (module->timeout_milliSeconds != 0) { *blocktime = module->timeout_milliSeconds; } + } + } + else + { + // Idle Timeout did not occur yet, so set the blocktime for the rest of the timeout + *blocktime = (int)(module->timeout_milliSeconds - activity); + } + } + } + + if (module->PAUSE < 0) *blocktime = 0; + if (module->FinConnect == 0) + { + // Not Connected Yet + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->internalSocket, writeset); + FD_SET(module->internalSocket, errorset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + else + { + if (module->PAUSE == 0) // Only if this is zero. <0 is resume, so we want to process first + { + // Already Connected, just needs reading + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->internalSocket, readset); + FD_SET(module->internalSocket, errorset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + } + + if (module->PendingSend_Head != NULL) + { + // If there is pending data to be sent, then we need to check when the socket is writable + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->internalSocket, writeset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + } + + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PreSelect", 2, module);) + sem_post(&(module->SendLock)); + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "...AsyncSocket[%p] exited PreSelect", (void*)module); +} + +void ILibAsyncSocket_PrivateShutdown(void* socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + + // If this is an SSL socket, close down the SSL state + + // Now shutdown the socket and set it to zero + #if defined(WIN32) + #if defined(WINSOCK2) + shutdown(module->internalSocket, SD_BOTH); + #endif + closesocket(module->internalSocket); + #elif defined(_POSIX) + shutdown(module->internalSocket, SHUT_RDWR); + close(module->internalSocket); + #endif + module->internalSocket = (SOCKET)~0; + module->timeout_handler = NULL; + module->timeout_milliSeconds = 0; + + // This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently + if (module->FinConnect <= 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed + if (module->FinConnect > 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected + module->FinConnect = 0; +} + +// +// Chained PostSelect handler for ILibAsyncSocket +// +// +// +// +// +// +void ILibAsyncSocket_PostSelect(void* socketModule, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset) +{ + int TriggerSendOK = 0; + struct ILibAsyncSocket_SendData *temp; + int bytesSent = 0; + int flags, len; + int TRY_TO_SEND = 1; + int triggerReadSet = 0; + int triggerResume = 0; + int triggerWriteSet = 0; + int serr = 0, serrlen = sizeof(serr); + int fd_error, fd_read, fd_write; + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + + // If there is no internal socket or no events, just return now. + if (module->internalSocket == -1 || module->FinConnect == -1) return; + fd_error = FD_ISSET(module->internalSocket, errorset); + fd_read = FD_ISSET(module->internalSocket, readset); + fd_write = FD_ISSET(module->internalSocket, writeset); + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "AsyncSocket[%p] entered PostSelect", (void*)module); + + + + UNREFERENCED_PARAMETER( slct ); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PostSelect", 1, module);) + sem_wait(&(module->SendLock)); // Lock! + + //if (fd_error != 0) printf("ILibAsyncSocket_PostSelect-ERROR\r\n"); + //if (fd_read != 0) printf("ILibAsyncSocket_PostSelect-READ\r\n"); + //if (fd_write != 0) printf("ILibAsyncSocket_PostSelect-WRITE\r\n"); + + // + // Error Handling. If the ERROR flag is set we have a problem. If not, we must check the socket status for an error. + // Yes, this is odd, but it's possible for a socket to report a read set and still have an error, in this past this + // was not handled and caused a lot of problems. + // + if (fd_error != 0) + { + serr = 1; + } + else + { + // Fetch the socket error code +#if defined(WINSOCK2) + getsockopt(module->internalSocket, SOL_SOCKET, SO_ERROR, (char*)&serr, (int*)&serrlen); +#else + getsockopt(module->internalSocket, SOL_SOCKET, SO_ERROR, (char*)&serr, (socklen_t*)&serrlen); +#endif + } + + #ifdef MICROSTACK_PROXY + // Handle proxy, we need to read the proxy response, all of it and not a byte more. + if (module->FinConnect == 1 && module->ProxyState == 1 && serr == 0 && fd_read != 0) + { + char *ptr1, *ptr2; + int len2, len3; + serr = 555; // Fake proxy error + len2 = recv(module->internalSocket, ILibScratchPad2, 1024, MSG_PEEK | MSG_NOSIGNAL); + if (len2 > 0 && len2 < 1024) + { + ILibScratchPad2[len2] = 0; + ptr1 = strstr(ILibScratchPad2, "\r\n\r\n"); + ptr2 = strstr(ILibScratchPad2, " 200 "); + if (ptr1 != NULL && ptr2 != NULL && ptr2 < ptr1) + { + len3 = (int)((ptr1 + 4) - ILibScratchPad2); + recv(module->internalSocket, ILibScratchPad2, len3, MSG_NOSIGNAL); + module->FinConnect = 0; // Let pretend we never connected, this will trigger all the connection stuff. + module->ProxyState = 2; // Move the proxy connection state forward. + serr = 0; // Proxy connected collectly. + } + } + } + #endif + + // If there are any errors, shutdown this socket + if (serr != 0) + { + // Unlock before fireing the event + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + ILibAsyncSocket_PrivateShutdown(module); + } + else + { + // There are no errors, lets keep processing the socket normally + if (module->FinConnect == 0) + { + // Check to see if the socket is connected +#ifdef MICROSTACK_PROXY + if (fd_write != 0 || module->ProxyState == 2) +#else + if (fd_write != 0) +#endif + { + // Connected + len = sizeof(struct sockaddr_in6); +#if defined(WINSOCK2) + getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), (int*)&len); +#else + getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), (socklen_t*)&len); +#endif + module->FinConnect = 1; + module->PAUSE = 0; + + // Set the socket to non-blocking mode, so we can play nice and share the thread + #if defined(WIN32) + flags = 1; + ioctlsocket(module->internalSocket, FIONBIO, (u_long *)(&flags)); + #elif defined(_POSIX) + flags = fcntl(module->internalSocket, F_GETFL,0); + fcntl(module->internalSocket, F_SETFL, O_NONBLOCK|flags); + #endif + + // If this is a proxy connection, send the proxy connect header now. +#ifdef MICROSTACK_PROXY + if (module->ProxyAddress.sin6_family != 0 && module->ProxyState == 0) + { + int len2; + ILibInet_ntop((int)(module->RemoteAddress.sin6_family), (void*)&(((struct sockaddr_in*)&(module->RemoteAddress))->sin_addr), ILibScratchPad, 4096); + if (module->ProxyUser == NULL || module->ProxyPass == NULL) + { + len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\n\r\n", ILibScratchPad, ntohs(module->RemoteAddress.sin6_port), ILibScratchPad); + } else { + char* ProxyAuth = NULL; + len2 = sprintf_s(ILibScratchPad2, 4096, "%s:%s", module->ProxyUser, module->ProxyPass); + len2 = ILibBase64Encode((unsigned char*)ILibScratchPad2, len2, (unsigned char**)&ProxyAuth); + len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\nProxy-authorization: basic %s\r\n\r\n", ILibScratchPad, ntohs(module->RemoteAddress.sin6_port), ILibScratchPad, ProxyAuth); + if (ProxyAuth != NULL) free(ProxyAuth); + } + module->timeout_lastActivity = ILibGetUptime(); + send(module->internalSocket, ILibScratchPad2, len2, MSG_NOSIGNAL); + module->ProxyState = 1; + // TODO: Set timeout. If the proxy does not respond, we need to close this connection. + // On the other hand... This is not generally a problem, proxies will disconnect after a timeout anyway. + + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + return; + } + if (module->ProxyState == 2) module->ProxyState = 3; +#endif + + // Connection Complete + triggerWriteSet = 1; + } + + // Unlock before fireing the event + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + + // If we did connect, we got more things to do + if (triggerWriteSet != 0) + { + module->timeout_lastActivity = ILibGetUptime(); + { + // If this is a normal socket, event the connection now. + if (module->OnConnect != NULL) module->OnConnect(module, -1, module->user); + } + } + } + else + { + // Connected socket, we need to read data + if (fd_read != 0) + { + module->timeout_lastActivity = ILibGetUptime(); + triggerReadSet = 1; // Data Available + } + else if (module->PAUSE < 0) + { + // Someone resumed a paused connection, but the FD_SET was not triggered because there is no new data on the socket. + module->timeout_lastActivity = ILibGetUptime(); + triggerResume = 1; + ++module->PAUSE; + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] was RESUMED with no new data on socket", (void*)module); + } + + // Unlock before fireing the event + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + + if (triggerReadSet != 0 || triggerResume != 0) ILibProcessAsyncSocket(module, triggerReadSet); + } + } + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PostSelect", 1, module);) + sem_wait(&(module->SendLock)); + // Write Handling + if (module->FinConnect > 0 && module->internalSocket != ~0 && fd_write != 0 && module->PendingSend_Head != NULL) + { + // + // Keep trying to send data, until we are told we can't + // + module->timeout_lastActivity = ILibGetUptime(); + while (TRY_TO_SEND != 0) + { + if (module->PendingSend_Head == NULL) break; + if (module->PendingSend_Head->remoteAddress.sin6_family == 0) + { + bytesSent = (int)send(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent, MSG_NOSIGNAL); // Klocwork reports that this could block while holding a lock... This socket has been set to O_NONBLOCK, so that will never happen + } + else + { + bytesSent = (int)sendto(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent, MSG_NOSIGNAL, (struct sockaddr*)&module->PendingSend_Head->remoteAddress, INET_SOCKADDR_LENGTH(module->PendingSend_Head->remoteAddress.sin6_family)); // Klocwork reports that this could block while holding a lock... This socket has been set to O_NONBLOCK, so that will never happen + } + + if (bytesSent == 0) { TRY_TO_SEND = 0; } //To avoid get stuck in an infinite loop when bytesSent == 0 + + if (bytesSent > 0) + { + module->PendingBytesToSend -= bytesSent; + module->TotalBytesSent += bytesSent; + module->PendingSend_Head->bytesSent += bytesSent; + if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize) + { + // Finished Sending this block + if (module->PendingSend_Head == module->PendingSend_Tail) + { + module->PendingSend_Tail = NULL; + } + if (module->PendingSend_Head->UserFree == 0) + { + free(module->PendingSend_Head->buffer); + } + temp = module->PendingSend_Head->Next; + free(module->PendingSend_Head); + module->PendingSend_Head = temp; + if (module->PendingSend_Head == NULL) { TRY_TO_SEND = 0; } + } + else + { + // We sent data, but not everything that needs to get sent was sent, try again + TRY_TO_SEND = 1; + } + } + if (bytesSent == -1) + { + // Error, clean up everything + TRY_TO_SEND = 0; +#if defined(WIN32) + if (WSAGetLastError() != WSAEWOULDBLOCK) +#elif defined(_POSIX) + if (errno != EWOULDBLOCK) +#endif + { + // There was an error sending + ILibAsyncSocket_ClearPendingSend(socketModule); + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + } + } + } + // This triggers OnSendOK, if all the pending data has been sent. + if (module->PendingSend_Head == NULL && bytesSent != -1) { TriggerSendOK = 1; } + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 2, module);) + sem_post(&(module->SendLock)); + if (TriggerSendOK != 0) + { + module->OnSendOK(module, module->user); + if (module->Transport.SendOkPtr != NULL) { module->Transport.SendOkPtr(module); } + } + + if (bytesSent == 0) { ILibAsyncSocket_ClearPendingSend(socketModule); } //If bytesSent == 0 then clear pending data + } + else + { + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 2, module);) + sem_post(&(module->SendLock)); + } + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "...AsyncSocket[%p] exited PostSelect", (void*)module); +} + +/*! \fn ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule) +\brief Determines if an ILibAsyncSocket is in use +\param socketModule The ILibAsyncSocket to query +\returns 0 if in use, nonzero otherwise +*/ +int ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return((module == NULL || module->internalSocket==~0)?1:0); +} + +int ILibAsyncSocket_IsConnected(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return module->FinConnect; +} + +/*! \fn ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the number of bytes that are pending to be sent +\param socketModule The ILibAsyncSocket to query +\returns Number of pending bytes +*/ +unsigned int ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return(module->PendingBytesToSend); +} + +/*! \fn ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the total number of bytes that have been sent, since the last reset +\param socketModule The ILibAsyncSocket to query +\returns Number of bytes sent +*/ +unsigned int ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return(module->TotalBytesSent); +} + +/*! \fn ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +\brief Resets the total bytes sent counter +\param socketModule The ILibAsyncSocket to reset +*/ +void ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + module->TotalBytesSent = 0; +} + +/*! \fn ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer) +\brief Returns the buffer associated with an ILibAsyncSocket +\param socketModule The ILibAsyncSocket to obtain the buffer from +\param[out] buffer The buffer +\param[out] BeginPointer Stating offset of the buffer +\param[out] EndPointer Length of buffer +*/ +void ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer) +{ + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + + *buffer = module->buffer; + *BeginPointer = module->BeginPointer; + *EndPointer = module->EndPointer; +} + +void ILibAsyncSocket_ModuleOnConnect(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + if (module != NULL && module->OnConnect != NULL) module->OnConnect(module, -1, module->user); +} + +//! Set the SSL client context used by all connections done by this socket module. The SSL context must +//! be set before using this module. If left to NULL, all connections are in the clear using TCP. +//! +//! This is utilized by the ILibAsyncServerSocket module +/*! + \param socketModule The ILibAsyncSocket to modify + \param ssl_ctx The ssl_ctx structure + \param server ILibAsyncSocket_TLS_Mode Configuration setting to set +*/ + +//! Sets the remote address field. +//! This is utilized by the ILibAsyncServerSocket module +/*! + \param socketModule ILibAsyncSocket_SocketModule to modify + \param remoteAddress The remote endpoint to set +*/ +void ILibAsyncSocket_SetRemoteAddress(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress) +{ + if (socketModule != NULL) + { + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + memcpy_s(&(module->RemoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); + } +} + +/*! \fn ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule,void* UseThisSocket,ILibAsyncSocket_OnInterrupt InterruptPtr,void *user) +\brief Associates an actual socket with ILibAsyncSocket +\par +Instead of calling \a ConnectTo, you can call this method to associate with an already +connected socket. +\param socketModule The ILibAsyncSocket to associate +\param UseThisSocket The socket to associate +\param InterruptPtr Function Pointer that triggers when the TCP connection is interrupted +\param user User object to associate with this session +*/ +#if defined(WIN32) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, SOCKET UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +#elif defined(_POSIX) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, int UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +#endif +{ + int flags; + char *tmp; + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + + module->PendingBytesToSend = 0; + module->TotalBytesSent = 0; + module->internalSocket = UseThisSocket; + module->OnInterrupt = InterruptPtr; + module->user = user; + module->FinConnect = 1; + module->PAUSE = 0; + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] Initialized", (void*)module); + + // + // If the buffer is too small/big, we need to realloc it to the minimum specified size + // + if (module->buffer != ILibScratchPad2) + { + if ((tmp = (char*)realloc(module->buffer, module->InitialSize)) == NULL) ILIBCRITICALEXIT(254); + module->buffer = tmp; + module->MallocSize = module->InitialSize; + } + module->BeginPointer = 0; + module->EndPointer = 0; + + // + // Make sure the socket is non-blocking, so we can play nice and share the thread + // +#if defined(WIN32) + flags = 1; + ioctlsocket(module->internalSocket, FIONBIO,(u_long *)(&flags)); +#elif defined(_POSIX) + flags = fcntl(module->internalSocket,F_GETFL,0); + fcntl(module->internalSocket,F_SETFL,O_NONBLOCK|flags); +#endif +} + +/*! \fn ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the Remote Interface of a connected session +\param socketModule The ILibAsyncSocket to query +\param[in,out] remoteAddress The remote interface +\returns Number of bytes written into remoteAddress +*/ +int ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + if (module->RemoteAddress.sin6_family != 0) + { + memcpy_s(remoteAddress, sizeof(struct sockaddr_in6), &(module->RemoteAddress), INET_SOCKADDR_LENGTH(module->RemoteAddress.sin6_family)); + return INET_SOCKADDR_LENGTH(module->RemoteAddress.sin6_family); + } + memcpy_s(remoteAddress, sizeof(struct sockaddr_in6), &(module->SourceAddress), INET_SOCKADDR_LENGTH(module->SourceAddress.sin6_family)); + return INET_SOCKADDR_LENGTH(module->SourceAddress.sin6_family); +} + +/*! \fn ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the Local Interface of a connected session, in network order +\param socketModule The ILibAsyncSocket to query +\param[in,out] localAddress The local interface +\returns The number of bytes written to localAddress +*/ +int ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *localAddress) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + int receivingAddressLength = sizeof(struct sockaddr_in6); + + if (module->LocalAddress.sin6_family !=0) + { + memcpy_s(localAddress, INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family), &(module->LocalAddress), INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family)); + return INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family); + } + else + { +#if defined(WINSOCK2) + getsockname(module->internalSocket, localAddress, (int*)&receivingAddressLength); +#else + getsockname(module->internalSocket, localAddress, (socklen_t*)&receivingAddressLength); +#endif + return receivingAddressLength; + } +} +//! Get's the locally bound port +/*! + \param socketModule ILibAsyncSocket_SocketModule object to query + \return The locallly bound port +*/ +unsigned short ILibAsyncSocket_GetLocalPort(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct sockaddr_in6 localAddress; + + if (module->LocalAddress.sin6_family == AF_INET6) return ntohs(module->LocalAddress.sin6_port); + if (module->LocalAddress.sin6_family == AF_INET) return ntohs((((struct sockaddr_in*)(&(module->LocalAddress)))->sin_port)); +#if defined(WINSOCK2) + getsockname(module->internalSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength); +#else + getsockname(module->internalSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength); +#endif + if (localAddress.sin6_family == AF_INET6) return ntohs(localAddress.sin6_port); + if (localAddress.sin6_family == AF_INET) return ntohs((((struct sockaddr_in*)(&localAddress))->sin_port)); + return 0; +} +/*! \fn ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule) +\brief Pauses a session +\par +Sessions can be paused, such that further data is not read from the socket until resumed. NOTE: MUST be called from Microstack thread +\param socketModule The ILibAsyncSocket to pause. +*/ +void ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule; + if (socketModule == NULL) { return; } + + sm->PAUSE = 1; +} +/*! \fn ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule) +\brief Resumes a paused session +\par +Sessions can be paused, such that further data is not read from the socket until resumed +\param socketModule The ILibAsyncSocket to resume +*/ +void ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule; + if (sm == NULL) { return; } + ILibRemoteLogging_printf(ILibChainGetLogger(sm->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] was RESUMED", (void*)socketModule); + if (sm->PAUSE > 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(sm->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Unblocking Chain"); + sm->PAUSE = -1; + ILibForceUnBlockChain(sm->Transport.ChainLink.ParentChain); + } +} + +/*! \fn ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module) +\brief Obtain the underlying raw socket +\param module The ILibAsyncSocket to query +\returns The pointer to raw socket +*/ +void* ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + return(&(sm->internalSocket)); +} + +void ILibAsyncSocket_Shutdown(ILibAsyncSocket_SocketModule module) +{ + ILibAsyncSocket_PrivateShutdown(module); +} + +//! Sets the local endpoint associated with this ILibAsyncSocket +/*! + \param module ILibAsyncSocket_SocketModule to modify + \param LocalAddress Local endpoint to set +*/ +void ILibAsyncSocket_SetLocalInterface(ILibAsyncSocket_SocketModule module, struct sockaddr *LocalAddress) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + memcpy_s(&(sm->LocalAddress), sizeof(struct sockaddr_in6), LocalAddress, INET_SOCKADDR_LENGTH(LocalAddress->sa_family)); +} +//! Sets the maximum size that the internal buffer can be grown +/*! + \param module ILibAsyncSocket_SocketModule to configure + \param maxSize Maximum size in bytes + \param OnBufferSizeExceededCallback ILibAsyncSocket_OnBufferSizeExceeded handler to be dispatched if the max size is exceeded + \param user Custom user state data +*/ +void ILibAsyncSocket_SetMaximumBufferSize(ILibAsyncSocket_SocketModule module, int maxSize, ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceededCallback, void *user) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + sm->MaxBufferSize = maxSize; + sm->OnBufferSizeExceeded = OnBufferSizeExceededCallback; + sm->MaxBufferSizeUserObject = user; +} +//! Sets the SendOK event handler +/*! + \param module ILibAsyncSocket_SocketModule to configure + \param OnSendOK ILibAsyncSocket_OnSendOK handler to dispatch on an OnSendOK event +*/ +void ILibAsyncSocket_SetSendOK(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnSendOK OnSendOK) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + sm->OnSendOK = OnSendOK; +} + +//! Determines if the specified IPv6 address is a link local address +/*! + \param LocalAddress IPv6 Address + \return 1 = Link Local, 0 = Not Link Local +*/ +int ILibAsyncSocket_IsIPv6LinkLocal(struct sockaddr *LocalAddress) +{ + struct sockaddr_in6 *x = (struct sockaddr_in6*)LocalAddress; +#if defined(WIN32) + if (LocalAddress->sa_family == AF_INET6 && x->sin6_addr.u.Byte[0] == 0xFE && x->sin6_addr.u.Byte[1] == 0x80) return 1; +#else + if (LocalAddress->sa_family == AF_INET6 && x->sin6_addr.s6_addr[0] == 0xFE && x->sin6_addr.s6_addr[1] == 0x80) return 1; +#endif + return 0; +} +//! Determines if the internal socket is a link local socket +/*! + \param socketModule ILibAsyncSocket_SocketModule to query + \return 0 = Not Link Local, 1 = Link Local +*/ +int ILibAsyncSocket_IsModuleIPv6LinkLocal(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return ILibAsyncSocket_IsIPv6LinkLocal((struct sockaddr*)&(module->LocalAddress)); +} +//! Returns 1 if the ILibAsyncSocket was disconnected because the buffer size was exceeded +/*! + \param socketModule ILibAsyncSocket_SocketModule to query + \return 1 = BufferSizeExceeded +*/ +int ILibAsyncSocket_WasClosedBecauseBufferSizeExceeded(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule; + return(sm->MaxBufferSizeExceeded); +} + +void ILibAsyncSocket_UpdateOnData(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnData OnData) +{ + ((ILibAsyncSocketModule*)module)->OnData = OnData; +} diff --git a/MicroLMS/microstack/ILibAsyncSocket.h b/MicroLMS/microstack/ILibAsyncSocket.h new file mode 100644 index 0000000..a4776a7 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncSocket.h @@ -0,0 +1,195 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef ___ILibAsyncSocket___ +#define ___ILibAsyncSocket___ + +/*! \file ILibAsyncSocket.h +\brief MicroStack APIs for TCP Client Functionality +*/ + +/*! \defgroup ILibAsyncSocket ILibAsyncSocket Module +\{ +*/ + +#if defined(WIN32) +#include +#elif defined(_POSIX) +#include +#endif + +//#include "ssl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \def MEMORYCHUNKSIZE +\brief Incrementally grow the buffer by this amount of bytes +*/ +#define MEMORYCHUNKSIZE 4096 + +#define ILibTransports_AsyncSocket 0x40 + +typedef enum ILibAsyncSocket_SendStatus +{ + ILibAsyncSocket_ALL_DATA_SENT = 1, /*!< All of the data has already been sent */ + ILibAsyncSocket_NOT_ALL_DATA_SENT_YET = 0, /*!< Not all of the data could be sent, but is queued to be sent as soon as possible */ + ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR = -4 /*!< A send operation was attmepted on a closed socket */ +}ILibAsyncSocket_SendStatus; + +/*! \enum ILibAsyncSocket_MemoryOwnership +\brief Enumeration values for Memory Ownership of variables +*/ +typedef enum ILibAsyncSocket_MemoryOwnership +{ + ILibAsyncSocket_MemoryOwnership_CHAIN = 0, /*!< The Microstack will own this memory, and free it when it is done with it */ + ILibAsyncSocket_MemoryOwnership_STATIC = 1, /*!< This memory is static, so the Microstack will not free it, and assume it will not go away, so it won't copy it either */ + ILibAsyncSocket_MemoryOwnership_USER = 2 /*!< The Microstack doesn't own this memory, so if necessary the memory will be copied */ +}ILibAsyncSocket_MemoryOwnership; + +/*! \typedef ILibAsyncSocket_SocketModule +\brief The handle for an ILibAsyncSocket module +*/ +typedef void* ILibAsyncSocket_SocketModule; +/*! \typedef ILibAsyncSocket_OnInterrupt +\brief Handler for when a session was interrupted by a call to ILibStopChain +\param socketModule The \a ILibAsyncSocket_SocketModule that was interrupted +\param user The user object that was associated with this connection +*/ + +//typedef void(*ILibAsyncSocket_OnReplaceSocket)(ILibAsyncSocket_SocketModule socketModule, void *user); +typedef void(*ILibAsyncSocket_OnBufferSizeExceeded)(ILibAsyncSocket_SocketModule socketModule, void *user); + +typedef void(*ILibAsyncSocket_OnInterrupt)(ILibAsyncSocket_SocketModule socketModule, void *user); +/*! \typedef ILibAsyncSocket_OnData +\brief Handler for when data is received +\par +Note on memory handling: +When you process the received buffer, you must advance \a p_beginPointer the number of bytes that you +have processed. If \a p_beginPointer does not equal \a endPointer when this method completes, +the system will continue to reclaim any memory that has already been processed, and call this method again +until no more memory has been processed. If no memory has been processed, and more data has been received +on the network, the buffer will be automatically grown (according to a specific alogrythm), to accomodate any new data. +\param socketModule The \a ILibAsyncSocket_SocketModule that received data +\param buffer The data that was received +\param[in,out] p_beginPointer The start index of the data that was received +\param endPointer The end index of the data that was received +\param[in,out] OnInterrupt Set this pointer to receive notification if this session is interrupted +\param[in,out] user Set a custom user object +\param[out] PAUSE Flag to indicate if the system should continue reading data off the network +*/ +typedef void(*ILibAsyncSocket_OnData)(ILibAsyncSocket_SocketModule socketModule, char* buffer, int *p_beginPointer, int endPointer,ILibAsyncSocket_OnInterrupt* OnInterrupt, void **user, int *PAUSE); +/*! \typedef ILibAsyncSocket_OnConnect +\brief Handler for when a connection is made +\param socketModule The \a ILibAsyncSocket_SocketModule that was connected +\param user The user object that was associated with this object +*/ +typedef void(*ILibAsyncSocket_OnConnect)(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user); +/*! \typedef ILibAsyncSocket_OnDisconnect +\brief Handler for when a connection is terminated normally +\param socketModule The \a ILibAsyncSocket_SocketModule that was disconnected +\param user User object that was associated with this connection +*/ +typedef void(*ILibAsyncSocket_OnDisconnect)(ILibAsyncSocket_SocketModule socketModule, void *user); +/*! \typedef ILibAsyncSocket_OnSendOK +\brief Handler for when pending send operations have completed +\par +This handler will only be called if a call to \a ILibAsyncSocket_Send returned a value greater +than 0, which indicates that not all of the data could be sent. +\param socketModule The \a ILibAsyncSocket_SocketModule whos pending sends have completed +\param user User object that was associated with this connection +*/ +typedef void(*ILibAsyncSocket_OnSendOK)(ILibAsyncSocket_SocketModule socketModule, void *user); +/*! \typedef ILibAsyncSocket_OnBufferReAllocated +\brief Handler for when the internal data buffer has been resized. +\par +Note: This is only useful if you are storing pointer values into the buffer supplied in \a ILibAsyncSocket_OnData. +\param AsyncSocketToken The \a ILibAsyncSocket_SocketModule whos buffer was resized +\param user The user object that was associated with this connection +\param newOffset The new offset differential. Simply add this value to your existing pointers, to obtain the correct pointer into the resized buffer. +*/ +typedef void(*ILibAsyncSocket_OnBufferReAllocated)(ILibAsyncSocket_SocketModule AsyncSocketToken, void *user, ptrdiff_t newOffset); + +/*! \defgroup TLSGroup TLS Related Methods +* @{ +*/ +/*! @} */ + +extern const int ILibMemory_ASYNCSOCKET_CONTAINERSIZE; + +void ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback); +void *ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetUser(ILibAsyncSocket_SocketModule socketModule, void* user); +void *ILibAsyncSocket_GetUser2(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetUser2(ILibAsyncSocket_SocketModule socketModule, void* user); +int ILibAsyncSocket_GetUser3(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetUser3(ILibAsyncSocket_SocketModule socketModule, int user); +void ILibAsyncSocket_UpdateOnData(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnData OnData); + +#define ILibCreateAsyncSocketModule(Chain, initialBufferSize, OnData, OnConnect, OnDisconnect, OnSendOK) ILibCreateAsyncSocketModuleWithMemory(Chain, initialBufferSize, OnData, OnConnect, OnDisconnect, OnSendOK, 0) +ILibAsyncSocket_SocketModule ILibCreateAsyncSocketModuleWithMemory(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect, ILibAsyncSocket_OnSendOK OnSendOK, int UserMappedMemorySize); + +void *ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module); +void ILibAsyncSocket_Shutdown(ILibAsyncSocket_SocketModule module); + +unsigned int ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule); +unsigned int ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule); + +void ILibAsyncSocket_ConnectTo(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); + +#ifdef MICROSTACK_PROXY +void ILibAsyncSocket_ConnectToProxy(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, struct sockaddr *proxyAddress, char* proxyUser, char* proxyPass, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); +#endif + +enum ILibAsyncSocket_SendStatus ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, struct sockaddr *remoteAddress, enum ILibAsyncSocket_MemoryOwnership UserFree); + +/*! \def ILibAsyncSocket_Send +\brief Sends data onto the TCP stream +\param socketModule The \a ILibAsyncSocket_SocketModule to send data on +\param buffer The data to be sent +\param length The length of \a buffer +\param UserFree The \a ILibAsyncSocket_MemoryOwnership enumeration, that identifies how the memory pointer to by \a buffer is to be handled +\returns \a ILibAsyncSocket_SendStatus indicating the send status +*/ +#define ILibAsyncSocket_Send(socketModule, buffer, length, UserFree) ILibAsyncSocket_SendTo(socketModule, buffer, length, NULL, UserFree) +void ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer); + +#if defined(WIN32) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, SOCKET UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); +#elif defined(_POSIX) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, int UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); +#endif + + +void ILibAsyncSocket_SetRemoteAddress(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress); +void ILibAsyncSocket_SetLocalInterface(ILibAsyncSocket_SocketModule module, struct sockaddr *localAddress); + +int ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule); +int ILibAsyncSocket_IsConnected(ILibAsyncSocket_SocketModule socketModule); +int ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *localAddress); +int ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress); +unsigned short ILibAsyncSocket_GetLocalPort(ILibAsyncSocket_SocketModule socketModule); + +void ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule); +int ILibAsyncSocket_WasClosedBecauseBufferSizeExceeded(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetMaximumBufferSize(ILibAsyncSocket_SocketModule module, int maxSize, ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceededCallback, void *user); +void ILibAsyncSocket_SetSendOK(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnSendOK OnSendOK); +int ILibAsyncSocket_IsIPv6LinkLocal(struct sockaddr *LocalAddress); +int ILibAsyncSocket_IsModuleIPv6LinkLocal(ILibAsyncSocket_SocketModule module); + +typedef void(*ILibAsyncSocket_TimeoutHandler)(ILibAsyncSocket_SocketModule module, void *user); +void ILibAsyncSocket_SetTimeout(ILibAsyncSocket_SocketModule module, int timeoutSeconds, ILibAsyncSocket_TimeoutHandler timeoutHandler); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/MicroLMS/microstack/ILibLMS.c b/MicroLMS/microstack/ILibLMS.c new file mode 100644 index 0000000..f82d9e7 --- /dev/null +++ b/MicroLMS/microstack/ILibLMS.c @@ -0,0 +1,834 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +/* +Real LMS code can be found here: http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers +*/ + +#if !defined(_NOHECI) + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif + +#if defined(WINSOCK2) + #include + #include +#elif defined(WINSOCK1) + #include + #include +#endif + +#include "ILibParsers.h" +#include "ILibAsyncSocket.h" +#include "ILibAsyncServerSocket.h" + +#ifdef WIN32 + #include "../heci/heciwin.h" + #include "WinBase.h" +#endif +#ifdef _POSIX + #include "../heci/HECILinux.h" + #define UNREFERENCED_PARAMETER(P) (P) +#endif +#include "../heci/PTHICommand.h" +#include "../heci/LMEConnection.h" + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) +#define LMS_MAX_CONNECTIONS 16 // Maximum is 32 +#define LMS_MAX_SESSIONS 32 +// #define _DEBUGLMS +#ifdef _DEBUGLMS +#define LMSDEBUG(...) printf(__VA_ARGS__); +#else +#define LMSDEBUG(...) +#endif + +char * g_SelfLMSDir=NULL; +int LastId = 2; + +struct ILibLMS_StateModule* IlibExternLMS = NULL; // Glocal pointer to the LMS module. Since we can only have one of these modules running, a global pointer is sometimes useful. +void ILibLMS_SetupConnection(struct ILibLMS_StateModule* module, int i); +void ILibLMS_LaunchHoldingSessions(struct ILibLMS_StateModule* module); + +// Each LMS session can be in one of these states. +enum LME_CHANNEL_STATUS { + LME_CS_FREE = 0, // The session slot is free, can be used at any time. + LME_CS_PENDING_CONNECT = 1, // A connection as been made to LMS and a notice has been send to Intel AMT asking for a CHANNEL OPEN. + LME_CS_CONNECTED = 2, // Intel AMT confirmed the connection and data can flow in both directions. + LME_CS_PENDING_LMS_DISCONNECT = 3, // The connection to LMS was disconnected, Intel AMT has been notified and we are waitting for Intel AMT confirmation. + LME_CS_PENDING_AMT_DISCONNECT = 4, // Intel AMT decided to close the connection. We are disconnecting the LMS TCP connection. + LME_CS_HOLDING = 5, // We got too much data from the LMS TCP connection, more than Intel AMT can handle. We are holding the connection until AMT can handle more. + LME_CS_CONNECTION_WAIT = 6 // A TCP connection to LMS was made, but there all LMS connections are currently busy. Waitting for one to free up. +}; + +// This is the structure for a session +struct LMEChannel +{ + int ourid; // The iddentifier of this channel on our side, this is the same as the index in the "Channel Sessions[LMS_MAX_SESSIONS];" array below. + int amtid; // The Intel AMT identifier for this channel. + enum LME_CHANNEL_STATUS status; // Current status of the channel. + int sockettype; // Type of connected socket: 0 = TCP, 1 = WebSocket + void* socketmodule; // The microstack associated with the LMS connection. + int txwindow; // Transmit window. + int rxwindow; // Receive window. + unsigned short localport; // The Intel AMT connection port. + int errorcount; // Open channel error count. + char* pending; // Buffer pointing to pending data that needs to be sent to Intel AMT, used for websocket only. + int pendingcount; // Buffer pointing to pending data size that needs to be sent to Intel AMT, used for websocket only. + int pendingptr; // Pointer to start of the pending buffer. +}; + +enum LME_VERSION_HANDSHAKING { + LME_NOT_INITIATED, + LME_INITIATED, + LME_AGREED +}; + +enum LME_SERVICE_STATUS { + LME_NOT_STARTED, + LME_STARTED +}; + +// This is the Intel AMT LMS chain module structure +struct ILibLMS_StateModule +{ + ILibChain_Link ChainLink; + void *Server1; // Microstack TCP server for port 16992 + void *Server2; // Microstack TCP server for port 16993 + void *ControlSocket; // Pointer to the LMS control web socket, NULL if not connected. + + struct LMEConnection MeConnection; // The MEI connection structure + struct LMEChannel Sessions[LMS_MAX_SESSIONS]; // List of sessions + //ILibWebServer_ServerToken WebServer; // LMS Web Server + enum LME_VERSION_HANDSHAKING handshakingStatus; + enum LME_SERVICE_STATUS pfwdService; + unsigned int AmtProtVersionMajor; // Intel AMT MEI major version + unsigned int AmtProtVersionMinor; // Intel AMT MEI minor version + sem_t Lock; // Global lock, this is needed because MEI listener is a different thread from the microstack thread +}; + +/* +// Convert a block of data to HEX +// The "out" must have (len*2)+1 free space. +char utils_HexTable[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; +char* __fastcall util_tohex(char* data, int len, char* out) +{ + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for (i = 0; i < len; i++) + { + *(p++) = utils_HexTable[((unsigned char)data[i]) >> 4]; + *(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} +*/ + +int GetFreeSlot(struct ILibLMS_StateModule* module){ + int i; + int cur = LastId; + // Look for an empty session + for (i = 0; i < LMS_MAX_SESSIONS-3; i++) { + cur++; + if (cur >= LMS_MAX_SESSIONS){ + cur = 3; + } + if (module->Sessions[cur].status == LME_CS_FREE) { + LastId = cur; + return cur; + } + } + return -1; +} + +//int GetFreeSlot(struct ILibLMS_StateModule* module){ +// int i; +// for (i = 0; i < LMS_MAX_SESSIONS - 3; i++) { +// if (cur >= LMS_MAX_SESSIONS){ +// cur = 3; +// } +// if (module->Sessions[cur].status == LME_CS_FREE) { +// LastId = cur; +// return cur; +// } +// } +// return -1; +//} + +// This function is called each time a MEI message is received from Intel AMT +void ILibLMS_MEICallback(struct LMEConnection* lmemodule, void *param, void *rxBuffer, unsigned int len) +{ + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)param; + int disconnects = 0; // This is a counter of how many sessions are freed up. We use this at the end to place HOLDING sessions into free slots. + + //ILibAsyncServerSocket_ServerModule newModule; + //ILibLinkedList list; + //void* node; + //ILibTransport* curModule; + + // Happens when the chain is being destroyed, don't call anything chain related. + if (rxBuffer == NULL) return; + + sem_wait(&(module->Lock)); + LMSDEBUG("ILibLMS_MEICallback %d\r\n", ((unsigned char*)rxBuffer)[0]); + + switch (((unsigned char*)rxBuffer)[0]) + { + case APF_GLOBAL_REQUEST: // 80 + { + + int request = 0; + unsigned char *pCurrent; + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + pHeader->StringLength = ntohl(pHeader->StringLength); + + if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST)) == 0) { request = 1; } + else if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST)) == 0) { request = 2; } + else if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_UDP_SEND_TO) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_UDP_SEND_TO, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_UDP_SEND_TO)) == 0) { request = 3; } + + + if (request == 1 || request == 2) + { + int port = 0; + unsigned int len2; + unsigned int bytesRead = len; + unsigned int hsize=0; + if (request==1) + hsize = sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST) + sizeof(UINT8); + else if (request==2) + hsize = sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST) + sizeof(UINT8); + + pCurrent = (unsigned char*)rxBuffer + hsize; + bytesRead -= hsize; + if (bytesRead < sizeof(unsigned int)) { + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + + len2 = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + if (bytesRead < (sizeof(unsigned int) + len2 + sizeof(unsigned int))) { + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + + // addr = (char*)pCurrent; + pCurrent += len2; + port = ntohl(*((unsigned int *)pCurrent)); + + if (request == 1) + { + //newModule = ILibCreateAsyncServerSocketModule(module->Chain, LMS_MAX_CONNECTIONS, port, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); + //if (newModule){ + // ILibAsyncServerSocket_SetTag(newModule, module); + // LME_TcpForwardReplySuccess(lmemodule, port); + //} + //else{ + // LME_TcpForwardReplyFailure(lmemodule); + //} + + // New forwarding request + if (port == 16992 || port == 16993) + LME_TcpForwardReplySuccess(lmemodule, port); + else + LME_TcpForwardReplyFailure(lmemodule); + } + else + { + // Cancel a forwarding request + + + + //list = ILibGetModuleList(module->Chain); + //node = ILibLinkedList_GetNode_Head(list); + //while (node != NULL){ + // curModule = (ILibTransport*)ILibLinkedList_GetDataFromNode(node); + // if (curModule->IdentifierFlags == ILibTransports_ILibAsyncServerSocket){ + // if (ILibAsyncServerSocket_GetPortNumber(curModule)){ + // ILibAsyncServerSocket_Close(curModule); + // } + // } + // node = ILibLinkedList_GetNextNode(node); + //} + + + LME_TcpForwardCancelReplySuccess(lmemodule); + } + } + else if (request == 3) + { + // Send a UDP packet + // TODO: Send UDP + } + else{ + // do nothing + } + } + break; + case APF_CHANNEL_OPEN: // (90) Sent by Intel AMT when a channel needs to be open from Intel AMT. This is not common, but WSMAN events are a good example of channel coming from AMT. + { + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + if (pHeader->StringLength == APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT) && memcmp((char*)pHeader->String, APF_OPEN_CHANNEL_REQUEST_DIRECT, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT)) == 0) + { + unsigned int len2; + unsigned char *pCurrent; + struct LMEChannelOpenRequestMessage channelOpenRequest; + + if (len < sizeof(APF_GENERIC_HEADER) + ntohl(pHeader->StringLength) + 7 + (5 * sizeof(UINT32))) { LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } + pCurrent = (unsigned char*)rxBuffer + sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT); + + channelOpenRequest.ChannelType = APF_CHANNEL_DIRECT; + channelOpenRequest.SenderChannel = ntohl(*((UINT32 *)pCurrent)); + pCurrent += sizeof(UINT32); + channelOpenRequest.InitialWindow = ntohl(*((UINT32 *)pCurrent)); + pCurrent += 2 * sizeof(UINT32); + len2 = ntohl(*((UINT32 *)pCurrent)); + pCurrent += sizeof(UINT32); + channelOpenRequest.Address = (char*)pCurrent; + pCurrent += len2; + channelOpenRequest.Port = ntohl(*((UINT32 *)pCurrent)); + pCurrent += sizeof(UINT32); + + LME_ChannelOpenReplyFailure(lmemodule, channelOpenRequest.SenderChannel, OPEN_FAILURE_REASON_CONNECT_FAILED); + } + } + break; + case APF_DISCONNECT: // (1) Intel AMT wants to completely disconnect. Not sure when this happens. + { + // First, we decode the message. + struct LMEDisconnectMessage disconnectMessage; + disconnectMessage.MessageType = APF_DISCONNECT; + disconnectMessage.ReasonCode = ((APF_DISCONNECT_REASON_CODE)ntohl(((APF_DISCONNECT_MESSAGE *)rxBuffer)->ReasonCode)); + LMSDEBUG("LME requested to disconnect with reason code 0x%08x\r\n", disconnectMessage.ReasonCode); + LME_Deinit(lmemodule); + } + break; + case APF_SERVICE_REQUEST: // (5) + { + + int service = 0; + APF_SERVICE_REQUEST_MESSAGE *pMessage = (APF_SERVICE_REQUEST_MESSAGE *)rxBuffer; + pMessage->ServiceNameLength = ntohl(pMessage->ServiceNameLength); + if (pMessage->ServiceNameLength == 18) { + if (memcmp(pMessage->ServiceName, "pfwd@amt.intel.com", 18) == 0) service = 1; + else if (memcmp(pMessage->ServiceName, "auth@amt.intel.com", 18) == 0) service = 2; + } + + if (service > 0) + { + if (service == 1) + { + LME_ServiceAccept(lmemodule, "pfwd@amt.intel.com"); + module->pfwdService = LME_STARTED; + } + else if (service == 2) + { + LME_ServiceAccept(lmemodule, "auth@amt.intel.com"); + } + } else { + LMSDEBUG("APF_SERVICE_REQUEST - APF_DISCONNECT_SERVICE_NOT_AVAILABLE\r\n"); + LME_Disconnect(lmemodule, APF_DISCONNECT_SERVICE_NOT_AVAILABLE); + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + } + break; + case APF_CHANNEL_OPEN_CONFIRMATION: // (91) Intel AMT confirmation to an APF_CHANNEL_OPEN request. + { + + // First, we decode the message. + struct LMEChannel* channel; + APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE *pMessage = (APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE *)rxBuffer; + + + struct LMEChannelOpenReplySuccessMessage channelOpenReply; + channelOpenReply.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. + channelOpenReply.SenderChannel = ntohl(pMessage->SenderChannel); // This is the identifier on the Intel AMT side. + channelOpenReply.InitialWindow = ntohl(pMessage->InitialWindowSize); // This is the starting window size for flow control. + channel = &(module->Sessions[channelOpenReply.RecipientChannel]); // Get the current session for this message. + + if (channel == NULL) + break; // This should never happen. + + LMSDEBUG("MEI OPEN OK OUR:%d AMT:%d\r\n", channelOpenReply.RecipientChannel, channelOpenReply.SenderChannel); + + if (channel->status == LME_CS_PENDING_CONNECT) // If the channel is in PENDING_CONNECT mode, move the session to connected state. + { + channel->amtid = channelOpenReply.SenderChannel; // We have to set the Intel AMT identifier for this session. + channel->txwindow = channelOpenReply.InitialWindow; // Set the session txwindow. + channel->status = LME_CS_CONNECTED; // Now set the session as CONNECTED. + LMSDEBUG("Channel %d now CONNECTED by AMT %d\r\n", channel->ourid, channel->socketmodule); + if (channel->sockettype == 0) // If the current socket is PAUSED, lets resume it so data can start flowing again. + { + ILibAsyncSocket_Resume(channel->socketmodule); // TCP socket resume + } + } + else if (channel->status == LME_CS_PENDING_LMS_DISCONNECT) // If the channel is in PENDING_DISCONNECT, we have to disconnect the session now. Happens when we disconnect while connection is pending. We don't want to stop a channel during connection, that is bad. + { + channel->amtid = channelOpenReply.SenderChannel; // We have to set the Intel AMT identifier for this session. + LME_ChannelClose(&(module->MeConnection), channel->amtid, channel->ourid); // Send the Intel AMT close. We keep the channel in LME_CS_PENDING_LMS_DISCONNECT state until the close is confirmed. + LMSDEBUG("Channel %d now CONNECTED by AMT %d, but CLOSING it now\r\n", channel->ourid, channel->socketmodule); + } + else + { + // Here, we get an APF_CHANNEL_OPEN in an unexpected state, this should never happen. + LMSDEBUG("Channel %d, unexpected CONNECTED by AMT %d\r\n", channel->ourid, channel->socketmodule); + } + } + break; + case APF_CHANNEL_OPEN_FAILURE: // (92) Intel AMT rejected our connection attempt. + { + + // First, we decode the message. + struct LMEChannel* channel; + APF_CHANNEL_OPEN_FAILURE_MESSAGE *pMessage = (APF_CHANNEL_OPEN_FAILURE_MESSAGE *)rxBuffer; + struct LMEChannelOpenReplyFailureMessage channelOpenReply; + channelOpenReply.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. + + channelOpenReply.ReasonCode = (OPEN_FAILURE_REASON)(ntohl(pMessage->ReasonCode)); // Get the error reason code. + channel = &(module->Sessions[channelOpenReply.RecipientChannel]); // Get the current session for this message. + if (channel == NULL) break; // This should never happen. + + LMSDEBUG("**OPEN FAIL OUR:%d ERR:%d\r\n", channelOpenReply.RecipientChannel, channelOpenReply.ReasonCode); + + if (channel->errorcount++ >= 0 || channel->status == LME_CS_PENDING_LMS_DISCONNECT) + { + // Fail connection + channel->status = LME_CS_FREE; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + sem_post(&(module->Lock)); + ILibAsyncSocket_Disconnect(channel->socketmodule); + //ILibLMS_LaunchHoldingSessions(module); + return; + } + else + { + // Try again + //ILibLMS_SetupConnection(module, channel->ourid); + } + } + break; + case APF_CHANNEL_CLOSE: // (97) Intel AMT is closing this channel, we need to disconnect the LMS TCP connection + { + + // First, we decode the message. + struct LMEChannel* channel; + APF_CHANNEL_CLOSE_MESSAGE *pMessage = (APF_CHANNEL_CLOSE_MESSAGE *)rxBuffer; + struct LMEChannelCloseMessage channelClose; + channelClose.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. + channel = &(module->Sessions[channelClose.RecipientChannel]); // Get the current session for this message. + if (channel == NULL) break; // This should never happen. + + //LMSDEBUG("CLOSE OUR:%d\r\n", channelClose.RecipientChannel); + + if (channel->status == LME_CS_CONNECTED) + { + if (ILibAsyncSocket_IsConnected(channel->socketmodule)) + { + channel->status = LME_CS_PENDING_AMT_DISCONNECT; + LMSDEBUG("Channel %d now PENDING_AMT_DISCONNECT by AMT, calling microstack disconnect %d\r\n", channel->ourid, channel->socketmodule); + LME_ChannelClose(lmemodule, channel->amtid, channel->ourid); + sem_post(&(module->Lock)); + if (channel->sockettype == 0) // If the current socket is PAUSED, lets resume it so data can start flowing again. + { + ILibAsyncSocket_Disconnect(channel->socketmodule); // TCP socket close + } + + sem_wait(&(module->Lock)); + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + } + else + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + } + } + else if (channel->status == LME_CS_PENDING_LMS_DISCONNECT) + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + } + else + { + LMSDEBUG("Channel %d CLOSE, UNEXPECTED STATE %d\r\n", channel->ourid, channel->status); + } + } + break; + case APF_CHANNEL_DATA: // (94) Intel AMT is sending data that we must relay into an LMS TCP connection. + { + //sem_wait(&(module->Lock)); + + struct LMEChannel* channel; + APF_CHANNEL_DATA_MESSAGE *pMessage = (APF_CHANNEL_DATA_MESSAGE *)rxBuffer; + struct LMEChannelDataMessage channelData; + enum ILibAsyncSocket_SendStatus r; + channelData.MessageType = APF_CHANNEL_DATA; + channelData.RecipientChannel = ntohl(pMessage->RecipientChannel); + channelData.DataLength = ntohl(pMessage->DataLength); + channelData.Data = (unsigned char*)rxBuffer + sizeof(APF_CHANNEL_DATA_MESSAGE); + channel = &(module->Sessions[channelData.RecipientChannel]); + + + if (channel == NULL || channel->socketmodule == NULL){ + sem_post(&(module->Lock)); + break; + } + + if (channel->sockettype == 0) + { + r = ILibAsyncSocket_Send(channel->socketmodule, (char*)(channelData.Data), channelData.DataLength, ILibAsyncSocket_MemoryOwnership_USER); // TCP socket + } + + channel->rxwindow += channelData.DataLength; + if (r == ILibAsyncSocket_ALL_DATA_SENT && channel->rxwindow > 1024) { + LME_ChannelWindowAdjust(lmemodule, channel->amtid, channel->rxwindow); channel->rxwindow = 0; + } + //sem_post(&(module->Lock)); + } + break; + case APF_CHANNEL_WINDOW_ADJUST: // 93 + { + //sem_wait(&(module->Lock)); + + struct LMEChannel* channel; + APF_WINDOW_ADJUST_MESSAGE *pMessage = (APF_WINDOW_ADJUST_MESSAGE *)rxBuffer; + struct LMEChannelWindowAdjustMessage channelWindowAdjust; + channelWindowAdjust.MessageType = APF_CHANNEL_WINDOW_ADJUST; + channelWindowAdjust.RecipientChannel = ntohl(pMessage->RecipientChannel); + channelWindowAdjust.BytesToAdd = ntohl(pMessage->BytesToAdd); + channel = &(module->Sessions[channelWindowAdjust.RecipientChannel]); + if (channel == NULL || channel->status == LME_CS_FREE){ + sem_post(&(module->Lock)); + break; + } + channel->txwindow += channelWindowAdjust.BytesToAdd; + if (channel->sockettype == 0) + { + ILibAsyncSocket_Resume(channel->socketmodule); // TCP socket + } + //sem_post(&(module->Lock)); + } + break; + case APF_PROTOCOLVERSION: // 192 + { + APF_PROTOCOL_VERSION_MESSAGE *pMessage = (APF_PROTOCOL_VERSION_MESSAGE *)rxBuffer; + struct LMEProtocolVersionMessage protVersion; + protVersion.MajorVersion = ntohl(pMessage->MajorVersion); + protVersion.MinorVersion = ntohl(pMessage->MinorVersion); + protVersion.TriggerReason = (APF_TRIGGER_REASON)ntohl(pMessage->TriggerReason); + + switch (module->handshakingStatus) + { + case LME_AGREED: + case LME_NOT_INITIATED: + { + LME_ProtocolVersion(lmemodule, 1, 0, protVersion.TriggerReason); + } + case LME_INITIATED: + if (protVersion.MajorVersion != 1 || protVersion.MinorVersion != 0) + { + LMSDEBUG("LME Version %d.%d is not supported.\r\n", protVersion.MajorVersion, protVersion.MinorVersion); + LME_Disconnect(lmemodule, APF_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED); + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + module->AmtProtVersionMajor = protVersion.MajorVersion; + module->AmtProtVersionMinor = protVersion.MinorVersion; + module->handshakingStatus = LME_AGREED; + break; + default: + LME_Disconnect(lmemodule, APF_DISCONNECT_BY_APPLICATION); + LME_Deinit(lmemodule); + break; + } + } + break; + case APF_USERAUTH_REQUEST: // 50 + { + // _lme.UserAuthSuccess(); + } + break; + default: + // Unknown request. + LMSDEBUG("**Unknown LME command: %d\r\n", ((unsigned char*)rxBuffer)[0]); + LME_Disconnect(lmemodule, APF_DISCONNECT_PROTOCOL_ERROR); + LME_Deinit(lmemodule); + break; + } + + sem_post(&(module->Lock)); + //if (disconnects > 0) ILibLMS_LaunchHoldingSessions(module); // If disconnects is set to anything, we have free session slots we can fill up. +} + +void ILibLMS_OnReceive(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncServerSocket_OnInterrupt *OnInterrupt, void **user, int *PAUSE) +{ + int r, maxread = endPointer; + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + struct LMEChannel* channel = (struct LMEChannel*)*user; + + UNREFERENCED_PARAMETER( AsyncServerSocketModule ); + UNREFERENCED_PARAMETER( ConnectionToken ); + UNREFERENCED_PARAMETER( OnInterrupt ); + UNREFERENCED_PARAMETER( PAUSE ); + + if (channel == NULL) return; + sem_wait(&(module->Lock)); + if (channel->socketmodule != ConnectionToken) { sem_post(&(module->Lock)); ILibAsyncSocket_Disconnect(ConnectionToken); return; } + if (channel->txwindow < endPointer) maxread = channel->txwindow; + if (channel->status != LME_CS_CONNECTED || maxread == 0) { *PAUSE = 1; sem_post(&(module->Lock)); return; } + //printf("%.*s", maxread, buffer); + r = LME_ChannelData(&(module->MeConnection), channel->amtid, maxread, (unsigned char*)buffer); + //LMSDEBUG("ILibLMS_OnReceive, status = %d, txwindow = %d, endPointer = %d, r = %d\r\n", channel->status, channel->txwindow, endPointer, r); + if (r != maxread) + { + //LMSDEBUG("ILibLMS_OnReceive, DISCONNECT %d\r\n", channel->ourid); + sem_post(&(module->Lock)); + ILibAsyncSocket_Disconnect(ConnectionToken); // Drop the connection + return; + } + channel->txwindow -= maxread; + *p_beginPointer = maxread; + sem_post(&(module->Lock)); +} + +void ILibLMS_SetupConnection(struct ILibLMS_StateModule* module, int i) +{ + int rport = 0; + char* laddr = NULL; + struct sockaddr_in6 remoteAddress; + + if (module->Sessions[i].sockettype == 0) + { + // Fetch the socket remote TCP address + ILibAsyncSocket_GetRemoteInterface(module->Sessions[i].socketmodule, (struct sockaddr*)&remoteAddress); + if (remoteAddress.sin6_family == AF_INET6) + { + laddr = "::1"; // TODO: decode this properly into a string + rport = ntohs(remoteAddress.sin6_port); + } + else + { + laddr = "127.0.0.1"; // TODO: decode this properly into a string + rport = ntohs(((struct sockaddr_in*)(&remoteAddress))->sin_port); + } + } + else + { + // Fetch the socket remote web socket address + laddr = "127.0.0.1"; // TODO: decode this properly into a string + rport = 123; // TODO: decode the remote port + } + + // Setup a new LME session + LME_ChannelOpenForwardedRequest(&(module->MeConnection), (unsigned int)i, laddr, module->Sessions[i].localport, laddr, rport); + //LMSDEBUG("ILibLMS_OnReceive, CONNECT %d\r\n", i); +} + + +int OnConnect = 0; +void ILibLMS_OnConnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void **user) +{ + OnConnect++; + LMSDEBUG("OnCOnnect:%d", OnConnect); + int i, sessionid = -1, activecount = 0; + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + + sem_wait(&(module->Lock)); + + // Count the number of active sessions + activecount = LMS_MAX_SESSIONS; + for (i = 0; i < LMS_MAX_SESSIONS; i++) { + if (module->Sessions[i].status == LME_CS_FREE || module->Sessions[i].status == LME_CS_CONNECTION_WAIT) { + activecount--; + } + } + + sessionid=GetFreeSlot(module); + + // Look for an empty session + //for (i = 1; i < LMS_MAX_SESSIONS; i++) { + // if (module->Sessions[i].status == LME_CS_FREE) { + // sessionid = i; + // break; + // } + //} + if (sessionid == -1) + { + sem_post(&(module->Lock)); + LMSDEBUG("ILibLMS_OnConnect NO SESSION SLOTS AVAILABLE\r\n"); + ILibAsyncSocket_Disconnect(ConnectionToken); // Drop the connection + return; + } + i = sessionid; + + // LMSDEBUG("USING SLOT OUR:%d\r\n", i); + module->Sessions[i].amtid = -1; + module->Sessions[i].ourid = i; + module->Sessions[i].sockettype = 0; // TCP Type + module->Sessions[i].socketmodule = ConnectionToken; + module->Sessions[i].rxwindow = 0; + module->Sessions[i].txwindow = 0; + module->Sessions[i].localport = ILibAsyncServerSocket_GetPortNumber(AsyncServerSocketModule); + module->Sessions[i].errorcount = 0; + *user = &(module->Sessions[i]); + + /* + if (activecount >= LMS_MAX_CONNECTIONS) + { + module->Sessions[i].status = LME_CS_CONNECTION_WAIT; + LMSDEBUG("ILibLMS_OnConnect %d (HOLDING)\r\n", i); + } + else + */ + { + module->Sessions[i].status = LME_CS_PENDING_CONNECT; + LMSDEBUG("ILibLMS_OnConnect %d\r\n", i); + ILibLMS_SetupConnection(module, i); + } + + sem_post(&(module->Lock)); +} + +void ILibLMS_OnDisconnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) +{ + int disconnects = 0; + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + struct LMEChannel* channel = (struct LMEChannel*)user; + UNREFERENCED_PARAMETER( ConnectionToken ); + + sem_wait(&(module->Lock)); + + if (channel == NULL || channel->socketmodule != ConnectionToken) + { + LMSDEBUG("****ILibLMS_OnDisconnect EXIT\r\n"); + sem_post(&(module->Lock)); + return; + } + + LMSDEBUG("ILibLMS_OnDisconnect, Channel %d, %d\r\n", channel->ourid, ConnectionToken); + + if (channel->status == LME_CS_CONNECTED) + { + channel->status = LME_CS_PENDING_LMS_DISCONNECT; + if (channel->amtid!=-1 && !LME_ChannelClose(&(module->MeConnection), channel->amtid, channel->ourid)) + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by LMS because of failed close\r\n", channel->ourid); + } + else + { + LMSDEBUG("Channel %d now PENDING_LMS_DISCONNECT by LMS\r\n", channel->ourid); + //LMSDEBUG("LME_ChannelClose OK\r\n"); + } + } + else if (channel->status == LME_CS_PENDING_CONNECT) + { + channel->status = LME_CS_PENDING_LMS_DISCONNECT; + LMSDEBUG("Channel %d now PENDING_DISCONNECT by LMS\r\n", channel->ourid); + } + else if (channel->status == LME_CS_CONNECTION_WAIT) + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by LMS\r\n", channel->ourid); + } + //LMSDEBUG("ILibLMS_OnDisconnect, DISCONNECT %d, status = %d\r\n", channel->ourid, channel->status); + sem_post(&(module->Lock)); + + //if (disconnects > 0) ILibLMS_LaunchHoldingSessions(module); +} + +void ILibLMS_OnSendOK(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) +{ + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + struct LMEChannel* channel = (struct LMEChannel*)user; + UNREFERENCED_PARAMETER( ConnectionToken ); + + // Ok to send more on this socket, adjust the window + sem_wait(&(module->Lock)); + if (channel->rxwindow != 0) + { + //LMSDEBUG("LME_ChannelWindowAdjust id=%d, rxwindow=%d\r\n", channel->amtid, channel->rxwindow); + LME_ChannelWindowAdjust(&(module->MeConnection), channel->amtid, channel->rxwindow); + channel->rxwindow = 0; + } + sem_post(&(module->Lock)); +} +// Private method called when the chain is destroyed, we want to do our cleanup here +void ILibLMS_Destroy(void *object) +{ + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)object; + UNREFERENCED_PARAMETER( object ); + + sem_wait(&(module->Lock)); + LME_Disconnect(&(module->MeConnection), APF_DISCONNECT_BY_APPLICATION); + LME_Exit(&(module->MeConnection)); + sem_destroy(&(module->Lock)); + if (IlibExternLMS == module) { IlibExternLMS = NULL; } // Clear the global reference to the the LMS module. +} + +// Create a new Reflector module. +struct ILibLMS_StateModule *ILibLMS_Create(void *Chain) +{ + struct ILibLMS_StateModule *module; + + // Allocate the new module + module = (struct ILibLMS_StateModule*)malloc(sizeof(struct ILibLMS_StateModule)); + if (module == NULL) { ILIBCRITICALEXIT(254); } + memset(module, 0, sizeof(struct ILibLMS_StateModule)); + + // Setup MEI with LMS interface, if we can't return null + if (LME_Init(&(module->MeConnection), &ILibLMS_MEICallback, module) == 0) { + free(module); + return NULL; + } + + // Setup the module + module->ChainLink.DestroyHandler = &ILibLMS_Destroy; + module->ChainLink.ParentChain = Chain; + sem_init(&(module->Lock), 0, 1); + + // TCP servers + module->Server1 = ILibCreateAsyncServerSocketModule(Chain, LMS_MAX_CONNECTIONS, 16992, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); + if (module->Server1 == NULL) { sem_destroy(&(module->Lock)); free(module); return NULL; } + ILibAsyncServerSocket_SetTag(module->Server1, module); + + module->Server2 = ILibCreateAsyncServerSocketModule(Chain, LMS_MAX_CONNECTIONS, 16993, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); + if (module->Server2 == NULL) { sem_destroy(&(module->Lock)); free(module->Server1); free(module); return NULL; } + ILibAsyncServerSocket_SetTag(module->Server2, module); + + + + + // TODO: US6986 - The meshcommander site should not be served on localhost:16994. + // This code should be commented out - in the future(late 2018) IMC could be put there instead. + // Web Server + //module->WebServer = ILibWebServer_CreateEx(Chain, 8, 16994, 2, &ILibLMS_WebServer_OnSession, module); + + IlibExternLMS = module; // Set the global reference to the LMS module. + ILibAddToChain(Chain, module); // Add this module to the chain. + return module; +} + +#endif diff --git a/MicroLMS/microstack/ILibLMS.h b/MicroLMS/microstack/ILibLMS.h new file mode 100644 index 0000000..a0fcf7e --- /dev/null +++ b/MicroLMS/microstack/ILibLMS.h @@ -0,0 +1,26 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if !defined(_NOHECI) + +#ifndef __ILibLMS__ +#define __ILibLMS__ +#include "ILibAsyncServerSocket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ILibLMS_StateModule *ILibLMS_Create(void *Chain); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + diff --git a/MicroLMS/microstack/ILibParsers.c b/MicroLMS/microstack/ILibParsers.c new file mode 100644 index 0000000..0e4565c --- /dev/null +++ b/MicroLMS/microstack/ILibParsers.c @@ -0,0 +1,8763 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if defined(_POSIX) +#ifndef _VX_CPU +#include +#endif +#include +#include +#include +#include +#define UNREFERENCED_PARAMETER(P) (P) +#endif +#ifdef _MINCORE +#define strncmp(a,b,c) strcmp(a,b) +#endif + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#ifdef WIN32 +#include +#include +#include +#ifndef _MINCORE +#include +#endif +#include +#endif + +#include + +#if defined(WIN32) && !defined(WSA_FLAG_NO_HANDLE_INHERIT) +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#include "ILibParsers.h" +#include "ILibRemoteLogging.h" + +#define MINPORTNUMBER 50000 +#define PORTNUMBERRANGE 15000 +#define UPNP_MAX_WAIT 86400 // 24 Hours + +#ifdef _REMOTELOGGINGSERVER +#include "ILibWebServer.h" +#endif + +#include + +#if defined(MICROSTACK_NOTLS) +char utils_HexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; +char utils_HexTable2[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +#endif + +// This is to compile on Windows XP, not needed at runtime +#if !defined(IPV6_V6ONLY) +#define IPV6_V6ONLY 27 +#endif + +#if defined(WIN32) +static sem_t ILibChainLock = { 0 }; +#else +#ifndef errno +extern int errno; +#endif +static sem_t ILibChainLock; +#endif + +#ifdef WIN32 +char* ILibCriticalLogFilename = NULL; +#else +char* ILibCriticalLogFilename = NULL; +#endif + +#ifdef _MINCORE // Win32 mincore needs these defines +CONST IN6_ADDR in6addr_any = { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } }; +CONST IN6_ADDR in6addr_loopback = { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } } }; +#endif + +char ILibScratchPad[4096]; // General buffer +char ILibScratchPad2[65536]; // Often used for UDP packet processing +char ILibScratchPad_WWWAuth1[1024]; // Used to construct WWW auth digest request +char ILibScratchPad_WWWAuth2[4096]; // Used to construct WWW auth digest request + +void* gILibChain = NULL; // Global Chain Instance used for Remote Logging when a chain instance isn't otherwise exposed + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) + +long long ILibGetUptime(); +static int ILibChainLock_RefCounter = 0; + +static int malloc_counter = 0; +void* dbg_malloc(int sz) +{ + ++malloc_counter; + return((void*)malloc(sz)); +} +void dbg_free(void* ptr) +{ + --malloc_counter; + free(ptr); +} +int dbg_GetCount() +{ + return(malloc_counter); +} + +unsigned long long ILibHTONLL(unsigned long long v) +{ + { union { unsigned long lv[2]; unsigned long long llv; } u; u.lv[0] = htonl(v >> 32); u.lv[1] = htonl(v & 0xFFFFFFFFULL); return u.llv; } +} +unsigned long long ILibNTOHLL(unsigned long long v) +{ + { union { unsigned long lv[2]; unsigned long long llv; } u; u.llv = v; return ((unsigned long long)ntohl(u.lv[0]) << 32) | (unsigned long long)ntohl(u.lv[1]); } +} + +//! Returns X where 2^X = Specified Integer +/*! + \param number Integer value to use to solve equation + \return X where 2^X = Specified Integer +*/ +int ILibWhichPowerOfTwo(int number) +{ + int retVal = 0; + if(number < 1) {return(-1);} + while((number = number >> 1) != 0) {++retVal;} + return(retVal); +} + + +// +// All of the following structures are meant to be private internal structures +// +typedef struct ILibSparseArray_Node +{ + int index; + void* ptr; +}ILibSparseArray_Node; +typedef struct ILibSparseArray_Root +{ + ILibSparseArray_Node* bucket; + int bucketSize; + ILibSparseArray_Bucketizer bucketizer; + sem_t LOCK; + int userMemorySize; +}ILibSparseArray_Root; +const int ILibMemory_SparseArray_CONTAINERSIZE = sizeof(ILibSparseArray_Root); + +typedef enum ILibHashtable_Flags +{ + ILibHashtable_Flags_NONE = 0x00, + ILibHashtable_Flags_ADD = 0x01, + ILibHashtable_Flags_REMOVE = 0x02 +}ILibHashtable_Flags; +typedef struct ILibHashtable_Node +{ + struct ILibHashtable_Node* next; + struct ILibHashtable_Node* prev; + void* Key1; + char* Key2; + int Key2Len; + void *Data; +}ILibHashtable_Node; +typedef struct ILibHashtable_Root +{ + ILibSparseArray table; + ILibHashtable_Hash_Func hashFunc; + ILibSparseArray_Bucketizer bucketizer; +}ILibHashtable_Root; + + + +struct ILibStackNode +{ + void *Data; + struct ILibStackNode *Next; +}; +struct ILibQueueNode +{ + struct ILibStackNode *Head; + struct ILibStackNode *Tail; + sem_t LOCK; +}; +struct HashNode_Root +{ + struct HashNode *Root; + int CaseInSensitive; + sem_t LOCK; +}; +struct HashNode +{ + struct HashNode *Next; + struct HashNode *Prev; + int KeyHash; + char *KeyValue; + int KeyLength; + void *Data; + int DataEx; +}; +struct HashNodeEnumerator +{ + struct HashNode *node; +}; +typedef struct ILibLinkedListNode +{ + void *Data; + struct ILibLinkedListNode_Root *Root; + struct ILibLinkedListNode *Next; + struct ILibLinkedListNode *Previous; +}ILibLinkedListNode; +typedef struct ILibLinkedListNode_Root +{ + sem_t LOCK; + long count; + void* Tag; + struct ILibLinkedListNode *Head; + struct ILibLinkedListNode *Tail; + void *ExtraMemory; +}ILibLinkedListNode_Root; + +struct ILibReaderWriterLock_Data +{ + ILibChain_PreSelect Pre; + ILibChain_PostSelect Post; + ILibChain_Destroy Destroy; + + sem_t WriteLock; + sem_t ReadLock; + sem_t CounterLock; + sem_t ExitLock; + int ActiveReaders; + int WaitingReaders; + int PendingWriters; + int Exit; +}; +//! Get the amount of pending data in the send buffer of the ILibTransport Abstraction +/*! + \param transport ILibTransport Abstraction to query + \return Number of pending bytes +*/ +unsigned int ILibTransport_PendingBytesToSend(void *transport) +{ + return(((ILibTransport*)transport)->PendingBytesPtr(transport)); +} +//! Send data on an ILibTransport Abstraction +/*! + \param transport ILibTransport Abstraction + \param buffer Data to send + \param bufferLength Data Length to send + \param ownership Who will free the data to send? + \param done Data complete? + \return Send Status +*/ +ILibTransport_DoneState ILibTransport_Send(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done) +{ + return(((ILibTransport*)transport)->SendPtr(transport, buffer, bufferLength, ownership, done)); +} +//! Close an ILibTransport Abstraction +/*! + \param transport ILibTransport Abstraction to close +*/ +void ILibTransport_Close(void *transport) +{ + if (transport != NULL) { ((ILibTransport*)transport)->ClosePtr(transport); } +} + + +#if defined(MICROSTACK_NOTLS) +int util_random_seeded = 0; +void __fastcall util_random(int length, char* result) +{ + short val; + int i; + + if (util_random_seeded == 0) + { + time_t t; + srand((unsigned int)time(&t)); + util_random_seeded = 1; + } + + for (i = 0; i < length; i += 2) + { + val = rand(); + memcpy_s(result + i, length - i, &val, (length - i) >= 2 ? 2 : (length - i)); + } +} +char* __fastcall util_tohex(char* data, int len, char* out) +{ + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for (i = 0; i < len; i++) + { + *(p++) = utils_HexTable[((unsigned char)data[i]) >> 4]; + *(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} +int __fastcall util_hexToint(char *hexString, int hexStringLength) +{ + int i, res = 0; + + // Ignore the leading zeroes + while (*hexString == '0' && hexStringLength > 0) { hexString++; hexStringLength--; } + + // Process the rest of the string + for (i = 0; i < hexStringLength; i++) + { + if (hexString[i] >= '0' && hexString[i] <= '9') { res = (res << 4) + (hexString[i] - '0'); } + else if (hexString[i] >= 'a' && hexString[i] <= 'f') { res = (res << 4) + (hexString[i] - 'a' + 10); } + else if (hexString[i] >= 'A' && hexString[i] <= 'F') { res = (res << 4) + (hexString[i] - 'A' + 10); } + } + return res; +} + +// Convert hex string to int +int __fastcall util_hexToBuf(char *hexString, int hexStringLength, char* output) +{ + int i, x = hexStringLength / 2; + for (i = 0; i < x; i++) { output[i] = (char)util_hexToint(hexString + (i * 2), 2); } + return i; +} +#endif + + +#ifdef WIN32 +int ILibGetLocalIPAddressNetMask(unsigned int address) +{ + SOCKET s; + DWORD bytesReturned; + SOCKADDR_IN* pAddrInet; + INTERFACE_INFO localAddr[10]; // Assume there will be no more than 10 IP interfaces + int numLocalAddr; + int i; + + if ((s = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0)) == INVALID_SOCKET) + { + fprintf(stderr, "Socket creation failed\n"); + return(0); + } + + // Enumerate all IP interfaces + if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &localAddr, sizeof(localAddr), &bytesReturned, NULL, NULL) != SOCKET_ERROR) + { + fprintf(stderr, "WSAIoctl fails with error %d\n", (int)GetLastError()); + closesocket(s); + return(0); + } + + closesocket(s); + + // Display interface information + numLocalAddr = (bytesReturned/sizeof(INTERFACE_INFO)); + for (i=0; isin_addr.S_un.S_addr == address) + { + pAddrInet = (SOCKADDR_IN*)&localAddr[i].iiNetmask; + return(pAddrInet->sin_addr.s_addr); + } + } + return(0); +} +#endif + +//! Fetches the list of valid IPv4 adapters +/*! + \param[out] addresslist sockaddr_in array of adapters + \param includeloopback [0 = Include Loopback, 1 = Exclude Loopback] + \return number of adapters +*/ +int ILibGetLocalIPv4AddressList(struct sockaddr_in** addresslist, int includeloopback) +{ +#ifdef WIN32 + DWORD i, j = 0; + SOCKET sock; + DWORD interfaces_count; + INTERFACE_INFO interfaces[128]; + + // Fetch the list of interfaces + *addresslist = NULL; + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { return(0); } + if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &interfaces, 128 * sizeof(INTERFACE_INFO), &interfaces_count, NULL, NULL) != 0) { interfaces_count = 0; } + + if (interfaces_count > 0) + { + interfaces_count = interfaces_count / sizeof(INTERFACE_INFO); + for (i = 0; i < interfaces_count; i++) { if (includeloopback != 0 || interfaces[i].iiAddress.AddressIn.sin_addr.S_un.S_addr != 0x0100007F) j++; } // M S Static Analysis says this could read outside readable range, but that is only true if WSAioct returns the wrong value in interfaces_count + + // Allocate the IPv4 list and copy the elements into it + if ((*addresslist = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in) * j)) == NULL) ILIBCRITICALEXIT(254); + j = 0; + for (i = 0; i < interfaces_count; i++) + { + if (includeloopback != 0 || interfaces[i].iiAddress.AddressIn.sin_addr.S_un.S_addr != 0x0100007F) + { + memcpy_s(&((*addresslist)[j++]), sizeof(struct sockaddr_in), &(interfaces[i].iiAddress.AddressIn), sizeof(struct sockaddr_in)); + } + } + } + closesocket(sock); + return j; +#elif defined(_POSIX) + // + // Posix will also use an Ioctl call to get the IPAddress List + // + char szBuffer[16*sizeof(struct ifreq)]; + struct ifconf ifConf; + struct ifreq ifReq; + int nResult; + int LocalSock; + struct sockaddr_in LocalAddr; + //int tempresults[16]; + int ctr = 0; + int i; + if ((*addresslist = malloc(sizeof(struct sockaddr_in) * 32)) == NULL) ILIBCRITICALEXIT(254); // TODO: Support more than 32 adapters + + // Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. + if ((LocalSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "socket(..) error in ILibGetLocalIPv4AddressList()"); + ILIBCRITICALERREXIT(253); + } + // Get the interface configuration information... + ifConf.ifc_len = sizeof szBuffer; + ifConf.ifc_ifcu.ifcu_buf = (caddr_t)szBuffer; + nResult = ioctl(LocalSock, SIOCGIFCONF, &ifConf); + + if (nResult < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFCONF) error in ILibGetLocalIPv4AddressList()"); + ILIBCRITICALERREXIT(253); + } + // Cycle through the list of interfaces looking for IP addresses. + for (i = 0;(i < ifConf.ifc_len);) + { + struct ifreq *pifReq = (struct ifreq *)((caddr_t)ifConf.ifc_req + i); + i += sizeof *pifReq; + // See if this is the sort of interface we want to deal with. + strcpy_s (ifReq.ifr_name, sizeof(ifReq.ifr_name), pifReq -> ifr_name); + if (ioctl(LocalSock, SIOCGIFFLAGS, &ifReq) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFFLAGS) error in ILibGetLocalIPv4AddressList()"); + ILIBCRITICALERREXIT(253); + } + // Skip loopback, point-to-point and down interfaces, except don't skip down interfaces + // if we're trying to get a list of configurable interfaces. + if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) + { + continue; + } + if (pifReq->ifr_addr.sa_family == AF_INET) + { + // Get a pointer to the address. + memcpy_s(&LocalAddr, sizeof(LocalAddr), &pifReq -> ifr_addr, sizeof pifReq -> ifr_addr); + if (includeloopback != 0 || LocalAddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) + { + //tempresults[ctr] = LocalAddr.sin_addr.s_addr; + memcpy_s(&((*addresslist)[ctr]), sizeof(struct sockaddr_in), &LocalAddr, sizeof(struct sockaddr_in)); + ++ctr; + } + } + } + close(LocalSock); + //*pp_int = (int*)malloc(sizeof(int)*(ctr)); + //memcpy(*pp_int,tempresults,sizeof(int)*ctr); + + return ctr; +#endif +} + +/* +// Get the list of valid IPv6 adapters, skip loopback and other junk adapters. +int ILibGetLocalIPv6AddressList(struct sockaddr_in6** addresslist) +{ +#if defined(WINSOCK2) +PIP_ADAPTER_ADDRESSES pAddresses = NULL; +PIP_ADAPTER_ADDRESSES pAddressesPtr = NULL; +PIP_ADAPTER_UNICAST_ADDRESS_LH AddrPtr; +ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); +int AddrCount = 0; +struct sockaddr_in6 *ptr; + +// Perform first call +pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen); +if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) +{ +free(pAddresses); +pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen); +} + +// Perform second call +if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) != NO_ERROR) +{ +free(pAddresses); +return 0; +} + +// Count the total number of local IPv6 addresses +pAddressesPtr = pAddresses; +while (pAddressesPtr != NULL) +{ +// Skip any loopback adapters +if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->NoMulticast == 0) +{ +// For each adapter +AddrPtr = pAddressesPtr->FirstUnicastAddress; +while (AddrPtr != NULL) +{ +AddrCount++; +AddrPtr = AddrPtr->Next; +} +} +pAddressesPtr = pAddressesPtr->Next; +} + +// Allocate the array of IPv6 addresses +*addresslist = ptr = malloc(AddrCount * sizeof(struct sockaddr_in6)); + +// Copy each address into the array +pAddressesPtr = pAddresses; +while (pAddressesPtr != NULL) +{ +// Skip any loopback adapters +if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->NoMulticast == 0) +{ +// For each adapter +AddrPtr = pAddressesPtr->FirstUnicastAddress; +while (AddrPtr != NULL) +{ +memcpy(ptr, AddrPtr->Address.lpSockaddr, sizeof(struct sockaddr_in6)); +ptr++; +AddrPtr = AddrPtr->Next; +} +} +pAddressesPtr = pAddressesPtr->Next; +} + +free(pAddresses); +return AddrCount; +#endif +} +*/ + +//! Get the list of valid IPv6 adapter indexes, skip loopback and other junk adapters. +/*! + \param[out] indexlist List of valid IPv6 Adapter indexes + \return number of adapters +*/ +int ILibGetLocalIPv6IndexList(int** indexlist) +{ +#ifdef _MINCORE + return 0; +#else +#if defined(WIN32) + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pAddressesPtr = NULL; + ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); + int AddrCount = 0; + int* ptr; + + // Perform first call + if ((pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen)) == NULL) ILIBCRITICALEXIT(254); + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) + { + free(pAddresses); + if ((pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen)) == NULL) ILIBCRITICALEXIT(254); + } + + // Perform second call + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) != NO_ERROR) + { + free(pAddresses); + return 0; + } + + // Count the total number of local IPv6 addresses + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + // For each adapter + AddrCount++; + } + pAddressesPtr = pAddressesPtr->Next; + } + + // Allocate the array of IPv6 addresses + if ((*indexlist = ptr = (int*)malloc(AddrCount * sizeof(int))) == NULL) ILIBCRITICALEXIT(254); + + // Copy each address into the array + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + *ptr = pAddressesPtr->IfIndex; + ptr++; + } + pAddressesPtr = pAddressesPtr->Next; + } + + free(pAddresses); + return AddrCount; +#elif defined (_POSIX) + *indexlist = (int *)malloc(sizeof(int)); + *indexlist[0] = 0; + return 1; // TODO +#endif +#endif +} + +//! Get the list of valid IPv6 adapters, skip loopback and other junk adapters. +/*! + \param[out] list sockaddr_in6 array of valid IPv6 adapters + \return number of adapters +*/ +int ILibGetLocalIPv6List(struct sockaddr_in6** list) +{ +#ifdef _MINCORE + return 0; +#else +#if defined(WIN32) + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pAddressesPtr = NULL; + ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); + int AddrCount = 0; + struct sockaddr_in6* ptr; + *list = NULL; + + // Perform first call + pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) + { + free(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); + } + + // Perform second call + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) != NO_ERROR) + { + free(pAddresses); + return 0; // Call failed, return no IPv6 addresses. This happens on Windows XP. + } + + // Count the total number of local IPv6 addresses + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + // For each adapter + AddrCount++; + } + pAddressesPtr = pAddressesPtr->Next; + } + + if (AddrCount > 0) + { + // Allocate the array of IPv6 addresses + *list = ptr = (struct sockaddr_in6*)malloc(AddrCount * sizeof(struct sockaddr_in6)); + + // Copy each address into the array + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + memcpy_s(ptr, sizeof(struct sockaddr_in6), pAddressesPtr->FirstUnicastAddress->Address.lpSockaddr, pAddressesPtr->FirstUnicastAddress->Address.iSockaddrLength); + ptr++; + } + pAddressesPtr = pAddressesPtr->Next; + } + } + + free(pAddresses); + return AddrCount; +#elif defined (_POSIX) + //*indexlist = malloc(sizeof(int)); + //*indexlist[0] = 0; + //return 1; // TODO + *list = NULL; + return 0; +#endif +#endif +} + + +/*! \fn ILibGetLocalIPAddressList(int** pp_int) +\brief Gets a list of IP Addresses +\par +\b NOTE: \a pp_int must be freed +\param[out] pp_int Array of IP Addresses +\return Number of IP Addresses returned +*/ +int ILibGetLocalIPAddressList(int** pp_int) +{ +#if defined(WIN32) + int i; + char buffer[16 * sizeof(SOCKET_ADDRESS_LIST)]; + DWORD bufferSize; + SOCKET sock; + LPSOCKADDR addr; + + *pp_int = NULL; + if ((sock = socket(AF_INET,SOCK_DGRAM,0)) == -1) return 0; + + if (WSAIoctl(sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, 16 * sizeof(SOCKET_ADDRESS_LIST), &bufferSize, NULL, NULL) == SOCKET_ERROR) + { + closesocket(sock); + return 0; + } + + if ((*pp_int = (int*)malloc(sizeof(int) * (1 + ((SOCKET_ADDRESS_LIST*)buffer)->iAddressCount))) == NULL) ILIBCRITICALEXIT(254); + addr = ((((SOCKET_ADDRESS_LIST *)buffer))->Address)->lpSockaddr; + for (i = 0; i < ((SOCKET_ADDRESS_LIST*)buffer)->iAddressCount && i < 15; ++i) + { + (*pp_int)[i] = (((struct sockaddr_in*)addr)[i]).sin_addr.S_un.S_addr; + } + (*pp_int)[i] = inet_addr("127.0.0.1"); + closesocket(sock); + return(1 + ((SOCKET_ADDRESS_LIST*)buffer)->iAddressCount); +#elif defined(_POSIX) + // + // Posix will also use an Ioctl call to get the IPAddress List + // + char szBuffer[16*sizeof(struct ifreq)]; + struct ifconf ifConf; + struct ifreq ifReq; + int nResult; + int LocalSock; + struct sockaddr_in LocalAddr; + int tempresults[16]; + int ctr=0; + int i; + /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */ + if ((LocalSock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "socket(...) error in ILibGetLocalIPAddressList()"); + ILIBCRITICALERREXIT(253); + } + /* Get the interface configuration information... */ + ifConf.ifc_len = sizeof szBuffer; + ifConf.ifc_ifcu.ifcu_buf = (caddr_t)szBuffer; + nResult = ioctl(LocalSock, SIOCGIFCONF, &ifConf); + if (nResult < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFCONF) error in ILibGetLocalIPAddressList()"); + ILIBCRITICALERREXIT(253); + } + /* Cycle through the list of interfaces looking for IP addresses. */ + for (i = 0;(i < ifConf.ifc_len);) + { + struct ifreq *pifReq = (struct ifreq *)((caddr_t)ifConf.ifc_req + i); + i += sizeof *pifReq; + /* See if this is the sort of interface we want to deal with. */ + strcpy_s (ifReq.ifr_name, sizeof(ifReq.ifr_name), pifReq -> ifr_name); + if (ioctl (LocalSock, SIOCGIFFLAGS, &ifReq) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFFLAGS) error in ILibGetLocalIPAddressList()"); + ILIBCRITICALERREXIT(253); + } + /* Skip loopback, point-to-point and down interfaces, */ + /* except don't skip down interfaces */ + /* if we're trying to get a list of configurable interfaces. */ + if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) + { + continue; + } + if (pifReq -> ifr_addr.sa_family == AF_INET) + { + /* Get a pointer to the address... */ + memcpy_s (&LocalAddr, sizeof(LocalAddr), &pifReq -> ifr_addr, sizeof pifReq -> ifr_addr); + if (LocalAddr.sin_addr.s_addr != htonl (INADDR_LOOPBACK)) + { + tempresults[ctr] = LocalAddr.sin_addr.s_addr; + ++ctr; + } + } + } + close(LocalSock); + if ((*pp_int = (int*)malloc(sizeof(int)*(ctr))) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(*pp_int, sizeof(int)*ctr, tempresults, sizeof(int)*ctr); + return(ctr); +#endif +} + + + +struct ILibChain_SubChain +{ + ILibChain_PreSelect PreSelect; + ILibChain_PostSelect PostSelect; + ILibChain_Destroy Destroy; + + void *subChain; +}; + +typedef struct LifeTimeMonitorData +{ + long long ExpirationTick; + void *data; + ILibLifeTime_OnCallback CallbackPtr; + ILibLifeTime_OnCallback DestroyPtr; +}LifeTimeMonitorData; +struct ILibLifeTime +{ + ILibChain_Link ChainLink; + struct LifeTimeMonitorData *LM; + long long NextTriggerTick; + + void *Reserved; + + void *ObjectList; + int ObjectCount; +}; + +typedef struct ILibChain_Link_Hook +{ + ILibChain_EventHookHandler Handler; + int MaxTimeout; +}ILibChain_Link_Hook; +struct ILibBaseChain_SafeData +{ + void *Chain; + void *Object; +}; + +typedef enum ILibChain_ContinuationStates +{ + ILibChain_ContinuationState_INACTIVE = 0, + ILibChain_ContinuationState_CONTINUE = 1, + ILibChain_ContinuationState_END_CONTINUE= 2 +}ILibChain_ContinuationStates; + +typedef struct ILibBaseChain +{ + int TerminateFlag; + int RunningFlag; +#ifdef _REMOTELOGGING + ILibRemoteLogging ChainLogger; +#ifdef _REMOTELOGGINGSERVER + void* LoggingWebServer; + ILibTransport* LoggingWebServerFileTransport; +#endif +#endif + /* + * + * DO NOT MODIFY STRUCT DEFINITION ABOVE THIS COMMENT BLOCK + * + */ + + +#if defined(WIN32) + DWORD ChainThreadID; + HANDLE ChainProcessHandle; + HANDLE MicrostackThreadHandle; + CONTEXT MicrostackThreadContext; +#else + pthread_t ChainThreadID; +#endif +#if defined(WIN32) + SOCKET TerminateSock; +#else + FILE *TerminateReadPipe; + FILE *TerminateWritePipe; +#endif + + void *Timer; + void *Reserved; + ILibLinkedList Links; + ILibLinkedList LinksPendingDelete; + ILibHashtable ChainStash; + ILibChain_ContinuationStates continuationState; + unsigned int PreSelectCount; + unsigned int PostSelectCount; + void *WatchDogThread; +#ifdef WIN32 + HANDLE WatchDogTerminator; +#else + int WatchDogTerminator[2]; +#endif + void *node; +}ILibBaseChain; + +const int ILibMemory_CHAIN_CONTAINERSIZE = sizeof(ILibBaseChain); + +void* ILibMemory_Allocate(int containerSize, int extraMemorySize, void** allocatedContainer, void **extraMemory) +{ + char* retVal = (char*)malloc(containerSize + extraMemorySize + (extraMemorySize > 0 ? 4 : 0)); + if (retVal == NULL) { ILIBCRITICALEXIT(254); } + memset(retVal, 0, containerSize + extraMemorySize + (extraMemorySize > 0 ? 4 : 0)); + if (extraMemorySize > 0) + { + ((int*)(retVal + containerSize))[0] = extraMemorySize; + if (extraMemory != NULL) { *extraMemory = (retVal + containerSize + 4); } + } + else + { + if (extraMemory != NULL) { *extraMemory = NULL; } + } + if (allocatedContainer != NULL) { *allocatedContainer = retVal; } + return(retVal); +} +ILibExportMethod void* ILibMemory_GetExtraMemory(void *container, int containerSize) +{ + return((char*)container + 4 + containerSize); +} +int ILibMemory_GetExtraMemorySize(void* extraMemory) +{ + if (extraMemory == NULL) { return(0); } + return(((int*)((char*)extraMemory - 4))[0]); +} + +ILibChain_Link* ILibChain_Link_Allocate(int structSize, int extraMemorySize) +{ + ILibChain_Link *retVal; + void* extraMemory; + ILibMemory_Allocate(structSize, extraMemorySize, (void**)&retVal, &extraMemory); + retVal->ExtraMemoryPtr = extraMemory; + return(retVal); +} +int ILibChain_Link_GetExtraMemorySize(ILibChain_Link* link) +{ + return(ILibMemory_GetExtraMemorySize(link->ExtraMemoryPtr)); +} + +//! Get the base Hashtable for this chain + +/*! + \b Note: Hashtable is created upon first use + \param chain Microstack Chain to fetch the hashtable from + \return Associated Hashtable +*/ +ILibHashtable ILibChain_GetBaseHashtable(void* chain) +{ + struct ILibBaseChain *b = (struct ILibBaseChain*)chain; + if(b->ChainStash == NULL) {b->ChainStash = ILibHashtable_Create();} + return(b->ChainStash); +} + +void ILibChain_OnDestroyEvent_Sink(void *object) +{ + ILibChain_Link *link = (ILibChain_Link*)object; + ILibChain_DestroyEvent e = (ILibChain_DestroyEvent)((void**)link->ExtraMemoryPtr)[0]; + + if (e != NULL) + { + e(link->ParentChain, ((void**)link->ExtraMemoryPtr)[1]); + } +} +//! Add an event handler to be dispatched when the Microstack Chain is shutdown +/*! + \param chain Microstack Chain to add an event handler to + \param sink Event Handler to dispatch on shutdown + \param user Custom user state data to dispatch +*/ +void ILibChain_OnDestroyEvent_AddHandler(void *chain, ILibChain_DestroyEvent sink, void *user) +{ + ILibChain_Link *link = ILibChain_Link_Allocate(sizeof(ILibChain_Link), 2 * sizeof(void*)); + link->ParentChain = chain; + link->DestroyHandler = (ILibChain_Destroy)&ILibChain_OnDestroyEvent_Sink; + ((void**)link->ExtraMemoryPtr)[0] = sink; + ((void**)link->ExtraMemoryPtr)[1] = user; + + if (ILibIsChainRunning(chain) == 0) + { + ILibAddToChain(chain, link); + } + else + { + ILibChain_SafeAdd(chain, link); + } +} +void ILibChain_OnStartEvent_Sink(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + ILibChain_Link* link = (ILibChain_Link*)object; + ILibChain_StartEvent e; + + UNREFERENCED_PARAMETER(readset); + UNREFERENCED_PARAMETER(writeset); + UNREFERENCED_PARAMETER(errorset); + UNREFERENCED_PARAMETER(blocktime); + + e = (ILibChain_StartEvent)((void**)link->ExtraMemoryPtr)[0]; + if (e != NULL) + { + e(link->ParentChain, ((void**)link->ExtraMemoryPtr)[1]); + } + + // Adding ourselves to be deleted, since we don't need to be called again. + // Don't need to lock anything, because we're on the Microstack Thread + ILibLinkedList_AddTail(((ILibBaseChain*)link->ParentChain)->LinksPendingDelete, object); +} +//! Add an event handler to the Microstack Chain to be dispatched on startup of the chain +/*! + \param chain Microstack Chain to add the event handler to + \param sink Event Handler to be dispatched on startup + \param user Custom user state data to dispatch +*/ +void ILibChain_OnStartEvent_AddHandler(void *chain, ILibChain_StartEvent sink, void *user) +{ + ILibChain_Link *link = ILibChain_Link_Allocate(sizeof(ILibChain_Link), 2 * sizeof(void*)); + link->ParentChain = chain; + ((void**)link->ExtraMemoryPtr)[0] = sink; + ((void**)link->ExtraMemoryPtr)[1] = user; + link->PreSelectHandler = ILibChain_OnStartEvent_Sink; + + if (ILibIsChainRunning(chain) == 0) + { + ILibAddToChain(chain, link); + } + else + { + ILibChain_SafeAdd(chain, link); + } +} + +#if defined(_REMOTELOGGING) && defined(_REMOTELOGGINGSERVER) +unsigned char ILibDefaultLogger_HTML[4783] = +{ + 0x1F,0x8B,0x08,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xED,0x3C,0xEB,0x72,0xDB,0x3A,0x73,0xFF,0x3B,0xD3,0x77,0x40,0x98,0x39,0xC7,0x52,0xA3,0xFB,0xCD,0xB6,0x64,0x39, + 0x63,0x4B,0x4A,0xE2,0xF9,0x1C,0x27,0xB1,0x9C,0xE4,0x9C,0x39,0x73,0x9A,0xA1,0x44,0x48,0xE2,0x17,0x8A,0x54,0x49,0xCA,0xB6,0x8E,0xEB,0x27,0xEB,0x8F,0x3E,0x52,0x5F, + 0xA1,0xBB,0x00,0x48,0x82,0x24,0x48,0x5A,0x4E,0x7A,0x99,0x69,0xE5,0x4C,0x4C,0x82,0x8B,0xBD,0x61,0x77,0xB1,0xBB,0x84,0xFC,0x1F,0xFF,0xF6,0xEF,0x27,0x2F,0xC6,0x1F, + 0x46,0x37,0xBF,0x7F,0x9C,0x90,0x95,0xBF,0xB6,0xC8,0xC7,0xCF,0xE7,0x97,0x17,0x23,0xA2,0x55,0xEB,0xF5,0xAF,0xED,0x51,0xBD,0x3E,0xBE,0x19,0x93,0xDF,0xDE,0xDD,0xBC, + 0xBF,0x24,0xCD,0x5A,0x83,0xDC,0xB8,0xBA,0xED,0x99,0xBE,0xE9,0xD8,0xBA,0x55,0xAF,0x4F,0xAE,0x34,0xA2,0xAD,0x7C,0x7F,0xD3,0xAF,0xD7,0xEF,0xEE,0xEE,0x6A,0x77,0xED, + 0x9A,0xE3,0x2E,0xEB,0x37,0xD7,0xF5,0x7B,0xC4,0xD5,0xC4,0xC9,0xE2,0xB2,0xEA,0x4B,0x33,0x6B,0x86,0x6F,0x68,0xA7,0xE4,0x04,0x9F,0xE0,0x2F,0xAA,0x1B,0xF0,0x6B,0x4D, + 0x7D,0x9D,0xCC,0x1D,0xDB,0xA7,0xB6,0x3F,0xD4,0x7C,0x7A,0xEF,0xD7,0x11,0x60,0x40,0xE6,0x2B,0xDD,0xF5,0xA8,0x3F,0xDC,0xFA,0x8B,0xEA,0x91,0x46,0x90,0x60,0x95,0xFE, + 0xCB,0xD6,0xBC,0x1D,0x6A,0x23,0x0E,0x5E,0xBD,0xD9,0x6D,0xA8,0x16,0xE0,0x90,0x01,0x7E,0xAB,0x7E,0x3E,0xAB,0x8E,0x9C,0xF5,0x46,0xF7,0xCD,0x99,0x45,0xB5,0x88,0xC0, + 0xC5,0x64,0x38,0x19,0xBF,0x9D,0x84,0xB3,0x6C,0x7D,0x4D,0x87,0xDA,0xC2,0x71,0xD7,0xBA,0x5F,0x35,0xA8,0x4F,0xE7,0xC8,0xAC,0x26,0x73,0x64,0xD1,0xCD,0xCA,0xB1,0xE9, + 0xD0,0x76,0x70,0x96,0x6F,0xFA,0x16,0x3D,0xFD,0x4A,0x67,0xD7,0x37,0x23,0x32,0xA6,0xB3,0xED,0xF2,0xA4,0xCE,0xC7,0xC8,0x89,0xE7,0xEF,0xF0,0xF7,0xCC,0x31,0x76,0x0F, + 0x6B,0xDD,0x5D,0x9A,0x76,0xBF,0x31,0xD8,0xE8,0x86,0x61,0xDA,0x4B,0xB8,0x9A,0x39,0xAE,0x41,0x5D,0xB8,0x98,0x3B,0x96,0xE3,0xF6,0x67,0x96,0x3E,0xFF,0x3E,0x58,0x00, + 0xA1,0xAA,0x67,0xFE,0x45,0xFB,0xCD,0xF6,0xE6,0x9E,0xDF,0x2E,0xF4,0xB5,0x69,0xED,0xFA,0xDA,0x8D,0x0B,0xF8,0xE7,0x2B,0xEA,0x93,0xF7,0x53,0xAD,0x42,0xCE,0x5C,0x53, + 0xB7,0x2A,0xE4,0x1D,0xB5,0x6E,0xA9,0x6F,0xCE,0xF5,0x0A,0xF1,0x40,0xBB,0x55,0x8F,0xBA,0xE6,0x62,0x30,0x03,0x64,0x4B,0xD7,0xD9,0xDA,0x46,0x95,0xA3,0x7F,0x69,0xB4, + 0x8D,0x63,0xA3,0x37,0x78,0x7C,0x89,0xB2,0xE8,0xA6,0x4D,0xDD,0x87,0x34,0xD0,0x62,0xB1,0x18,0xDC,0x99,0x86,0xBF,0xEA,0x37,0x1B,0x8D,0x06,0x30,0x10,0xF0,0x4D,0xF4, + 0xAD,0xEF,0x08,0x96,0xAB,0xBE,0xB3,0x09,0xF9,0xAF,0xBA,0xE6,0x72,0xE5,0xF7,0x9B,0x9B,0x7B,0xE2,0x39,0x96,0x69,0x90,0x97,0xB3,0x43,0xFC,0x09,0x1E,0xCF,0x1C,0xDF, + 0x77,0xD6,0x11,0xB8,0x45,0x17,0x2A,0xE8,0x48,0x2D,0x8F,0x2F,0xD7,0xBA,0xE7,0xA3,0x39,0x3C,0x70,0x4E,0x18,0x65,0x85,0xFE,0x9C,0x5B,0xEA,0x2E,0x2C,0xE7,0x8E,0x03, + 0xA0,0xA9,0x54,0x75,0xCB,0x5C,0xDA,0x7D,0xC6,0x91,0x42,0x03,0x8D,0x76,0x4F,0x08,0x77,0xDC,0x43,0xD9,0x22,0x88,0xBE,0xBB,0x9C,0x95,0x3A,0xDD,0xCA,0x51,0xAF,0xD2, + 0x6C,0x1F,0x96,0x07,0x44,0x7A,0x54,0x5D,0x3B,0x7F,0x55,0x2D,0xD0,0x97,0xEE,0x56,0x97,0xAE,0x6E,0x98,0x60,0x07,0x25,0x94,0xA2,0x42,0x60,0x96,0x1E,0x4D,0xAB,0x34, + 0xCB,0xA4,0xF1,0x8B,0x18,0x6D,0x54,0xBA,0xCD,0x4A,0xB3,0xD1,0xC2,0xC1,0xD6,0xF1,0x2F,0x09,0x94,0x77,0x74,0xF6,0xDD,0xF4,0x25,0x74,0x0C,0x7D,0x85,0x20,0x5A,0x02, + 0xDA,0x05,0x24,0x28,0x03,0xBF,0x64,0xDC,0x57,0x3D,0xB8,0x2E,0x01,0xFA,0x14,0xCD,0x72,0x0C,0x02,0x68,0x55,0x92,0x0C,0x94,0xD5,0xD4,0xF7,0x90,0xE9,0x09,0x22,0x39, + 0x3F,0x19,0xDF,0xDA,0xFB,0xB9,0x08,0x93,0xC8,0x7C,0x87,0xEB,0x78,0x3F,0x84,0x0B,0xD3,0xF2,0xC1,0x67,0x37,0xAE,0xB3,0x34,0x8D,0xFE,0xF8,0xB7,0x8B,0xB5,0xBE,0xA4, + 0x2C,0x1E,0x62,0xCC,0xA8,0xBD,0x37,0xE7,0xAE,0xE3,0x39,0x0B,0xBF,0x16,0xD2,0x21,0x9E,0xAF,0xBB,0xFE,0x08,0x57,0xC8,0xF3,0xDD,0xE1,0xC1,0xCB,0x96,0xD1,0xED,0x1D, + 0x1D,0x1F,0x54,0x08,0xB5,0x0D,0x69,0xB8,0xD1,0x68,0xB7,0x7B,0xBD,0x83,0xCA,0x5B,0x31,0x11,0x83,0xD9,0xB0,0x49,0x80,0x26,0x7A,0xAC,0xB5,0x5D,0xDB,0xDF,0xAC,0x87, + 0x8D,0xC3,0xA3,0x67,0xDF,0xA5,0x16,0xC4,0xB2,0x5B,0x3A,0x00,0x07,0xD0,0xFD,0x3E,0x6A,0x26,0x30,0xED,0xB6,0xEC,0xB6,0x91,0xBB,0x90,0x66,0x37,0x66,0xF2,0xB2,0xC7, + 0x3F,0xBE,0x5C,0x38,0x0E,0xC8,0xF5,0x30,0xB7,0x40,0x47,0x7D,0x70,0xD9,0x55,0xC2,0xBB,0x64,0xB7,0x91,0x3C,0x6D,0x0E,0x8C,0x52,0x57,0x81,0xB5,0xD9,0x6C,0x1F,0xF7, + 0x5A,0x01,0x75,0x16,0x30,0x90,0x7C,0x70,0x2F,0x82,0x02,0x0E,0x05,0xB4,0x89,0xFE,0x20,0x71,0xC4,0x68,0x18,0x74,0xEE,0xB8,0x3A,0x93,0x17,0x50,0x53,0x17,0x97,0x50, + 0x82,0xEF,0xAF,0x90,0xC7,0xBC,0x59,0xB6,0x83,0x13,0x42,0xC4,0xE8,0xFF,0x39,0x88,0x6B,0x2C,0x5E,0xB7,0x1F,0xD2,0xF2,0xF1,0xF9,0x77,0x2B,0xD3,0xA7,0x0A,0x59,0x8F, + 0x1A,0xF8,0xC3,0x43,0xF5,0x1D,0x65,0xD1,0x70,0xE6,0x58,0x46,0x80,0xB0,0xF7,0x20,0x6B,0xA1,0x95,0xD6,0x42,0x4B,0xB9,0x2E,0xA3,0x06,0xFE,0x00,0x12,0x30,0x39,0x7A, + 0xE3,0x38,0xD6,0x4C,0x77,0x65,0xD6,0xD8,0x92,0xA7,0xA7,0x4D,0x3A,0x93,0xE3,0xC9,0xA1,0x14,0xA8,0xAB,0x8C,0x89,0x3E,0x8B,0xB6,0xF2,0x30,0x5F,0x51,0xA4,0xFD,0x58, + 0xBB,0xF7,0xF8,0x3E,0xA7,0xD8,0x10,0x26,0x0D,0xFC,0x09,0xCD,0x08,0x56,0x8C,0xB4,0x1A,0xF8,0x5F,0x70,0x15,0x6E,0x03,0x60,0xB6,0x5B,0x0F,0xB6,0x0D,0x86,0x71,0x61, + 0xDE,0x53,0x03,0x35,0xF2,0x20,0xEF,0x60,0x73,0x67,0xEB,0x9A,0x14,0xE2,0x9C,0x26,0xAE,0x88,0x4D,0xEF,0x60,0x27,0x5B,0x3B,0xB6,0xE3,0x6D,0xF4,0x39,0x2E,0x82,0x69, + 0x2F,0x1C,0xD0,0xBA,0xBB,0x8B,0xA9,0xAD,0xC9,0xB0,0x5E,0x9E,0x9D,0x4F,0x2E,0x6F,0x7E,0xBB,0x79,0x20,0xF2,0x96,0xF9,0x58,0x9B,0xDE,0x7C,0xBE,0xFA,0x76,0x31,0x9A, + 0x7C,0x7B,0xFF,0x61,0xFC,0xF9,0x72,0x12,0x3D,0xDE,0x22,0xC6,0xF1,0xCD,0xE5,0x34,0xF1,0xC4,0xD0,0x5D,0x90,0x93,0x52,0x1B,0x27,0x8F,0x6E,0x3E,0x2A,0x1E,0xDF,0x9A, + 0x8E,0x05,0x3B,0xED,0x63,0xED,0x6C,0xFA,0xFB,0xD5,0x68,0xFA,0x61,0xF4,0xB7,0xC9,0x4D,0x02,0x0C,0x5C,0x7D,0x37,0x73,0x9D,0x3B,0x1B,0xA0,0xBE,0x4E,0xCE,0x93,0xD4, + 0xC5,0x93,0xB7,0x93,0xAB,0xC9,0xF5,0xC5,0x28,0x49,0x82,0xD2,0xCD,0xC6,0xB4,0xBF,0x03,0xC0,0xE7,0xAB,0xBF,0x5D,0x7D,0xF8,0x7A,0x95,0x44,0x4E,0xD1,0x82,0x2E,0x3F, + 0xBC,0xFD,0x76,0x39,0xF9,0x32,0xB9,0xFC,0xD6,0x7C,0x20,0xF2,0x6D,0x2B,0x7E,0xDB,0x8E,0xDF,0x76,0xE2,0xB7,0xDD,0xF8,0x6D,0x2F,0xB8,0xFD,0x72,0x31,0xBD,0x38,0x47, + 0x92,0x86,0xE9,0x6D,0x2C,0x7D,0xD7,0x37,0x6D,0xE1,0x0A,0xF8,0xF4,0xDD,0xC5,0x78,0x3C,0xB9,0x8A,0x1E,0x72,0x6F,0xAA,0xB1,0x54,0xE8,0xDE,0x7F,0x4F,0xED,0xAD,0x64, + 0x2F,0xFD,0x97,0x6F,0x8E,0xF1,0x07,0xCC,0xE1,0xBE,0xEA,0xAD,0x74,0x03,0x22,0x47,0x83,0x40,0xD4,0x01,0x0B,0xE3,0x11,0x96,0x34,0x2A,0xE2,0x5F,0xAD,0x0D,0x61,0x4D, + 0x64,0x3F,0x52,0x26,0x30,0x9F,0xCF,0x07,0x71,0x62,0x61,0xB8,0xD3,0x67,0x00,0xB3,0x05,0xEF,0xE3,0x99,0x07,0xCB,0x21,0xE0,0x97,0xE9,0xF9,0xC2,0xBC,0x19,0x78,0x2A, + 0xE8,0x61,0x7C,0x59,0x9B,0xB6,0xB0,0xF5,0x2E,0x0F,0x8C,0xF7,0x81,0xE9,0xB3,0xFB,0xBF,0xAA,0x26,0x44,0x80,0x7B,0x78,0xD8,0x90,0x73,0xAF,0x23,0x60,0x0B,0x44,0x5D, + 0xA3,0xA4,0x41,0xF8,0xE8,0x74,0x3A,0x83,0xB8,0xA2,0xAA,0x33,0xCB,0x01,0x03,0x0C,0x0C,0x95,0xB1,0x75,0x24,0x39,0x38,0x4F,0x8D,0xE4,0x91,0x9C,0x40,0xA8,0x0C,0x5E, + 0x22,0xEE,0x76,0x7F,0x19,0xCC,0xB7,0xAE,0xC7,0xEC,0x66,0xA1,0x6F,0x2D,0x3F,0x0A,0xCF,0x2B,0xD3,0x30,0xC0,0x8E,0xD3,0x3B,0x43,0xC0,0x7E,0x22,0x4C,0x1E,0xE3,0x8F, + 0x9C,0xFA,0x30,0xB9,0x1E,0x6B,0x6B,0x58,0xCF,0x44,0x88,0xF9,0xA1,0xE8,0x27,0x94,0xCC,0x92,0xC8,0x15,0x1F,0x6F,0xF7,0xE0,0x5A,0xC8,0xB1,0x71,0x4C,0x86,0x3C,0xAE, + 0x73,0x79,0x3B,0x93,0x55,0xC6,0x97,0x43,0xB0,0x28,0x04,0x52,0xE4,0x77,0xE3,0xA3,0xEE,0xF8,0x4D,0x24,0x4B,0x6B,0x6F,0x61,0x1A,0xB3,0x2E,0x3D,0x9E,0xFF,0x77,0x0A, + 0xD3,0x2A,0x94,0xE6,0xA4,0x2E,0xEA,0x88,0x93,0xBA,0xA8,0x91,0xB0,0xA0,0x20,0x8E,0x0D,0xD8,0x8D,0xA1,0x66,0x2E,0x48,0xC9,0x87,0x5C,0xC1,0x59,0x94,0x58,0xA6,0xB1, + 0xDD,0x94,0xC9,0x8B,0xE1,0x90,0x1C,0xE0,0xCE,0xB6,0x00,0x23,0x35,0x0E,0xCA,0x44,0x3C,0x29,0x95,0x07,0x58,0xB3,0x18,0xE6,0x2D,0x31,0x61,0x6A,0x58,0x08,0xC8,0x83, + 0x41,0xF2,0xAD,0x11,0x46,0x76,0xA8,0x09,0x79,0x7B,0x28,0x6F,0x58,0x1A,0xFC,0x92,0x34,0xC0,0x00,0x85,0x98,0x24,0x89,0x2E,0xCF,0x17,0xA2,0xCD,0x8F,0xF0,0x27,0xEE, + 0x37,0x6C,0x2F,0x49,0xA8,0x49,0x3B,0x85,0x1A,0xCA,0x75,0xEC,0xE5,0xE9,0x09,0xAA,0x36,0x44,0x1E,0xAA,0xB9,0xD3,0x4B,0xD4,0x48,0xB9,0x35,0x91,0x76,0xCA,0xF3,0x33, + 0x1F,0x74,0x4D,0xAE,0xA9,0x6E,0xF9,0xE6,0x9A,0x92,0x4B,0x67,0x09,0x51,0x03,0xCA,0x36,0xC4,0x73,0x8A,0xDA,0xE6,0x14,0xEB,0x20,0xCE,0x8F,0x0A,0xD5,0x4D,0xC8,0xD4, + 0xEC,0x3C,0x41,0x28,0x04,0xDA,0x47,0xA8,0x13,0xD8,0x3A,0x6D,0xB6,0x76,0xAC,0xF0,0x5C,0x81,0x80,0x1A,0xCA,0x01,0xA3,0xA7,0x59,0x52,0x49,0xC2,0xB1,0x89,0xCE,0x06, + 0x6C,0x91,0xD5,0xB3,0x3A,0x14,0xC8,0x01,0x4F,0xD2,0x72,0x0B,0x81,0x5B,0x98,0x30,0x40,0x39,0x4C,0x2D,0x4B,0xC8,0x35,0xD4,0x1A,0xFC,0x1E,0x37,0xF0,0xF0,0xDE,0xD2, + 0x3D,0x0F,0x64,0x8A,0x52,0x17,0x86,0xDB,0xC5,0xFF,0x0C,0x46,0x71,0x0A,0x75,0xF4,0xDC,0xA7,0xC6,0x7B,0xC7,0xD8,0x5A,0xD4,0x0B,0xA7,0x08,0xB7,0xD0,0x4E,0x03,0x00, + 0x22,0x20,0xA0,0xAC,0x36,0xA2,0xE9,0x5F,0xA8,0x3B,0xC3,0x88,0xB7,0xE3,0x60,0xE9,0xE9,0x21,0x40,0x9F,0x34,0xE3,0x53,0x47,0x98,0xE3,0x66,0x4D,0x63,0x0F,0xD1,0x24, + 0x12,0xF4,0x3E,0xEA,0x5B,0x8F,0x66,0x4D,0x62,0x0F,0xE3,0xF0,0x6F,0x40,0x72,0x61,0x58,0x69,0x78,0x7C,0x28,0xC0,0xEB,0x4C,0x27,0x75,0xA6,0xF5,0xF4,0xB2,0x6C,0xA0, + 0xB8,0xF8,0x26,0x5A,0x0F,0x71,0xC7,0xE5,0xF5,0x80,0x34,0x76,0xE0,0xD2,0x85,0x4B,0xBD,0xD5,0xF9,0x16,0x36,0x14,0x7B,0x6C,0xDE,0x1E,0xC4,0xAD,0x96,0xD7,0xC4,0x7C, + 0x87,0xE4,0x89,0x28,0xFA,0x9B,0xBC,0xDB,0x82,0xCD,0x98,0xF6,0x66,0x0B,0x45,0x27,0x56,0x1E,0xDA,0x8C,0x21,0xD2,0xC8,0xAD,0x0E,0x59,0xD4,0x50,0xBB,0xE6,0xE8,0x35, + 0x88,0x3B,0x73,0xCB,0x9C,0x7F,0x1F,0x6A,0x82,0x20,0x8B,0x29,0x12,0xE3,0xA7,0x27,0xAB,0x66,0x64,0x8C,0xF8,0x68,0xD5,0x0C,0x9F,0x6F,0x44,0x88,0x31,0x6D,0x59,0x1C, + 0x70,0x48,0x7F,0xEB,0xC1,0x5D,0x32,0xE6,0x74,0x1A,0x69,0x26,0xF9,0x34,0x16,0xD3,0x41,0x11,0x2C,0xA8,0x87,0xD3,0x82,0x04,0x20,0xE9,0x75,0xAC,0x6C,0x89,0x1C,0x0C, + 0x45,0x47,0x44,0xA1,0xDF,0x70,0x06,0x70,0xB3,0x08,0x1D,0x87,0x24,0x3C,0x45,0xED,0x37,0xB0,0xC4,0x5F,0x21,0x7F,0x70,0xEE,0xB4,0x6C,0x97,0xE9,0x31,0x21,0x82,0x70, + 0x59,0xDD,0xF5,0x3D,0x88,0x41,0x16,0x5B,0xBB,0x04,0x36,0x5E,0xD8,0x44,0x5E,0x98,0xEB,0x65,0xCD,0x86,0x82,0x66,0xC2,0xCB,0x0E,0xEE,0x39,0xCA,0xD0,0x16,0x92,0xF5, + 0x43,0x62,0xFD,0x43,0x13,0x4E,0x83,0x73,0xFB,0xE1,0xA1,0xCE,0xC2,0x6B,0x28,0x70,0x77,0xDA,0xE9,0x85,0xCD,0x7B,0x65,0x90,0x79,0x10,0xDD,0x27,0x27,0x7A,0x30,0x35, + 0x09,0x49,0x56,0x60,0x2F,0x43,0xD6,0x27,0xF4,0xFA,0xF5,0x3A,0x66,0xF9,0xB0,0x05,0x7A,0x2B,0x5C,0x41,0x57,0xB7,0x20,0xAB,0x5C,0x6B,0xA7,0xAA,0xD1,0x93,0xBA,0x7E, + 0x9A,0xEB,0x2C,0x09,0x25,0x4A,0xE9,0x69,0xE8,0x79,0xF2,0x98,0x00,0x0D,0x9E,0xB0,0x14,0x49,0xB2,0x6A,0x1F,0x9C,0xD6,0xA2,0xAC,0x91,0xC7,0x03,0x4F,0xE9,0x60,0xEA, + 0x6F,0xED,0x8B,0x39,0x1D,0xAD,0xE8,0xFC,0x3B,0x64,0xB7,0x58,0xB9,0xDF,0x02,0x7F,0xE5,0xD0,0x61,0x58,0x3C,0x8B,0x03,0x69,0xC2,0x8B,0xE6,0xE1,0xBD,0xA1,0xFB,0x7A, + 0x75,0xBE,0x06,0xD0,0xC6,0x7D,0xA3,0xA5,0x89,0x96,0x61,0x9F,0x60,0xED,0x52,0x87,0xDA,0xE5,0x64,0xE6,0xC6,0xF6,0x9C,0x3D,0x18,0x1C,0xFB,0x96,0x97,0xCF,0x9D,0x0C, + 0x51,0xC0,0x5A,0x27,0x62,0x0D,0x0B,0xA7,0x1F,0x60,0x6B,0x3A,0xF7,0x37,0x05,0x4A,0x93,0x20,0x0A,0xD8,0x3A,0x92,0x34,0x06,0x05,0xDB,0x0F,0xB0,0x75,0xE6,0xED,0xEC, + 0xF9,0x14,0xF2,0x76,0xEA,0xE7,0x73,0xA7,0x00,0xCC,0x67,0xB2,0xD3,0x90,0x33,0x8C,0x3E,0x91,0x10,0xFC,0x00,0xBF,0x20,0xF6,0x94,0xBA,0x10,0x3E,0xF2,0xB9,0x4D,0x81, + 0xE5,0xF3,0x7A,0x94,0xE0,0x35,0x9C,0xFE,0x03,0x9C,0xBE,0xA5,0x90,0x4B,0x9A,0xF3,0x7C,0x3E,0x13,0x40,0xF9,0x5C,0x42,0x4C,0x8B,0xB3,0x29,0x66,0xCB,0x4C,0x26,0xFC, + 0xFF,0x36,0xD8,0xF8,0x33,0x23,0x40,0x10,0xA2,0x54,0xFB,0x4A,0x96,0xB4,0x10,0xE8,0x2F,0x41,0x18,0xAB,0xD4,0x8C,0x84,0x8A,0xA5,0x18,0x4F,0xD4,0x5A,0x88,0xA7,0xA5, + 0xC4,0xD3,0xDA,0x1B,0x4F,0x5B,0x89,0xA7,0xBD,0x37,0x9E,0x8E,0x12,0x4F,0x67,0x6F,0x3C,0x5D,0x25,0x9E,0x6E,0xCE,0x82,0x2D,0xA2,0x54,0xE9,0xA7,0x2E,0x19,0x37,0x50, + 0x29,0x11,0x2B,0x1D,0x48,0x37,0x13,0x1B,0x77,0x11,0x43,0x6D,0xA3,0x69,0xB8,0x94,0x99,0x9E,0x8A,0x07,0x4F,0x55,0x10,0xA4,0x4A,0xD4,0x47,0xBC,0x6F,0x2C,0x7D,0xE9, + 0x95,0x80,0xDE,0x35,0x8E,0x10,0x76,0xFB,0x74,0x24,0xBA,0x01,0x7C,0x21,0x1A,0x8E,0x41,0x37,0x30,0x57,0x25,0x2C,0xA1,0x7C,0x22,0x0E,0xD6,0xE2,0x95,0x90,0x84,0x19, + 0x6F,0x0A,0x4B,0xF0,0x0B,0xCB,0x4D,0x56,0x7D,0xE2,0x8B,0xBA,0x13,0xC8,0x5E,0xCC,0x4D,0x90,0x24,0xB2,0xF7,0x73,0x7F,0xD7,0x6F,0x75,0x3E,0xAA,0x9D,0xFE,0xE3,0x3F, + 0x10,0xF1,0xB9,0x05,0xAC,0x1F,0xCF,0x3E,0x4F,0x27,0x63,0x32,0x24,0x0B,0xDD,0xF2,0xE8,0x20,0xFE,0xF0,0xCE,0x63,0x91,0x31,0x31,0x0A,0xAB,0x6E,0xF3,0xBC,0x5F,0x3D, + 0x6B,0xF4,0x1E,0xF1,0x3D,0x10,0xED,0xEA,0xC3,0xD5,0x44,0xEB,0x13,0xD8,0x1A,0x1A,0x15,0xA2,0x5D,0x7E,0x78,0xFB,0x76,0x72,0xCD,0xEF,0xC1,0x45,0xB5,0xA0,0x23,0xC8, + 0x47,0xC0,0xD9,0x34,0xDC,0xCC,0xF8,0x5D,0x07,0x9F,0xC3,0x1E,0xC2,0xEF,0x8E,0xE0,0x4E,0x6A,0xF1,0xB1,0xC1,0x0E,0xA2,0xFC,0x3A,0x39,0x9F,0x4E,0xAE,0xBF,0x08,0xAC, + 0x47,0x38,0x24,0x5A,0x79,0x6C,0x00,0x42,0x13,0x79,0x4C,0x30,0xC7,0xB9,0xF8,0xF6,0xE6,0xF2,0xEC,0xED,0x94,0x73,0x39,0xB9,0x3A,0x3B,0xBF,0x9C,0x04,0x13,0x00,0xC5, + 0xF5,0x64,0x3A,0xB9,0xF9,0xF6,0xE6,0x42,0x0C,0xB6,0xC4,0xE0,0xD9,0x58,0x10,0x96,0x60,0x10,0x8B,0xA0,0x0D,0xA4,0xE2,0x94,0x42,0xDF,0x02,0x32,0x4D,0xE0,0x22,0x7A, + 0xBA,0xD8,0xDA,0xAC,0x97,0x4B,0x3E,0x95,0xEE,0xCB,0xC0,0x82,0x4B,0xFD,0xAD,0x6B,0x13,0xC3,0x99,0x6F,0xA1,0x00,0xF1,0x6B,0x4B,0xEA,0x4F,0x2C,0x8A,0x97,0xE7,0xBB, + 0x0B,0x03,0x60,0x06,0xE4,0x91,0xD4,0xEB,0x44,0xFB,0xA4,0xA9,0x90,0x4C,0x63,0x58,0x10,0x27,0xEF,0x6A,0xE3,0xAC,0xC4,0x87,0x23,0xE1,0xAE,0xAA,0x42,0x35,0x29,0xDD, + 0x57,0xC8,0x0E,0xB1,0x31,0x34,0xE0,0xCB,0xCC,0x83,0x40,0x82,0x17,0xBB,0x04,0x3A,0x81,0x8A,0x32,0x1F,0x53,0xE1,0xFA,0x22,0x70,0x15,0x7F,0x04,0xAA,0x5B,0xD3,0x33, + 0x63,0xB8,0x1E,0xA2,0x4B,0xFC,0x60,0x1B,0x05,0x94,0x39,0x24,0x61,0xDB,0xA4,0x9C,0x04,0xC1,0x8F,0xD0,0x43,0x89,0xE9,0xA5,0x26,0xC2,0x11,0x4E,0x3B,0xC0,0x90,0x74, + 0x40,0x5E,0x73,0xA3,0x25,0x7D,0xE2,0xBB,0x5B,0x5A,0x1E,0xC4,0x31,0x3C,0xC6,0x6F,0x29,0x40,0xC6,0x47,0x14,0x14,0x13,0x94,0x90,0xCB,0xD7,0xE4,0xE0,0x00,0x28,0x70, + 0x92,0x39,0x34,0x1E,0x89,0x4A,0x75,0x67,0xF1,0x65,0x30,0xC1,0xDF,0x5C,0xF6,0x6A,0xFF,0xD5,0x90,0x24,0xD6,0x41,0xE8,0x4E,0xDF,0x6C,0xA8,0x6D,0xA8,0x70,0xBD,0xCB, + 0xC2,0x95,0x42,0x15,0xE0,0xC2,0x20,0xA2,0xC2,0x34,0x2A,0xB2,0x56,0xEF,0x7C,0x37,0xC2,0x90,0x76,0xA5,0xAF,0x29,0xB7,0x5A,0x25,0x1A,0x61,0x18,0x99,0xEB,0x8C,0xCE, + 0x43,0x05,0x46,0xE0,0x92,0xD1,0x4D,0x68,0x10,0x2A,0x1A,0x52,0x42,0x38,0x13,0x00,0x1A,0x64,0x00,0xBF,0x4F,0xC2,0x39,0x35,0x8B,0xDA,0x4B,0x7F,0x05,0x83,0xAF,0x5E, + 0x95,0x0B,0x17,0x2F,0x98,0xF5,0x87,0xF9,0x27,0x77,0x1B,0x69,0x21,0x77,0x39,0x0B,0xA7,0x74,0xE8,0x0B,0xDB,0x6F,0xB7,0x6E,0x9C,0xA9,0xEF,0x96,0x6E,0x25,0x5D,0xC1, + 0x3D,0xEC,0x53,0xB5,0x85,0xEB,0xAC,0x47,0x2B,0xDD,0x1D,0x39,0x06,0x2D,0x95,0x6E,0xC9,0xE9,0x29,0x69,0x75,0xCA,0xE4,0x57,0x88,0x1F,0x6F,0xDE,0x54,0x08,0x1F,0x69, + 0xF6,0x92,0x23,0x47,0xD1,0xC0,0xAD,0xB8,0xCA,0xD0,0x2D,0x90,0x6F,0xF6,0xF6,0x21,0xBF,0x17,0xEA,0xA3,0xA7,0x61,0x2E,0x40,0x84,0x3B,0xE2,0xF9,0xCE,0xA7,0x25,0x4C, + 0x22,0x2B,0x64,0xE3,0xBB,0x12,0x3E,0x36,0x58,0x9B,0x0B,0x54,0x67,0x7E,0x09,0x1F,0xE7,0x20,0x9A,0xAE,0x1C,0xD7,0x7F,0x3A,0x26,0x72,0x72,0x82,0x22,0xBF,0x22,0x8A, + 0x87,0x30,0xDA,0xCC,0xA1,0x04,0xF2,0xEF,0x41,0xE7,0x9F,0x60,0x19,0x0F,0x0F,0x0F,0x5B,0xB8,0x98,0xAF,0x94,0x30,0x8C,0x1C,0xC0,0xF5,0xBA,0xDD,0x76,0x1E,0x50,0x0B, + 0x81,0x5A,0xDD,0x5E,0x36,0xD7,0xED,0x1C,0xAE,0x2F,0x1D,0xC8,0xA9,0x94,0x6C,0x67,0xF1,0x7D,0xD8,0x6A,0x74,0x0F,0xBB,0xC7,0x9D,0x46,0xFB,0xF0,0xB8,0x75,0x78,0x9C, + 0xCB,0x1B,0x13,0xA0,0x75,0xD4,0xEC,0x1C,0x76,0x8E,0x0F,0x7B,0x87,0xCD,0x46,0xAF,0x5B,0x28,0x4A,0xB3,0x71,0x7C,0xDC,0x6D,0x36,0x7B,0x2D,0x50,0x4F,0x1E,0x70,0x1B, + 0x81,0x3B,0xAD,0xE3,0xCE,0x71,0xEF,0xB0,0x75,0x9C,0x23,0x7E,0xE7,0x89,0xDA,0xEE,0x3E,0x45,0xDB,0xBD,0x42,0x6D,0x1F,0x72,0x6D,0x2B,0xF4,0x3D,0x65,0x87,0x05,0xD6, + 0x6B,0xDD,0x36,0x4A,0x50,0x1B,0x55,0x18,0x82,0xFC,0x28,0xE7,0x01,0xCA,0xA1,0xEC,0xB5,0x30,0x2F,0x20,0x3D,0x48,0x83,0xCF,0x4C,0x1B,0xC0,0x6D,0x7A,0xC7,0xBC,0xF1, + 0xCC,0x75,0xF5,0x5D,0x09,0x50,0x88,0x78,0x97,0x8C,0x91,0x79,0x11,0x93,0x07,0xCC,0x68,0x2E,0x8F,0x95,0x60,0x1E,0x40,0x02,0x02,0x21,0x80,0xE0,0x33,0x49,0x78,0x33, + 0xC8,0x41,0x46,0x8E,0x0D,0xE5,0x1B,0xB6,0xC4,0xD1,0xF3,0x89,0xEF,0xE0,0x14,0xDD,0xDD,0x65,0x12,0x16,0x39,0x64,0xCD,0x03,0xF5,0x94,0x00,0x56,0x66,0x53,0xAD,0xC8, + 0x64,0x1E,0x9E,0xAB,0xC2,0x35,0x6F,0x3D,0xA3,0x50,0x90,0x66,0xC6,0xD2,0x2C,0xFC,0x60,0xCE,0xF0,0xA9,0x94,0x6A,0xFD,0x94,0x6B,0xAC,0x4A,0x60,0x19,0x44,0x88,0xE2, + 0x5F,0x87,0x98,0xBA,0x86,0x6F,0xAA,0x07,0xC9,0x5C,0x40,0xE0,0x8A,0x35,0x6A,0xF2,0x10,0x61,0x3A,0x3B,0x88,0xED,0xF0,0x32,0x47,0x72,0x5F,0x25,0x97,0x1D,0x48,0x83, + 0x33,0xB1,0xA8,0xFA,0x1F,0x79,0xC8,0xA4,0x2C,0x3A,0x4B,0xBC,0x74,0x93,0x22,0x0F,0x61,0x98,0x81,0x67,0xB2,0x98,0x6C,0x26,0xE4,0x61,0x13,0xC9,0x3B,0xC7,0x15,0xC7, + 0x26,0x7B,0x17,0x82,0xF2,0x3C,0xBE,0x22,0x3B,0x8F,0x9C,0xDA,0xD7,0xA4,0x04,0x1D,0x3D,0x4A,0x02,0x13,0x34,0x13,0xA3,0x58,0x94,0xE0,0xF6,0x10,0x66,0xED,0xB8,0xED, + 0x28,0x4C,0x5A,0x15,0x69,0x47,0xA2,0x5E,0xCB,0xB3,0x55,0xC8,0xC3,0xA4,0x56,0x34,0x54,0x10,0x5A,0xD2,0x5F,0xCF,0x0C,0x2C,0x1C,0x4B,0xDA,0x1F,0xA3,0xCB,0xC9,0xD9, + 0xF5,0x64,0xFC,0xA7,0x56,0xEC,0x2A,0xEF,0x4C,0x83,0x7E,0xB6,0x79,0x19,0x6D,0xE4,0x50,0x2F,0x72,0x03,0x4C,0x8F,0x59,0x56,0xCC,0xF2,0x44,0xC8,0xD0,0xB4,0xC4,0x79, + 0x0D,0xE4,0x98,0x55,0xF4,0xE5,0xC1,0x3E,0x5E,0x91,0xC6,0x2B,0x9D,0xF4,0x90,0x70,0x66,0xD8,0xA2,0xDA,0x49,0x14,0xCC,0x46,0xE7,0x43,0x8A,0x91,0xE6,0xFA,0x4C,0x1A, + 0x77,0xFA,0x6C,0x49,0x31,0x89,0x1C,0x1F,0x4A,0x13,0x88,0x8E,0xA5,0x14,0x23,0xCE,0x74,0xA6,0x34,0xDA,0xF8,0x99,0x96,0x0C,0xD4,0x6A,0xAB,0xBA,0x61,0xF6,0xC4,0x5E, + 0x99,0x5D,0x53,0x0F,0xB2,0xFC,0x1C,0xCB,0x0A,0xFB,0x07,0x2F,0xF8,0xD5,0x20,0x6D,0xF6,0xF2,0x8B,0x39,0xC8,0x69,0xC5,0x8C,0xD7,0x50,0x44,0x33,0xE4,0x1A,0xD4,0x49, + 0x1C,0x46,0x2B,0x27,0x7D,0xE2,0x53,0x7C,0x36,0x88,0x1B,0xD4,0x17,0x40,0x31,0x42,0x14,0xBC,0xA8,0x67,0xA8,0x82,0x77,0x78,0x83,0x7C,0xA7,0x4D,0xF7,0x9E,0x4C,0x23, + 0xE8,0x34,0x65,0x7B,0x31,0x28,0x36,0xD9,0x0C,0xAB,0x08,0xC5,0xA7,0x58,0x37,0x0D,0x69,0x79,0xC8,0x8B,0x52,0x6C,0x24,0x09,0xBE,0x6F,0x7C,0xE3,0x5D,0x0B,0x11,0xC4, + 0x44,0x66,0x1E,0x27,0xF9,0x9A,0x35,0x5A,0x08,0xEF,0xBF,0x48,0xD1,0xEC,0x29,0x6A,0x91,0x7B,0xC6,0x4A,0xB5,0x90,0x34,0x36,0xFC,0x44,0x3B,0x32,0x16,0x6E,0x8C,0x1D, + 0xCC,0x65,0x60,0x3F,0xAF,0x41,0x6A,0xB3,0xBF,0x86,0xD2,0xD8,0x17,0x98,0x13,0x84,0xC8,0x23,0x59,0x15,0x01,0x9C,0x8B,0xDE,0x4C,0xA1,0x91,0x55,0xCD,0x99,0x8D,0xA9, + 0x99,0x51,0x28,0xA7,0xA9,0xE3,0xDA,0xCB,0x0D,0xCF,0x8C,0x75,0x0F,0xDB,0xAC,0x21,0x23,0x81,0xFA,0x94,0xC9,0x09,0x7B,0x54,0xDB,0xB8,0xEC,0xF7,0x98,0x9F,0x1C,0x2A, + 0x13,0xD5,0x68,0x29,0x49,0x29,0x9A,0x8E,0xC7,0x8A,0x3F,0xBA,0xCE,0x46,0x5F,0xB2,0xD7,0x7C,0xC1,0xFC,0xC4,0x70,0xA9,0x5C,0xE0,0x12,0x21,0xEB,0xD6,0xAD,0x55,0xE8, + 0x0A,0x72,0x7B,0x0B,0xE0,0x15,0x7E,0x9F,0x3C,0x00,0x00,0x21,0x48,0xEA,0x37,0x6B,0x60,0xBA,0x30,0x2F,0xE5,0x36,0xA0,0xE5,0xF8,0xFB,0x01,0x49,0xCF,0x69,0x73,0xE0, + 0xD9,0xAC,0x22,0xD5,0x35,0x59,0xD7,0x0D,0xD3,0xDC,0x21,0xE9,0x0D,0xC8,0xAB,0x57,0x66,0x71,0x2B,0x80,0xC5,0xCD,0xE8,0x0C,0x1E,0x72,0x68,0x56,0x38,0x0A,0xE0,0x14, + 0x03,0x0D,0x3F,0x53,0xC6,0xC2,0x8C,0x88,0xA6,0xB9,0xDD,0xA3,0xC4,0xF6,0x5C,0xE0,0xF2,0xD8,0x2E,0xAD,0x14,0xE5,0x24,0xFF,0xDB,0x6D,0x48,0x64,0x31,0x73,0x28,0x1B, + 0x2A,0x64,0xED,0x2D,0x2B,0xDC,0x63,0xF3,0x93,0x78,0x83,0x85,0x75,0x58,0x31,0x4D,0x53,0xF0,0x07,0x58,0x8A,0xBB,0x7E,0x9F,0xCE,0x12,0xF9,0x95,0xD4,0x61,0x3F,0x08, + 0x0E,0xA5,0x1E,0x90,0x53,0x66,0x77,0xC8,0x1D,0xFC,0xD2,0x4E,0xEA,0x61,0x2F,0x3D,0xB5,0x94,0xF8,0xE1,0xC5,0x72,0x7A,0x8D,0xD3,0x3C,0xF2,0xBD,0x28,0x2A,0xB0,0x45, + 0xB5,0x34,0x06,0xCD,0x11,0x0B,0x32,0x43,0x72,0xB7,0x82,0x6D,0x83,0x6C,0x70,0x33,0x33,0x54,0x86,0x6C,0xA1,0xE3,0x8D,0x02,0x35,0x10,0xE9,0x50,0xA9,0xA6,0x36,0x7C, + 0xDB,0xF4,0x4D,0xDD,0xFA,0x82,0x3D,0x53,0xD3,0xE2,0x6E,0xC8,0x43,0x17,0x9A,0xAB,0x2A,0x1A,0xBE,0x16,0x68,0xD9,0x89,0x52,0x6C,0x6E,0xA3,0x15,0x93,0xE8,0x14,0x69, + 0x4A,0x03,0xDE,0x9D,0xE9,0xCF,0x57,0x02,0x6B,0xB1,0xF7,0xCC,0x21,0xCC,0xB3,0x4E,0x7E,0x3F,0xFD,0x0C,0x3F,0xF9,0x12,0xAA,0x66,0xCC,0x5C,0xAA,0x7F,0x57,0x3C,0x0B, + 0x28,0x75,0xF6,0xA5,0xD4,0x7A,0x2E,0xA5,0xA3,0x7D,0x29,0xB5,0x9F,0x49,0xA9,0xD9,0xD8,0x97,0x52,0xE7,0x99,0x94,0x5A,0x7B,0x53,0xEA,0xEE,0x4B,0x49,0x1C,0x84,0xFD, + 0x2F,0xB5,0x87,0x94,0x37,0x06,0x56,0xCB,0x7C,0xFC,0x9F,0xF9,0xAB,0x99,0xC6,0x13,0xAD,0x57,0x2E,0xFF,0x33,0xB8,0x8E,0x42,0x55,0xB2,0x42,0x7A,0xCE,0x32,0x04,0x6D, + 0x82,0x42,0x62,0x72,0xD9,0xF4,0x5C,0x42,0x58,0x25,0x15,0x4B,0x25,0x95,0x52,0xCF,0x25,0x24,0x95,0x4C,0x85,0xF4,0x14,0xE5,0xD5,0x73,0xC9,0x86,0xBD,0x88,0x42,0xA2, + 0x52,0xC9,0xF5,0x5C,0x62,0xA2,0xBC,0x2A,0x24,0x95,0x28,0xC3,0x7E,0xAA,0x07,0x45,0x54,0xE2,0xDF,0x4F,0xF8,0x21,0xFF,0xC9,0xDD,0x48,0x71,0xF3,0x14,0x54,0x5F,0xC9, + 0x0E,0xFC,0x4A,0xB1,0x1F,0xC1,0xFE,0x1A,0xEC,0xB7,0xB8,0x83,0xE7,0x6C,0xB7,0xAA,0x3C,0x22,0x3C,0x0E,0x9D,0x74,0xD6,0xF0,0xBD,0x93,0x78,0x5D,0x0E,0xE2,0xAF,0x20, + 0x83,0xB2,0xE8,0x28,0xCA,0xCD,0x53,0x3B,0x66,0xD0,0x5B,0x99,0x78,0x78,0x32,0xCD,0xF4,0x56,0xD8,0xBB,0x14,0xEF,0xB1,0x81,0x5A,0xAD,0x56,0x4B,0xED,0x7E,0xA2,0x6F, + 0x29,0x1A,0xAE,0x58,0xD1,0xB3,0xFB,0x92,0x76,0x87,0x87,0xE3,0x50,0xAA,0x3B,0xA6,0xA2,0x9A,0xE5,0xCC,0x59,0x6A,0x54,0xC3,0x53,0xBD,0x28,0x66,0x3D,0x0B,0x57,0x8D, + 0xB7,0x4A,0xF1,0x6B,0x63,0xB8,0x68,0x3A,0x36,0x70,0x67,0xDB,0xC5,0x82,0xBA,0xC9,0x15,0x0B,0x26,0x38,0xB6,0xB3,0xA1,0xD8,0xF4,0x0D,0xF5,0x02,0xD9,0x9A,0x8F,0x69, + 0x86,0xFC,0x12,0x1E,0xDF,0x63,0x0E,0x42,0x19,0xC3,0x27,0x5C,0x2A,0xDE,0x4E,0xE5,0x29,0x38,0x20,0xF0,0x4A,0xE9,0xE6,0x42,0x44,0x6D,0x6E,0x39,0x1E,0x2D,0x22,0xC7, + 0xDF,0xF9,0x87,0xF4,0x46,0xA1,0x1A,0x21,0xD3,0xF1,0x7C,0x41,0x35,0x93,0xC4,0x9A,0x7A,0x9E,0xBE,0x4C,0x13,0x29,0x8C,0xD0,0xAC,0xFA,0x13,0xCB,0x81,0x65,0x3B,0xBE, + 0xDF,0xA0,0x6E,0x2A,0xA3,0x65,0x56,0x54,0xE3,0x47,0xEF,0xE3,0x44,0xCA,0x69,0x40,0x05,0x99,0x80,0x14,0xEF,0xCD,0xD3,0x1A,0xD8,0xE1,0x12,0x18,0x07,0x2D,0xE2,0x77, + 0x3A,0xD4,0xF0,0xD9,0x58,0xD8,0x2E,0x34,0x94,0xDE,0x55,0x01,0xDA,0x0A,0x69,0xA8,0x98,0x0E,0x65,0x14,0x15,0x6E,0x62,0x4E,0x2B,0x6B,0x0E,0xA6,0x9F,0x7C,0xB7,0xFB, + 0x35,0xD8,0xED,0x30,0x5D,0x56,0x6E,0x7C,0x05,0x72,0xE3,0x27,0x96,0xC0,0xE3,0x5B,0x00,0x6F,0x3B,0xE3,0xCD,0xFE,0x52,0xA7,0x1C,0x64,0xF3,0x19,0xAC,0x3C,0xAA,0x87, + 0xD3,0x2F,0xD1,0x9F,0xC0,0x07,0x14,0x1D,0x73,0x30,0x95,0xF0,0x8D,0x0A,0xE3,0x87,0x51,0x4F,0xB3,0xB5,0x07,0x3F,0x8A,0xA1,0x45,0x0D,0x0F,0xED,0x9C,0x79,0xE7,0xCC, + 0x3B,0xF9,0x2B,0xCD,0x12,0x5A,0xD9,0xB9,0xE5,0xCC,0x4A,0x7F,0x80,0x71,0xB2,0x26,0xC6,0x9F,0x29,0x32,0x8F,0xF9,0x21,0x2C,0x4F,0x82,0xFC,0x77,0x43,0x3C,0x87,0x61, + 0x33,0xF6,0xC8,0x5C,0x78,0x7F,0x28,0x63,0xAF,0x40,0x2B,0x11,0x96,0x35,0x24,0xAA,0x4E,0xD2,0xDE,0x0B,0x24,0x98,0x4C,0xBC,0xCF,0x6D,0x94,0x33,0x30,0x15,0x60,0x0B, + 0x25,0xC1,0x6E,0x55,0x86,0x0C,0xF2,0xE7,0x53,0x49,0x75,0x04,0x4C,0x6E,0x27,0x25,0x8F,0x26,0x65,0x7D,0xB2,0xF6,0x5E,0x15,0x67,0xCD,0x9F,0xC2,0x19,0x8B,0xD7,0x3F, + 0xCE,0x58,0x86,0xB7,0x65,0x0C,0x67,0x6C,0xFE,0x8A,0xCB,0xF8,0x8B,0x38,0x79,0xE7,0xC8,0x34,0xD8,0xFC,0x77,0x0B,0xCA,0xA5,0xC8,0x79,0x67,0x90,0x05,0x9F,0xF5,0x3A, + 0x20,0x0B,0xBE,0xA0,0xD3,0x9F,0x35,0x2D,0xB7,0x7B,0xAF,0x9C,0xF4,0x33,0x9A,0xB7,0x60,0x5D,0xED,0x72,0x51,0x56,0x94,0xCA,0x74,0x4A,0x4F,0x68,0x58,0x67,0xB6,0xD3, + 0x92,0x80,0x4F,0xE8,0x6E,0x16,0x34,0xC0,0xD3,0x7D,0x0A,0x3C,0x5E,0x03,0x5A,0x0B,0xB3,0x36,0x71,0xDC,0xE6,0x8D,0xEB,0xAC,0x3F,0xE2,0x57,0xF2,0x82,0xE6,0x15,0xA4, + 0x04,0xBF,0x91,0x6A,0x04,0x87,0x27,0x1A,0x6B,0xFC,0x6B,0x18,0x97,0xEC,0x6F,0x0A,0x44,0x70,0xBF,0x67,0xC0,0xDD,0x38,0x1B,0x65,0x7B,0x0B,0x39,0xF8,0xF5,0x57,0xCE, + 0xC9,0x0B,0xC8,0x1F,0xB6,0x96,0x15,0xDC,0xD7,0x4C,0xF6,0xE6,0x24,0xF5,0x55,0xA7,0xA7,0x65,0x23,0x42,0x5F,0xF8,0xC2,0x01,0x73,0x64,0x6C,0x4A,0xC7,0x74,0xA8,0xDA, + 0x96,0x70,0x9E,0x0B,0xB4,0x38,0x74,0x8A,0x2E,0x9E,0xA6,0x3A,0xC7,0xAF,0x18,0x82,0x72,0x47,0x16,0xFE,0x91,0x81,0x6B,0x00,0x50,0x26,0x39,0x71,0xEA,0xE2,0xEC,0x12, + 0xFB,0xEB,0x14,0x43,0x46,0x81,0x5F,0x43,0x3E,0xBA,0xB9,0x57,0x15,0x04,0xCA,0xE9,0xBE,0xB3,0x09,0x66,0xE3,0xE5,0x7E,0x93,0xA3,0x73,0x53,0x1A,0xFB,0xD2,0x6D,0x72, + 0xA2,0xE2,0x68,0xDD,0x93,0x17,0x28,0xD9,0x4B,0x7E,0xFE,0x02,0xC5,0xBD,0xA1,0x78,0x89,0x52,0x94,0xFF,0x7F,0x89,0xD4,0x4B,0x24,0x7F,0x69,0xAF,0x78,0x79,0xA4,0x77, + 0xE6,0x3F,0x91,0x87,0xD8,0xCB,0xC2,0x42,0x1E,0x14,0xEF,0x39,0x7F,0x22,0x2F,0xF2,0xF7,0x0B,0x9F,0x6F,0xAE,0xC9,0x60,0x5B,0x6C,0xB0,0x31,0xBA,0xFF,0x87,0x8C,0xF5, + 0x7F,0xF2,0x4D,0x08,0x7E,0x82,0x93,0x75,0xA9,0x7D,0x93,0xB5,0x58,0x14,0x9B,0x39,0x7F,0x2F,0xC4,0xFE,0xBE,0x4C,0xC9,0x64,0x22,0x63,0x65,0xA3,0xFB,0x34,0xE7,0x1C, + 0x07,0x03,0xFB,0x43,0x4B,0x7E,0x15,0x5D,0xE3,0x47,0xB6,0x60,0x2E,0x9E,0x3C,0xC6,0x3F,0xAA,0xC1,0x4E,0x1F,0x2F,0x5D,0xBA,0x3B,0x28,0x3C,0x44,0x12,0xFB,0xCA,0x82, + 0x44,0x3A,0xB9,0x48,0xEC,0xF3,0x63,0x89,0x0F,0x9E,0xA2,0x4F,0x26,0x39,0x0A,0x8E,0xE2,0x5F,0x80,0xC8,0xD4,0xC6,0x33,0xCF,0x04,0x5D,0x40,0xEA,0xA5,0x48,0xB4,0x4E, + 0xEA,0xFC,0x1B,0x12,0xA7,0xFF,0x09,0xDE,0x39,0xEA,0x1B,0x6C,0x4D,0x00,0x00 +}; +void ILibDefaultLogger_WebSocket_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done) +{ + ILibRemoteLogging logger = ILibChainGetLogger(ILibTransportChain(sender)); + + if (logger == NULL) { return; } + if (done == ILibWebServer_DoneFlag_Done) + { + // Web Socket Disconnected + ILibRemoteLogging_DeleteUserContext(logger, sender); + return; + } + if (done != ILibWebServer_DoneFlag_NotDone) { *beginPointer = endPointer; return; } // We're only going to handle the case where we receive a full fragment + + if (ILibRemoteLogging_Dispatch(logger, bodyBuffer, endPointer, sender) < 0) + { + ILibWebServer_WebSocket_Close(sender); + } + + *beginPointer = endPointer; +} +void ILibDefaultLogger_Session_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done) +{ + ILibRemoteLogging logger = ILibChainGetLogger(ILibTransportChain(sender)); + if (done != ILibWebServer_DoneFlag_Done || logger == NULL) { return; } + + switch (ILibWebServer_WebSocket_GetDataType(sender)) + { + case ILibWebServer_WebSocket_DataType_UNKNOWN: + if (header->DirectiveLength == 3 && strncasecmp(header->Directive, "GET", 3) == 0) + { + if (header->DirectiveObjLength == 1 && strncasecmp(header->DirectiveObj, "/", 1) == 0) + { + int flen; + char* f; + + flen = ILibReadFileFromDiskEx(&f, "web\\index.html"); + if (flen == 0) + { + ILibWebServer_StreamHeader_Raw(sender, 200, "OK", "\r\nContent-Type: text/html\r\nContent-Encoding: gzip", ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, (char*)ILibDefaultLogger_HTML, sizeof(ILibDefaultLogger_HTML), ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + else + { + ILibWebServer_StreamHeader_Raw(sender, 200, "OK", "\r\nContent-Type: text/html", ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, f, flen, ILibAsyncSocket_MemoryOwnership_CHAIN, ILibWebServer_DoneFlag_Done); + } + } + else + { + ILibWebServer_StreamHeader_Raw(sender, 404, "Not Found", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + } + else + { + ILibWebServer_StreamHeader_Raw(sender, 400, "Bad Request", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + break; + case ILibWebServer_WebSocket_DataType_REQUEST: + if (ILibWebServer_IsCrossSiteRequest(sender) == NULL) + { + sender->OnReceive = &ILibDefaultLogger_WebSocket_OnReceive; + ILibWebServer_UpgradeWebSocket(sender, 8192); + } + else + { + ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_Microstack_Web, ILibRemoteLogging_Flags_VerbosityLevel_1, "Cross-Site Request!"); + ILibWebServer_StreamHeader_Raw(sender, 400, "Bad Request", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + break; + default: + ILibWebServer_StreamHeader_Raw(sender, 400, "Bad Request", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + break; + } +} +void ILibDefaultLogger_OnSession(struct ILibWebServer_Session *SessionToken, void *User) +{ + SessionToken->OnReceive = &ILibDefaultLogger_Session_OnReceive; +} +void ILibDefaultLogger_OnWrite(ILibRemoteLogging module, char* data, int dataLen, void *userContext) +{ + ILibTransport_Send(userContext, data, dataLen, ILibTransport_MemoryOwnership_USER, ILibTransport_DoneState_COMPLETE); +} +//! Initializes and starts a default ILibRemoteLogging module +/*! + \param chain Microstack chain to associate the logger with + \param portNumber Local port number to bind the WebListener to, which will serve the Web Interface (0 = random port) +\param path Path to the file to use for logging + \return The locally bound port number allocated to the WebListener +*/ +ILibExportMethod unsigned short ILibStartDefaultLoggerEx(void* chain, unsigned short portNumber, char *path) +{ + ((ILibBaseChain*)chain)->ChainLogger = ILibRemoteLogging_Create(ILibDefaultLogger_OnWrite); + ((ILibBaseChain*)chain)->LoggingWebServer = ILibWebServer_Create(chain, 5, portNumber, ILibDefaultLogger_OnSession, NULL); + if (path != NULL) + { + ((ILibBaseChain*)chain)->LoggingWebServerFileTransport = ILibRemoteLogging_CreateFileTransport(((ILibBaseChain*)chain)->ChainLogger, ILibRemoteLogging_Modules_UNKNOWN, ILibRemoteLogging_Flags_NONE, path, -1); + } + + ILibForceUnBlockChain(chain); + return(ILibWebServer_GetPortNumber(((ILibBaseChain*)chain)->LoggingWebServer)); +} +#endif + + +// +// Do not Modify this method. +// This decompresses compressed string blocks, which is used to store the +// various description documents +// +char* ILibDecompressString(unsigned char* CurrentCompressed, const int bufferLength, const int DecompressedLength) +{ + unsigned char *RetVal = (unsigned char*)malloc(DecompressedLength+1); + unsigned char *CurrentUnCompressed = RetVal; + unsigned char *EndPtr = RetVal + DecompressedLength; + int offset,length; + if (RetVal == NULL) ILIBCRITICALEXIT(254); + + UNREFERENCED_PARAMETER( bufferLength ); + + do + { + // UnCompressed Data Block + memcpy_s(CurrentUnCompressed, (int)*CurrentCompressed, CurrentCompressed + 1, (int)*CurrentCompressed); + MEMCHECK(assert((int)*CurrentCompressed <= (DecompressedLength+1) );) + + CurrentUnCompressed += (int)*CurrentCompressed; + CurrentCompressed += 1+((int)*CurrentCompressed); + + // CompressedBlock +#ifdef REQUIRES_MEMORY_ALIGNMENT + length = (unsigned short)((*(CurrentCompressed)) & 63); + offset = ((unsigned short)(*(CurrentCompressed+1))<<2) + (((unsigned short)(*(CurrentCompressed))) >> 6); +#else + length = (*((unsigned short*)(CurrentCompressed)))&((unsigned short)63); + offset = (*((unsigned short*)(CurrentCompressed)))>>6; +#endif + memcpy_s(CurrentUnCompressed, length, CurrentUnCompressed - offset, length); + MEMCHECK(assert(length <= (DecompressedLength+1)-(CurrentUnCompressed - RetVal));) + + CurrentCompressed += 2; + CurrentUnCompressed += length; + } while (CurrentUnCompressed < EndPtr); + + RetVal[DecompressedLength] = 0; + return((char*)RetVal); +} + +void ILibChain_RunOnMicrostackThreadSink(void *obj) +{ + void* chain = ((void**)obj)[0]; + ILibChain_StartEvent handler = (ILibChain_StartEvent)((void**)obj)[1]; + void* user = ((void**)obj)[2]; + + if (ILibIsChainBeingDestroyed(chain) == 0) + { + // Only Dispatch if the chain is still running + if (handler != NULL) { handler(chain, user); } + } + free(obj); +} +//! Dispatch an operation to the Microstack Chain thread +/*! + \param chain Microstack Chain to dispatch to + \param handler Event to dispatch on the microstack thread + \param user Custom user data to dispatch to the microstack thread +*/ +void ILibChain_RunOnMicrostackThreadEx(void *chain, ILibChain_StartEvent handler, void *user) +{ + void** value = NULL; + + value = (void**)ILibMemory_Allocate(3 * sizeof(void*), 0, NULL, NULL); + + value[0] = chain; + value[1] = handler; + value[2] = user; + ILibLifeTime_AddEx(ILibGetBaseTimer(chain), value, 0, &ILibChain_RunOnMicrostackThreadSink, &ILibChain_RunOnMicrostackThreadSink); +} + +void ILibChain_Safe_Destroy(void *object) +{ + free(object); +} +void ILibChain_SafeAddSink(void *object) +{ + struct ILibBaseChain_SafeData *data = (struct ILibBaseChain_SafeData*)object; + + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_2, "ILibChain_SafeAdd: %p [Pre: %p, Post: %p, Destroy: %p]", (void*)data->Object, (void*)((ILibChain_Link*)data->Object)->PreSelectHandler, (void*)((ILibChain_Link*)data->Object)->PostSelectHandler, (void*)((ILibChain_Link*)data->Object)->DestroyHandler); + + ILibAddToChain(data->Chain,data->Object); + free(data); +} + +/*! \fn void ILibChain_SafeAdd(void *chain, void *object) +\brief Dynamically add a link to a chain that is already running. +\par +\b Note: All objects added to the chain must extend/implement ILibChain +\param chain The chain to add the link to +\param object The link to add to the chain +*/ +void ILibChain_SafeAdd(void *chain, void *object) +{ + struct ILibBaseChain *baseChain = (struct ILibBaseChain*)chain; + struct ILibBaseChain_SafeData *data; + if ((data = (struct ILibBaseChain_SafeData*)malloc(sizeof(struct ILibBaseChain_SafeData))) == NULL) ILIBCRITICALEXIT(254); + memset(data, 0, sizeof(struct ILibBaseChain_SafeData)); + data->Chain = chain; + data->Object = object; + + ILibLifeTime_Add(baseChain->Timer, data, 0, &ILibChain_SafeAddSink, &ILibChain_Safe_Destroy); +} +void ILibChain_SafeRemoveEx(void *chain, void *object) +{ + ILibLinkedList links = ILibChain_GetLinks(chain); + void *node = ILibLinkedList_GetNode_Search(links, NULL, object); + ILibChain_Link *link = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(node); + + if (link != NULL) + { + if (link->DestroyHandler != NULL) { link->DestroyHandler(link); } + free(link); + ILibLinkedList_Remove(node); + } +} +/*! \fn void ILibChain_SafeRemove(void *chain, void *object) +\brief Dynamically remove a link from a chain that is already running. +\param chain The chain to remove a link from +\param object The link to remove +*/ +void ILibChain_SafeRemove(void *chain, void *object) +{ + if (ILibIsChainBeingDestroyed(chain) == 0) + { + // + // We are always going to context switch to microstack thread, even if we are already on microstack thread, + // because we cannot remove an item from the chain, if the call originated from the link that is being removed. + // By forcing a context switch, we will gaurantee that the call originates from the BaseTimer link. + // + ILibRemoteLogging_printf(ILibChainGetLogger(chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_2, "ILibChain_SafeRemove (%p)", (void*)object); + ILibChain_RunOnMicrostackThreadEx(chain, ILibChain_SafeRemoveEx, object); + } +} +//! Get the Links of a Chain object +/*! + \param chain Chain object to query + \return ILibLinkedList data structure holding the links +*/ +ILibLinkedList ILibChain_GetLinks(void *chain) +{ + return(((ILibBaseChain*)chain)->Links); +} + +void *ILibCreateChainEx(int extraMemorySize) +{ + struct ILibBaseChain *RetVal; + +#if defined(WIN32) + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 0); + + + if (WSAStartup(wVersionRequested, &wsaData) != 0) { ILIBCRITICALEXIT(1); } +#endif + RetVal = ILibMemory_Allocate(sizeof(ILibBaseChain), extraMemorySize, NULL, NULL); + + RetVal->Links = ILibLinkedList_CreateEx(sizeof(ILibChain_Link_Hook)); + RetVal->LinksPendingDelete = ILibLinkedList_Create(); + +#if defined(WIN32) + RetVal->TerminateFlag = 1; + RetVal->TerminateSock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); + RetVal->ChainProcessHandle = GetCurrentProcess(); + if (!SymInitialize(RetVal->ChainProcessHandle, NULL, TRUE)) { RetVal->ChainProcessHandle = NULL; } +#endif + + RetVal->TerminateFlag = 0; + if (ILibChainLock_RefCounter == 0) + { + sem_init(&ILibChainLock, 0, 1); + } + ILibChainLock_RefCounter++; + + RetVal->Timer = ILibCreateLifeTime(RetVal); + + return(RetVal); +} + +/*! \fn ILibCreateChain() +\brief Creates an empty Chain +\return Chain +*/ +ILibExportMethod void *ILibCreateChain() +{ + return(ILibCreateChainEx(0)); +} + +/*! \fn ILibAddToChain(void *Chain, void *object) +\brief Add links to the chain +\par +\b Notes: +

Do NOT use this method to add a link to a chain that is already running.
All objects added to the chain must extend/implement ILibChain. +\param Chain The chain to add the link to +\param object The link to add to the chain +*/ +void ILibAddToChain(void *Chain, void *object) +{ + // + // Add link to the end of the chain (Linked List) + // + ILibLinkedList_AddTail(((ILibBaseChain*)Chain)->Links, object); + ((ILibChain_Link*)object)->ParentChain = Chain; +} + +//! Return the base timer for this chain. Most of the time, new timers probably don't need to be created +/*! + \param chain Microstack Chain to fetch the timer from + \return Base Timer +*/ +void* ILibGetBaseTimer(void *chain) +{ + //return ILibCreateLifeTime(chain); + return ((struct ILibBaseChain*)chain)->Timer; +} + +#ifdef WIN32 +void __stdcall ILibForceUnBlockChain_APC(ULONG_PTR obj) +{ + struct ILibBaseChain *c = (struct ILibBaseChain*)obj; + if (c->TerminateSock != ~0) + { + closesocket(c->TerminateSock); + } + c->TerminateSock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); +} +#endif +/*! \fn ILibForceUnBlockChain(void *Chain) +\brief Forces a Chain to unblock, and check for pending operations +\param Chain The chain to unblock +*/ +void ILibForceUnBlockChain(void* Chain) +{ + struct ILibBaseChain *c = (struct ILibBaseChain*)Chain; + +#if defined(WIN32) + QueueUserAPC((PAPCFUNC)ILibForceUnBlockChain_APC, c->MicrostackThreadHandle !=NULL ? c->MicrostackThreadHandle : GetCurrentThread(), (ULONG_PTR)c); +#else + // + // Writing data on the pipe will trigger the select on Posix + // + if (c->TerminateWritePipe != NULL) + { + fprintf(c->TerminateWritePipe," "); + fflush(c->TerminateWritePipe); + } +#endif +} + +/*! \fn void ILibChain_DestroyEx(void *subChain) +\brief Destroys a chain or subchain that was never started. +\par +Do NOT call this on a chain that is running, or has been stopped. +
Do NOT call this on a subchain that was already added to another chain. +\param subChain The chain or subchain to destroy +*/ +void ILibChain_DestroyEx(void *subChain) +{ + ILibChain_Link* module; + void* node; + + if (subChain == NULL) return; + ((ILibBaseChain*)subChain)->TerminateFlag = 1; + + node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)subChain)->Links); + while (node != NULL && (module = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(node)) != NULL) + { + if (module->DestroyHandler != NULL) { module->DestroyHandler((void*)module); } + free(module); + node = ILibLinkedList_GetNextNode(node); + } + ILibLinkedList_Destroy(((ILibBaseChain*)subChain)->Links); + ILibLinkedList_Destroy(((ILibBaseChain*)subChain)->LinksPendingDelete); + +#ifdef _REMOTELOGGINGSERVER + ILibRemoteLogging_Destroy(((ILibBaseChain*)subChain)->ChainLogger); + if(((ILibBaseChain*)subChain)->LoggingWebServerFileTransport != NULL) { ILibTransport_Close(((ILibBaseChain*)subChain)->LoggingWebServerFileTransport); } +#endif + + free(subChain); +} +ILibChain_EventHookToken ILibChain_SetEventHook(void* chainLinkObject, int maxTimeout, ILibChain_EventHookHandler handler) +{ + ILibChain_Link_Hook *hook; + ILibChain_Link *link = (ILibChain_Link*)chainLinkObject; + ILibBaseChain *bChain = (ILibBaseChain*)link->ParentChain; + void *node = ILibLinkedList_GetNode_Search(bChain->Links, NULL, chainLinkObject); + hook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(node); + if (node != NULL && hook != NULL) + { + if (maxTimeout <= 0 && hook != NULL) { memset(hook, 0, sizeof(ILibChain_Link_Hook)); return(NULL); } + hook->MaxTimeout = maxTimeout; + hook->Handler = handler; + return(node); + } + else + { + return(NULL); + } +} +void ILibChain_UpdateEventHook(ILibChain_EventHookToken token, int maxTimeout) +{ + ILibChain_Link_Hook *hook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(token); + if (hook == NULL) { return; } + if (maxTimeout > 0) + { + hook->MaxTimeout = maxTimeout; + } + else + { + memset(hook, 0, sizeof(ILibChain_Link_Hook)); + } +} +ILibExportMethod void ILibChain_Continue(void *chain, ILibChain_Link **modules, int moduleCount, int maxTimeout) +{ + int i; + ILibBaseChain *root = (ILibBaseChain*)chain; + int slct = 0, v = 0, vX = 0; + struct timeval tv; + long endTime; + fd_set readset; + fd_set errorset; + fd_set writeset; + void *currentNode; + + if (root->continuationState != ILibChain_ContinuationState_INACTIVE) { return; } + root->continuationState = ILibChain_ContinuationState_CONTINUE; + currentNode = root->node; + + gettimeofday(&tv, NULL); + endTime = tv.tv_sec + maxTimeout; + + ILibRemoteLogging_printf(ILibChainGetLogger(chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ContinueChain..."); + + while (root->TerminateFlag == 0 && root->continuationState == ILibChain_ContinuationState_CONTINUE) + { + slct = 0; + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + + gettimeofday(&tv, NULL); + + if (tv.tv_sec >= endTime) + { + break; + } + + tv.tv_sec = endTime - tv.tv_sec; + tv.tv_usec = 0; + v = (int)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + + for (i = 0; i < moduleCount; ++i) + { + if (modules[i]->PreSelectHandler != NULL) + { + root->node = modules[i]; + vX = v; + modules[i]->PreSelectHandler((void*)modules[i], &readset, &writeset, &errorset, &vX); + if (vX < v) { v = vX; } + } + } + tv.tv_sec = v / 1000; + tv.tv_usec = 1000 * (v % 1000); + +#if defined(WIN32) + // + // Put the Terminate socket in the FDSET, for ILibForceUnblockChain + // + + FD_SET(root->TerminateSock, &errorset); +#else + // + // Put the Read end of the Pipe in the FDSET, for ILibForceUnBlockChain + // + FD_SET(fileno(root->TerminateReadPipe), &readset); +#endif + + + slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv); + if (slct == -1) + { + // + // If the select simply timed out, we need to clear these sets + // + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&errorset); + } + +#ifndef WIN32 + if (FD_ISSET(fileno(root->TerminateReadPipe), &readset)) + { + // + // Empty the pipe + // + while (fgetc(root->TerminateReadPipe) != EOF) + { + } + } +#endif + + for (i = 0; i < moduleCount && root->continuationState == ILibChain_ContinuationState_CONTINUE; ++i) + { + if (modules[i]->PostSelectHandler != NULL) + { + root->node = modules[i]; + modules[i]->PostSelectHandler((void*)modules[i], slct, &readset, &writeset, &errorset); + } + } + + } + ILibRemoteLogging_printf(ILibChainGetLogger(chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ContinueChain...Ending..."); + + root->continuationState = ILibChain_ContinuationState_INACTIVE; + root->node = currentNode; +} + +ILibExportMethod void ILibChain_EndContinue(void *chain) +{ + ((ILibBaseChain*)chain)->continuationState = ILibChain_ContinuationState_END_CONTINUE; + ILibForceUnBlockChain(chain); +} + +#if defined(WIN32) +int ILib_WindowsExceptionFilter(DWORD exceptionCode, void *exceptionInfo, CONTEXT *exceptionContext) +{ + if (IsDebuggerPresent()) { return(EXCEPTION_CONTINUE_SEARCH); } + if (exceptionCode == EXCEPTION_ACCESS_VIOLATION || exceptionCode == EXCEPTION_STACK_OVERFLOW || exceptionCode == EXCEPTION_INVALID_HANDLE) + { + memcpy_s(exceptionContext, sizeof(CONTEXT), ((EXCEPTION_POINTERS*)exceptionInfo)->ContextRecord, sizeof(CONTEXT)); + return(EXCEPTION_EXECUTE_HANDLER); + } + return(EXCEPTION_CONTINUE_SEARCH); +} + +void ILib_WindowsExceptionDebug(CONTEXT* exceptionContext) +{ + char buffer[4096]; + STACKFRAME64 StackFrame; + char symBuffer[4096]; + char imgBuffer[4096]; + DWORD MachineType; + int len = 0; + + ZeroMemory(&StackFrame, sizeof(STACKFRAME64)); + buffer[0] = 0; + +#ifndef WIN64 + MachineType = IMAGE_FILE_MACHINE_I386; + StackFrame.AddrPC.Offset = exceptionContext->Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = exceptionContext->Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = exceptionContext->Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#else + MachineType = IMAGE_FILE_MACHINE_AMD64; + StackFrame.AddrPC.Offset = exceptionContext->Rip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = exceptionContext->Rsp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = exceptionContext->Rsp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#endif + + HANDLE hProcess = GetCurrentProcess(); + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS); + if (SymInitialize(hProcess, NULL, TRUE)) + { + if (StackWalk64( + MachineType, + GetCurrentProcess(), + GetCurrentThread(), + &StackFrame, + MachineType == IMAGE_FILE_MACHINE_I386 + ? NULL + : exceptionContext, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) + { + + if (StackFrame.AddrPC.Offset != 0) + { + // + // Valid frame. + // + PIMAGEHLP_LINE64 pimg = (PIMAGEHLP_LINE64)imgBuffer; + DWORD64 tmp = 0; + DWORD tmp2; + PSYMBOL_INFO psym = (PSYMBOL_INFO)symBuffer; + psym->SizeOfStruct = sizeof(SYMBOL_INFO); + psym->MaxNameLen = MAX_SYM_NAME; + pimg->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + len = sprintf_s(buffer, sizeof(buffer), "FATAL UNHANDLED WINDOWS EXCEPTION."); + + if (SymFromAddr(GetCurrentProcess(), StackFrame.AddrPC.Offset, &tmp, psym)) + { + len += sprintf_s(buffer + len, sizeof(buffer) - len, "[%s", (char*)(psym->Name)); + if (SymGetLineFromAddr64(GetCurrentProcess(), StackFrame.AddrPC.Offset, &tmp2, pimg)) + { + len += sprintf_s(buffer + len, sizeof(buffer) - len, " => %s:%d]\n", (char*)(pimg->FileName), pimg->LineNumber); + } + else + { + len += sprintf_s(buffer + len, sizeof(buffer) - len, "]\n"); + } + } + else + { + util_tohex((char*)&(StackFrame.AddrPC.Offset), sizeof(DWORD64), imgBuffer); + len += sprintf_s(buffer + len, sizeof(buffer) - len, "[FuncAddr: 0x%s]\n", imgBuffer); + } + } + } + ILIBCRITICALEXITMSG(254, buffer); + } +} +#elif defined(_POSIX) +char ILib_POSIX_CrashParamBuffer[5 * sizeof(void*)]; +void ILib_POSIX_CrashHandler(int code) +{ + char msgBuffer[16384]; + int msgLen = 0; + void *buffer[100]; + char **strings; + int sz, i, c; + char *ptr; + + signal(SIGSEGV, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + + sz = backtrace(buffer, 100); + strings = backtrace_symbols(buffer, sz); + + if (code == SIGSEGV || code == SIGABRT) + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "** CRASH **\n", 12); + msgLen += 12; + } + else if (code == 254) + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "** CRITICAL EXIT **\n", 20); + msgLen += 20; + } + else + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "** Microstack Thread STUCK **\n", 30); + msgLen += 30; + } + + for (i = 1; i < sz; ++i) + { + pid_t pid; + int fd[2]; + size_t symlen = strnlen_s(strings[i], sizeof(msgBuffer)); + + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, strings[i], symlen); + msgLen += symlen; + msgBuffer[msgLen] = '\n'; + msgLen += 1; + + + ptr = strings[i]; + for (c = 0; c < (int)symlen; ++c) + { + if ((strings[i])[c] == '[') { ptr = strings[i] + c + 1; } + if ((strings[i])[c] == ']') { (strings[i])[c] = 0; } + if ((strings[i])[c] == 0) { break; } + } + + if (pipe(fd) == 0) + { + pid = vfork(); + if (pid == 0) + { + dup2(fd[1], STDOUT_FILENO); + close(fd[1]); + + ((char**)ILib_POSIX_CrashParamBuffer)[1] = ptr; + execv("/usr/bin/addr2line", (char**)ILib_POSIX_CrashParamBuffer); + if (write(STDOUT_FILENO, "??:0", 4)) {} + exit(0); + } + if (pid > 0) + { + char tmp[8192]; + int len; + + len = read(fd[0], tmp, 8192); + if (len > 0 && tmp[0] != '?') + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "=> ", 3); + msgLen += 3; + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, tmp, len); + msgLen += len; + } + close(fd[0]); + } + } + } + msgBuffer[msgLen] = 0; + ILIBCRITICALEXITMSG(254, buffer); +} +char* ILib_POSIX_InstallCrashHandler(char *exename) +{ + char *retVal = NULL; + if ((retVal = realpath(exename, NULL)) != NULL) + { + ((char**)ILib_POSIX_CrashParamBuffer)[0] = "addr2line"; + ((char**)ILib_POSIX_CrashParamBuffer)[2] = "-e"; + ((char**)ILib_POSIX_CrashParamBuffer)[3] = retVal; + ((char**)ILib_POSIX_CrashParamBuffer)[4] = NULL; + signal(SIGSEGV, ILib_POSIX_CrashHandler); + } + return(retVal); +} +#endif + +void ILibChain_DebugOffset(char *buffer, int bufferLen, uint64_t addrOffset) +{ +#ifdef WIN32 + int len = 0; + char symBuffer[4096]; + char imgBuffer[4096]; + PIMAGEHLP_LINE64 pimg = (PIMAGEHLP_LINE64)imgBuffer; + DWORD64 tmp = 0; + DWORD tmp2; + PSYMBOL_INFO psym = (PSYMBOL_INFO)symBuffer; + psym->SizeOfStruct = sizeof(SYMBOL_INFO); + psym->MaxNameLen = MAX_SYM_NAME; + pimg->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + SymInitialize(GetCurrentProcess(), NULL, TRUE); + + sprintf_s(buffer, bufferLen, "[Unable to decode function address]"); + + if (SymFromAddr(GetCurrentProcess(), (DWORD64)addrOffset, &tmp, psym)) + { + len += sprintf_s(buffer + len, bufferLen - len, "[%s", (char*)(psym->Name)); + if (SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addrOffset, &tmp2, pimg)) + { + len += sprintf_s(buffer + len, bufferLen - len, " => %s:%d]\n", (char*)(pimg->FileName), pimg->LineNumber); + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "]\n"); + } + } +#endif +} + +char* ILibChain_Debug(void *chain, char* buffer, int bufferLen) +{ + char *retVal = NULL; + ILibBaseChain *bChain = (ILibBaseChain*)chain; +#if defined(WIN32) + char symBuffer[4096]; + char imgBuffer[4096]; + DWORD MachineType; + STACKFRAME64 StackFrame; + DWORD64 addrOffset; + int len = 0; + CONTEXT ctx; + + ZeroMemory(&StackFrame, sizeof(STACKFRAME64)); + if (chain != NULL) + { + if (bChain->MicrostackThreadHandle != NULL) + { + SuspendThread(bChain->MicrostackThreadHandle); + memset(&(bChain->MicrostackThreadContext), 0, sizeof(CONTEXT)); + bChain->MicrostackThreadContext.ContextFlags = CONTEXT_FULL; + if (GetThreadContext(bChain->MicrostackThreadHandle, &(bChain->MicrostackThreadContext)) == 0) + { + len = GetLastError(); + } + ResumeThread(bChain->MicrostackThreadHandle); + } + } + else + { + RtlCaptureContext(&ctx); + } + + if (len != 0) { return(NULL); } + +#ifndef WIN64 + MachineType = IMAGE_FILE_MACHINE_I386; + StackFrame.AddrPC.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Eip : ctx.Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Ebp : ctx.Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Esp : ctx.Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#else + MachineType = IMAGE_FILE_MACHINE_AMD64; + StackFrame.AddrPC.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Rip : ctx.Rip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Rsp : ctx.Rsp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Rsp : ctx.Rsp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#endif + + buffer[0] = 0; + retVal = buffer; + + while (1) + { + if (!StackWalk64( + MachineType, + bChain != NULL ? bChain->ChainProcessHandle : GetCurrentProcess(), + bChain != NULL ? bChain->MicrostackThreadHandle : GetCurrentThread(), + &StackFrame, + MachineType == IMAGE_FILE_MACHINE_I386 + ? NULL + : (bChain!=NULL ? &(bChain->MicrostackThreadContext) : &ctx), + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) + { + // + // Maybe it failed, maybe we have finished walking the stack. + // + break; + } + + if (StackFrame.AddrPC.Offset != 0) + { + // + // Valid frame. + // + addrOffset = StackFrame.AddrPC.Offset; + { + PIMAGEHLP_LINE64 pimg = (PIMAGEHLP_LINE64)imgBuffer; + DWORD64 tmp = 0; + DWORD tmp2; + PSYMBOL_INFO psym = (PSYMBOL_INFO)symBuffer; + psym->SizeOfStruct = sizeof(SYMBOL_INFO); + psym->MaxNameLen = MAX_SYM_NAME; + pimg->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + if (SymFromAddr(bChain!=NULL && bChain->ChainProcessHandle != NULL ? bChain->ChainProcessHandle : GetCurrentProcess(), addrOffset, &tmp, psym)) + { + len += sprintf_s(buffer + len, bufferLen - len, "[%s", (char*)(psym->Name)); + if (SymGetLineFromAddr64(bChain != NULL && bChain->ChainProcessHandle != NULL ? bChain->ChainProcessHandle : GetCurrentProcess(), addrOffset, &tmp2, pimg)) + { + len += sprintf_s(buffer + len, bufferLen - len, " => %s:%d]\n", (char*)(pimg->FileName), pimg->LineNumber); + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "]\n"); + } + } + else + { + char addrBuffer[255]; + util_tohex((char*)&addrOffset, sizeof(DWORD64), addrBuffer); + len += sprintf_s(buffer + len, bufferLen - len, "[FuncAddr: 0x%s]\n", addrBuffer); + } + } + if (bChain != NULL && bChain->MicrostackThreadHandle == NULL) + { + break; + } + } + else + { + // + // Base reached. + // + break; + } + } +#else + pthread_kill(bChain->ChainThreadID, SIGUSR1); + return(NULL); +#endif + return(retVal); +} + + +#ifdef ILibChain_WATCHDOG_TIMEOUT +void ILibChain_WatchDogStart(void *obj) +{ + char msg[4096]; + ILibBaseChain *chain = (ILibBaseChain*)obj; + +#ifndef WIN32 + fd_set readset, writeset, errorset; + int slct; + struct timeval tv; + long stamp = ILibGetTimeStamp(); + tv.tv_usec = 0; + tv.tv_sec = ILibChain_WATCHDOG_TIMEOUT / 1000; +#endif + int Pre1=0, Pre2=0, Post1=0, Post2=0; + sprintf_s(msg, sizeof(msg), "Microstack STUCK: @ "); + +#ifdef WIN32 + while (WaitForSingleObject(chain->WatchDogTerminator, ILibChain_WATCHDOG_TIMEOUT) == WAIT_TIMEOUT) +#else + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + FD_SET(chain->WatchDogTerminator[0], &readset); + while((slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv)) == 0 || slct == EINTR) +#endif + { +#ifndef WIN32 + tv.tv_usec = 0; + tv.tv_sec = ILibChain_WATCHDOG_TIMEOUT / 1000; + + if (slct == EINTR) + { + slct = (int)(ILibGetTimeStamp() - stamp); + if (slct < ILibChain_WATCHDOG_TIMEOUT) + { + tv.tv_sec = (ILibChain_WATCHDOG_TIMEOUT - slct) / 1000; + continue; + } + } + stamp = ILibGetTimeStamp(); + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + FD_SET(chain->WatchDogTerminator[0], &readset); +#endif + Pre1 = chain->PreSelectCount; + Post1 = chain->PostSelectCount; + + if (Pre1 == Post1) + { + if (Pre1 == Pre2 && Pre2 != 0) + { + // STUCK! +#ifdef WIN32 + ILibChain_Debug(chain, msg+20, sizeof(msg)-20); + ILIBCRITICALEXITMSG(254, msg); +#else + pthread_kill(chain->ChainThreadID, SIGUSR1); + break; +#endif + } + else + { + Pre2 = Pre1; + Post2 = Post1; + } + } + } +} +#endif + +/*! \fn ILibStartChain(void *Chain) +\brief Starts a Chain +\par +This method will use the current thread. This thread will be refered to as the +microstack thread. All events and processing will be done on this thread. This method +will not return until ILibStopChain is called. +\param Chain The chain to start +*/ +ILibExportMethod void ILibStartChain(void *Chain) +{ + ILibBaseChain *chain = (ILibBaseChain*)Chain; + ILibChain_Link *module; + ILibChain_Link_Hook *nodeHook; + + fd_set readset; + fd_set errorset; + fd_set writeset; + + struct timeval tv; + int slct; + int v, vX; +#if !defined(WIN32) + int TerminatePipe[2]; + int flags; +#endif + + if (Chain == NULL) { return; } + chain->PreSelectCount = chain->PostSelectCount = 0; + +#if defined(WIN32) + chain->ChainThreadID = GetCurrentThreadId(); + chain->ChainProcessHandle = GetCurrentProcess(); + chain->MicrostackThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, chain->ChainThreadID); +#else + chain->ChainThreadID = pthread_self(); +#endif + + if (gILibChain == NULL) {gILibChain = Chain;} // Set the global instance if it's not already set +#if defined(ILibChain_WATCHDOG_TIMEOUT) +#ifdef WIN32 + chain->WatchDogTerminator = CreateEvent(NULL, TRUE, FALSE, NULL); +#else + if (pipe(chain->WatchDogTerminator) == 0) + { + flags = fcntl(chain->WatchDogTerminator[0], F_GETFL, 0); + fcntl(chain->WatchDogTerminator[0], F_SETFL, O_NONBLOCK | flags); + signal(SIGUSR1, ILib_POSIX_CrashHandler); + } +#endif + chain->WatchDogThread = ILibSpawnNormalThread(ILibChain_WatchDogStart, chain); +#endif + + // + // Use this thread as if it's our own. Keep looping until we are signaled to stop + // + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + +#if !defined(WIN32) + // + // For posix, we need to use a pipe to force unblock the select loop + // + if (pipe(TerminatePipe) == -1) {} // TODO: Pipe error + flags = fcntl(TerminatePipe[0],F_GETFL,0); + // + // We need to set the pipe to nonblock, so we can blindly empty the pipe + // + fcntl(TerminatePipe[0],F_SETFL,O_NONBLOCK|flags); + ((struct ILibBaseChain*)Chain)->TerminateReadPipe = fdopen(TerminatePipe[0],"r"); + ((struct ILibBaseChain*)Chain)->TerminateWritePipe = fdopen(TerminatePipe[1],"w"); +#endif + + chain->RunningFlag = 1; + while (chain->TerminateFlag == 0) + { + slct = 0; + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + tv.tv_sec = UPNP_MAX_WAIT; + tv.tv_usec = 0; + + // + // Iterate through all the PreSelect function pointers in the chain + // + chain->node = ILibLinkedList_GetNode_Head(chain->Links); + v = (int)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + while(chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + if(module->PreSelectHandler != NULL) + { +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + vX = v; + module->PreSelectHandler((void*)module, &readset, &writeset, &errorset, &vX); + if (vX < v) { v = vX; } +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + } + nodeHook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(chain->node); + if (nodeHook->MaxTimeout > 0 && nodeHook->MaxTimeout < v) { v = nodeHook->MaxTimeout; } + chain->node = ILibLinkedList_GetNextNode(chain->node); + } + tv.tv_sec = v / 1000; + tv.tv_usec = 1000 * (v % 1000); + + +#if defined(WIN32) + // + // Add the fake socket, for ILibForceUnBlockChain + // + FD_SET(chain->TerminateSock, &errorset); +#else + // + // Put the Read end of the Pipe in the FDSET, for ILibForceUnBlockChain + // + FD_SET(TerminatePipe[0], &readset); +#endif + sem_wait(&ILibChainLock); + while(ILibLinkedList_GetCount(((ILibBaseChain*)Chain)->LinksPendingDelete) > 0) + { + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->LinksPendingDelete); + module = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node); + ILibLinkedList_Remove_ByData(((ILibBaseChain*)Chain)->Links, module); + ILibLinkedList_Remove(chain->node); + if (module != NULL) + { + if (module->DestroyHandler != NULL) { module->DestroyHandler((void*)module); } + free(module); + } + } + sem_post(&ILibChainLock); + + // + // The actual Select Statement + // + chain->PreSelectCount++; +#ifdef WIN32 + if (readset.fd_count == 0 && writeset.fd_count == 0) + { + SleepEx(v, TRUE); // If there is no pending IO, we must force the thread into an alertable wait state, so ILibForceUnblockChain can function. + slct = -1; + } + else + { + slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv); + } +#else + slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv); +#endif + chain->PostSelectCount++; + + if (slct == -1) + { + // + // If the select simply timed out, we need to clear these sets + // + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&errorset); + } + +#ifndef WIN32 + if (FD_ISSET(TerminatePipe[0], &readset)) + { + // + // Empty the pipe + // + while (fgetc(((struct ILibBaseChain*)Chain)->TerminateReadPipe)!=EOF) + { + } + } +#endif + // + // Iterate through all of the PostSelect in the chain + // + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->Links); + while(chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + if (module->PostSelectHandler != NULL) + { +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + module->PostSelectHandler((void*)module, slct, &readset, &writeset, &errorset); +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + } + nodeHook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(chain->node); + if (nodeHook->Handler != NULL) { nodeHook->Handler(module, chain->node); } + chain->node = ILibLinkedList_GetNextNode(chain->node); + } + } + + // + // This loop will start, when the Chain was signaled to quit. Clean up the chain by iterating + // through all the Destroy methods. Not all modules in the chain will have a destroy method, + // but call the ones that do. + // + if (chain->WatchDogThread != NULL) + { +#ifdef WIN32 + SetEvent(chain->WatchDogTerminator); +#else + if (write(chain->WatchDogTerminator[1], " ", 1)) {} +#endif + } + + // Because many modules in the chain are using the base chain timer which is the first node + // in the chain in the base timer), these modules may start cleaning up their timers. So, we + // are going to make an exception and Destroy the base timer last, something that would usualy + // be destroyed first since it's at the start of the chain. This will allow modules to clean + // up nicely. + // + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->Links); + if(chain->node != NULL) {chain->node = ILibLinkedList_GetNextNode(chain->node);} // Skip the base timer which is the first element in the chain. + + // + // Set the Terminate Flag to 1, so that ILibIsChainBeingDestroyed returns non-zero when we start cleanup + // + ((struct ILibBaseChain*)Chain)->TerminateFlag = 1; + + while(chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + // + // If this module has a destroy method, call it. + // + if (module->DestroyHandler != NULL) module->DestroyHandler((void*)module); + // + // After calling the Destroy, we free the module and move to the next + // + free(module); + chain->node = ILibLinkedList_Remove(chain->node); + } + + if (gILibChain == Chain) { gILibChain = NULL; } // Reset the global instance if it was set + + // + // Go back and destroy the base timer for this chain, the first element in the chain. + // + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->Links); + if (chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + module->DestroyHandler((void*)module); + free(module); + ((ILibBaseChain*)Chain)->Timer = NULL; + } + + ILibLinkedList_Destroy(((ILibBaseChain*)Chain)->Links); + + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->LinksPendingDelete); + while(chain->node != NULL && (module = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node)) != NULL) + { + if(module->DestroyHandler != NULL) {module->DestroyHandler((void*)module);} + free(module); + chain->node = ILibLinkedList_GetNextNode(chain->node); + } + ILibLinkedList_Destroy(((ILibBaseChain*)Chain)->LinksPendingDelete); + +#ifdef _REMOTELOGGINGSERVER + if (((ILibBaseChain*)Chain)->LoggingWebServer != NULL && ((ILibBaseChain*)Chain)->ChainLogger != NULL) + { + if (((ILibBaseChain*)Chain)->LoggingWebServerFileTransport != NULL) { ILibTransport_Close(((ILibBaseChain*)Chain)->LoggingWebServerFileTransport); } + ILibRemoteLogging_Destroy(((ILibBaseChain*)Chain)->ChainLogger); + } +#endif + if(((struct ILibBaseChain*)Chain)->ChainStash != NULL) {ILibHashtable_Destroy(((struct ILibBaseChain*)Chain)->ChainStash);} + + // + // Now we actually free the chain, before we just freed what the chain pointed to. + // +#if !defined(WIN32) + // + // Free the pipe resources + // + fclose(((ILibBaseChain*)Chain)->TerminateReadPipe); + fclose(((ILibBaseChain*)Chain)->TerminateWritePipe); + ((ILibBaseChain*)Chain)->TerminateReadPipe=0; + ((ILibBaseChain*)Chain)->TerminateWritePipe=0; +#endif +#if defined(WIN32) + if (((ILibBaseChain*)Chain)->TerminateSock != ~0) + { + closesocket(((ILibBaseChain*)Chain)->TerminateSock); + ((ILibBaseChain*)Chain)->TerminateSock = (SOCKET)~0; + } +#endif + +#ifdef WIN32 + WSACleanup(); +#endif + if (ILibChainLock_RefCounter == 1) + { + sem_destroy(&ILibChainLock); + } + --ILibChainLock_RefCounter; + free(Chain); +} + +/*! \fn ILibStopChain(void *Chain) +\brief Stops a chain +\par +This will signal the microstack thread to shutdown. When the chain cleans itself up, +the thread that is blocked on ILibStartChain will return. +\param Chain The Chain to stop +*/ +ILibExportMethod void ILibStopChain(void *Chain) +{ + ((struct ILibBaseChain*)Chain)->TerminateFlag = 2; + ILibForceUnBlockChain(Chain); +} + +/*! \fn ILibDestructXMLNodeList(struct ILibXMLNode *node) +\brief Frees resources from an XMLNodeList tree that was returned from ILibParseXML +\param node The XML Tree to clean up +*/ +void ILibDestructXMLNodeList(struct ILibXMLNode *node) +{ + struct ILibXMLNode *temp; + while (node!=NULL) + { + temp = node->Next; + if (node->Reserved2!=NULL) + { + // If there was a namespace table, delete it + ILibDestroyHashTree(node->Reserved2); + } + free(node); + node = temp; + } +} + +/*! \fn ILibDestructXMLAttributeList(struct ILibXMLAttribute *attribute) +\brief Frees resources from an AttributeList that was returned from ILibGetXMLAttributes +\param attribute The Attribute Tree to clean up +*/ +void ILibDestructXMLAttributeList(struct ILibXMLAttribute *attribute) +{ + struct ILibXMLAttribute *temp; + while (attribute!=NULL) + { + temp = attribute->Next; + free(attribute); + attribute = temp; + } +} + +/*! \fn ILibProcessXMLNodeList(struct ILibXMLNode *nodeList) +\brief Pro-process an XML node list +\par +Checks XML for validity, while at the same time populate helper properties on each node, +such as Parent, Peer, etc, to aid in XML parsing. +\param nodeList The XML Tree to process +\return 0 if the XML is valid, nonzero otherwise +*/ +int ILibProcessXMLNodeList(struct ILibXMLNode *nodeList) +{ + int RetVal = 0; + struct ILibXMLNode *current = nodeList; + struct ILibXMLNode *temp; + void *TagStack; + + ILibCreateStack(&TagStack); + + // + // Iterate through the node list, and setup all the pointers + // such that all StartElements have pointers to EndElements, + // And all StartElements have pointers to siblings and parents. + // + while (current != NULL) + { + if (current->Name == NULL) { ILibClearStack(&TagStack); return -1; } + if (memcmp(current->Name, "!", 1) == 0) + { + // Comment + temp = current; + current = (struct ILibXMLNode*)ILibPeekStack(&TagStack); + if (current!=NULL) + { + current->Next = temp->Next; + } + else + { + current = temp; + } + } + else if (current->StartTag!=0) + { + // Start Tag + current->Parent = (struct ILibXMLNode*)ILibPeekStack(&TagStack); + ILibPushStack(&TagStack,current); + } + else + { + // Close Tag + + // + // Check to see if there is supposed to be an EndElement + // + temp = (struct ILibXMLNode*)ILibPopStack(&TagStack); + if (temp != NULL) + { + // + // Checking to see if this EndElement is correct in scope + // + if (temp->NameLength == current->NameLength && memcmp(temp->Name, current->Name, current->NameLength)==0) + { + // + // Now that we know this EndElement is correct, set the Peer + // pointers of the previous sibling + // + if (current->Next != NULL) + { + if (current->Next->StartTag != 0) + { + temp->Peer = current->Next; + } + } + temp->ClosingTag = current; + current->StartingTag = temp; + } + else + { + // Illegal Close Tag Order + RetVal = -2; + break; + } + } + else + { + // Illegal Close Tag + RetVal = -1; + break; + } + } + current = current->Next; + } + + // + // If there are still elements in the stack, that means not all the StartElements + // have associated EndElements, which means this XML is not valid XML. + // + if (TagStack!=NULL) + { + // Incomplete XML + RetVal = -3; + ILibClearStack(&TagStack); + } + + return(RetVal); +} + +/*! \fn ILibXML_LookupNamespace(struct ILibXMLNode *currentLocation, char *prefix, int prefixLength) +\brief Resolves a namespace prefix from the scope of the given node +\param currentLocation The node used to start the resolve +\param prefix The namespace prefix to resolve +\param prefixLength The lenght of the prefix +\return The resolved namespace. NULL if unable to resolve +*/ +char* ILibXML_LookupNamespace(struct ILibXMLNode *currentLocation, char *prefix, int prefixLength) +{ + struct ILibXMLNode *temp = currentLocation; + int done = 0; + char* RetVal = ""; + + // + // If the specified Prefix is zero length, we interpret that to mean + // they want to lookup the default namespace + // + if (prefixLength == 0) + { + // + // This is the default namespace prefix + // + prefix = "xmlns"; + prefixLength = 5; + } + + // + // From the current node, keep traversing up the parents, until we find a match. + // Each step we go up, is a step wider in scope. + // + do + { + if (temp->Reserved2 != NULL) + { + if (ILibHasEntry(temp->Reserved2, prefix, prefixLength) != 0) + { + // + // As soon as we find the namespace declaration, stop + // iterating the tree, as it would be a waste of time + // + RetVal = (char*)ILibGetEntry(temp->Reserved2, prefix, prefixLength); + done = 1; + } + } + temp = temp->Parent; + } while (temp != NULL && done == 0); + return RetVal; +} + +/*! \fn ILibXML_BuildNamespaceLookupTable(struct ILibXMLNode *node) +\brief Builds the lookup table used by ILibXML_LookupNamespace +\param node This node will be the highest scoped +*/ +void ILibXML_BuildNamespaceLookupTable(struct ILibXMLNode *node) +{ + struct ILibXMLAttribute *attr,*currentAttr; + struct ILibXMLNode *current = node; + + // + // Iterate through all the StartElements, and build a table of the declared namespaces + // + while (current != NULL) + { + if (current->StartTag != 0) + { + // + // Reserved2 is the HashTable containing the fully qualified namespace + // keyed by the namespace prefix + // + current->Reserved2 = ILibInitHashTree(); + currentAttr = attr = ILibGetXMLAttributes(current); + if (attr != NULL) + { + // + // Iterate through all the attributes to find namespace declarations + // + while (currentAttr != NULL) + { + if (currentAttr->NameLength == 5 && currentAttr->Name != NULL && memcmp(currentAttr->Name, "xmlns", 5) == 0) + { + // Default Namespace Declaration + currentAttr->Value[currentAttr->ValueLength] = 0; + ILibAddEntry(current->Reserved2, "xmlns", 5, currentAttr->Value); + } + else if (currentAttr->PrefixLength == 5 && currentAttr->Prefix != NULL && memcmp(currentAttr->Prefix, "xmlns", 5) == 0) + { + // Other Namespace Declaration + currentAttr->Value[currentAttr->ValueLength] = 0; + ILibAddEntry(current->Reserved2, currentAttr->Name, currentAttr->NameLength, currentAttr->Value); + } + currentAttr=currentAttr->Next; + } + ILibDestructXMLAttributeList(attr); + } + } + current = current->Next; + } +} + + +/*! \fn ILibReadInnerXML(struct ILibXMLNode *node, char **RetVal) +\brief Reads the data segment from an ILibXMLNode +\par +The data is a pointer into the original string that the XML was read from. +\param node The node to read the data from +\param[out] RetVal The data +\return The length of the data read +*/ +int ILibReadInnerXML(struct ILibXMLNode *node, char **RetVal) +{ + struct ILibXMLNode *x = node; + int length = 0; + void *TagStack; + *RetVal = NULL; + + // + // Starting with the current StartElement, we use this stack to find the matching + // EndElement, so we can figure out what we need to return + // + ILibCreateStack(&TagStack); + do + { + if (x == NULL) + { + ILibClearStack(&TagStack); + return(0); + } + if (x->StartTag != 0) { ILibPushStack(&TagStack, x); } + + x = x->Next; + + if (x==NULL) + { + ILibClearStack(&TagStack); + return(0); + } + }while (!(x->StartTag==0 && ILibPopStack(&TagStack)==node && x->NameLength==node->NameLength && memcmp(x->Name,node->Name,node->NameLength)==0)); + + // + // The Reserved fields of the StartElement and EndElement are used as pointers representing + // the data segment of the XML + // + length = (int)((char*)x->Reserved - (char*)node->Reserved - 1); + if (length<0) {length=0;} + *RetVal = (char*)node->Reserved; + return(length); +} + +/*! \fn ILibGetXMLAttributes(struct ILibXMLNode *node) +\brief Reads the attributes from an XML node +\param node The node to read the attributes from +\return A linked list of attributes +*/ +struct ILibXMLAttribute *ILibGetXMLAttributes(struct ILibXMLNode *node) +{ + struct ILibXMLAttribute *RetVal = NULL; + struct ILibXMLAttribute *current = NULL; + char *c; + int EndReserved = (node->EmptyTag==0)?1:2; + int i; + int CheckName = node->Name[node->NameLength]==0?1:0; + + struct parser_result *xml; + struct parser_result_field *field,*field2; + struct parser_result *temp2; + struct parser_result *temp3; + + + // + // The reserved field is used to show where the data segments start and stop. We + // can also use them to figure out where the attributes start and stop + // + c = (char*)node->Reserved - 1; + while (*c!='<') + { + // + // The Reserved field of the StartElement points to the first character after + // the '>' of the StartElement. Just work our way backwards to find the start of + // the StartElement + // + c = c -1; + } + c = c +1; + + // + // Now that we isolated the string in between the '<' and the '>' we can parse the + // string as delimited by ' ', because thats what delineates attributes. We need + // to use ILibParseStringAdv because these attributes can be within quotation marks + // + // + // But before we start, replace linefeeds and carriage-return-linefeeds to spaces + // + for(i=0;i<(int)((char*)node->Reserved - c -EndReserved);++i) + { + if (c[i]==10 || c[i]==13 || c[i]==9 || c[i]==0) + { + c[i]=' '; + } + } + xml = ILibParseStringAdv(c,0,(int)((char*)node->Reserved - c -EndReserved)," ",1); + field = xml->FirstResult; + // + // We skip the first token, because the first token, is the Element name + // + if (field != NULL) {field = field->NextResult;} + + // + // Iterate through all the other tokens, as these are all attributes + // + while (field != NULL) + { + if (field->datalength > 0) + { + if (RetVal == NULL) + { + // + // If we haven't already created an Attribute node, create it now + // + if ((RetVal = (struct ILibXMLAttribute*)malloc(sizeof(struct ILibXMLAttribute))) == NULL) ILIBCRITICALEXIT(254); + RetVal->Next = NULL; + } + else + { + // + // We already created an Attribute node, so simply create a new one, and + // attach it on the beginning of the old one. + // + if ((current = (struct ILibXMLAttribute*)malloc(sizeof(struct ILibXMLAttribute))) == NULL) ILIBCRITICALEXIT(254); + current->Next = RetVal; + RetVal = current; + } + + // + // Parse each token by the ':' + // If this results in more than one token, we can figure that the first token + // is the namespace prefix + // + temp2 = ILibParseStringAdv(field->data,0,field->datalength,":",1); + if (temp2->NumResults == 1) + { + // + // This attribute has no prefix, so just parse on the '=' + // The first token is the attribute name, the other is the value + // + RetVal->Prefix = NULL; + RetVal->PrefixLength = 0; + temp3 = ILibParseStringAdv(field->data,0,field->datalength,"=",1); + if (temp3->NumResults == 1) + { + // + // There were whitespaces around the '=' + // + field2 = field->NextResult; + while (field2!=NULL) + { + if (!(field2->datalength==1 && memcmp(field2->data,"=",1)==0) && field2->datalength>0) + { + ILibDestructParserResults(temp3); + temp3 = ILibParseStringAdv(field->data,0,(int)((field2->data-field->data)+field2->datalength),"=",1); + field = field2; + break; + } + field2 = field2->NextResult; + } + } + } + else + { + // + // Since there is a namespace prefix, seperate that out, and parse the remainder + // on the '=' to figure out what the attribute name and value are + // + RetVal->Prefix = temp2->FirstResult->data; + RetVal->PrefixLength = temp2->FirstResult->datalength; + temp3 = ILibParseStringAdv(field->data,RetVal->PrefixLength+1,field->datalength-RetVal->PrefixLength-1,"=",1); + if (temp3->NumResults==1) + { + // + // There were whitespaces around the '=' + // + field2 = field->NextResult; + while (field2!=NULL) + { + if (!(field2->datalength==1 && memcmp(field2->data,"=",1)==0) && field2->datalength>0) + { + ILibDestructParserResults(temp3); + temp3 = ILibParseStringAdv(field->data,RetVal->PrefixLength+1,(int)((field2->data-field->data)+field2->datalength-RetVal->PrefixLength-1),"=",1); + field = field2; + break; + } + field2 = field2->NextResult; + } + } + } + // + // After attaching the pointers, we can free the results, as all the data + // is a pointer into the original string. We can think of the nodes we created + // as templates. Once done, we don't need them anymore. + // + ILibDestructParserResults(temp2); + RetVal->Parent = node; + RetVal->Name = temp3->FirstResult->data; + RetVal->Value = temp3->LastResult->data; + RetVal->NameLength = ILibTrimString(&(RetVal->Name),temp3->FirstResult->datalength); + RetVal->ValueLength = ILibTrimString(&(RetVal->Value),temp3->LastResult->datalength); + // + // Remove the Quote or Apostraphe if it exists + // + if (RetVal->ValueLength>=2 && (RetVal->Value[0]=='"' || RetVal->Value[0]=='\'')) + { + RetVal->Value += 1; + RetVal->ValueLength -= 2; + } + ILibDestructParserResults(temp3); + } + field = field->NextResult; + } + + ILibDestructParserResults(xml); + if (CheckName) + { + node->Name[node->NameLength]=0; + } + return(RetVal); +} + +/*! \fn ILibParseXML(char *buffer, int offset, int length) +\brief Parses an XML string. +\par +The strings are never copied. Everything is referenced via pointers into the original buffer +\param buffer The string to parse +\param offset starting index of \a buffer +\param length Length of \a buffer +\return A tree of ILibXMLNodes, representing the XML document +*/ +struct ILibXMLNode *ILibParseXML(char *buffer, int offset, int length) +{ + struct parser_result *xml; + struct parser_result_field *field; + struct parser_result *temp2; + struct parser_result *temp3; + char* TagName; + int TagNameLength; + int StartTag; + int EmptyTag; + int i; + int wsi; + + struct ILibXMLNode *RetVal = NULL; + struct ILibXMLNode *current = NULL; + struct ILibXMLNode *x = NULL; + + char *NSTag; + int NSTagLength; + + char *CommentEnd = 0; + int CommentIndex; + + // + // Even though "technically" the first character of an XML document must be < + // we're going to be nice, and not enforce that + // + while (buffer[offset]!='<' && length>0) + { + ++offset; + --length; + } + + if (length==0) + { + // Garbage in Garbage out :) + if ((RetVal = (struct ILibXMLNode*)malloc(sizeof(struct ILibXMLNode))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct ILibXMLNode)); + return(RetVal); + } + + // + // All XML Elements start with a '<' character. If we delineate the string with + // this character, we can go from there. + // + xml = ILibParseString(buffer,offset,length,"<",1); + field = xml->FirstResult; + while (field!=NULL) + { + // + // Ignore the XML declarator + // + if (field->datalength !=0 && memcmp(field->data,"?",1)!=0 && (field->data > CommentEnd)) + { + if (field->datalength>3 && memcmp(field->data,"!--",3)==0) + { + // + // XML Comment, find where it ends + // + CommentIndex = 3; + while (field->data+CommentIndex < buffer+offset+length && memcmp(field->data+CommentIndex,"-->",3)!=0) + { + ++CommentIndex; + } + CommentEnd = field->data + CommentIndex; + field = field->NextResult; + continue; + } + else + { + EmptyTag = 0; + if (memcmp(field->data,"/",1)==0) + { + // + // The first character after the '<' was a '/', so we know this is the + // EndElement + // + StartTag = 0; + field->data = field->data+1; + field->datalength -= 1; + // + // If we look for the '>' we can find the end of this element + // + temp2 = ILibParseString(field->data,0,field->datalength,">",1); + } + else + { + // + // The first character after the '<' was not a '/' so we know this is a + // StartElement + // + StartTag = -1; + // + // If we look for the '>' we can find the end of this element + // + temp2 = ILibParseString(field->data,0,field->datalength,">",1); + if (temp2->FirstResult->datalength>0 && temp2->FirstResult->data[temp2->FirstResult->datalength-1]=='/') + { + // + // If this element ended with a '/' this is an EmptyElement + // + EmptyTag = -1; + } + } + } + // + // Parsing on the ' ', we can isolate the Element name from the attributes. + // The first token, being the element name + // + wsi = ILibString_IndexOfFirstWhiteSpace(temp2->FirstResult->data,temp2->FirstResult->datalength); + // + // Now that we have the token that contains the element name, we need to parse on the ":" + // because we need to figure out what the namespace qualifiers are + // + temp3 = ILibParseString(temp2->FirstResult->data,0,wsi!=-1?wsi:temp2->FirstResult->datalength,":",1); + if (temp3->NumResults==1) + { + // + // If there is only one token, there was no namespace prefix. + // The whole token is the attribute name + // + NSTag = NULL; + NSTagLength = 0; + TagName = temp3->FirstResult->data; + TagNameLength = temp3->FirstResult->datalength; + } + else + { + // + // The first token is the namespace prefix, the second is the attribute name + // + NSTag = temp3->FirstResult->data; + NSTagLength = temp3->FirstResult->datalength; + TagName = temp3->FirstResult->NextResult->data; // Note: Klocwork reports that this may be NULL, but that is not possible, becuase NumResults is > 1 in this block + TagNameLength = temp3->FirstResult->NextResult->datalength; + } + ILibDestructParserResults(temp3); + + // + // Iterate through the tag name, to figure out what the exact length is, as + // well as check to see if its an empty element + // + for(i=0;i')||(TagName[i]=='\t')||(TagName[i]=='\r')||(TagName[i]=='\n') ) + { + if (i!=0) + { + if (TagName[i]=='/') + { + EmptyTag = -1; + } + TagNameLength = i; + break; + } + } + } + + if (TagNameLength!=0) + { + // + // Instantiate a new ILibXMLNode for this element + // + if ((x = (struct ILibXMLNode*)malloc(sizeof(struct ILibXMLNode))) == NULL) ILIBCRITICALEXIT(254); + memset(x,0,sizeof(struct ILibXMLNode)); + x->Name = TagName; + x->NameLength = TagNameLength; + x->StartTag = StartTag; + x->NSTag = NSTag; + x->NSLength = NSTagLength; + + if (StartTag==0) + { + // + // The Reserved field of StartElements point to te first character before + // the '<'. + // + x->Reserved = field->data; + do + { + x->Reserved = ((char*)x->Reserved) - 1; + }while (*((char*)x->Reserved)=='<'); + } + else + { + // + // The Reserved field of EndElements point to the end of the element + // + x->Reserved = temp2->LastResult->data; + } + + if (RetVal==NULL) + { + RetVal = x; + } + else + { + current->Next = x; + } + current = x; + if (EmptyTag!=0) + { + // + // If this was an empty element, we need to create a bogus EndElement, + // just so the tree is consistent. No point in introducing unnecessary complexity + // + if ((x = (struct ILibXMLNode*)malloc(sizeof(struct ILibXMLNode))) == NULL) ILIBCRITICALEXIT(254); + memset(x,0,sizeof(struct ILibXMLNode)); + x->Name = TagName; + x->NameLength = TagNameLength; + x->NSTag = NSTag; + x->NSLength = NSTagLength; + + x->Reserved = current->Reserved; + current->EmptyTag = -1; + current->Next = x; + current = x; + } + } + + ILibDestructParserResults(temp2); + } + field = field->NextResult; + } + + ILibDestructParserResults(xml); + return(RetVal); +} + +/*! \fn ILibQueue_Create() +\brief Create an empty Queue +\return An empty queue +*/ +ILibQueue ILibQueue_Create() +{ + return((ILibQueue)ILibLinkedList_Create()); +} + +/*! \fn ILibQueue_Lock(void *q) +\brief Locks a queue +\param q The queue to lock +*/ +void ILibQueue_Lock(ILibQueue q) +{ + ILibLinkedList_Lock((ILibLinkedList)q); +} + +/*! \fn ILibQueue_UnLock(void *q) +\brief Unlocks a queue +\param q The queue to unlock +*/ +void ILibQueue_UnLock(ILibQueue q) +{ + ILibLinkedList_UnLock((ILibLinkedList)q); +} + +/*! \fn ILibQueue_Destroy(void *q) +\brief Frees the resources associated with a queue +\param q The queue to free +*/ +void ILibQueue_Destroy(ILibQueue q) +{ + ILibLinkedList_Destroy((ILibLinkedList)q); +} + +/*! \fn ILibQueue_IsEmpty(void *q) +\brief Checks to see if a queue is empty +\param q The queue to check +\return zero value if not empty, non-zero if empty +*/ +int ILibQueue_IsEmpty(ILibQueue q) +{ + return(ILibLinkedList_GetNode_Head((ILibLinkedList)q)==NULL?1:0); +} + +/*! \fn ILibQueue_EnQueue(void *q, void *data) +\brief Add an item to the queue +\param q The queue to add to +\param data The data to add to the queue +*/ +void ILibQueue_EnQueue(ILibQueue q, void *data) +{ + ILibLinkedList_AddTail((ILibLinkedList)q,data); +} +/*! \fn ILibQueue_DeQueue(void *q) +\brief Removes an item from the queue +\param q The queue to pop the item from +\return The item popped off the queue. NULL if empty +*/ +void *ILibQueue_DeQueue(ILibQueue q) +{ + void *RetVal = NULL; + void *node; + + node = ILibLinkedList_GetNode_Head((ILibLinkedList)q); + if (node!=NULL) + { + RetVal = ILibLinkedList_GetDataFromNode(node); + ILibLinkedList_Remove(node); + } + return(RetVal); +} + +/*! \fn ILibQueue_PeekQueue(void *q) +\brief Peeks at an item from the queue +\param q The queue to peek an item from +\return The item from the queue. NULL if empty +*/ +void *ILibQueue_PeekQueue(ILibQueue q) +{ + void *RetVal = NULL; + void *node; + + node = ILibLinkedList_GetNode_Head((ILibLinkedList)q); + if (node!=NULL) + { + RetVal = ILibLinkedList_GetDataFromNode(node); + } + return(RetVal); +} + +/*! \fn ILibQueue_GetCount(void *q) +\brief Returns the number of items in the queue. +\param q The queue to query +\return The item count in the queue. +*/ +long ILibQueue_GetCount(ILibQueue q) +{ + return ILibLinkedList_GetCount((ILibLinkedList)q); +} + +/*! \fn ILibCreateStack(void **TheStack) +\brief Creates an empty Stack +\par +This module uses a void* that is preinitialized to NULL, eg:
+ +void *stack = NULL;
+ILibCreateStack(&stack);
+
+\param TheStack A void* to use for the stack. Simply pass in a void* by reference +*/ +void ILibCreateStack(void **TheStack) +{ + *TheStack = NULL; +} + +/*! \fn ILibPushStack(void **TheStack, void *data) +\brief Push an item onto the stack +\param TheStack The stack to push to +\param data The data to push onto the stack +*/ +void ILibPushStack(void **TheStack, void *data) +{ + struct ILibStackNode *RetVal; + if ((RetVal = (struct ILibStackNode*)malloc(sizeof(struct ILibStackNode))) == NULL) ILIBCRITICALEXIT(254); + RetVal->Data = data; + RetVal->Next = *TheStack; + *TheStack = RetVal; +} + +/*! \fn ILibPopStack(void **TheStack) +\brief Pop an item from the stack +\param TheStack The stack to pop from +\return The item that was popped from the stack +*/ +void *ILibPopStack(void **TheStack) +{ + void *RetVal = NULL; + void *Temp; + if (*TheStack!=NULL) + { + RetVal = ((struct ILibStackNode*)*TheStack)->Data; + Temp = *TheStack; + *TheStack = ((struct ILibStackNode*)*TheStack)->Next; + free(Temp); + } + return(RetVal); +} + +/*! \fn ILibPeekStack(void **TheStack) +\brief Peek at an item from the stack +\param TheStack The stack to peek from +\return The item that is currently on the top of the stack +*/ +void *ILibPeekStack(void **TheStack) +{ + void *RetVal = NULL; + if (*TheStack!=NULL) + { + RetVal = ((struct ILibStackNode*)*TheStack)->Data; + } + return(RetVal); +} + +/*! \fn ILibClearStack(void **TheStack) +\brief Clears all the items from the stack +\param TheStack The stack to clear +*/ +void ILibClearStack(void **TheStack) +{ + void *Temp = *TheStack; + do + { + ILibPopStack(&Temp); + }while (Temp!=NULL); + *TheStack = NULL; +} + +/*! \fn ILibHashTree_Lock(void *hashtree) +\brief Locks a HashTree +\param hashtree The HashTree to lock +*/ +void ILibHashTree_Lock(void *hashtree) +{ + struct HashNode_Root *r = (struct HashNode_Root*)hashtree; + sem_wait(&(r->LOCK)); +} + +/*! \fn ILibHashTree_UnLock(void *hashtree) +\brief Unlocks a HashTree +\param hashtree The HashTree to unlock +*/ +void ILibHashTree_UnLock(void *hashtree) +{ + struct HashNode_Root *r = (struct HashNode_Root*)hashtree; + sem_post(&(r->LOCK)); +} + +/*! \fn ILibDestroyHashTree(void *tree) +\brief Frees resources associated with a HashTree +\param tree The HashTree to free +*/ +void ILibDestroyHashTree(void *tree) +{ + struct HashNode_Root *r = (struct HashNode_Root*)tree; + struct HashNode *c = r->Root; + struct HashNode *n; + + sem_destroy(&(r->LOCK)); + while (c != NULL) + { + // + // Iterate through each node, and free all the resources + // + n = c->Next; + if (c->KeyValue!=NULL) free(c->KeyValue); + free(c); + c = n; + } + free(r); +} + +/*! \fn ILibHashTree_GetEnumerator(void *tree) +\brief Returns an Enumerator for a HashTree +\par +Functionally identicle to an IDictionaryEnumerator in .NET +\param tree The HashTree to get an enumerator for +\return An enumerator +*/ +void *ILibHashTree_GetEnumerator(void *tree) +{ + // + // The enumerator is basically a state machine that keeps track of which node we are at + // in the tree. So initialize it to the root. + // + struct HashNodeEnumerator *en; + if ((en = (struct HashNodeEnumerator*)malloc(sizeof(struct HashNodeEnumerator))) == NULL) ILIBCRITICALEXIT(254); + en->node = ((struct HashNode_Root*)tree)->Root; + return en; +} + +/*! \fn ILibHashTree_DestroyEnumerator(void *tree_enumerator) +\brief Frees resources associated with an Enumerator created by ILibHashTree_GetEnumerator +\param tree_enumerator The enumerator to free +*/ +void ILibHashTree_DestroyEnumerator(void *tree_enumerator) +{ + // + // The enumerator just contains a pointer, so we can just free the enumerator + // + free(tree_enumerator); +} + +/*! \fn ILibHashTree_MoveNext(void *tree_enumerator) +\brief Advances an enumerator to the next item +\param tree_enumerator The enumerator to advance +\return A zero value if successful, nonzero if no more items +*/ +int ILibHashTree_MoveNext(void *tree_enumerator) +{ + struct HashNodeEnumerator *en = (struct HashNodeEnumerator*)tree_enumerator; + if (en->node != NULL) + { + // + // Advance the enumerator to point to the next node. If there is a node + // return 0, else return 1 + // + en->node = en->node->Next; + return(en->node != NULL?0:1); + } + else + { + // + // There are no more nodes, so just return 1 + // + return 1; + } +} + +/*! \fn ILibHashTree_GetValue(void *tree_enumerator, char **key, int *keyLength, void **data) +\brief Reads from the current item of an enumerator +\param tree_enumerator The enumerator to read from +\param[out] key The key of the current item +\param[out] keyLength The length of the key of the current item +\param[out] data The data of the current item +*/ +void ILibHashTree_GetValue(void *tree_enumerator, char **key, int *keyLength, void **data) +{ + struct HashNodeEnumerator *en = (struct HashNodeEnumerator*)tree_enumerator; + + // + // All we do, is just assign the pointers. + // + if (key != NULL) {*key = en->node->KeyValue;} + if (keyLength != NULL) {*keyLength = en->node->KeyLength;} + if (data != NULL) {*data = en->node->Data;} +} +/*! \fn void ILibHashTree_GetValueEx(void *tree_enumerator, char **key, int *keyLength, void **data, int *dataEx) +\brief Reads from the current item of an enumerator +\param tree_enumerator The enumerator to read from +\param[out] key The key of the current item +\param[out] keyLength The length of the key of the current item +\param[out] data The data of the current item +\param[out] dataEx The extended data of the current item +*/ +void ILibHashTree_GetValueEx(void *tree_enumerator, char **key, int *keyLength, void **data, int *dataEx) +{ + struct HashNodeEnumerator *en = (struct HashNodeEnumerator*)tree_enumerator; + + // + // All we do, is just assign the pointers. + // + if (key != NULL) {*key = en->node->KeyValue;} + if (keyLength != NULL) {*keyLength = en->node->KeyLength;} + if (data != NULL) {*data = en->node->Data;} + if (dataEx != NULL) {*dataEx = en->node->DataEx;} +} + +/*! \fn ILibInitHashTree() +\brief Creates an empty ILibHashTree, whose keys are case sensitive. +\return An empty ILibHashTree +*/ +void* ILibInitHashTree() +{ + struct HashNode_Root *Root; + struct HashNode *RetVal; + if ((Root = (struct HashNode_Root*)malloc(sizeof(struct HashNode_Root))) == NULL) ILIBCRITICALEXIT(254); + if ((RetVal = (struct HashNode*)malloc(sizeof(struct HashNode))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal, 0, sizeof(struct HashNode)); + memset(Root, 0, sizeof(struct HashNode_Root)); + Root->Root = RetVal; + sem_init(&(Root->LOCK), 0, 1); + return(Root); +} +/*! \fn void* ILibInitHashTree_CaseInSensitive() +\brief Creates an empty ILibHashTree, whose keys are case insensitive. +\return An empty ILibHashTree +*/ +void* ILibInitHashTree_CaseInSensitive() +{ + struct HashNode_Root *Root = (struct HashNode_Root*)ILibInitHashTree(); + Root->CaseInSensitive = 1; + return(Root); +} + + +void ILibToUpper(const char *in, int inLength, char *out) +{ + int i; + + for(i = 0; i < inLength; ++i) + { + if (in[i]>=97 && in[i]<=122) + { + //LOWER + out[i] = in[i]-32; + } + else + { + //CAP + out[i] = in[i]; + } + } +} +void ILibToLower(const char *in, int inLength, char *out) +{ + int i; + + for(i = 0; i < inLength; ++i) + { + if (in[i] >= 65 && in[i] <= 90) + { + //CAP + out[i] = in[i]+32; + } + else + { + //LOWER + out[i] = in[i]; + } + } +} + +int ILibGetHashValueEx(char *key, int keylength, int caseInSensitiveText) +{ + int HashValue=0; + char TempValue[4]; + + if (keylength <= 4) + { + // + // If the key length is <= 4, the hash is just the key expressed as an integer + // + memset(TempValue, 0, 4); + if (caseInSensitiveText==0) + { + memcpy_s(TempValue, sizeof(TempValue), key, keylength); + } + else + { + ILibToLower(key, keylength, TempValue); + } + MEMCHECK(assert(keylength <= 4);) + + HashValue = *((int*)TempValue); + } + else + { + // + // If the key length is >4, the hash is the first 4 bytes XOR with the last 4 + // + + if (caseInSensitiveText == 0) + { + memcpy_s(TempValue, sizeof(TempValue), key, 4); + } + else + { + ILibToLower(key, 4, TempValue); + } + HashValue = *((int*)TempValue); + if (caseInSensitiveText==0) + { + memcpy_s(TempValue, sizeof(TempValue), (char*)key+(keylength-4),4); + } + else + { + ILibToLower((char*)key+(keylength-4),4,TempValue); + } + HashValue = HashValue^(*((int*)TempValue)); + + + // + // If the key length is >= 10, the hash is also XOR with the middle 4 bytes + // + if (keylength>=10) + { + if (caseInSensitiveText==0) + { + memcpy_s(TempValue, sizeof(TempValue), (char*)key+(keylength/2),4); + } + else + { + ILibToLower((char*)key+(keylength/2),4,TempValue); + } + HashValue = HashValue^(*((int*)TempValue)); + } + } + return(HashValue); +} + +/*! \fn ILibGetHashValue(char *key, int keylength) +\brief Calculates a numeric Hash from a given string +\par +Used by ILibHashTree methods +\param key The string to hash +\param keylength The length of the string to hash +\return A hash value +*/ +int ILibGetHashValue(char *key, int keylength) +{ + return(ILibGetHashValueEx(key,keylength,0)); +} + + + +// +// Determines if a key entry exists in a HashTree, and creates it if requested +// +struct HashNode* ILibFindEntry(void *hashtree, void *key, int keylength, int create) +{ + struct HashNode_Root *root = (struct HashNode_Root*)hashtree; + struct HashNode *current = root->Root; + int HashValue = ILibGetHashValueEx(key, keylength, root->CaseInSensitive); + int done = 0; + + if (keylength == 0){return(NULL);} + + // + // Iterate through our tree to see if we can find this key entry + // + while (done == 0) + { + // + // Integer compares are very fast, this will weed out most non-matches + // + if (current->KeyHash == HashValue) + { + // + // Verify this is really a match + // + if (root->CaseInSensitive == 0) + { + if (current->KeyLength == keylength && memcmp(current->KeyValue, key, keylength) == 0) + { + return current; + } + } + else + { + if (current->KeyLength == keylength && strncasecmp(current->KeyValue, key, keylength) == 0) + { + return current; + } + } + } + + if (current->Next != NULL) + { + current = current->Next; + } + else if (create != 0) + { + // + // If there is no match, and the create flag is set, we need to create an entry + // + if ((current->Next = (struct HashNode*)malloc(sizeof(struct HashNode))) == NULL) ILIBCRITICALEXIT(254); + memset(current->Next,0,sizeof(struct HashNode)); + current->Next->Prev = current; + current->Next->KeyHash = HashValue; + if ((current->Next->KeyValue = (void*)malloc(keylength + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(current->Next->KeyValue, keylength + 1, key ,keylength); + current->Next->KeyValue[keylength] = 0; + current->Next->KeyLength = keylength; + return(current->Next); + } + else + { + return NULL; + } + } + return NULL; +} + +/*! \fn ILibHasEntry(void *hashtree, char* key, int keylength) +\brief Determines if a key entry exists in a HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\return 0 if does not exist, nonzero otherwise +*/ +int ILibHasEntry(void *hashtree, char* key, int keylength) +{ + // + // This can be duplicated by calling Find entry, but setting the create flag to false + // + return(ILibFindEntry(hashtree, key, keylength, 0) != NULL?1:0); +} + +/*! \fn ILibAddEntry(void* hashtree, char* key, int keylength, void *value) +\brief Adds an item to the HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\param value The data to add into the HashTree +*/ +void ILibAddEntry(void* hashtree, char* key, int keylength, void *value) +{ + // + // This can be duplicated by calling FindEntry, and setting create to true + // + struct HashNode* n = ILibFindEntry(hashtree, key, keylength, 1); + if (n != NULL) n->Data = value; +} +/*! \fn ILibAddEntryEx(void* hashtree, char* key, int keylength, void *value, int valueLength) +\brief Adds an extended item to the HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\param value The data to add into the HashTree +\param valueEx An optional int value +*/ +void ILibAddEntryEx(void* hashtree, char* key, int keylength, void *value, int valueEx) +{ + // + // This can be duplicated by calling FindEntry, and setting create to true + // + struct HashNode* n = ILibFindEntry(hashtree, key, keylength, 1); + if (n != NULL) + { + n->Data = value; + n->DataEx = valueEx; + } +} + + +/*! \fn ILibGetEntry(void *hashtree, char* key, int keylength) +\brief Gets an item from a HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\return The data in the HashTree. NULL if key does not exist +*/ +void* ILibGetEntry(void *hashtree, char* key, int keylength) +{ + // + // This can be duplicated by calling FindEntry and setting create to false. + // If a match is found, just return the data + // + struct HashNode* n = ILibFindEntry(hashtree, key, keylength, 0); + if (n == NULL) + { + return(NULL); + } + else + { + return(n->Data); + } +} +/*! \fn void ILibGetEntryEx(void *hashtree, char *key, int keyLength, void **value, int* valueEx) +\brief Gets an extended item from a HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keyLength The length of the key +\param[out] value The data in the HashTree. NULL if key does not exist +\param[out] valueEx The extended data in the HashTree. +*/ +ILibExportMethod void ILibGetEntryEx(void *hashtree, char *key, int keyLength, void **value, int* valueEx) +{ + // + // This can be duplicated by calling FindEntry and setting create to false. + // If a match is found, just return the data + // + struct HashNode* n = ILibFindEntry(hashtree, key, keyLength, 0); + if (n == NULL) + { + *value = NULL; + *valueEx = 0; + } + else + { + *value = n->Data; + *valueEx = n->DataEx; + } +} + +/*! \fn ILibDeleteEntry(void *hashtree, char* key, int keylength) +\brief Deletes a keyed item from the HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +*/ +void ILibDeleteEntry(void *hashtree, char* key, int keylength) +{ + // + // First find the entry + // + struct HashNode* n = ILibFindEntry(hashtree,key,keylength,0); + if (n != NULL) + { + // + // Then remove it from the tree + // + n->Prev->Next = n->Next; + if (n->Next != NULL) n->Next->Prev = n->Prev; + free(n->KeyValue); + free(n); + } +} + +/*! \fn ILibGetLong(char *TestValue, int TestValueLength, long* NumericValue) +\brief Reads a long value from a string, in a validating fashion +\param TestValue The string to read from +\param TestValueLength The length of the string +\param NumericValue The long value extracted from the string +\return 0 if succesful, nonzero if there was an error +*/ +int ILibGetLong(char *TestValue, int TestValueLength, long* NumericValue) +{ + char* StopString; + char* TempBuffer; + + if ((TempBuffer = (char*)malloc(1+sizeof(char)*TestValueLength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(TempBuffer, 1 + sizeof(char)*TestValueLength, TestValue, TestValueLength); + TempBuffer[TestValueLength] = '\0'; + *NumericValue = strtol(TempBuffer,&StopString,10); + if (*StopString != '\0') + { + // + // If strtol stopped somewhere other than the end, there was an error + // + free(TempBuffer); + return(-1); + } + else + { + // + // Now just check errno to see if there was an error reported + // + free(TempBuffer); + if (errno!=ERANGE) return(0); else return(-1); + } +} + +/*! \fn int ILibGetULong(const char *TestValue, const int TestValueLength, unsigned long* NumericValue) +\brief Reads an unsigned long value from a string, in a validating fashion +\param TestValue The string to read from +\param TestValueLength The length of the string +\param NumericValue The long value extracted from the string +\return 0 if succesful, nonzero if there was an error +*/ +int ILibGetULong(const char *TestValue, const int TestValueLength, unsigned long* NumericValue) +{ + char* StopString; + char* TempBuffer; + + if ((TempBuffer = (char*)malloc(1 + sizeof(char)*TestValueLength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(TempBuffer, 1 + sizeof(char)*TestValueLength, (char*)TestValue, TestValueLength); + TempBuffer[TestValueLength] = '\0'; + *NumericValue = strtoul(TempBuffer, &StopString, 10); + + if (*StopString!='\0') + { + free(TempBuffer); + return(-1); + } + else + { + free(TempBuffer); + + if (errno!=ERANGE) + { + if (memcmp(TestValue,"-",1) == 0) return(-1); + return(0); + } + else + { + return(-1); + } + } +} + + +// +// Determines if a buffer offset is a delimiter +// +int ILibIsDelimiter (const char* buffer, int offset, int buffersize, const char* Delimiter, int DelimiterLength) +{ + // + // For simplicity sake, we'll assume a match unless proven otherwise + // + int i=0; + int RetVal = 1; + if (DelimiterLength>buffersize) + { + // + // If the offset plus delimiter length is greater than the buffersize + // There can't possible be a match, so don't bother looking + // + return(0); + } + + for(i=0;iFirstResult = NULL; + RetVal->NumResults = 0; + + // + // By default we will always return at least one token, which will be the + // entire string if the delimiter is not found. + // + // Iterate through the string to find delimiters + // + Token = buffer + offset; + for(i = offset;i < (length+offset);++i) + { + if (StringDelimiter == 0) + { + if (buffer[i] == '"') + { + // + // Ignore everything inside double quotes + // + StringDelimiter = '"'; + Ignore = 1; + } + else + { + if (buffer[i] == '\'') + { + // + // Ignore everything inside single quotes + // + StringDelimiter = '\''; + Ignore = 1; + } + } + } + else + { + // + // Once we isolated everything inside double or single quotes, we can get + // on with the real parsing + // + if (buffer[i] == StringDelimiter) + { + Ignore = ((Ignore == 0)?1:0); + } + } + if (Ignore == 0 && ILibIsDelimiter(buffer, i, length, Delimiter, DelimiterLength)) + { + // + // We found a delimiter in the string + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(253); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + + // + // After we populate the values, we advance the token to after the delimiter + // to prep for the next token + // + ++RetVal->NumResults; + i = i + DelimiterLength -1; + Token = Token + TokenLength + DelimiterLength; + TokenLength = 0; + } + else + { + // + // No match yet, so just increment this counter + // + ++TokenLength; + } + } + + // + // Create a result for the last token, since it won't be caught in the above loop + // because if there are no more delimiters, than the entire last portion of the string since the + // last delimiter is the token + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(254); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + ++RetVal->NumResults; + + return(RetVal); +} + +/*! \fn ILibTrimString(char **theString, int length) +\brief Trims leading and trailing whitespace characters +\param theString The string to trim +\param length Length of \a theString +\return Length of the trimmed string +*/ +int ILibTrimString(char **theString, int length) +{ + while (length > 0 && ((*theString)[0] == 9 || (*theString)[0] == 32)) { length--; (*theString)++; } // Remove any blank chars at the start + while (length > 0 && ((*theString)[length - 1] == 9 || (*theString)[length - 1] == 32)) { length--; } // Remove any blank chars at the end + return length; +} + +/*! \fn ILibParseString(char* buffer, int offset, int length, char* Delimiter, int DelimiterLength) +\brief Parses a string into a linked list of tokens. +\par +Differs from \a ILibParseStringAdv, in that this method does not ignore characters contained within +quotation marks, whereas \a ILibParseStringAdv does. +\param buffer The buffer to parse +\param offset The offset of the buffer to start parsing +\param length The length of the buffer to parse +\param Delimiter The delimiter +\param DelimiterLength The length of the delimiter +\return A list of tokens +*/ +struct parser_result* ILibParseString(char* buffer, int offset, int length, const char* Delimiter, int DelimiterLength) +{ + int i = 0; + char* Token = NULL; + int TokenLength = 0; + struct parser_result* RetVal; + struct parser_result_field *p_resultfield; + + if ((RetVal = (struct parser_result*)malloc(sizeof(struct parser_result))) == NULL) ILIBCRITICALEXIT(254); + RetVal->FirstResult = NULL; + RetVal->NumResults = 0; + + // + // By default we will always return at least one token, which will be the + // entire string if the delimiter is not found. + // + // Iterate through the string to find delimiters + // + Token = buffer + offset; + for(i = offset; i < length; ++i) + { + if (ILibIsDelimiter(buffer, i, length, Delimiter, DelimiterLength)) + { + // + // We found a delimiter in the string + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(254); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + + // + // After we populate the values, we advance the token to after the delimiter + // to prep for the next token + // + ++RetVal->NumResults; + i = i + DelimiterLength -1; + Token = Token + TokenLength + DelimiterLength; + TokenLength = 0; + } + else + { + // + // No match yet, so just increment this counter + // + ++TokenLength; + } + } + + // + // Create a result for the last token, since it won't be caught in the above loop + // because if there are no more delimiters, than the entire last portion of the string since the + // last delimiter is the token + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(254); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + ++RetVal->NumResults; + + return(RetVal); +} + +parser_result_field* ILibParseString_GetResultIndex(parser_result* r, int index) +{ + int i; + parser_result_field *retVal = r->FirstResult; + + for (i = 0; i < index - 1; ++i) + { + retVal = retVal != NULL ? retVal->NextResult : NULL; + } + return(retVal); +} +/*! \fn ILibDestructParserResults(struct parser_result *result) +\brief Frees resources associated with the list of tokens returned from ILibParseString and ILibParseStringAdv. +\param result The list of tokens to free +*/ +void ILibDestructParserResults(struct parser_result *result) +{ + // + // All of these nodes only contain pointers + // so we just need to iterate through all the nodes and free them + // + struct parser_result_field *node = result->FirstResult; + struct parser_result_field *temp; + + while (node != NULL) + { + temp = node->NextResult; + free(node); + node = temp; + } + free(result); +} + +/*! \fn ILibDestructPacket(struct packetheader *packet) +\brief Frees resources associated with a Packet that was created either by \a ILibCreateEmptyPacket or \a ILibParsePacket +\param packet The packet to free +*/ +void ILibDestructPacket(struct packetheader *packet) +{ + struct packetheader_field_node *node = packet->FirstField; // TODO: *********** CRASH ON EXIT SOMETIMES OCCURS HERE + struct packetheader_field_node *nextnode; + + // + // Iterate through all the headers + // + while (node != NULL) + { + nextnode = node->NextField; + if (node->UserAllocStrings != 0) + { + // + // If the user allocated the string, then we need to free it. + // Otherwise these are just pointers into others strings, in which + // case we don't want to free them + // + free(node->Field); + free(node->FieldData); + } + free(node); + node = nextnode; + } + if (packet->UserAllocStrings != 0) + { + // + // If this flag was set, it means the used ILibCreateEmptyPacket, + // and set these fields manually, which means the string was copied. + // In which case, we need to free the strings + // + if (packet->StatusData != NULL) {free(packet->StatusData);} + if (packet->Directive != NULL) {free(packet->Directive);} + if (packet->Reserved == NULL && packet->DirectiveObj != NULL) {free(packet->DirectiveObj);} + if (packet->Reserved != NULL) {free(packet->Reserved);} + if (packet->Body != NULL) free(packet->Body); + } + if (packet->UserAllocVersion != 0) + { + free(packet->Version); + } + ILibDestroyHashTree(packet->HeaderTable); + free(packet); +} + +/*! \fn ILibHTTPEscape(char* outdata, const char* data) +\brief Escapes a string according to HTTP Specifications. +\par +The string you would want to escape would typically be the string used in the Path portion +of an HTTP request. eg:
+GET foo/bar.txt HTTP/1.1
+
+\b Note: It should be noted that the output buffer needs to be allocated prior to calling this method. +The required space can be determined by calling \a ILibHTTPEscapeLength. +\param outdata The escaped string +\param[in,out] data The string to escape +\return The length of the escaped string +*/ +int ILibHTTPEscapeEx(char* outdata, const char* data, size_t dataLen) +{ + int i = 0; + int x = 0; + char hex[4]; + + while (data[x] != 0 && x < (int)dataLen) + { + if ( (data[x]>=63 && data[x]<=90) || (data[x]>=97 && data[x]<=122) || (data[x]>=47 && data[x]<=57) \ + || data[x]==59 || data[x]==47 || data[x]==63 || data[x]==58 || data[x]==64 || data[x]==61 \ + || data[x]==43 || data[x]==36 || data[x]==45 || data[x]==95 || data[x]==46 || data[x]==42) + { + // + // These are all the allowed values for HTTP. If it's one of these characters, we're ok + // + outdata[i] = data[x]; + ++i; + } + else + { + // + // If it wasn't one of these characters, then we need to escape it + // + sprintf_s(hex, 4, "%02X", (unsigned char)data[x]); + outdata[i] = '%'; + outdata[i+1] = hex[0]; + outdata[i+2] = hex[1]; + i+=3; + } + ++x; + } + outdata[i] = 0; + return (i+1); +} + +/*! \fn ILibHTTPEscapeLength(const char* data) +\brief Determines the buffer space required to HTTP escape a particular string. +\param data Calculates the length requirements as if \a data was escaped +\return The minimum required length +*/ +int ILibHTTPEscapeLengthEx(const char* data, size_t dataLen) +{ + int i=0; + int x=0; + while (data[x] != 0 && i < (int)dataLen) + { + if ((data[x] >= 63 && data[x] <= 90) || (data[x] >= 97 && data[x] <= 122) || (data[x] >= 47 && data[x] <= 57) \ + || data[x] == 59 || data[x] == 47 || data[x] == 63 || data[x] == 58 || data[x] == 64 || data[x] == 61 \ + || data[x] == 43 || data[x] == 36 || data[x] == 45 || data[x] == 95 || data[x] == 46 || data[x] == 42) + { + // No need to escape + ++i; + } + else + { + // Need to escape + i += 3; + } + ++x; + } + return(i+1); +} + +/*! \fn ILibInPlaceHTTPUnEscape(char* data) +\brief Unescapes a given string according to HTTP encoding rules +\par +The escaped representation of a string is always longer than the unescaped version +so this method will overwrite the escaped string, with the unescaped result. +\param[in,out] data The buffer to unescape +\return The length of the unescaped string +*/ +int ILibInPlaceHTTPUnEscapeEx(char* data, int length) +{ + char hex[3]; + char *stp; + int src_x=0; + int dst_x=0; + + hex[2]=0; + + while (src_xHeaderTable = ILibInitHashTree_CaseInSensitive(); + + // + // All the headers are delineated with a CRLF, so we parse on that + // + p = (struct parser_result*)ILibParseString(buffer, offset, length, "\r\n", 2); + _packet = p; + f = p->FirstResult; + // + // The first token is where we can figure out the Method, Path, Version, etc. + // + StartLine = (struct parser_result*)ILibParseString(f->data, 0, f->datalength, " ", 1); + HeaderLine = f->NextResult; + if (memcmp(StartLine->FirstResult->data, "HTTP/", 5) == 0 && StartLine->FirstResult->NextResult != NULL) + { + // + // If the StartLine starts with HTTP/, then we know this is a response packet. + // We parse on the '/' character to determine the Version, as it follows. + // eg: HTTP/1.1 200 OK + // + p = (struct parser_result*)ILibParseString(StartLine->FirstResult->data, 0, StartLine->FirstResult->datalength, "/", 1); + RetVal->Version = p->LastResult->data; + RetVal->VersionLength = p->LastResult->datalength; + RetVal->Version[RetVal->VersionLength] = 0; + ILibDestructParserResults(p); + if ((tempbuffer = (char*)malloc(1+sizeof(char)*(StartLine->FirstResult->NextResult->datalength))) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(tempbuffer,1 + StartLine->FirstResult->NextResult->datalength, StartLine->FirstResult->NextResult->data, StartLine->FirstResult->NextResult->datalength); + MEMCHECK(assert(StartLine->FirstResult->NextResult->datalength <= 1+(int)sizeof(char)*(StartLine->FirstResult->NextResult->datalength));) + + // + // The other tokens contain the Status code and data + // + tempbuffer[StartLine->FirstResult->NextResult->datalength] = '\0'; + RetVal->StatusCode = (int)atoi(tempbuffer); + free(tempbuffer); + RetVal->StatusData = StartLine->FirstResult->NextResult->NextResult->data; + RetVal->StatusDataLength = StartLine->FirstResult->NextResult->NextResult->datalength; + } + else + { + + // + // If the packet didn't start with HTTP/ then we know it's a request packet + // eg: GET /index.html HTTP/1.1 + // The method (or directive), is the first token, and the Path + // (or DirectiveObj) is the second, and version in the 3rd. + // + RetVal->Directive = StartLine->FirstResult->data; + RetVal->DirectiveLength = StartLine->FirstResult->datalength; + if (StartLine->FirstResult->NextResult!=NULL) + { + RetVal->DirectiveObj = StartLine->FirstResult->NextResult->data; + RetVal->DirectiveObjLength = StartLine->FirstResult->NextResult->datalength; + } + else + { + // Invalid packet + ILibDestructParserResults(_packet); + ILibDestructParserResults(StartLine); + ILibDestructPacket(RetVal); + return(NULL); + } + + RetVal->StatusCode = -1; + // + // We parse the last token on '/' to find the version + // + p = (struct parser_result*)ILibParseString(StartLine->LastResult->data, 0, StartLine->LastResult->datalength, "/", 1); + RetVal->Version = p->LastResult->data; + RetVal->VersionLength = p->LastResult->datalength; + RetVal->Version[RetVal->VersionLength] = 0; + ILibDestructParserResults(p); + + RetVal->Directive[RetVal->DirectiveLength] = '\0'; + RetVal->DirectiveObj[RetVal->DirectiveObjLength] = '\0'; + } + // + // Headerline starts with the second token. Then we iterate through the rest of the tokens + // + while (HeaderLine != NULL) + { + if (HeaderLine->datalength == 0 || HeaderLine->data == NULL) + { + // + // An empty line signals the end of the headers + // + break; + } + if (node != NULL && (HeaderLine->data[0] == ' ' || HeaderLine->data[0] == 9)) + { + // + // This is a multi-line continuation + // + if (node->UserAllocStrings == 0) + { + tempbuffer = node->FieldData; + if ((node->FieldData = (char*)malloc(node->FieldDataLength + HeaderLine->datalength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->FieldData, node->FieldDataLength + HeaderLine->datalength, tempbuffer, node->FieldDataLength); + + tempbuffer = node->Field; + if ((node->Field = (char*)malloc(node->FieldLength + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->Field, node->FieldLength + 1, tempbuffer, node->FieldLength); + + node->UserAllocStrings = -1; + } + else + { + if ((tmp = (char*)realloc(node->FieldData, node->FieldDataLength + HeaderLine->datalength)) == NULL) ILIBCRITICALEXIT(254); + node->FieldData = tmp; + } + memcpy_s(node->FieldData+node->FieldDataLength, HeaderLine->datalength, HeaderLine->data + 1, HeaderLine->datalength - 1); + node->FieldDataLength += (HeaderLine->datalength-1); + } + else + { + // + // Instantiate a new header entry for each new token + // + if ((node = (struct packetheader_field_node*)malloc(sizeof(struct packetheader_field_node))) == NULL) ILIBCRITICALEXIT(254); + memset(node, 0, sizeof(struct packetheader_field_node)); + for(i = 0; i < HeaderLine->datalength; ++i) + { + if (*((HeaderLine->data) + i) == ':') + { + node->Field = HeaderLine->data; + node->FieldLength = i; + node->FieldData = HeaderLine->data + i + 1; + node->FieldDataLength = (HeaderLine->datalength)-i-1; + break; + } + } + if (node->Field == NULL) + { + // + // Invalid header line. Let's just ignore it and move on + // + free(node); + node = NULL; + HeaderLine = HeaderLine->NextResult; + continue; + } + // + // We need to do white space processing, because we need to ignore them in the + // headers + // So do a 'trim' operation + // + node->FieldDataLength = ILibTrimString(&(node->FieldData),node->FieldDataLength); + node->Field[node->FieldLength] = '\0'; + node->FieldData[node->FieldDataLength] = '\0'; + + // + // Since we are parsing an existing string, we set this flag to zero, so that it doesn't + // get freed + // + node->UserAllocStrings = 0; + node->NextField = NULL; + + if (RetVal->FirstField==NULL) + { + // + // If there aren't any headers yet, this will be the first + // + RetVal->FirstField = node; + RetVal->LastField = node; + } + else + { + // + // There are already headers, so link this in the tail + // + RetVal->LastField->NextField = node; // Note: Klocwork says LastField could be NULL/dereferenced, but LastField is never going to be NULL. + } + RetVal->LastField = node; + ILibAddEntryEx(RetVal->HeaderTable,node->Field,node->FieldLength,node->FieldData,node->FieldDataLength); + } + HeaderLine = HeaderLine->NextResult; + } + ILibDestructParserResults(_packet); + ILibDestructParserResults(StartLine); + return(RetVal); +} + +/*! \fn ILibFragmentTextLength(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength) +\brief Determines the buffer size required to fragment a string +\param text The string to fragment +\param textLength Length of \a text +\param delimiter Line delimiter +\param delimiterLength Length of \a delimiter +\param tokenLength The maximum size of each fragment or token +\return The length of the buffer required to call \a ILibFragmentText +*/ +int ILibFragmentTextLength(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength) +{ + int RetVal; + + UNREFERENCED_PARAMETER( text ); + UNREFERENCED_PARAMETER( delimiter ); + + RetVal = textLength + (((textLength/tokenLength)==1?0:(textLength/tokenLength))*delimiterLength); + return(RetVal); +} + +/*! \fn ILibFragmentText(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength, char **RetVal) +\brief Fragments a string into multiple tokens +\param text The string to fragment +\param textLength Length of \a text +\param delimiter Line delimiter +\param delimiterLength Length of \a delimiter +\param tokenLength The maximum size of each fragment or token +\param RetVal The buffer to store the resultant string +\return The length of the written string +*/ +int ILibFragmentText(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength, char **RetVal) +{ + char *Buffer; + int i=0,i2=0; + int BufferSize = 0; + int allocSize = ILibFragmentTextLength(text, textLength, delimiter, delimiterLength, tokenLength); + if ((*RetVal = (char*)malloc(allocSize)) == NULL) ILIBCRITICALEXIT(254); + + Buffer = *RetVal; + + i2 = textLength; + while (i2!=0) + { + if (i2!=textLength) + { + memcpy_s(Buffer+i, allocSize - i, delimiter,delimiterLength); + i+=delimiterLength; + BufferSize += delimiterLength; + } + memcpy_s(Buffer+i, allocSize - i, text + (textLength-i2),i2>tokenLength?tokenLength:i2); + i+=i2>tokenLength?tokenLength:i2; + BufferSize += i2>tokenLength?tokenLength:i2; + i2 -= i2>tokenLength?tokenLength:i2; + } + return(BufferSize); +} + +/*! \fn ILibGetRawPacket(struct packetheader* packet,char **RetVal) +\brief Converts a packetheader structure into a raw char* buffer +\par +\b Note: The returned buffer must be freed +\param packet The packetheader struture to convert +\param[out] RetVal The output char* buffer +\return The length of the output buffer +*/ +int ILibGetRawPacket(struct packetheader* packet, char **RetVal) +{ + int i,i2; + int BufferSize = 0; + char* Buffer, *temp; + + void *en; + + char *Field; + int FieldLength; + void *FieldData; + int FieldDataLength; + + if (packet->StatusCode != -1) + { + BufferSize = 12 + packet->VersionLength + packet->StatusDataLength; + // + // HTTP/1.1 200 OK\r\n + // 12 is the total number of literal characters. Just add Version and StatusData + // HTTP/ OK \r\n + // + } + else + { + BufferSize = packet->DirectiveLength + packet->DirectiveObjLength + 12; + // + // GET / HTTP/1.1\r\n + // This is calculating the length for a request packet. + // ToDo: This isn't completely correct + // But it will work as long as the version is not > 9.9 + // It should also add the length of the Version, but it's not critical. + } + + en = ILibHashTree_GetEnumerator(packet->HeaderTable); + while (ILibHashTree_MoveNext(en)==0) + { + ILibHashTree_GetValueEx(en,&Field,&FieldLength,(void**)&FieldData,&FieldDataLength); + if (FieldDataLength < 0) { continue; } + // + // A conservative estimate adding the lengths of the header name and value, plus + // 4 characters for the ':' and CRLF + // + BufferSize += FieldLength + FieldDataLength + 4; + + // + // If the header is longer than MAX_HEADER_LENGTH, we need to break it up + // into multiple lines, so we need to calculate the space needed for the + // delimiters. + // + if (FieldDataLength>MAX_HEADER_LENGTH) + { + BufferSize += ILibFragmentTextLength(FieldData, FieldDataLength, "\r\n ", 3, MAX_HEADER_LENGTH); + } + } + ILibHashTree_DestroyEnumerator(en); + + // + // Another conservative estimate adding in the packet body length plus a padding of 3 + // for the empty line + // + BufferSize += (3 + packet->BodyLength); + + // + // Allocate the buffer + // + if ((Buffer = *RetVal = (char*)malloc(BufferSize)) == NULL) ILIBCRITICALEXIT(254); + if (packet->StatusCode != -1) + { + // + // Write the response + // + memcpy_s(Buffer, BufferSize, "HTTP/", 5); + memcpy_s(Buffer + 5, BufferSize - 5, packet->Version, packet->VersionLength); + i = 5 + packet->VersionLength; + + i += sprintf_s(Buffer + i, BufferSize - i, " %d ", packet->StatusCode); + memcpy_s(Buffer + i, BufferSize - i, packet->StatusData ,packet->StatusDataLength); + i += packet->StatusDataLength; + + memcpy_s(Buffer + i, BufferSize - i, "\r\n", 2); + i += 2; + /* HTTP/1.1 200 OK\r\n */ + } + else + { + // + // Write the Request + // + memcpy_s(Buffer, BufferSize, packet->Directive,packet->DirectiveLength); + i = packet->DirectiveLength; + memcpy_s(Buffer+i, BufferSize - i, " ",1); + i+=1; + memcpy_s(Buffer+i, BufferSize - i, packet->DirectiveObj,packet->DirectiveObjLength); + i+=packet->DirectiveObjLength; + memcpy_s(Buffer+i, BufferSize - i, " HTTP/",6); + i+=6; + memcpy_s(Buffer+i, BufferSize - i, packet->Version,packet->VersionLength); + i+=packet->VersionLength; + memcpy_s(Buffer+i, BufferSize - i, "\r\n",2); + i+=2; + /* GET / HTTP/1.1\r\n */ + } + + en = ILibHashTree_GetEnumerator(packet->HeaderTable); + while (ILibHashTree_MoveNext(en)==0) + { + ILibHashTree_GetValueEx(en,&Field,&FieldLength,(void**)&FieldData,&FieldDataLength); + if (FieldDataLength < 0) { continue; } + + // + // Write each header + // + memcpy_s(Buffer+i, BufferSize - i, Field,FieldLength); + i+=FieldLength; + memcpy_s(Buffer+i, BufferSize - i, ": ",2); + i+=2; + + if (ILibFragmentTextLength(FieldData,FieldDataLength,"\r\n ",3, MAX_HEADER_LENGTH)>FieldDataLength) + { + // Fragment this + i2 = ILibFragmentText(FieldData,FieldDataLength,"\r\n ",3, MAX_HEADER_LENGTH,&temp); + memcpy_s(Buffer+i, BufferSize - i, temp,i2); + i += i2; + free(temp); + } + else + { + // No need to fragment this + memcpy_s(Buffer+i, BufferSize - i, FieldData,FieldDataLength); + i += FieldDataLength; + } + + memcpy_s(Buffer+i, BufferSize - i, "\r\n",2); + i+=2; + } + ILibHashTree_DestroyEnumerator(en); + + // + // Write the empty line + // + memcpy_s(Buffer+i, BufferSize - i, "\r\n",2); + i+=2; + + // + // Write the body + // + memcpy_s(Buffer+i, BufferSize - i, packet->Body,packet->BodyLength); + i+=packet->BodyLength; + Buffer[i] = '\0'; + + return(i); +} + +// TCP: SOCK_STREAM, IPPROTO_TCP +// UDP: SOCK_DGRAM, IPPROTO_UDP +SOCKET ILibGetSocket(struct sockaddr *localif, int type, int protocol) +{ + int off = 0; + SOCKET sock; + if (localif->sa_family == AF_INET6 && g_ILibDetectIPv6Support == 0) { ILIBMARKPOSITION(1); return 0; } + if ((sock = socket(localif->sa_family, type, protocol)) == -1) { ILIBMARKPOSITION(2); return 0; } + if (localif->sa_family == AF_INET6) if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); +#ifdef SO_NOSIGPIPE + { + int set = 1; + setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket + } +#endif +#if defined(WIN32) + if (bind(sock, localif, INET_SOCKADDR_LENGTH(localif->sa_family)) != 0) { ILIBMARKPOSITION(3); closesocket(sock); return 0; } +#else + if (bind(sock, localif, INET_SOCKADDR_LENGTH(localif->sa_family)) != 0) { ILIBMARKPOSITION(4); close(sock); return 0; } +#endif + return sock; +} + + +//! Parses a URI string, into its IP Address, Port Number, and Path components +/*! +\b Note: The Address and Path components must be freed +\param URI The URI to parse +\param[out] Addr The Address component +\param Port The Port component. Default is 80 +\param[out] Path The Path component +\param[in,out] AddrStruct sockaddr_in6 structure holding the address and port [Can be NULL] +*/ +ILibParseUriResult ILibParseUri (const char* URI, char** Addr, unsigned short* Port, char** Path, struct sockaddr_in6* AddrStruct) +{ + struct parser_result *result, *result2, *result3; + char *TempString, *TempString2; + int TempStringLength, TempStringLength2; + unsigned short lport; + char* laddr = NULL; + + ILibParseUriResult retVal = ILibParseUriResult_UNKNOWN_SCHEME; + + // A scheme has the format xxx://yyy , so if we parse on ://, we can extract the path info + result = ILibParseString((char *)URI, 0, (int)strnlen_s(URI, sizeof(ILibScratchPad)), "://", 3); + + // Check the Scheme + switch (result->FirstResult->datalength) + { + case 2: + if (strncmp(result->FirstResult->data, "ws", 2) == 0 || strncmp(result->FirstResult->data, "WS", 2) == 0) { retVal = ILibParseUriResult_NO_TLS; } + break; + case 3: + if (strncmp(result->FirstResult->data, "wss", 3) == 0 || strncmp(result->FirstResult->data, "WSS", 3) == 0) { retVal = ILibParseUriResult_TLS; } + break; + case 4: + if (strncmp(result->FirstResult->data, "http", 4) == 0 || strncmp(result->FirstResult->data, "HTTP", 4) == 0) { retVal = ILibParseUriResult_NO_TLS; } + break; + case 5: + if (strncmp(result->FirstResult->data, "https", 5) == 0 || strncmp(result->FirstResult->data, "HTTPS", 5) == 0) { retVal = ILibParseUriResult_TLS; } + break; + default: + retVal = ILibParseUriResult_UNKNOWN_SCHEME; + break; + } + + TempString = result->LastResult->data; + TempStringLength = result->LastResult->datalength; + + // Parse Path. The first '/' will occur after the IPAddress:Port combination + result2 = ILibParseString(TempString, 0, TempStringLength,"/",1); + TempStringLength2 = TempStringLength-result2->FirstResult->datalength; + if (Path != NULL) + { + if ((*Path = (char*)malloc(TempStringLength2 + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(*Path, TempStringLength2 + 1, TempString + (result2->FirstResult->datalength), TempStringLength2); + (*Path)[TempStringLength2] = '\0'; + } + + // Parse Port Number + result3 = ILibParseString(result2->FirstResult->data, 0, result2->FirstResult->datalength, ":", 1); + if (result3->NumResults == 1) + { + // The default port if non is specified, assuming HTTP(s) or WS(s) + lport = (retVal == ILibParseUriResult_TLS) ? 443 : 80; + } + else + { + // If a port was specified, use that + if ((TempString2 = (char*)malloc(result3->LastResult->datalength + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(TempString2, result3->LastResult->datalength + 1, result3->LastResult->data, result3->LastResult->datalength); + TempString2[result3->LastResult->datalength] = '\0'; + lport = (unsigned short)atoi(TempString2); + free(TempString2); + } + + // Parse IP Address + if (result3->FirstResult->data[0] == '[') + { + // This is an IPv6 address + TempStringLength2 = ILibString_IndexOf(result2->FirstResult->data, result2->FirstResult->datalength, "]", 1); + if (TempStringLength2 > 0) + { + if ((laddr = (char*)malloc(TempStringLength2 + 2)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(laddr, TempStringLength2 + 2, result3->FirstResult->data, TempStringLength2 + 1); + (laddr)[TempStringLength2 + 1] = '\0'; + } + } + else + { + // This is an IPv4 address + TempStringLength2 = result3->FirstResult->datalength; + if ((laddr = (char*)malloc(TempStringLength2 + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(laddr, TempStringLength2 + 1, result3->FirstResult->data, TempStringLength2); + (laddr)[TempStringLength2] = '\0'; + } + + // Cleanup + ILibDestructParserResults(result3); + ILibDestructParserResults(result2); + ILibDestructParserResults(result); + + // Convert to sockaddr if needed + if (AddrStruct != NULL) + { + // Convert the string address into a sockaddr + memset(AddrStruct, 0, sizeof(struct sockaddr_in6)); + if (laddr != NULL && laddr[0] == '[') + { + // IPv6 + AddrStruct->sin6_family = AF_INET6; + ILibInet_pton(AF_INET6, laddr + 1, &(AddrStruct->sin6_addr)); + AddrStruct->sin6_port = (unsigned short)htons(lport); + } + else + { + // IPv4 + AddrStruct->sin6_family = AF_INET; + if (ILibInet_pton(AF_INET, laddr, &(((struct sockaddr_in*)AddrStruct)->sin_addr)) == 0) + { + // This was not an IP Address, but a DNS name + if (ILibResolveEx(laddr, lport, AddrStruct) != 0) + { + // Failed to resolve + AddrStruct->sin6_family = AF_UNSPEC; + } + } + ((struct sockaddr_in*)AddrStruct)->sin_port = (unsigned short)htons(lport); + } + } + + if (Port != NULL) *Port = lport; + if (Addr != NULL) *Addr = laddr; else if (laddr != NULL) free(laddr); + return(retVal); +} + +/*! \fn ILibCreateEmptyPacket() +\brief Creates an empty packetheader structure +\return An empty packet +*/ +struct packetheader *ILibCreateEmptyPacket() +{ + struct packetheader *RetVal; + if ((RetVal = (struct packetheader*)malloc(sizeof(struct packetheader))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct packetheader)); + + RetVal->UserAllocStrings = -1; + RetVal->StatusCode = -1; + RetVal->Version = "1.0"; + RetVal->VersionLength = 3; + RetVal->HeaderTable = ILibInitHashTree_CaseInSensitive(); + + return(RetVal); +} + +/*! \fn ILibClonePacket(struct packetheader *packet) +\brief Creates a Deep Copy of a packet structure +\par +Because ILibParsePacketHeader does not copy any data, the data will become invalid +once the data is flushed. This method is used to preserve the data. +\param packet The packet to clone +\return A cloned packet structure +*/ +struct packetheader* ILibClonePacket(struct packetheader *packet) +{ + struct packetheader *RetVal = ILibCreateEmptyPacket(); + struct packetheader_field_node *n; + + RetVal->ClonedPacket = 1; + + // Copy the addresses + memcpy_s(&(RetVal->ReceivingAddress), sizeof(RetVal->ReceivingAddress), &(packet->ReceivingAddress), INET_SOCKADDR_LENGTH(((struct sockaddr_in6*)(packet->ReceivingAddress))->sin6_family)); + memcpy_s(&(RetVal->Source), sizeof(RetVal->Source), &(packet->Source), INET_SOCKADDR_LENGTH(((struct sockaddr_in6*)(packet->ReceivingAddress))->sin6_family)); + + // These three calls will result in the fields being copied + ILibSetDirective( + RetVal, + packet->Directive, + packet->DirectiveLength, + packet->DirectiveObj, + packet->DirectiveObjLength); + + ILibSetStatusCode( + RetVal, + packet->StatusCode, + packet->StatusData, + packet->StatusDataLength); + + ILibSetVersion(RetVal,packet->Version,packet->VersionLength); + + // Iterate through each header, and copy them + n = packet->FirstField; + while (n!=NULL) + { + ILibAddHeaderLine( + RetVal, + n->Field, + n->FieldLength, + n->FieldData, + n->FieldDataLength); + n = n->NextField; + } + return(RetVal); +} + +/*! \fn ILibSetVersion(struct packetheader *packet, char* Version, int VersionLength) +\brief Sets the version of a packetheader structure. The Default version is 1.0 +\param packet The packet to modify +\param Version The version string to write. eg: 1.1 +\param VersionLength The length of the \a Version +*/ +void ILibSetVersion(struct packetheader *packet, char* Version, int VersionLength) +{ + if (packet->UserAllocVersion!=0) {free(packet->Version);} + packet->UserAllocVersion = 1; + if ((packet->Version = (char*)malloc(1+VersionLength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->Version, 1 + VersionLength, Version, VersionLength); + packet->Version[VersionLength] = '\0'; +} + +/*! \fn ILibSetStatusCode(struct packetheader *packet, int StatusCode, char *StatusData, int StatusDataLength) +\brief Sets the status code of a packetheader structure +\param packet The packet to modify +\param StatusCode The status code, eg: 200 +\param StatusData The status string, eg: OK +\param StatusDataLength The length of \a StatusData +*/ +void ILibSetStatusCode(struct packetheader *packet, int StatusCode, char *StatusData, int StatusDataLength) +{ + if (StatusDataLength < 0) { StatusDataLength = (int)strnlen_s(StatusData, 255); } + packet->StatusCode = StatusCode; + if (packet->StatusData != NULL) { free(packet->StatusData); } + if ((packet->StatusData = (char*)malloc(StatusDataLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->StatusData, StatusDataLength + 1, StatusData, StatusDataLength); + packet->StatusData[StatusDataLength] = '\0'; + packet->StatusDataLength = StatusDataLength; +} + +/*! \fn ILibSetDirective(struct packetheader *packet, char* Directive, int DirectiveLength, char* DirectiveObj, int DirectiveObjLength) +\brief Sets the /a Method and /a Path of a packetheader structure +\param packet The packet to modify +\param Directive The Method to write, eg: \b GET +\param DirectiveLength The length of \a Directive +\param DirectiveObj The path component of the method, eg: \b /index.html +\param DirectiveObjLength The length of \a DirectiveObj +*/ +void ILibSetDirective(struct packetheader *packet, char* Directive, int DirectiveLength, char* DirectiveObj, int DirectiveObjLength) +{ + if (DirectiveLength < 0)DirectiveLength = (int)strnlen_s(Directive, 255); + if (DirectiveObjLength < 0)DirectiveObjLength = (int)strnlen_s(DirectiveObj, 255); + + if ((packet->Directive = (char*)malloc(DirectiveLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->Directive, DirectiveLength + 1, Directive,DirectiveLength); + packet->Directive[DirectiveLength] = '\0'; + packet->DirectiveLength = DirectiveLength; + + if ((packet->DirectiveObj = (char*)malloc(DirectiveObjLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->DirectiveObj, DirectiveObjLength + 1, DirectiveObj, DirectiveObjLength); + packet->DirectiveObj[DirectiveObjLength] = '\0'; + packet->DirectiveObjLength = DirectiveObjLength; + packet->UserAllocStrings = -1; +} + +void ILibHTTPPacket_Stash_Put(ILibHTTPPacket *packet, char* key, int keyLen, void *data) +{ + if (keyLen < 0) { keyLen = (int)strnlen_s(key, 255); } + ILibAddEntryEx(packet->HeaderTable, key, keyLen, data, -1); +} +int ILibHTTPPacket_Stash_HasKey(ILibHTTPPacket *packet, char* key, int keyLen) +{ + return(ILibHasEntry(packet->HeaderTable, key, keyLen)); +} +void* ILibHTTPPacket_Stash_Get(ILibHTTPPacket *packet, char* key, int keyLen) +{ + if (keyLen < 0) { keyLen = (int)strnlen_s(key, 255); } + return(ILibGetEntry(packet->HeaderTable, key, keyLen)); +} + +/*! \fn void ILibDeleteHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength) +\brief Removes an HTTP header entry from a packetheader structure +\param packet The packet to modify +\param FieldName The header name, eg: \b CONTENT-TYPE +\param FieldNameLength The length of the \a FieldName +*/ +void ILibDeleteHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength) +{ + ILibDeleteEntry(packet->HeaderTable,FieldName,FieldNameLength); +} +/*! \fn ILibAddHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength, char* FieldData, int FieldDataLength) +\brief Adds an HTTP header entry into a packetheader structure +\param packet The packet to modify +\param FieldName The header name, eg: \b CONTENT-TYPE +\param FieldNameLength The length of the \a FieldName +\param FieldData The header value, eg: \b text/xml +\param FieldDataLength The length of the \a FieldData +*/ +void ILibAddHeaderLine(struct packetheader *packet, const char* FieldName, int FieldNameLength, const char* FieldData, int FieldDataLength) +{ + struct packetheader_field_node *node; + if (FieldNameLength < 0) { FieldNameLength = (int)strnlen_s(FieldName, 255); } + if (FieldDataLength < 0) { FieldDataLength = (int)strnlen_s(FieldData, 255); } + + // + // Create the Header Node + // + if ((node = (struct packetheader_field_node*)malloc(sizeof(struct packetheader_field_node))) == NULL) ILIBCRITICALEXIT(254); + node->UserAllocStrings = -1; + if ((node->Field = (char*)malloc(FieldNameLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->Field, FieldNameLength + 1, (char*)FieldName, FieldNameLength); + node->Field[FieldNameLength] = '\0'; + node->FieldLength = FieldNameLength; + + if ((node->FieldData = (char*)malloc(FieldDataLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->FieldData, FieldDataLength + 1, (char*)FieldData, FieldDataLength); + node->FieldData[FieldDataLength] = '\0'; + node->FieldDataLength = FieldDataLength; + + node->NextField = NULL; + + ILibAddEntryEx(packet->HeaderTable,node->Field,node->FieldLength,node->FieldData,node->FieldDataLength); + + // + // And attach it to the linked list + // + if (packet->LastField!=NULL) + { + packet->LastField->NextField = node; + packet->LastField = node; + } + else + { + packet->LastField = node; + packet->FirstField = node; + } +} + +char* ILibUrl_GetHost(char *url, int urlLen) +{ + int startX, endX; + char *retVal = NULL; + urlLen = urlLen >= 0 ? urlLen : (int)strnlen_s(url, sizeof(ILibScratchPad)); + + startX = ILibString_IndexOf(url, urlLen, "://", 3); + if (startX < 0) { startX = 0; } else { startX += 3; } + + endX = ILibString_IndexOf(url + startX, urlLen - startX, "/", 1); + retVal = url + startX; + retVal[endX > 0 ? endX : urlLen - startX] = 0; + + return(retVal); +} + +/*! \fn ILibGetHeaderLineEx(struct packetheader *packet, char* FieldName, int FieldNameLength, int *len) +\brief Retrieves an HTTP header value from a packet structure +\par +\param packet The packet to introspect +\param FieldName The header name to lookup +\param FieldNameLength The length of \a FieldName +\param len The length of the return value. Can be NULL +\return The header value. NULL if not found +*/ +char* ILibGetHeaderLineEx(struct packetheader *packet, char* FieldName, int FieldNameLength, int *len) +{ + void* RetVal = NULL; + int valLength; + + ILibGetEntryEx(packet->HeaderTable,FieldName,FieldNameLength,(void**)&RetVal,&valLength); + if (valLength!=0) + { + ((char*)RetVal)[valLength]=0; + } + if (len != NULL) { *len = valLength; } + return((char*)RetVal); +} +char* ILibGetHeaderLineSP_Next(char* PreviousValue, char* FieldName, int FieldNameLength) +{ + packetheader_field_node *header = (packetheader_field_node*)(PreviousValue - sizeof(void*)); + char *retVal = ILibScratchPad + sizeof(void*); + + while (header != NULL) + { + if (FieldNameLength == header->FieldLength && strncasecmp(FieldName, header->Field, FieldNameLength) == 0) + { + ((void**)ILibScratchPad)[0] = header->NextField; + memcpy_s(retVal, sizeof(ILibScratchPad) - sizeof(void*), header->FieldData, header->FieldDataLength); + retVal[header->FieldDataLength] = 0; + return(retVal); + } + header = header->NextField; + } + return(NULL); +} + +/*! \fn ILibGetHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength) +\brief Retrieves an HTTP header value from a packet structure, copied into the ScratchPad +\par +\param packet The packet to introspect +\param FieldName The header name to lookup +\param FieldNameLength The length of \a FieldName +\return The header value. NULL if not found +*/ +char* ILibGetHeaderLineSP(struct packetheader *packet, char* FieldName, int FieldNameLength) +{ + char* retVal = ILibScratchPad + sizeof(void*); + ((void**)ILibScratchPad)[0] = packet->FirstField;; + + return(ILibGetHeaderLineSP_Next(retVal, FieldName, FieldNameLength)); +} + +static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +/* encode 3 8-bit binary bytes as 4 '6-bit' characters */ +void ILibencodeblock( unsigned char in[3], unsigned char out[4], int len ) +{ + out[0] = cb64[ in[0] >> 2 ]; + out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ]; + out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '='); + out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '='); +} + +/*! \fn int ILibBase64EncodeLength(const int inputLen) +\brief Returns the length the ILibBase64Encode function would use a stream adding padding and line breaks as per spec. +\par +\param input The length of the en-encoded data +\return The length of the encoded stream +*/ +int ILibBase64EncodeLength(const int inputLen) +{ + return ((inputLen * 4) / 3) + 5; +} + +/*! \fn ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output) +\brief Base64 encode a stream adding padding and line breaks as per spec. +\par +\b Note: The encoded stream must be freed if **ouput is passed in as NULL +\param input The stream to encode +\param inputlen The length of \a input +\param[in,out] output The encoded stream. if *output is not NULL, then the base64 will be copied there, if it is NULL, then the function will malloc +\return The length of the encoded stream +*/ +int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output) +{ + unsigned char* out; + unsigned char* in; + + if (*output == NULL) { if ((*output = (unsigned char*)malloc(((inputlen * 4) / 3) + 5)) == NULL) ILIBCRITICALEXIT(254); } + out = *output; + in = input; + + if (input == NULL || inputlen == 0) { *output = NULL; return 0; } + while ((in+3) <= (input+inputlen)) { ILibencodeblock(in, out, 3); in += 3; out += 4; } + if ((input+inputlen)-in == 1) { ILibencodeblock(in, out, 1); out += 4; } + else if ((input+inputlen)-in == 2) { ILibencodeblock(in, out, 2); out += 4; } + *out = 0; + + return (int)(out-*output); +} + +/* Decode 4 '6-bit' characters into 3 8-bit binary bytes */ +void ILibdecodeblock( unsigned char in[4], unsigned char out[3] ) +{ + out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4); + out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2); + out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]); +} + +/*! \fn int ILibBase64DecodeLength(const int inputLen) +\brief Returns the length the ILibBase64Decode function would use to store the decoded string value +\par +\param input The length of the en-encoded data +\return The length of the decoded stream +*/ +int ILibBase64DecodeLength(const int inputLen) +{ + return ((inputLen * 3) / 4) + 4; +} + +/*! \fn ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output) +\brief Decode a base64 encoded stream discarding padding, line breaks and noise +\par +\b Note: The encoded stream must be freed if **ouput is passed in as NULL +\param input The stream to decode +\param inputlen The length of \a input +\param[in,out] The encoded stream. if *output is not NULL, then the base64 will be copied there, if it is NULL, then the function will malloc +\return The length of the decoded stream +*/ +int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output) +{ + unsigned char* inptr; + unsigned char* out; + unsigned char v; + unsigned char in[4]; + int i, len; + + if (input == NULL || inputlen == 0) + { + *output = NULL; + return 0; + } + if(*output==NULL) + if ((*output = (unsigned char*)malloc(((inputlen * 3) / 4) + 4)) == NULL) ILIBCRITICALEXIT(254); + out = *output; + inptr = input; + + memset(in, 0, 4); + while (inptr <= (input + inputlen)) + { + for(len = 0, i = 0; i < 4 && inptr <= (input + inputlen); i++) + { + v = 0; + while ( inptr <= (input+inputlen) && v == 0 ) { + v = (unsigned char) *inptr; + inptr++; + v = (unsigned char)((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]); + if (v) { + v = (unsigned char)((v == '$') ? 0 : v - 61); + } + } + if (inptr <= (input + inputlen)) { + len++; + if (v) { + in[i] = (unsigned char)(v - 1); + } + } + else { + in[i] = 0; + } + } + if (len) + { + ILibdecodeblock(in, out); + out += len - 1; + } + } + *out = 0; + return (int)(out-*output); +} + +/*! \fn ILibInPlaceXmlUnEscape(char* data) +\brief Unescapes a string according to XML parsing rules +\par +Since an escaped XML string is always larger than its unescaped form, this method +will overwrite the escaped string with the unescaped string, while decoding. +\param data The XML string to unescape +\return The length of the unescaped XML string +*/ +int ILibInPlaceXmlUnEscapeEx(char* data, size_t dataLen) +{ + char* end = data + dataLen; + char* i = data; /* src */ + char* j = data; /* dest */ + + // + // Iterate through the string to find escaped sequences + // + while (j < end) + { + if (j[0] == '&' && j[1] == 'q' && j[2] == 'u' && j[3] == 'o' && j[4] == 't' && j[5] == ';') // " + { + // Double Quote + i[0] = '"'; + j += 5; + } + else if (j[0] == '&' && j[1] == 'a' && j[2] == 'p' && j[3] == 'o' && j[4] == 's' && j[5] == ';') // ' + { + // Single Quote (apostrophe) + i[0] = '\''; + j += 5; + } + else if (j[0] == '&' && j[1] == 'a' && j[2] == 'm' && j[3] == 'p' && j[4] == ';') // & + { + // Ampersand + i[0] = '&'; + j += 4; + } + else if (j[0] == '&' && j[1] == 'l' && j[2] == 't' && j[3] == ';') // < + { + // Less Than + i[0] = '<'; + j += 3; + } + else if (j[0] == '&' && j[1] == 'g' && j[2] == 't' && j[3] == ';') // > + { + // Greater Than + i[0] = '>'; + j += 3; + } + else + { + i[0] = j[0]; + } + i++; + j++; + } + i[0] = '\0'; + return (int)(i - data); +} + +/*! \fn ILibXmlEscapeLength(const char* data) +\brief Calculates the minimum required buffer space, to escape an xml string +\par +\b Note: This calculation does not include space for a null terminator +\param data The XML string to calculate buffer requirments with +\return The minimum required buffer size +*/ +int ILibXmlEscapeLengthEx(const char* data, size_t dataLen) +{ + int i = 0, j = 0; + while (data[i] != 0 && i < (int)dataLen) + { + switch (data[i]) + { + case '"': + j += 6; + break; + case '\'': + j += 6; + break; + case '<': + j += 4; + break; + case '>': + j += 4; + break; + case '&': + j += 5; + break; + default: + j++; + } + i++; + } + return j; +} + + +/*! \fn ILibXmlEscape(char* outdata, const char* indata) +\brief Escapes a string according to XML parsing rules +\par +\b Note: \a outdata must be pre-allocated and freed +\param outdata The escaped XML string +\param indata The string to escape +\return The length of the escaped string +*/ +int ILibXmlEscapeEx(char* outdata, const char* indata, size_t indataLen) +{ + int i=0; + char* out; + + out = outdata; + + for (i=0; i < (int)indataLen; i++) + { + if (indata[i] == '"') + { + memcpy_s(out, 6, """, 6); + out = out + 6; + } + else + if (indata[i] == '\'') + { + memcpy_s(out, 6, "'", 6); + out = out + 6; + } + else + if (indata[i] == '<') + { + memcpy_s(out, 4, "<", 4); + out = out + 4; + } + else + if (indata[i] == '>') + { + memcpy_s(out, 4, ">", 4); + out = out + 4; + } + else + if (indata[i] == '&') + { + memcpy_s(out, 5, "&", 5); + out = out + 5; + } + else + { + out[0] = indata[i]; + out++; + } + } + + out[0] = 0; + + return (int)(out - outdata); +} + +// Return the number of milliseconds until trigger, -1 if not found. +long long ILibLifeTime_GetExpiration(void *LifetimeMonitorObject, void *data) +{ + void *node; + struct LifeTimeMonitorData *temp; + struct ILibLifeTime *LifeTimeMonitor = (struct ILibLifeTime*)LifetimeMonitorObject; + + node = ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList); + while (node != NULL) + { + temp = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (temp->data == data) return temp->ExpirationTick; + node = ILibLinkedList_GetNextNode(node); + } + return -1; +} + +/*! \fn ILibLifeTime_AddEx(void *LifetimeMonitorObject,void *data, int ms, void* Callback, void* Destroy) +\brief Registers a timed callback with millisecond granularity +\param LifetimeMonitorObject The \a ILibLifeTime object to add the timed callback to +\param data The data object to associate with the timed callback +\param ms The number of milliseconds for the timed callback +\param Callback The callback function pointer to trigger when the specified time elapses +\param Destroy The abort function pointer, which triggers all non-triggered timed callbacks, upon shutdown +*/ +void ILibLifeTime_AddEx(void *LifetimeMonitorObject,void *data, int ms, ILibLifeTime_OnCallback Callback, ILibLifeTime_OnCallback Destroy) +{ + struct LifeTimeMonitorData *temp; + struct LifeTimeMonitorData *ltms; + struct ILibLifeTime *LifeTimeMonitor = (struct ILibLifeTime*)LifetimeMonitorObject; + void *node; + + if ((ltms = (struct LifeTimeMonitorData*)malloc(sizeof(struct LifeTimeMonitorData))) == NULL) ILIBCRITICALEXIT(254); + memset(ltms,0,sizeof(struct LifeTimeMonitorData)); + + // + // Set the trigger time + // + ltms->data = data; + ltms->ExpirationTick = ms != 0 ? (ILibGetUptime() + (long long)(ms)) : 0; + + // + // Set the callback handlers + // + ltms->CallbackPtr = Callback; + ltms->DestroyPtr = Destroy; + + ILibLinkedList_Lock(LifeTimeMonitor->ObjectList); + + // Add the node to the list + node = ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList); + if (node == NULL) + { + ILibLinkedList_AddTail(LifeTimeMonitor->ObjectList, ltms); + ILibForceUnBlockChain(LifeTimeMonitor->ChainLink.ParentChain); + } + else + { + while (node != NULL) + { + temp = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (ltms->ExpirationTick < temp->ExpirationTick) + { + ILibLinkedList_InsertBefore(node, ltms); + break; + } + node = ILibLinkedList_GetNextNode(node); + } + if (node == NULL) + { + ILibLinkedList_AddTail(LifeTimeMonitor->ObjectList,ltms); + } + else if (ILibLinkedList_GetDataFromNode(ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList)) == ltms) + { + ILibForceUnBlockChain(LifeTimeMonitor->ChainLink.ParentChain); + } + } + + // If this notification is sooner than the existing one, replace it. + if (LifeTimeMonitor->NextTriggerTick > ltms->ExpirationTick) LifeTimeMonitor->NextTriggerTick = ltms->ExpirationTick; + + ILibLinkedList_UnLock(LifeTimeMonitor->ObjectList); +} + +// +// An internal method used by the ILibLifeTime methods +// +void ILibLifeTime_Check(void *LifeTimeMonitorObject, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + void *node; + int removed; + void *EventQueue; + long long CurrentTick; + struct ILibLinkedListNode_Root root; + struct LifeTimeMonitorData *EVT, *Temp = NULL; + struct ILibLifeTime *LifeTimeMonitor = (struct ILibLifeTime*)LifeTimeMonitorObject; + + UNREFERENCED_PARAMETER( readset ); + UNREFERENCED_PARAMETER( writeset ); + UNREFERENCED_PARAMETER( errorset ); + + + // + // Get the current tick count for reference + // + CurrentTick = ILibGetUptime(); + + // + // This will speed things up by skipping the timer check + // + if(LifeTimeMonitor->NextTriggerTick !=0 && ((LifeTimeMonitor->NextTriggerTick > CurrentTick) && (LifeTimeMonitor->NextTriggerTick != -1) && (*blocktime > (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick)))) + { + *blocktime = (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick); + return; + } + LifeTimeMonitor->NextTriggerTick = -1; + + // This is an optimization. We are going to create the root of this linked list on the stack instead of the heap. + // This also fixes a crash with malloc returns NULL if this is created in the heap - No idea why this occurs. + memset(&root, 0, sizeof(struct ILibLinkedListNode_Root)); + EventQueue = (void*)&root; + + ILibLinkedList_Lock(LifeTimeMonitor->Reserved); + ILibLinkedList_Lock(LifeTimeMonitor->ObjectList); + + while (ILibQueue_DeQueue(LifeTimeMonitor->Reserved) != NULL); + + node = ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList); + while (node != NULL) + { + Temp = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (Temp->ExpirationTick == 0 || Temp->ExpirationTick < CurrentTick) + { + ILibQueue_EnQueue(EventQueue, Temp); + node = ILibLinkedList_Remove(node); + } + else + { + if (LifeTimeMonitor->NextTriggerTick == -1 || Temp->ExpirationTick < LifeTimeMonitor->NextTriggerTick) LifeTimeMonitor->NextTriggerTick = Temp->ExpirationTick; // Save the next smallest value + node = ILibLinkedList_GetNextNode(node); + } + } + + ILibLinkedList_UnLock(LifeTimeMonitor->ObjectList); + ILibLinkedList_UnLock(LifeTimeMonitor->Reserved); + + // + // Iterate through all the triggers that we need to fire + // + node = ILibQueue_DeQueue(EventQueue); + while (node != NULL) + { + // + // Check to see if the item to be fired, is in the remove list. + // If it is, that means we shouldn't fire this item anymore. + // + ILibLinkedList_Lock(LifeTimeMonitor->Reserved); + removed = ILibLinkedList_Remove_ByData(LifeTimeMonitor->Reserved,node); + ILibLinkedList_UnLock(LifeTimeMonitor->Reserved); + + EVT = (struct LifeTimeMonitorData*)node; + if (removed == 0) + { + // Trigger the callback + EVT->CallbackPtr(EVT->data); + } + else + { + // + // This item was flagged for removal, so intead of triggering it, + // call the destroy pointer if it exists + // + if (EVT->DestroyPtr != NULL) { EVT->DestroyPtr(EVT->data); } + } + + free(EVT); + node = ILibQueue_DeQueue(EventQueue); + } + + // Compute how much time until next trigger + if (LifeTimeMonitor->NextTriggerTick != -1 && *blocktime > (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick)) + { + int delta = (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick); + if (delta < 1000) *blocktime = 1000; else *blocktime = delta; + } +} + +/*! \fn ILibLifeTime_Remove(void *LifeTimeToken, void *data) +\brief Removes a timed callback from an \a ILibLifeTime module +\param LifeTimeToken The \a ILibLifeTime object to remove the callback from +\param data The data object to remove +*/ +void ILibLifeTime_Remove(void *LifeTimeToken, void *data) +{ + void *node; + int removed = 0; + struct LifeTimeMonitorData *evt; + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + void *EventQueue; + + if (UPnPLifeTime->ObjectList == NULL) return; + EventQueue = ILibQueue_Create(); + ILibLinkedList_Lock(UPnPLifeTime->ObjectList); + + node = ILibLinkedList_GetNode_Head(UPnPLifeTime->ObjectList); + if (node != NULL) + { + while (node != NULL) + { + evt = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (evt->data == data) + { + ILibQueue_EnQueue(EventQueue, evt); + node = ILibLinkedList_Remove(node); + removed = 1; + } + else + { + node = ILibLinkedList_GetNextNode(node); + } + } + if (removed == 0) + { + // + // The item wasn't in the list, so maybe it is pending to be triggered + // + ILibLinkedList_Lock(UPnPLifeTime->Reserved); + ILibLinkedList_AddTail(UPnPLifeTime->Reserved, data); + ILibLinkedList_UnLock(UPnPLifeTime->Reserved); + } + } + ILibLinkedList_UnLock(UPnPLifeTime->ObjectList); + + // + // Iterate through each node that is to be removed + // + evt = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(EventQueue); + while (evt != NULL) + { + if (evt->DestroyPtr != NULL) {evt->DestroyPtr(evt->data);} + free(evt); + evt = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(EventQueue); + } + ILibQueue_Destroy(EventQueue); +} + +/*! \fn ILibLifeTime_Flush(void *LifeTimeToken) +\brief Flushes all timed callbacks from an ILibLifeTime module +\param LifeTimeToken The \a ILibLifeTime object to flush items from +*/ +void ILibLifeTime_Flush(void *LifeTimeToken) +{ + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + struct LifeTimeMonitorData *temp; + + ILibLinkedList_Lock(UPnPLifeTime->ObjectList); + + temp = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(UPnPLifeTime->ObjectList); + while (temp != NULL) + { + if (temp->DestroyPtr != NULL) temp->DestroyPtr(temp->data); + free(temp); + temp = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(UPnPLifeTime->ObjectList); + } + ILibLinkedList_UnLock(UPnPLifeTime->ObjectList); +} + +// +// An internal method used by the ILibLifeTime methods +// +void ILibLifeTime_Destroy(void *LifeTimeToken) +{ + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + ILibLifeTime_Flush(LifeTimeToken); + ILibLinkedList_Destroy(UPnPLifeTime->ObjectList); + ILibQueue_Destroy(UPnPLifeTime->Reserved); + UPnPLifeTime->ObjectCount = 0; + UPnPLifeTime->ObjectList = NULL; +} + +/*! \fn ILibCreateLifeTime(void *Chain) +\brief Creates an empty ILibLifeTime container for Timed Callbacks. +\par +\b Note: All events are triggered on the MicroStack thread. Developers must \b NEVER block this thread! +\param Chain The chain to add the \a ILibLifeTime to +\return An \a ILibLifeTime token, which is used to add/remove callbacks +*/ +void *ILibCreateLifeTime(void *Chain) +{ + struct ILibLifeTime *RetVal; + if ((RetVal = (struct ILibLifeTime*)malloc(sizeof(struct ILibLifeTime))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct ILibLifeTime)); + + RetVal->ObjectList = ILibLinkedList_Create(); + RetVal->ChainLink.PreSelectHandler = &ILibLifeTime_Check; + RetVal->ChainLink.DestroyHandler = &ILibLifeTime_Destroy; + RetVal->ChainLink.ParentChain = Chain; + RetVal->Reserved = ILibQueue_Create(); + + ILibAddToChain(Chain, RetVal); + return((void*)RetVal); +} + + +long ILibLifeTime_Count(void* LifeTimeToken) +{ + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + return ILibQueue_GetCount(UPnPLifeTime->ObjectList); +} + +/*! \fn ILibFindEntryInTable(char *Entry, char **Table) +\brief Find the index in \a Table that contains \a Entry. +\param Entry The char* to find +\param Table Array of char*, where the last entry is NULL +\return the index into \a Table, that contains \a Entry +*/ +int ILibFindEntryInTable(char *Entry, char **Table) +{ + int i = 0; + + while (Table[i]!=NULL) + { + if (strcmp(Entry,Table[i])==0) return(i); + ++i; + } + + return(-1); +} + +void ILibLinkedList_SetData(void *LinkedList_Node, void *data) +{ + ((struct ILibLinkedListNode*)LinkedList_Node)->Data = data; +} +//! Use the given comparer to insert data into the list. +//! A Duplicate (according to comparer) will result in the chooser being dispatched to determine which value to keep +/*! + \param LinkedList ILibLinkedList to perform the insert operation on + \param comparer ILibLinkedList_Comparer handler to dispatch to perform the compare operation + \param chooser ILibLinkedList_Chooser handler to dispatch to determine which value to keep, if a duplicate entry is detected + \param data The data to insert + \param user Custom user data to pass along with dispatched calls + \return The LinkedList node that was inserted +*/ +void* ILibLinkedList_SortedInsertEx(void* LinkedList, ILibLinkedList_Comparer comparer, ILibLinkedList_Chooser chooser, void *data, void *user) +{ + void* node = ILibLinkedList_GetNode_Head(LinkedList); + if(node == NULL) + { + node = ILibLinkedList_AddHead(LinkedList, chooser(NULL, data, user)); + } + else + { + while(node != NULL) + { + if(comparer(ILibLinkedList_GetDataFromNode(node), data) == 0) + { + // Duplicate. Replace entry + ILibLinkedList_SetData(node, chooser(ILibLinkedList_GetDataFromNode(node), data, user)); + break; + } + else if(comparer(ILibLinkedList_GetDataFromNode(node), data) < 0) + { + node = ILibLinkedList_InsertBefore(node, chooser(NULL, data, user)); + break; + } + node = ILibLinkedList_GetNextNode(node); + } + if(node == NULL) + { + node = ILibLinkedList_AddTail(LinkedList, chooser(NULL, data, user)); + } + } + return(node); +} + +void* ILibLinkedList_SortedInsert_DefaultChooser(void *oldObject, void *newObject, void *user) +{ + *((void**)user) = oldObject; + return(newObject); +} + + +//! Use the given comparer to insert data into the list. +//! A Duplicate (according to comparer) will result in the value being updated, and the old value being returned +/*! + \param LinkedList ILibLinkedList to perform the insert operation on + \param comparer ILibLinkedList_Comparer handler to dispatch to perform the compare operation + \param data The data to insert + \return Duplicates are not allowed, so if the comparison resulted in a duplicate, the old value will be returned, and the new value overwritten +*/ +void* ILibLinkedList_SortedInsert(void* LinkedList, ILibLinkedList_Comparer comparer, void *data) +{ + void *retVal = NULL; + ILibLinkedList_SortedInsertEx(LinkedList, comparer, &ILibLinkedList_SortedInsert_DefaultChooser, data, &retVal); + return(retVal); +} +//! Use the given comparer to return the Linked List node that matches with the specified object +/*! + \param LinkedList The ILibLinkedList to search + \param comparer The ILibLinkedList_Comparer handler to dispatch to perform the comparison + \param matchWith object to search + \return First matching node [NULL if none found] +*/ +void* ILibLinkedList_GetNode_Search(void* LinkedList, ILibLinkedList_Comparer comparer, void *matchWith) +{ + void *retVal = NULL; + void *node = ILibLinkedList_GetNode_Head(LinkedList); + while(node != NULL) + { + if((comparer!=NULL && comparer(ILibLinkedList_GetDataFromNode(node), matchWith)==0) || (comparer == NULL && ILibLinkedList_GetDataFromNode(node) == matchWith)) + { + retVal = node; + break; + } + node = ILibLinkedList_GetNextNode(node); + } + return(retVal); +} + +/*! \fn ILibLinkedList_CreateEx(int userMemorySize) +\brief Create an empty Linked List Data Structure +\param userMemorySize Amount of memory to allocate for user data when allocating a linked list node +\return Empty Linked List +*/ +void* ILibLinkedList_CreateEx(int userMemorySize) +{ + struct ILibLinkedListNode_Root *root; + void *mem; + + ILibMemory_Allocate(sizeof(ILibLinkedListNode_Root), userMemorySize, (void**)&root, &mem); + root->ExtraMemory = mem; + + sem_init(&(root->LOCK), 0, 1); + return root; +} + + +void* ILibLinkedList_Create() +{ + return(ILibLinkedList_CreateEx(0)); +} +//! Associate custom user data with a linked list object +/*! + \param list ILibLinkedList to associate + \param tag Custom user data to associate +*/ +void ILibLinkedList_SetTag(ILibLinkedList list, void *tag) +{ + ((struct ILibLinkedListNode_Root*)list)->Tag = tag; +} +//! Fetch the associated custom user data from a linked list +/*! + \param list ILibLinkedList to query + \return Associated custom user data [NULL if none associated] +*/ +void* ILibLinkedList_GetTag(ILibLinkedList list) +{ + return(((struct ILibLinkedListNode_Root*)list)->Tag); +} + +void* ILibLinkedList_AllocateNode(void *LinkedList) +{ + void* newNode; + ILibMemory_Allocate(sizeof(ILibLinkedListNode), ILibMemory_GetExtraMemorySize(((ILibLinkedListNode_Root*)LinkedList)->ExtraMemory), &newNode, NULL); + return(newNode); +} + +void* ILibLinkedList_GetExtendedMemory(void* LinkedList_Node) +{ + if (LinkedList_Node == NULL) { return(NULL); } + return(ILibMemory_GetExtraMemory(LinkedList_Node, sizeof(ILibLinkedListNode))); +} +/*! \fn ILibLinkedList_ShallowCopy(void *LinkedList) +\brief Create a shallow copy of a linked list. That is, the structure is copied, but none of the data contents are copied. The pointer values are just copied. +\param LinkedList The linked list to copy +\return The copy of the supplied linked list +*/ +void* ILibLinkedList_ShallowCopy(void *LinkedList) +{ + void *RetVal = ILibLinkedList_Create(); + void *node = ILibLinkedList_GetNode_Head(LinkedList); + while (node != NULL) + { + ILibLinkedList_AddTail(RetVal, ILibLinkedList_GetDataFromNode(node)); + node = ILibLinkedList_GetNextNode(node); + } + return(RetVal); +} + +/*! \fn ILibLinkedList_GetNode_Head(void *LinkedList) +\brief Returns the Head node of a linked list data structure +\param LinkedList The linked list +\return The first node of the linked list +*/ +void* ILibLinkedList_GetNode_Head(void *LinkedList) +{ + return(((struct ILibLinkedListNode_Root*)LinkedList)->Head); +} + +/*! \fn ILibLinkedList_GetNode_Tail(void *LinkedList) +\brief Returns the Tail node of a linked list data structure +\param LinkedList The linked list +\return The last node of the linked list +*/ +void* ILibLinkedList_GetNode_Tail(void *LinkedList) +{ + return(((struct ILibLinkedListNode_Root*)LinkedList)->Tail); +} + +/*! \fn ILibLinkedList_GetNextNode(void *LinkedList_Node) +\brief Returns the next node, from the specified linked list node +\param LinkedList_Node The current linked list node +\return The next adjacent node of the current one +*/ +void* ILibLinkedList_GetNextNode(void *LinkedList_Node) +{ + return(((struct ILibLinkedListNode*)LinkedList_Node)->Next); +} + +/*! \fn ILibLinkedList_GetPreviousNode(void *LinkedList_Node) +\brief Returns the previous node, from the specified linked list node +\param LinkedList_Node The current linked list node +\return The previous adjacent node of the current one +*/ +void* ILibLinkedList_GetPreviousNode(void *LinkedList_Node) +{ + return(((struct ILibLinkedListNode*)LinkedList_Node)->Previous); +} + +/*! \fn ILibLinkedList_GetDataFromNode(void *LinkedList_Node) +\brief Returns the data pointed to by a linked list node +\param LinkedList_Node The current linked list node +\return The data pointer +*/ +void *ILibLinkedList_GetDataFromNode(void *LinkedList_Node) +{ + return(LinkedList_Node != NULL ? (((struct ILibLinkedListNode*)LinkedList_Node)->Data) : NULL); +} + +/*! \fn ILibLinkedList_InsertBefore(void *LinkedList_Node, void *data) +\brief Creates a new element, and inserts it before the given node +\param LinkedList_Node The linked list node +\param data The data pointer to be referenced +\return The LinkedList node that was inserted +*/ +void* ILibLinkedList_InsertBefore(void *LinkedList_Node, void *data) +{ + struct ILibLinkedListNode_Root *r = ((struct ILibLinkedListNode*)LinkedList_Node)->Root; + struct ILibLinkedListNode *n = (struct ILibLinkedListNode*) LinkedList_Node; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + + // + // Attach ourselved before the specified node + // + newNode->Next = n; + newNode->Previous = n->Previous; + if (newNode->Previous!=NULL) + { + newNode->Previous->Next = newNode; + } + n->Previous = newNode; + // + // If we are the first node, we need to adjust the head + // + if (r->Head==n) + { + r->Head = newNode; + } + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_InsertAfter(void *LinkedList_Node, void *data) +\brief Creates a new element, and appends it after the given node +\param LinkedList_Node The linked list node +\param data The data pointer to be referenced +\return The LinkedList Node that was inserted +*/ +void* ILibLinkedList_InsertAfter(void *LinkedList_Node, void *data) +{ + struct ILibLinkedListNode_Root *r = ((struct ILibLinkedListNode*)LinkedList_Node)->Root; + struct ILibLinkedListNode *n = (struct ILibLinkedListNode*) LinkedList_Node; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + + // + // Attach ourselved after the specified node + // + newNode->Next = n->Next; + n->Next = newNode; + newNode->Previous = n; + if (newNode->Next!=NULL) newNode->Next->Previous = newNode; + + // + // If we are the last node, we need to adjust the tail + // + if (r->Tail==n) r->Tail = newNode; + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_Remove(void *LinkedList_Node) +\brief Removes the given node from a linked list data structure +\param LinkedList_Node The linked list node to remove +\return The next node +*/ +void* ILibLinkedList_Remove(void *LinkedList_Node) +{ + struct ILibLinkedListNode_Root *r; + struct ILibLinkedListNode *n; + void* RetVal; + + if (LinkedList_Node == NULL) { return(NULL); } + r = ((struct ILibLinkedListNode*)LinkedList_Node)->Root; + n = (struct ILibLinkedListNode*) LinkedList_Node; + + RetVal = n->Next; + + if (n->Previous!=NULL) + { + n->Previous->Next = n->Next; + } + if (n->Next!=NULL) + { + n->Next->Previous = n->Previous; + } + if (r->Head==n) + { + r->Head = n->Next; + } + if (r->Tail==n) + { + if (n->Next==NULL) + { + r->Tail = n->Previous; + } + else + { + r->Tail = n->Next; + } + } + --r->count; + free(n); + return(RetVal); +} + +/*! \fn ILibLinkedList_Remove_ByData(void *LinkedList, void *data) +\brief Removes a node from the Linked list, via comparison +\par +Given a data pointer, will traverse the linked list data structure, deleting +elements that point to this data pointer. +\param LinkedList The linked list to traverse +\param data The data pointer to compare +\return Non-zero if an item was removed +*/ +int ILibLinkedList_Remove_ByData(void *LinkedList, void *data) +{ + void *node; + int RetVal=0; + + node = ILibLinkedList_GetNode_Head(LinkedList); + while (node!=NULL) + { + if (ILibLinkedList_GetDataFromNode(node)==data) + { + ++RetVal; + node = ILibLinkedList_Remove(node); + } + else + { + node = ILibLinkedList_GetNextNode(node); + } + } + return(RetVal); +} + +/*! \fn ILibLinkedList_AddHead(void *LinkedList, void *data) +\brief Creates a new element, and inserts it at the top of the linked list. +\param LinkedList The linked list +\param data The data pointer to reference +*/ +void* ILibLinkedList_AddHead(void *LinkedList, void *data) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + newNode->Previous = NULL; + + newNode->Next = r->Head; + if (r->Head!=NULL) r->Head->Previous = newNode; + r->Head = newNode; + if (r->Tail==NULL) r->Tail = newNode; + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_AddTail(void *LinkedList, void *data) +\brief Creates a new element, and appends it to the end of the linked list +\param LinkedList The linked list +\param data The data pointer to reference +*/ +void* ILibLinkedList_AddTail(void *LinkedList, void *data) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + newNode->Next = NULL; + + newNode->Previous = r->Tail; + if (r->Tail != NULL) r->Tail->Next = newNode; + r->Tail = newNode; + if (r->Head == NULL) r->Head = newNode; + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_Lock(void *LinkedList) +\brief Locks the linked list with a non-recursive lock +\param LinkedList The linked list +*/ +void ILibLinkedList_Lock(void *LinkedList) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + sem_wait(&(r->LOCK)); +} + +/*! \fn void ILibLinkedList_UnLock(void *LinkedList) +\brief Unlocks the linked list's non-recursive lock +\param LinkedList The linked list +*/ +void ILibLinkedList_UnLock(void *LinkedList) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + sem_post(&(r->LOCK)); +} + + +/*! \fn ILibLinkedList_Destroy(void *LinkedList) +\brief Frees the resources used by the linked list. +\par +\b Note: The data pointer referenced needs to be freed by the user if required +\param LinkedList The linked list +*/ +void ILibLinkedList_Destroy(void *LinkedList) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + while (r->Head != NULL) ILibLinkedList_Remove(ILibLinkedList_GetNode_Head(LinkedList)); + sem_destroy(&(r->LOCK)); + free(r); +} + + +/*! \fn ILibLinkedList_GetCount(void *LinkedList) +\brief Returns the number of nodes in the linked list +\param LinkedList The linked list +\return Number of elements in the linked list +*/ +long ILibLinkedList_GetCount(void *LinkedList) +{ + return(((struct ILibLinkedListNode_Root*)LinkedList)->count); +} + +int ILibLinkedList_GetIndex(void *node) +{ + ILibLinkedListNode *current = (ILibLinkedListNode*)ILibLinkedList_GetNode_Head(((ILibLinkedListNode*)node)->Root); + int i = 0; + + while (current != NULL && current != node) + { + ++i; + current = current->Next; + } + return(current != NULL ? i : -1); +} + +typedef struct ILibHashtable_ClearCallbackStruct +{ + ILibHashtable source; + ILibHashtable_OnDestroy onClear; + void *user; +}ILibHashtable_ClearCallbackStruct; + +int ILibHashtable_DefaultBucketizer(int value) +{ + // Convert 4 bytes to 1 byte + unsigned char tmp[4]; + int retVal = 0; + + ((unsigned int*)tmp)[0] = value; + retVal = (int)(tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3]); //Klocwork is being retarded, and doesn't realize an unsigned int is 4 bytes + + return(retVal); +} +int ILibHashtable_DefaultHashFunc(void* Key1, char* Key2, int Key2Len) +{ + char tmp[4]; + int i; + int retVal = 0; + union{int i; void* p;}u; + + u.p = Key1; + if(Key1!=NULL) {retVal ^= u.i;} + if (Key2 != NULL) + { + if (Key2Len < 5) + { + ((int*)tmp)[0] = 0; + for (i = 0; i < Key2Len; ++i) + { + tmp[i] = Key2[i]; + } + retVal ^= ((int*)tmp)[0]; + } + + if (Key2Len > 4) + { + ((int*)tmp)[0] = 0; + for (i = 0; i < 4; ++i) + { + tmp[i] = Key2[Key2Len - 1 - i]; + } + retVal ^= ((int*)tmp)[0]; + + if (Key2Len > 12) + { + int x = Key2Len / 2; + ((int*)tmp)[0] = 0; + for (i = 0; i < 4; ++i) + { + tmp[i] = Key2[i + x]; + } + retVal ^= ((int*)tmp)[0]; + } + } + } + return(retVal & 0x7FFFFFFF); +} +//! Create an Advanced Hashtable using the default Hashing Function, and default SparseArray/Bucketizer +/*! + \return Hashtable +*/ +ILibHashtable ILibHashtable_Create() +{ + ILibHashtable_Root *retVal; + + if((retVal = (ILibHashtable_Root*)malloc(sizeof(ILibHashtable_Root))) == NULL) {ILIBCRITICALEXIT(254);} + memset(retVal, 0, sizeof(ILibHashtable_Root)); + + ILibHashtable_ChangeHashFunc(retVal, &ILibHashtable_DefaultHashFunc); + ILibHashtable_ChangeBucketizer(retVal, 256, &ILibHashtable_DefaultBucketizer); + + return(retVal); +} +void ILibHashtable_DestroyEx2(ILibSparseArray sender, int index, void *value, void *user) +{ + ILibHashtable_ClearCallbackStruct *state = (ILibHashtable_ClearCallbackStruct*)user; + UNREFERENCED_PARAMETER(sender); + UNREFERENCED_PARAMETER(index); + + if(value != NULL) + { + ILibHashtable_Node *tmp, *node = (ILibHashtable_Node*)value; + ILibHashtable_OnDestroy onDestroy = state->onClear; + while(node != NULL) + { + if(onDestroy!=NULL) {onDestroy(state->source, node->Key1, node->Key2, node->Key2Len, node->Data, state->user);} + tmp = node->next; + if(node->Key2 != NULL) { free(node->Key2); } + free(node); + node = tmp; + } + } +} +//! Free the resources associated with an Advanced Hashtable + +//! Dispatches event handler for each element that is destroyed/cleared +/*! + \param table Hashtable + \param onDestroy Event handler to dispatch for destroyed/cleared elements + \param user Custom User state data +*/ +void ILibHashtable_DestroyEx(ILibHashtable table, ILibHashtable_OnDestroy onDestroy, void *user) +{ + ILibHashtable_Root *root = (ILibHashtable_Root*)table; + ILibHashtable_ClearCallbackStruct state; + memset(&state, 0, sizeof(ILibHashtable_ClearCallbackStruct)); + + state.source = table; + state.onClear = onDestroy; + state.user = user; + + ILibSparseArray_DestroyEx(root->table, &ILibHashtable_DestroyEx2, &state); + free(root); +} + +void ILibHashtable_EnumerateSink(ILibSparseArray sender, int index, void *value, void *user) +{ + ILibHashtable_ClearCallbackStruct *state = (ILibHashtable_ClearCallbackStruct*)user; + UNREFERENCED_PARAMETER(sender); + UNREFERENCED_PARAMETER(index); + + if (value != NULL) + { + ILibHashtable_Node *node = (ILibHashtable_Node*)value; + ILibHashtable_OnDestroy onEnumerate = state->onClear; + while (node != NULL) + { + if (onEnumerate != NULL) { onEnumerate(state->source, node->Key1, node->Key2, node->Key2Len, node->Data, state->user); } + node = node->next; + } + } +} + +//! Enumerates the Hashtable +/*! + \param table Hashtable to enumerate + \param onEnumerate The callback to dispatch for each item + \param user User state object to pass thru to the callback +*/ +void ILibHashtable_Enumerate(ILibHashtable table, ILibHashtable_OnDestroy onEnumerate, void *user) +{ + ILibHashtable_ClearCallbackStruct state; + memset(&state, 0, sizeof(ILibHashtable_ClearCallbackStruct)); + + state.source = table; + state.onClear = onEnumerate; + state.user = user; + + ILibSparseArray_Enumerate(((ILibHashtable_Root*)table)->table, ILibHashtable_EnumerateSink, &state); +} + +//! Clear the Hashtable, with a callback for each removed item +/*! + \param table Hashtable to clear + \param onClear The callback to dispatch for each item removed + \param user User state object to pass thru to the callback +*/ +void ILibHashtable_ClearEx(ILibHashtable table, ILibHashtable_OnDestroy onClear, void *user) +{ + ILibHashtable_ClearCallbackStruct state; + memset(&state, 0, sizeof(ILibHashtable_ClearCallbackStruct)); + + state.source = table; + state.onClear = onClear; + state.user = user; + ILibSparseArray_ClearEx(((ILibHashtable_Root*)table)->table, ILibHashtable_DestroyEx2, &state); +} +//! Clear the Hashtable +/*! + \param table Hashtable to clear +*/ +void ILibHashtable_Clear(ILibHashtable table) +{ + ILibSparseArray_ClearEx(((ILibHashtable_Root*)table)->table, NULL, NULL); +} +//! Change the hashing function used by the specified hashtable +/*! + \param table Hashtable to modify + \param hashFunc Handler for the hashing function to use +*/ +void ILibHashtable_ChangeHashFunc(ILibHashtable table, ILibHashtable_Hash_Func hashFunc) +{ + ((ILibHashtable_Root*)table)->hashFunc = hashFunc; +} +//! Change the bucketizer function used by the underlying Sparse Array for the specified Hashtable +/*! + \param table Hashtable to modify + \param bucketizer Handler for the bucketizer function to use +*/ +void ILibHashtable_ChangeBucketizer(ILibHashtable table, int bucketCount, ILibSparseArray_Bucketizer bucketizer) +{ + if(((ILibHashtable_Root*)table)->table != NULL) {ILibSparseArray_Destroy(((ILibHashtable_Root*)table)->table);} + ((ILibHashtable_Root*)table)->table = ILibSparseArray_Create(bucketCount, bucketizer); +} +ILibHashtable_Node* ILibHashtable_CreateNode(void* Key1, char* Key2, int Key2Len, void* Data) +{ + ILibHashtable_Node *node; + + if((node = (ILibHashtable_Node*)malloc(sizeof(ILibHashtable_Node))) == NULL) {ILIBCRITICALEXIT(254);} + memset(node, 0, sizeof(ILibHashtable_Node)); + node->Data = Data; + node->Key1 = Key1; + node->Key2Len = Key2Len; + if(Key2Len > 0) + { + if((node->Key2 = (char*)malloc(Key2Len))==NULL) {ILIBCRITICALEXIT(254);} + memcpy_s(node->Key2, Key2Len, Key2, Key2Len); + } + return(node); +} + +ILibHashtable_Node* ILibHashtable_GetEx(ILibHashtable table, void *Key1, char* Key2, int Key2Len, ILibHashtable_Flags flags) +{ + ILibHashtable_Node *retVal = NULL; + ILibHashtable_Root* root = (ILibHashtable_Root*)table; + int hash = root->hashFunc(Key1, Key2, Key2Len); + ILibHashtable_Node *node = (ILibHashtable_Node*)ILibSparseArray_Get(root->table, hash); + + if(node == NULL) + { + // Entry doesn't exist, so create a new entry + if((flags & ILibHashtable_Flags_ADD) == ILibHashtable_Flags_ADD) + { + retVal = ILibHashtable_CreateNode(Key1, Key2, Key2Len, NULL); + ILibSparseArray_Add(root->table, hash, retVal); + } + } + else + { + // There is a hash entry, so lets enumerate to see if we really have a match + ILibHashtable_Node *prev = NULL; + while(node!=NULL) + { + if(node->Key1 == Key1 && node->Key2Len == Key2Len && memcmp(node->Key2, Key2, Key2Len)==0) + { + break; + } + prev = node; + node = node->next; + } + if(node == NULL) + { + // There were no matches in the hashes that were returned + + if((flags & ILibHashtable_Flags_ADD) == ILibHashtable_Flags_ADD) + { + // Create a new entry, and append ourself to the hash results + retVal = ILibHashtable_CreateNode(Key1, Key2, Key2Len, NULL); + prev->next = retVal; + retVal->prev = prev; + } + } + else + { + // There was a match! Update the value and return the old value + retVal = node; + if((flags & ILibHashtable_Flags_REMOVE) == ILibHashtable_Flags_REMOVE) + { + if(node->prev == NULL) + { + // This is the first entry, so we'll have to remove the entry from the SparseArray + ILibSparseArray_Remove(root->table, hash); + } + else + { + node->prev->next = node->next; + if(node->next != NULL) {node->next->prev = node->prev;} + } + } + } + } + return(retVal); +} +//! Add/Modify an entry in the Hashtable + +//! Key1 and Key2 can both be NULL, but not at the same time. +/*! + \param table Hashtable to perform the operation on + \param Key1 Address Key [Can be NULL] + \param Key2 String Key [Can be NULL] + \param Key2Len String Key LEngth + \param Data New Element Value + \return Old Element Value [NULL if it didn't exist] +*/ +void* ILibHashtable_Put(ILibHashtable table, void *Key1, char* Key2, int Key2Len, void* Data) +{ + ILibHashtable_Node *node = ILibHashtable_GetEx(table, Key1, Key2, Key2Len, ILibHashtable_Flags_ADD); + void *retVal = node->Data; + node->Data = Data; + return(retVal); +} +//! Get the specified Element Value associated with the specified Key(s). + +//! Key1 and Key2 can both be NULL, but not at the same time. +/*! + \param table Hashtable to fetch the value from + \param Key1 Address Key [Can be NULL] + \param Key2 String Key [Can be NULL] + \param Key2Len String Key Length + \return Element Value [NULL if it doesn't exist] +*/ +void* ILibHashtable_Get(ILibHashtable table, void *Key1, char* Key2, int Key2Len) +{ + ILibHashtable_Node *node = ILibHashtable_GetEx(table, Key1, Key2, Key2Len, ILibHashtable_Flags_NONE); + return(node != NULL ? node->Data : NULL); +} +//! Remove an entry from the hashtable + +//! Both Key1 and Key can be NULL, but not at the same time +/*! + \param table Hashtable to remove the entry from + \param Key1 Address Key [Can be NULL] + \param Key2 String Key [Can be NULL] + \param Key2Len String Key Length + \return Element Value that was removed [NULL if it didn't exist] +*/ +void* ILibHashtable_Remove(ILibHashtable table, void *Key1, char* Key2, int Key2Len) +{ + ILibHashtable_Node *node = ILibHashtable_GetEx(table, Key1, Key2, Key2Len, ILibHashtable_Flags_REMOVE); + void *retVal = node != NULL ? node->Data : NULL; + if(node != NULL) + { + if(node->Key2 != NULL) {free(node->Key2);} + free(node); + } + return(retVal); +} +//! Use the specified hashtable as a synchronization lock, and acquire it +/*! + \param table Hashtable to use as a synchronization lock +*/ +void ILibHashtable_Lock(ILibHashtable table) +{ + ILibSparseArray_Lock(((ILibHashtable_Root*)table)->table); +} +//! Use the specified hashtable as a synchronization lock, and release it +/*! + \param table Hashtable to use as a synchronization lock +*/ +void ILibHashtable_UnLock(ILibHashtable table) +{ + ILibSparseArray_UnLock(((ILibHashtable_Root*)table)->table); +} + + +//! Allocates a new SparseArray using the specified number of buckets and the given bucketizer method, which maps indexes to buckets. +/*! + \param numberOfBuckets Number of buckets to initialize + \param bucketizer Event handler to be triggered whenever an index value needs to be hashed into a bucket value + \param userMemorySize Size of extra memory to allocate for user state + \return ILibSparseArray object +*/ +ILibSparseArray ILibSparseArray_CreateWithUserMemory(int numberOfBuckets, ILibSparseArray_Bucketizer bucketizer, int userMemorySize) +{ + ILibSparseArray_Root *retVal = (ILibSparseArray_Root*)ILibMemory_Allocate(sizeof(ILibSparseArray_Root), userMemorySize, NULL, NULL); + + sem_init(&(retVal->LOCK), 0, 1); + retVal->bucketSize = numberOfBuckets; + retVal->bucketizer = bucketizer; + retVal->bucket = (ILibSparseArray_Node*)malloc(numberOfBuckets * sizeof(ILibSparseArray_Node)); + retVal->userMemorySize = userMemorySize; + memset(retVal->bucket, 0, numberOfBuckets * sizeof(ILibSparseArray_Node)); + + return(retVal); +} + +//! Allocates a new SparseArray using the same parameters as an existing SparseArray +/*! + \param source The ILibSparseArray to duplicate parameters from + \return New ILibSparseArray object +*/ +ILibSparseArray ILibSparseArray_CreateEx(ILibSparseArray source) +{ + ILibSparseArray retVal = ILibSparseArray_CreateWithUserMemory(((ILibSparseArray_Root*)source)->bucketSize, ((ILibSparseArray_Root*)source)->bucketizer, ((ILibSparseArray_Root*)source)->userMemorySize); + return(retVal); +} + + +int ILibSparseArray_Comparer(void *obj1, void *obj2) +{ + if(((ILibSparseArray_Node*)obj1)->index == ((ILibSparseArray_Node*)obj2)->index) + { + return(0); + } + else + { + return(((ILibSparseArray_Node*)obj1)->index < ((ILibSparseArray_Node*)obj2)->index ? -1 : 1); + } +} +//! Populates the "index" in the SparseArray. If that index is already defined, the new value is saved, and the old value is returned. +/*! + \param sarray Sparse Array Object + \param index Index to initialize/set + \param data Value to set to the index + \return Old value of index if it was initialized, NULL otherwise +*/ +void* ILibSparseArray_Add(ILibSparseArray sarray, int index, void *data) +{ + void* retVal = NULL; + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + int i = root->bucketizer(index); + + if(root->bucket[i].index == 0 && root->bucket[i].ptr == NULL) + { + // No Entry Exists + root->bucket[i].index = index; + root->bucket[i].ptr = data; + } + else if(root->bucket[i].index < 0) + { + // Need to use Linked List + ILibSparseArray_Node* n = (ILibSparseArray_Node*)malloc(sizeof(ILibSparseArray_Node)); + if (n == NULL) ILIBCRITICALEXIT(254); + n->index = index; + n->ptr = data; + n = (ILibSparseArray_Node*)ILibLinkedList_SortedInsert(root->bucket[i].ptr, &ILibSparseArray_Comparer, n); + if(n!=NULL) + { + // This duplicates an entry already in the list... Updated with new value, pass back the old + retVal = n->ptr; + free(n); + } + } + else + { + // No Linked List... We either need to create one, or check to see if this is possibly a duplicate entry + if(root->bucket[i].index == index) + { + // Today's our lucky day! We can just replace the value! (No return value) + retVal = root->bucket[i].ptr; + root->bucket[i].ptr = data; + } + else + { + // We need to create a linked list, add the old value, then insert our new value (No return value) + ILibSparseArray_Node* n = (ILibSparseArray_Node*)malloc(sizeof(ILibSparseArray_Node)); + if (n == NULL) ILIBCRITICALEXIT(254); + n->index = root->bucket[i].index; + n->ptr = root->bucket[i].ptr; + + root->bucket[i].index = -1; + root->bucket[i].ptr = ILibLinkedList_Create(); + ILibLinkedList_AddHead(root->bucket[i].ptr, n); + + n = (ILibSparseArray_Node*)malloc(sizeof(ILibSparseArray_Node)); + if (n == NULL) ILIBCRITICALEXIT(254); + n->index = index; + n->ptr = data; + ILibLinkedList_SortedInsert(root->bucket[i].ptr, &ILibSparseArray_Comparer, n); + } + } + return(retVal); +} + +void* ILibSparseArray_GetEx(ILibSparseArray sarray, int index, int remove) +{ + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + void *retVal = NULL; + int i = root->bucketizer(index); + + if(root->bucket[i].index == index) + { + // Direct Match + retVal = root->bucket[i].ptr; + if(remove!=0) + { + root->bucket[i].ptr = NULL; + root->bucket[i].index = 0; + } + } + else if(root->bucket[i].index < 0) + { + // Need to check the Linked List + void *listNode = ILibLinkedList_GetNode_Search(root->bucket[i].ptr, &ILibSparseArray_Comparer, (void*)&index); + retVal = listNode != NULL ? ((ILibSparseArray_Node*)ILibLinkedList_GetDataFromNode(listNode))->ptr : NULL; + if(remove!=0 && listNode!=NULL) + { + free(ILibLinkedList_GetDataFromNode(listNode)); + ILibLinkedList_Remove(listNode); + if(ILibLinkedList_GetCount(root->bucket[i].ptr)==0) + { + ILibLinkedList_Destroy(root->bucket[i].ptr); + root->bucket[i].ptr = NULL; + root->bucket[i].index = 0; + } + } + } + else + { + // Whatever is in this bucket doesn't match what we're looking for + retVal = NULL; + } + return(retVal); +} +//! Fetches the value at the index. NULL if the index is not defined +/*! + \param sarray Sparse Array Object + \param index Index to fetch + \return Value at specified index, NULL if it was not defined +*/ +void* ILibSparseArray_Get(ILibSparseArray sarray, int index) +{ + return(ILibSparseArray_GetEx(sarray, index, 0)); +} +//! Removes an index from the SparseArray (if it exists), and returns the associated value if it does exist. +/*! + \param sarray Sparse Array Object + \param index Index value to remove + \return Value defined at Index, NULL if it was not defined +*/ +void* ILibSparseArray_Remove(ILibSparseArray sarray, int index) +{ + return(ILibSparseArray_GetEx(sarray, index, 1)); +} +//! Clones the contents of the given Sparse Array into a new Sparse Array +/*! + \param sarray The Sparse Array to clone + \return Cloned Sparse Array +*/ +ILibSparseArray ILibSparseArray_Move(ILibSparseArray sarray) +{ + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + ILibSparseArray_Root *retVal = (ILibSparseArray_Root*)ILibSparseArray_CreateEx(sarray); + memcpy_s(retVal->bucket, root->bucketSize * sizeof(ILibSparseArray_Node), root->bucket, root->bucketSize * sizeof(ILibSparseArray_Node)); + return(retVal); +} +//! Clears the SparseArray, and dispatches an event for each defined index +/*! + \param sarray Sparse Array Object + \param onClear Event to dispatch for defined indexes + \param user Custom user state data + \param nonZeroWillDelete If this is zero, will just enumerato, otherwise will delete +*/ +void ILibSparseArray_ClearEx2(ILibSparseArray sarray, ILibSparseArray_OnValue onClear, void *user, int nonZeroWillDelete) +{ + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + int i; + + for(i=0;ibucketSize;++i) + { + if(root->bucket[i].ptr != NULL) + { + if(root->bucket[i].index < 0) + { + // There is a linked list here + void *node = ILibLinkedList_GetNode_Head(root->bucket[i].ptr); + while(node != NULL) + { + ILibSparseArray_Node *sn = (ILibSparseArray_Node*)ILibLinkedList_GetDataFromNode(node); + if(onClear!=NULL) { onClear(sarray, sn->index, sn->ptr, user);} + if (nonZeroWillDelete != 0) { free(sn); } + node = ILibLinkedList_GetNextNode(node); + } + if (nonZeroWillDelete != 0) { ILibLinkedList_Destroy(root->bucket[i].ptr); } + } + else + { + // No Linked List, just a direct map + if(onClear!=NULL) + { + onClear(sarray, root->bucket[i].index, root->bucket[i].ptr, user); + } + } + if (nonZeroWillDelete != 0) + { + root->bucket[i].index = 0; + root->bucket[i].ptr = NULL; + } + } + } +} +//! Frees the resources used by an ILibSparseArray, and dispatches an event for each defined index +/*! + \param sarray Sparse Array Object + \param onDestroy Event handler to dispatch for each defined entry + \param user Custom user state data +*/ +void ILibSparseArray_DestroyEx(ILibSparseArray sarray, ILibSparseArray_OnValue onDestroy, void *user) +{ + ILibSparseArray_ClearEx(sarray, onDestroy, user); + sem_destroy(&((ILibSparseArray_Root*)sarray)->LOCK); + free(((ILibSparseArray_Root*)sarray)->bucket); + free(sarray); +} +//! Frees the resources used by an ILibSparseArray +/*! + \param sarray Sparse Array Object to free +*/ +void ILibSparseArray_Destroy(ILibSparseArray sarray) +{ + ILibSparseArray_DestroyEx(sarray, NULL, NULL); +} +//! Use the Sparse Array as a synchronization lock, and acquire it +/*! + \param sarray Sparse Array to use as a synchronization lock +*/ +void ILibSparseArray_Lock(ILibSparseArray sarray) +{ + sem_wait(&(((ILibSparseArray_Root*)sarray)->LOCK)); +} +//! Use the Sparse Array as a synchronization lock, and release it +/*! + \param sarray Sparse Array to use as a synchronization lock +*/ +void ILibSparseArray_UnLock(ILibSparseArray sarray) +{ + sem_post(&(((ILibSparseArray_Root*)sarray)->LOCK)); +} + +int ILibString_IndexOfFirstWhiteSpace(const char *inString, int inStringLength) +{ + //CR, LF, space, tab + int i = 0; + for(i = 0;i < inStringLength; ++i) + { + if (inString[i] == 13 || inString[i] == 10 || inString[i] == 9 || inString[i] == 32) return(i); + } + return(-1); +} + +/*! \fn ILibString_EndsWithEx(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength, int caseSensitive) +\brief Determines if a string ends with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param endWithString The substring to match +\param endWithStringLength The length of \a startsWithString +\param caseSensitive 0 if the matching is case-insensitive +\return Non-zero if the string starts with the substring +*/ +int ILibString_EndsWithEx(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength, int caseSensitive) +{ + int RetVal = 0; + if (inStringLength < 0) { inStringLength = (int)strnlen_s(inString, sizeof(ILibScratchPad)); } + if (endWithStringLength < 0) { endWithStringLength = (int)strnlen_s(endWithString, sizeof(ILibScratchPad)); } + + if (inStringLength>=endWithStringLength) + { + if (caseSensitive!=0 && memcmp(inString+inStringLength-endWithStringLength,endWithString,endWithStringLength)==0) RetVal = 1; + else if (caseSensitive==0 && strncasecmp(inString+inStringLength-endWithStringLength,endWithString,endWithStringLength)==0) RetVal = 1; + } + return(RetVal); +} +/*! \fn ILibString_EndsWith(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength) +\brief Determines if a string ends with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param endWithString The substring to match +\param endWithStringLength The length of \a startsWithString +\return Non-zero if the string starts with the substring +*/ +int ILibString_EndsWith(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength) +{ + return(ILibString_EndsWithEx(inString,inStringLength,endWithString,endWithStringLength,1)); +} +/*! \fn ILibString_StartsWithEx(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength, int caseSensitive) +\brief Determines if a string starts with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param startsWithString The substring to match +\param startsWithStringLength The length of \a startsWithString +\param caseSensitive Non-zero if match is to be case sensitive +\return Non-zero if the string starts with the substring +*/ +int ILibString_StartsWithEx(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength, int caseSensitive) +{ + int RetVal = 0; + if (inStringLength>=startsWithStringLength) + { + if (caseSensitive!=0 && memcmp(inString,startsWithString,startsWithStringLength)==0) RetVal = 1; + else if (caseSensitive==0 && strncasecmp(inString,startsWithString,startsWithStringLength)==0) RetVal = 1; + } + return(RetVal); +} +/*! \fn ILibString_StartsWith(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength) +\brief Determines if a string starts with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param startsWithString The substring to match +\param startsWithStringLength The length of \a startsWithString +\return Non-zero if the string starts with the substring +*/ +int ILibString_StartsWith(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength) +{ + return(ILibString_StartsWithEx(inString,inStringLength,startsWithString,startsWithStringLength,1)); +} +/*! \fn ILibString_IndexOfEx(const char *inString, int inStringLength, const char *indexOf, int indexOfLength, int caseSensitive) +\brief Returns the position index of the first occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param indexOf The substring to search for +\param indexOfLength The length of \a lastIndexOf +\param caseSensitive Non-zero if the match is case sensitive +\return Position index of first occurance. -1 if the substring is not found +*/ +int ILibString_IndexOfEx(const char *inString, int inStringLength, const char *indexOf, int indexOfLength, int caseSensitive) +{ + int RetVal = -1; + int index = 0; + + while (inStringLength-index >= indexOfLength) + { + if (caseSensitive!=0 && memcmp(inString+index,indexOf,indexOfLength)==0) + { + RetVal = index; + break; + } + else if (caseSensitive==0 && strncasecmp(inString+index,indexOf,indexOfLength)==0) + { + RetVal = index; + break; + } + ++index; + } + return(RetVal); +} +/*! \fn ILibString_IndexOf(const char *inString, int inStringLength, const char *indexOf, int indexOfLength) +\brief Returns the position index of the first occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param indexOf The substring to search for +\param indexOfLength The length of \a lastIndexOf +\return Position index of first occurance. -1 if the substring is not found +*/ +int ILibString_IndexOf(const char *inString, int inStringLength, const char *indexOf, int indexOfLength) +{ + return(ILibString_IndexOfEx(inString,inStringLength,indexOf,indexOfLength,1)); +} +/*! \fn ILibString_LastIndexOfEx(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength, int caseSensitive) +\brief Returns the position index of the last occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param lastIndexOf The substring to search for +\param lastIndexOfLength The length of \a lastIndexOf +\param caseSensitive 0 for case insensitive matching, non-zero for case-sensitive matching +\return Position index of last occurance. -1 if the substring is not found +*/ +int ILibString_LastIndexOfEx(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength, int caseSensitive) +{ + int RetVal = -1; + int index = (inStringLength < 0 ? (int)strnlen_s(inString, sizeof(ILibScratchPad)) : inStringLength) - (lastIndexOfLength < 0 ? (int)strnlen_s(lastIndexOf, sizeof(ILibScratchPad)) : lastIndexOfLength); + + while (index >= 0) + { + if (caseSensitive!=0 && memcmp(inString+index,lastIndexOf,lastIndexOfLength)==0) + { + RetVal = index; + break; + } + else if (caseSensitive==0 && strncasecmp(inString+index,lastIndexOf,lastIndexOfLength)==0) + { + RetVal = index; + break; + } + --index; + } + return(RetVal); +} +/*! \fn ILibString_LastIndexOf(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength) +\brief Returns the position index of the last occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param lastIndexOf The substring to search for +\param lastIndexOfLength The length of \a lastIndexOf +\return Position index of last occurance. -1 if the substring is not found +*/ +int ILibString_LastIndexOf(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength) +{ + return(ILibString_LastIndexOfEx(inString,inStringLength,lastIndexOf,lastIndexOfLength,1)); +} +/*! \fn ILibString_Replace(const char *inString, int inStringLength, const char *replaceThis, int replaceThisLength, const char *replaceWithThis, int replaceWithThisLength) +\brief Replaces all occurances of a given substring that occur in the input string with another substring +\par +\b Note: \a Returned string must be freed +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param replaceThis The substring to replace +\param replaceThisLength The length of \a replaceThis +\param replaceWithThis The substring to substitute for \a replaceThis +\param replaceWithThisLength The length of \a replaceWithThis +\return New string with replaced values +*/ +char *ILibString_Replace(const char *inString, int inStringLength, const char *replaceThis, int replaceThisLength, const char *replaceWithThis, int replaceWithThisLength) +{ + char *RetVal; + int RetValLength; + struct parser_result *pr; + struct parser_result_field *prf; + int mallocSize; + + pr = ILibParseString((char*)inString, 0, inStringLength,(char*)replaceThis,replaceThisLength); + RetValLength = (pr->NumResults-1) * replaceWithThisLength; // string that will be inserted + RetValLength += (inStringLength - ((pr->NumResults-1) * replaceThisLength)); // Add the length of the rest of the string + mallocSize = RetValLength + 1; + + if ((RetVal = (char*)malloc(mallocSize)) == NULL) ILIBCRITICALEXIT(254); + RetVal[RetValLength]=0; + + RetValLength = 0; + prf = pr->FirstResult; + while (prf != NULL) + { + memcpy_s(RetVal + RetValLength, mallocSize - RetValLength, prf->data, prf->datalength); + RetValLength += prf->datalength; + + if (prf->NextResult!=NULL) + { + memcpy_s(RetVal + RetValLength, mallocSize - RetValLength, (char*)replaceWithThis, replaceWithThisLength); + RetValLength += replaceWithThisLength; + } + prf = prf->NextResult; + } + ILibDestructParserResults(pr); + return(RetVal); +} +char* ILibString_Cat_s(char *destination, size_t destinationSize, char *source) +{ + size_t sourceLen = strnlen_s(source, destinationSize); + int i; + int x = -1; + for (i = 0; i < (int)destinationSize - 1; ++i) + { + if (destination[i] == 0) { x = i; break; } + } + if (x < 0 || ((x + sourceLen + 1 )> destinationSize)) { ILIBCRITICALEXIT(254); } + memcpy_s(destination + x, destinationSize - x, source, sourceLen); + destination[x + sourceLen] = 0; + return(destination); +} +#ifndef WIN32 +#ifdef ILib_need_sprintf_s +int sprintf_s(void *dest, size_t destSize, const char *format, ...) +{ + int len = 0; + va_list argptr; + + va_start(argptr, format); + len += vsnprintf(dest + len, destSize - len, format, argptr); + va_end(argptr); + + return(len < destSize ? len : -1); +} +#endif +int ILibMemory_Copy_s(void *destination, size_t destinationSize, const void *source, size_t sourceLength) +{ + if (destinationSize >= sourceLength) + { + memcpy(destination, source, sourceLength); + } + else + { + ILIBCRITICALEXIT(254); + } + return(0); +} +int ILibMemory_Move_s(void *destination, size_t destinationSize, void *source, size_t sourceLength) +{ + if (destinationSize >= sourceLength) + { + memmove(destination, source, sourceLength); + } + else + { + ILIBCRITICALEXIT(254); + } + return(0); +} +#endif +int ILibString_n_Copy_s(char *destination, size_t destinationSize, char *source, size_t maxCount) +{ + size_t count = strnlen_s(source, maxCount == (size_t)-1 ? (destinationSize-1) : maxCount); + if ((count + 1) > destinationSize) + { + ILIBCRITICALEXIT(254); + } + memcpy_s(destination, destinationSize, source, count); + destination[count] = 0; + return(0); +} +int ILibString_Copy_s(char *destination, size_t destinationSize, char *source) +{ + size_t sourceLen = strnlen_s(source, destinationSize); + + if ((sourceLen + 1) > destinationSize) { ILIBCRITICALEXIT(254); } + memcpy_s(destination, destinationSize, source, sourceLen); + destination[sourceLen] = 0; + return(0); +} + +char* ILibString_Cat(const char *inString1, int inString1Len, const char *inString2, int inString2Len) +{ + char *RetVal; + if (inString1Len < 0) { inString1Len = (int)strnlen_s(inString1, sizeof(ILibScratchPad)); } + if (inString2Len < 0) { inString2Len = (int)strnlen_s(inString2, sizeof(ILibScratchPad)); } + if ((RetVal = (char*)malloc(inString1Len + inString2Len+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(RetVal, inString1Len + inString2Len + 1, (char*)inString1, inString1Len); + memcpy_s(RetVal + inString1Len, inString2Len + 1, (char*)inString2, inString2Len); + RetVal[inString1Len + inString2Len]=0; + return(RetVal); +} +char* ILibString_Copy(const char *inString, int length) +{ + char *RetVal; + if (length<0) length = (int)strnlen_s(inString, sizeof(ILibScratchPad)); + if ((RetVal = (char*)malloc(length + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(RetVal, length + 1, (char*)inString, length); + RetVal[length] = 0; + return(RetVal); +} +/*! \fn ILibString_ToUpper(const char *inString, int length) +\brief Coverts the given string to upper case +\par +\b Note: \a Returned string must be freed +\param inString Pointer to char* to convert to upper case +\param length The length of \a inString +\return Converted string +*/ +char *ILibString_ToUpper(const char *inString, int length) +{ + char *RetVal; + if ((RetVal = (char*)malloc(length + 1)) == NULL) ILIBCRITICALEXIT(254); + RetVal[length]=0; + ILibToUpper(inString, length, RetVal); + return(RetVal); +} +/*! \fn ILibString_ToLower(const char *inString, int length) +\brief Coverts the given string to lower case +\par +\b Note: \a Returned string must be freed +\param inString Pointer to char* to convert to lower case +\param length The length of \a inString +\return Converted string +*/ +char *ILibString_ToLower(const char *inString, int length) +{ + char *RetVal; + if ((RetVal = (char*)malloc(length + 1)) == NULL) ILIBCRITICALEXIT(254); + RetVal[length] = 0; + ILibToLower(inString, length, RetVal); + return(RetVal); +} +/*! \fn ILibReadFileFromDiskEx(char **Target, char *FileName) +\brief Reads a file into a char * +\par +\b Note: \a Target must be freed +\param Target Pointer to char* that will contain the data +\param FileName Filename of the file to read +\return length of the data read +*/ +int ILibReadFileFromDiskEx(char **Target, char *FileName) +{ + char *buffer; + int SourceFileLength; + FILE *SourceFile = NULL; + +#ifdef WIN32 + fopen_s(&SourceFile, FileName, "rb"); +#else + SourceFile = fopen(FileName, "rb"); +#endif + if (SourceFile == NULL) return(0); + + fseek(SourceFile, 0, SEEK_END); + SourceFileLength = (int)ftell(SourceFile); + fseek(SourceFile, 0, SEEK_SET); + if ((buffer = (char*)malloc(SourceFileLength)) == NULL) ILIBCRITICALEXIT(254); + SourceFileLength = (int)fread(buffer, sizeof(char), (size_t)SourceFileLength,SourceFile); + fclose(SourceFile); + + *Target = buffer; + return(SourceFileLength); +} + + +/*! \fn ILibReadFileFromDisk(char *FileName) +\brief Reads a file into a char * +\par +\b Note: Data must be null terminated +\param FileName Filename of the file to read +\return data read +*/ +char *ILibReadFileFromDisk(char *FileName) +{ + char *RetVal = NULL; + ILibReadFileFromDiskEx(&RetVal,FileName); + return(RetVal); +} + +/*! \fn ILibWriteStringToDisk(char *FileName, char *data) +\brief Writes a null terminated string to disk +\par +\b Note: Files that already exist will be overwritten +\param FileName Filename of the file to write +\param data data to write +*/ +void ILibWriteStringToDisk(char *FileName, char *data) +{ + ILibWriteStringToDiskEx(FileName, data, (int)strnlen_s(data, 65535)); +} +void ILibWriteStringToDiskEx(char *FileName, char *data, int dataLen) +{ + FILE *SourceFile = NULL; + +#ifdef WIN32 + fopen_s(&SourceFile, FileName, "wb"); +#else + SourceFile = fopen(FileName, "wb"); +#endif + + if (SourceFile != NULL) + { + fwrite(data, sizeof(char), dataLen, SourceFile); + fclose(SourceFile); + } +} +void ILibAppendStringToDiskEx(char *FileName, char *data, int dataLen) +{ + FILE *SourceFile = NULL; + +#ifdef WIN32 + fopen_s(&SourceFile, FileName, "ab"); +#else + SourceFile = fopen(FileName, "ab"); +#endif + + if (SourceFile != NULL) + { + fwrite(data, sizeof(char), dataLen, SourceFile); + fclose(SourceFile); + } +} +void ILibDeleteFileFromDisk(char *FileName) +{ +#if defined(WIN32) + DeleteFile((LPCTSTR)FileName); +#elif defined(_POSIX) + remove(FileName); +#endif +} + +void ILibGetDiskFreeSpace(void *i64FreeBytesToCaller, void *i64TotalBytes) +{ +#if defined (WIN32) + GetDiskFreeSpaceEx (NULL, (PULARGE_INTEGER)i64FreeBytesToCaller, (PULARGE_INTEGER)i64TotalBytes, NULL); +#elif defined (_POSIX) + struct statfs stfs; + statfs(".", &stfs); + *((uint64_t *)i64FreeBytesToCaller) = (uint64_t)stfs.f_bavail * stfs.f_bsize; + *((uint64_t *)i64TotalBytes)= (uint64_t)stfs.f_blocks * stfs.f_bsize; +#endif +} +long ILibGetTimeStamp() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +} +int ILibIsLittleEndian() { int v = 1; return (((char*)&v)[0] == 1 ? 1 : 0); } +int ILibGetCurrentTimezoneOffset_Minutes() +{ +#if defined(_WIN32) + int offset; + int dl = 0; + long tz = 0; + + tzset(); + _get_timezone(&tz); + offset = -1 * ((int)tz / 60); + _get_daylight(&dl); + offset += (dl * 60); + return offset; +#else + int offset; + + tzset(); + offset = -1 * ((int)timezone / 60); + offset += (daylight * 60); + return offset; +#endif +} +int ILibIsDaylightSavingsTime() +{ +#if defined(_WIN32) + int dl = 0; + tzset(); + _get_daylight(&dl); + return dl; +#else + tzset(); + return daylight; +#endif +} + +int ILibGetLocalTime(char *dest, int destLen) +{ + int retVal; + struct timeval tv; +#ifdef WIN32 + struct tm t; +#endif + gettimeofday(&tv, NULL); +#ifdef WIN32 + localtime_s(&t, (time_t*)&(tv.tv_sec)); + retVal = (int)strftime(dest, destLen, "%Y-%m-%d %I:%M:%S %p", &t); +#else + retVal = (int)strftime(dest, destLen, "%F %r", localtime((time_t*)&(tv.tv_sec))); +#endif + return(retVal); +} +#if defined(WIN32) +void ILibGetTimeOfDay(struct timeval *tp) +{ + __time64_t t; + _time64(&t); + tp->tv_usec = 0; + tp->tv_sec = (long)t; +} +#endif + +#ifdef _MINCORE +static long long ILibGetUptimeUpperEmulation1; +static long long ILibGetUptimeUpperEmulation2; +long long ILibGetUptime() +{ + // Windows XP with rollover prevention + long long r; + r = (long long)GetTickCount(); + if (r < ILibGetUptimeUpperEmulation1) ILibGetUptimeUpperEmulation2 += ((long long)1) << 32; + ILibGetUptimeUpperEmulation1 = r; + r += ILibGetUptimeUpperEmulation2; + return r; +} +#elif WIN32 +static int ILibGetUptimeFirst = 1; +static ULONGLONG (*pILibGetUptimeGetTickCount64)() = NULL; +static long long ILibGetUptimeUpperEmulation1; +static long long ILibGetUptimeUpperEmulation2; +long long ILibGetUptime() +{ + long long r; + + // Windows 7 & Vista + if (ILibGetUptimeFirst) { + + wchar_t w_dllPath[MAX_PATH]; + if (GetWindowsDllPath("\\KERNEL32.DLL", w_dllPath) == FALSE) return 0; + + HMODULE hlib = LoadLibrary(w_dllPath); + if (hlib == NULL) return 0; + pILibGetUptimeGetTickCount64 = (ULONGLONG(*)())GetProcAddress(hlib, "GetTickCount64"); + ILibGetUptimeFirst = 0; + } + if (pILibGetUptimeGetTickCount64 != NULL) return pILibGetUptimeGetTickCount64(); + + // Windows XP with rollover prevention + r = (long long)GetTickCount(); // Static Analyser reports this could roll over, but that's why this block is doing rollover prevention + if (r < ILibGetUptimeUpperEmulation1) ILibGetUptimeUpperEmulation2 += ((long long)1) << 32; + ILibGetUptimeUpperEmulation1 = r; + r += ILibGetUptimeUpperEmulation2; + return r; +} +#elif _MIPS +long long ILibGetUptime() +{ + // On MIPS routers, we just used the current clock. This could messup if the router's clock is changed. + time_t t; + time(&t); + return (t * 1000); +} +#else +long long ILibGetUptime() +{ + struct timespec ts; + memset(&ts, 0, sizeof ts); + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((long long)ts.tv_sec) * 1000) + ((((long long)ts.tv_nsec) / 1000) % 1000); +} +#endif + +int ILibReaderWriterLock_ReadLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int BlockRead = 0; + int RetVal = 0; + + int AlreadyExiting = 0; + + sem_wait(&(rw->CounterLock)); + AlreadyExiting = rw->Exit; + if (rw->Exit==0) + { + BlockRead = rw->PendingWriters>0; + if (BlockRead==0) + { + ++rw->ActiveReaders; + } + else + { + ++rw->WaitingReaders; + } + } + sem_post(&(rw->CounterLock)); + + if (BlockRead!=0) + { + sem_wait(&(rw->ReadLock)); + } + + // + // We made it through! + // + RetVal = rw->Exit; + if (AlreadyExiting == 0) { sem_post(&(rw->ExitLock)); } + return(RetVal); +} +int ILibReaderWriterLock_ReadUnLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int SignalWriter = 0; + + sem_wait(&(rw->CounterLock)); + + if (rw->Exit!=0) + { + // + // Unlock, and just abort + // + sem_post(&(rw->CounterLock)); + return(1); + } + + + --rw->ActiveReaders; + SignalWriter = (rw->ActiveReaders==0 && rw->PendingWriters>0); + sem_post(&(rw->CounterLock)); + + if (SignalWriter) { sem_post(&(rw->WriteLock)); } + + sem_wait(&(rw->ExitLock)); // This won't block. We're just keeping the semaphore value stable. + return(0); +} +int ILibReaderWriterLock_WriteLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int BlockWrite = 0; + int RetVal = 0; + int AlreadyExiting = 0; + + sem_wait(&(rw->CounterLock)); + AlreadyExiting = rw->Exit; + if (rw->Exit==0) + { + ++rw->PendingWriters; + BlockWrite = (rw->ActiveReaders>0 || rw->PendingWriters>1); + } + sem_post(&(rw->CounterLock)); + + if (BlockWrite) + { + sem_wait(&(rw->WriteLock)); + } + + // + // We made it through! + // + RetVal = rw->Exit; + if (AlreadyExiting==0) + { + sem_post(&(rw->ExitLock)); + } + return(RetVal); +} +int ILibReaderWriterLock_WriteUnLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int SignalAnotherWriter = 0; + int SignalReaders = 0; + + sem_wait(&(rw->CounterLock)); + + if (rw->Exit!=0) + { + // + // Unlock, and just abort + // + sem_post(&(rw->CounterLock)); + return(1); + } + + --rw->PendingWriters; + if (rw->PendingWriters>0) + { + // + // Still more Writers, so flag it. + // + SignalAnotherWriter=1; + } + else + { + // + // No more writers, so we can flag the waiting readers + // + while (rw->WaitingReaders>0) + { + --rw->WaitingReaders; + ++rw->ActiveReaders; + ++SignalReaders; + } + } + sem_post(&(rw->CounterLock)); + + if (SignalAnotherWriter) + { + sem_post(&(rw->WriteLock)); + } + else + { + while (SignalReaders>0) + { + --SignalReaders; + sem_post(&(rw->ReadLock)); + } + } + + sem_wait(&(rw->ExitLock)); // This won't block. We're just keeping the semaphore value stable. + return(0); +} +void ILibReaderWriterLock_Destroy2(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + + int NumberOfWaitingLocks = 0; + + sem_wait(&(rw->CounterLock)); + rw->Exit = 1; + + NumberOfWaitingLocks += rw->WaitingReaders; + if (rw->PendingWriters>1) + { + NumberOfWaitingLocks += (rw->PendingWriters-1); + } + + while (rw->WaitingReaders>0) + { + sem_post(&(rw->ReadLock)); + --rw->WaitingReaders; + } + while (rw->PendingWriters>1) + { + sem_post(&(rw->WriteLock)); + --rw->PendingWriters; + } + sem_post(&(rw->CounterLock)); + + + while (NumberOfWaitingLocks>0) + { + sem_wait(&(rw->ExitLock)); + --NumberOfWaitingLocks; + } + + sem_destroy(&(rw->ReadLock)); + sem_destroy(&(rw->WriteLock)); + sem_destroy(&(rw->CounterLock)); + sem_destroy(&(rw->ExitLock)); +} +void ILibReaderWriterLock_Destroy(ILibReaderWriterLock rwLock) +{ + ILibReaderWriterLock_Destroy2(rwLock); + free(rwLock); +} +ILibReaderWriterLock ILibReaderWriterLock_Create() +{ + struct ILibReaderWriterLock_Data *RetVal; + if ((RetVal = (struct ILibReaderWriterLock_Data*)malloc(sizeof(struct ILibReaderWriterLock_Data))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct ILibReaderWriterLock_Data)); + + sem_init(&(RetVal->CounterLock),0,1); + sem_init(&(RetVal->ReadLock),0,0); + sem_init(&(RetVal->WriteLock),0,0); + sem_init(&(RetVal->ExitLock),0,0); + RetVal->Destroy = &ILibReaderWriterLock_Destroy2; + return((ILibReaderWriterLock)RetVal); +} +ILibReaderWriterLock ILibReaderWriterLock_CreateEx(void *chain) +{ + struct ILibReaderWriterLock_Data *RetVal = (struct ILibReaderWriterLock_Data*)ILibReaderWriterLock_Create(); + ILibChain_SafeAdd(chain,RetVal); + return(RetVal); +} + + +char* ILibTime_Serialize(time_t timeVal) +{ +#if defined(_WIN32) + struct tm T; + char *RetVal; + if ((RetVal = (char*)malloc(20)) == NULL) ILIBCRITICALEXIT(254); + if (localtime_s(&T, &timeVal) != 0) ILIBCRITICALEXIT(253); + sprintf_s(RetVal, 20, "%04d-%02d-%02dT%02d:%02d:%02d", + T.tm_year + 1900, + T.tm_mon + 1, + T.tm_mday, + T.tm_hour, T.tm_min, T.tm_sec); +#else + struct tm *T; + char *RetVal; + if ((RetVal = (char*)malloc(20)) == NULL) ILIBCRITICALEXIT(254); + T = localtime(&timeVal); + if (T == NULL) ILIBCRITICALEXIT(253); + sprintf_s(RetVal, 20, "%04d-%02d-%02dT%02d:%02d:%02d", + T->tm_year + 1900, + T->tm_mon + 1, + T->tm_mday, + T->tm_hour, T->tm_min, T->tm_sec); +#endif + + return(RetVal); +} +int ILibTime_ValidateTimePortion(char *timeString) +{ + // + // Verify the format is hh:mm:ss+hh:mm + // hh:mm:ss-hh:mm + // hh:mm:ssZ + // hh:mm:ss + // + char *temp; + struct parser_result *pr; + int RetVal = 0; + int length = (int)strnlen_s(timeString, 20); + if (length==8 || length==9 || length==12 || length==13 || length==14 || length==18) + { + pr = ILibParseString(timeString,0,length,":",1); + if (pr->NumResults==3 || pr->NumResults==4) + { + if (pr->FirstResult->datalength==2 && pr->FirstResult->NextResult->datalength==2) // Klockwork says this could be NULL, but that is not possible because NumResults is 3 or 4 + { + temp = ILibString_Copy(pr->FirstResult->data,pr->FirstResult->datalength); + if (atoi(temp)<24 && atoi(temp)>=0) + { + // + // hh is correct + // + free(temp); + temp = ILibString_Copy(pr->FirstResult->NextResult->data,pr->FirstResult->NextResult->datalength); + if (atoi(temp)>=0 && atoi(temp)<60) + { + // + // mm is correct + // + free(temp); + temp = ILibString_Copy(pr->FirstResult->NextResult->NextResult->data,length-6); + switch((int)strnlen_s(temp, length-6)) + { + case 2: // ss + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal=1; + } + break; + case 3: // ssZ + if (temp[2]=='Z') + { + temp[2]=0; + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal=1; + } + } + else + { + RetVal=1; + } + break; + case 6: // ss.sss + case 7: // ss.sssZ + if (temp[2]=='.') + { + temp[2]=0; + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal = 1; + } + else if (temp[6]!= 'Z' && temp[6]!=0) + { + RetVal = 1; + } + } + else + { + RetVal = 1; + } + break; + case 8: // ss+hh:mm || ss-hh:mm + if (temp[2]=='-' || temp[2]=='+') + { + temp[2] = 0; + if (!(atoi(temp)>=0 && atoi(temp)<60 && atoi(temp+3)>=0 && atoi(temp+3)<24)) + { + RetVal=1; + } + } + else + { + RetVal=1; + } + break; + case 12: // ss.sss+hh:mm || ss.sss-hh:mm + if (temp[2]=='.' && temp[9]==':' && (temp[6]=='+' || temp[6]=='-')) + { + temp[2]=0; + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal = 1; + } + } + else + { + RetVal = 1; + } + break; + default: + RetVal=1; + break; + } + if (RetVal==0 && pr->NumResults==4) + { + // + // Check the last mm component + // + if (!(atoi(pr->LastResult->data)>=0 && atoi(pr->LastResult->data)<60)) + { + RetVal=1; + } + } + } + else + { + RetVal=1; + } + } + else + { + RetVal=1; + } + free(temp); + } + else + { + RetVal=1; + } + } + else + { + RetVal=1; + } + + ILibDestructParserResults(pr); + } + else + { + RetVal=1; + } + return(RetVal); +} +char* ILibTime_ValidateDatePortion(char *timeString) +{ + struct parser_result *pr; + char *startTime; + int errCode = 1; + int timeStringLen = (int)strnlen_s(timeString, 255); + char *RetVal = NULL; + int t; + + if (timeStringLen == 10) + { + pr = ILibParseString(timeString,0, timeStringLen,"-",1); + if (pr->NumResults==3) + { + // This means there it is in x-y-z format + if (pr->FirstResult->datalength==4) + { + // This means it is in yyyy-x-z format + if (pr->FirstResult->NextResult->datalength==2 && pr->LastResult->datalength==2) // Klockwork says this could be NULL, but that's not possible, as numresults is 3 + { + // This means it is in yyyy-xx-zz format + startTime = ILibString_Copy(pr->FirstResult->NextResult->data,pr->FirstResult->NextResult->datalength); + if (atoi(startTime)<=12 && atoi(startTime)>0) + { + // This means it is in yyyy-mm-xx format + free(startTime); + startTime = ILibString_Copy(pr->LastResult->data,pr->LastResult->datalength); + if (atoi(startTime)<=31 && atoi(startTime)>0) + { + // Everything in correct format + errCode = 0; + t = timeStringLen + 10; + if ((RetVal = (char*)malloc(t)) == NULL) ILIBCRITICALEXIT(254); + sprintf_s(RetVal, t, "%sT00:00:00", timeString); + } + } + free(startTime); + } + } + } + ILibDestructParserResults(pr); + } + if (errCode==0) + { + return(RetVal); + } + else + { + return(NULL); + } +} +int ILibTime_ParseEx(char *timeString, time_t *val) +{ + int errCode = 0; + time_t RetVal = 0; + struct parser_result *pr,*pr2; + struct tm t; + char *startTime; + int timeStringLen = (int)strnlen_s(timeString, 255); + int i; + + char *year=NULL,*month=NULL,*day=NULL; + char *hour=NULL,*minute=NULL,*second=NULL; + + char *tempString; + + if (ILibString_IndexOf(timeString, timeStringLen,"T",1)==-1) + { + // + // Doesn't have time Component, so format must be: yyyy-mm-dd + // + startTime = ILibTime_ValidateDatePortion(timeString); + if (startTime==NULL) + { + errCode = 1; + } + } + else + { + // + // Verify the format is yyyy-mm-ddThh:mm:ss+hh:mm + // yyyy-mm-ddThh:mm:ss-hh:mm + // yyyy-mm-ddThh:mm:ssZ + // yyyy-mm-ddThh:mm:ss + // + i = ILibString_IndexOf(timeString, timeStringLen,"T",1); + tempString = ILibString_Copy(timeString,i); + startTime = ILibTime_ValidateDatePortion(tempString); + free(tempString); + if (startTime!=NULL) + { + free(startTime); + // + // Valid Date Portion, now check the time portion + // + if (ILibTime_ValidateTimePortion(timeString+i+1)!=0) + { + errCode = 1; + } + } + else + { + free(startTime); + // + // Invalid Date portion + // + errCode = 1; + } + if (errCode==0) + { + startTime = ILibString_Copy(timeString, timeStringLen); + } + } + + + // + // If we got this far and errCode==0, then we're golden + // + if (errCode!=0) + { + return(1); + } + + memset(&t, 0, sizeof(struct tm)); + t.tm_isdst = -1; + + pr = ILibParseString(startTime, 0, (int)strnlen_s(startTime, timeStringLen), "T", 1); + if (pr->NumResults == 2) + { + pr2 = ILibParseString(pr->FirstResult->data, 0, pr->FirstResult->datalength,"-", 1); + + if (pr2->NumResults == 3) + { + year = pr2->FirstResult->data; + year[pr2->FirstResult->datalength]=0; + + month = pr2->FirstResult->NextResult->data; // Klockwork reports this could be NULL, but that's not possible becuase NumResults is 3 + month[pr2->FirstResult->NextResult->datalength]=0; + + day = pr2->LastResult->data; + day[pr2->LastResult->datalength]=0; + + t.tm_year = atoi(year)-1900; + t.tm_mon = atoi(month)-1; + t.tm_mday = atoi(day); + + ILibDestructParserResults(pr2); + } + } + pr2 = ILibParseString(pr->LastResult->data, 0, pr->LastResult->datalength, ":", 1); + switch(pr2->NumResults) + { + case 4: + // yyyy-mm-ddThh:mm:ss+hh:mm + // yyyy-mm-ddThh:mm:ss-hh:mm + hour = pr2->FirstResult->data; + hour[pr2->FirstResult->datalength]=0; + minute = pr2->FirstResult->NextResult->data; // Klockwork reports this could be NULL, but that's not possible because NumResults is 4 + minute[pr2->FirstResult->NextResult->datalength]=0; + second = pr2->FirstResult->NextResult->NextResult->data; + second[2]=0; + break; + case 3: + // yyyy-mm-ddThh:mm:ssZ + hour = pr2->FirstResult->data; + hour[pr2->FirstResult->datalength]=0; + minute = pr2->FirstResult->NextResult->data; // Klocwork reports this could be NULL, but that's not possible becuase NumResults is 3 + minute[pr2->FirstResult->NextResult->datalength]=0; + second = pr2->FirstResult->NextResult->NextResult->data; + second[2]=0; + break; + } + if (hour!=NULL && minute!=NULL && second!=NULL) + { + t.tm_hour = atoi(hour); + t.tm_min = atoi(minute); + t.tm_sec = atoi(second); + + RetVal = mktime(&t); + } + + ILibDestructParserResults(pr2); + ILibDestructParserResults(pr); + free(startTime); + *val = RetVal; + return(errCode); +} +time_t ILibTime_Parse(char *timeString) +{ + time_t retval; + ILibTime_ParseEx(timeString, &retval); + return(retval); +} + + +//! Copy an IPv4 address into an IPv6 mapped address +/*! + \param[in] addr4 IPv4 Address + \param[in,out] addr6 IPv6 Address +*/ +void ILibMakeIPv6Addr(struct sockaddr *addr4, struct sockaddr_in6 *addr6) +{ + if (addr4->sa_family == AF_INET6 || !g_ILibDetectIPv6Support) + { + // Direct copy + memcpy_s(addr6, sizeof(struct sockaddr_in6), addr4, sizeof(struct sockaddr_in6)); + } + else + { + // Conversion + memset(addr6, 0, sizeof(struct sockaddr_in6)); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = ((struct sockaddr_in*)addr4)->sin_port; + (((int*)&(addr6->sin6_addr)))[2] = 0xFFFF0000; + (((int*)&(addr6->sin6_addr)))[3] = ((int*)addr4)[1]; + } +} + +char IPv4MappedPrefix[12] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF}; +char IPv4MappedLoopback[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x7F,0x00,0x00,0x01}; +char IPv4Loopback[4] = {0x7F,0x00,0x00,0x01}; + +//! Copy the IP Address into an HTTP Header style string +/*! + \param[in] addr IP Address + \param[out] str HTTP Header Style String + \return Length of str +*/ +int ILibMakeHttpHeaderAddr(struct sockaddr *addr, char** str) +{ + int len; + if ((*str = (char *)malloc(256)) == NULL) ILIBCRITICALEXIT(254); + + if (addr->sa_family == AF_INET6) + { + // Check if this IPv6 addresses starts with the IPv4 mapped prefix + if (memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, IPv4MappedPrefix, 12) == 0) + { + // IPv4 translation conversion + ILibInet_ntop(AF_INET, &(((int*)&((struct sockaddr_in6*)addr)->sin6_addr)[3]), *str, 256); + len = (int)strnlen_s(*str, 256); + } + else + { + // IPv6 conversion + *str[0] = '['; + ILibInet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, *str + 1, 254); + len = (int)strnlen_s(*str, 256); + (*str)[len++] = ']'; + (*str)[len++] = 0; + } + } + else + { + // IPv4 conversion + ILibInet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), *str, 256); + len = (int)strnlen_s(*str, 256); + } + return len; +} + +//! Return true if the given address is an IPv4 mapped address in an IPv6 container +/*! + \param addr IP Address + \return [ZERO = FALSE, NON-ZERO = TRUE] +*/ +int ILibIsIPv4MappedAddr(struct sockaddr *addr) +{ + return (addr->sa_family == AF_INET6 && memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, IPv4MappedPrefix, 12) == 0); +} + +//! Checks if the given address is a loopback address (IPv4 or IPv6) +/*! + \param addr + \return [0 = NOT-LOOPBACK, 1 = LOOPBACK] +*/ +int ILibIsLoopback(struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) + { + if (memcmp(&((struct sockaddr_in*)addr)->sin_addr, IPv4Loopback, 4) == 0) return 1; else return 0; + } + else if (addr->sa_family == AF_INET6) + { + if (memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, &in6addr_loopback, 16) == 0) return 1; + if (memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, IPv4MappedLoopback, 16) == 0) return 1; + } + return 0; +} + +int ILibGetAddrBlob(struct sockaddr *addr, char** ptr) +{ + if (addr->sa_family == AF_INET) + { + // IPv4 + *ptr = (char*)&(((struct sockaddr_in*)addr)->sin_addr); + return 4; + } + if (addr->sa_family == AF_INET6) + { + // IPv6, check if this is a IPv4 mapped address + if (ILibIsIPv4MappedAddr(addr)) + { + // This is a IPv4 mapped address, only take 4 last bytes + *ptr = (char*)&(((struct sockaddr_in6*)addr)->sin6_addr) + 12; + return 4; + } + else + { + // This is a real IPv6 address + *ptr = (char*)&(((struct sockaddr_in6*)addr)->sin6_addr); + + // If the scope is not null, we need to store the address & scope (4 bytes more) + return (((struct sockaddr_in6*)addr)->sin6_scope_id == 0)?16:20; + } + } + return 0; +} + +void ILibGetAddrFromBlob(char* ptr, int len, unsigned short port, struct sockaddr_in6 *addr) +{ + // Fetch the IP address + memset(addr, 0, sizeof(struct sockaddr_in6)); + if (len == 4) + { + // IPv4 address + addr->sin6_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_port = port; + memcpy_s(&(((struct sockaddr_in*)addr)->sin_addr), sizeof(struct in_addr), ptr, 4); + } + else if (len == 16 || len == 20) + { + // IPv6 address, or IPv6 + Scope + addr->sin6_family = AF_INET6; + addr->sin6_port = port; + // memcpy(&(addr->sin6_addr), ptr, len); // Fast version (not klocworks friendly); + memcpy_s(&(addr->sin6_addr), sizeof(struct in6_addr), ptr, 16); + if (len == 20) memcpy_s(&(addr->sin6_scope_id), 4, ptr + 16, 4); + } + else ILIBCRITICALEXIT(253) +} + +int g_ILibDetectIPv6Support = -1; + +//! Determine if IPv6 is supported +/*! + \return [ZERO = NOT SUPPORTED, NON-ZERO = SUPPORTED] +*/ +int ILibDetectIPv6Support() +{ + SOCKET sock; + struct sockaddr_in6 addr; + + if (g_ILibDetectIPv6Support >= 0) return g_ILibDetectIPv6Support; + + memset(&addr, 0, sizeof(struct sockaddr_in6)); + addr.sin6_family = AF_INET6; + + // Get our listening socket + sock = ILibGetSocket((struct sockaddr*)&addr, SOCK_DGRAM, IPPROTO_UDP); + if (sock == 0) + { + g_ILibDetectIPv6Support = 0; + } + else + { + g_ILibDetectIPv6Support = 1; +#if defined(WIN32) + closesocket(sock); +#else + close(sock); +#endif + } + + return g_ILibDetectIPv6Support; // Klocwork is being dumb. This can't be lost, because sock is getting closed if it's valid... So Klocwork is saying an invalid resource is getting lost. But we can't close an invalid handle +} + +char* ILibInet_ntop2(struct sockaddr* addr, char *dst, size_t dstsize) +{ + if (addr != NULL && addr->sa_family == AF_INET) { ILibInet_ntop(AF_INET, &(((int*)&((struct sockaddr_in*)addr)->sin_addr)[0]), dst, dstsize); return dst; } + if (addr != NULL && addr->sa_family == AF_INET6) { ILibInet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, dst, dstsize); return dst; } + dst[0] = 0; + return NULL; +} + +#if defined(WIN32) + +typedef PCTSTR (WSAAPI *inet_ntop_type)(__in INT Family, __in PVOID pAddr, __out PTSTR pStringBuf, __in size_t StringBufSize); +typedef INT (WSAAPI *inet_pton_type)(__in INT Family, __in PCTSTR pszAddrString, __out PVOID pAddrBuf); +inet_ntop_type inet_ntop_ptr = NULL; +inet_pton_type inet_pton_ptr = NULL; +int inet_pton_setup_done = 0; + +void inet_pton_setup() +{ + HMODULE h = NULL; + if (inet_pton_setup_done == 1) return; + inet_pton_setup_done = 1; + h = GetModuleHandle(TEXT("Ws2_32.dll")); + if (h == NULL) return; + inet_ntop_ptr = (inet_ntop_type)GetProcAddress(h, "inet_ntop"); + inet_pton_ptr = (inet_pton_type)GetProcAddress(h, "inet_pton"); + if (inet_ntop_ptr == NULL || inet_pton_ptr == NULL) inet_pton_setup_done = 2; +} + +char* ILibInet_ntop(int af, const char *src, char *dst, size_t dstsize) +{ + if (inet_pton_setup_done == 0) inet_pton_setup(); + + if (inet_pton_setup_done == 1) + { + // Use the real IPv6 version + return (char*)((inet_ntop_ptr)(af, (char*)src, (PTSTR)dst, dstsize)); + } + else + { + // Use the alternate IPv4 only version + if (af != AF_INET) return NULL; + _snprintf_s(dst, dstsize, dstsize, "%u.%u.%u.%u", (unsigned char)src[0], (unsigned char)src[1], (unsigned char)src[2], (unsigned char)src[3]); + return dst; + } +} + +int ILibInet_pton(int af, const char *src, char *dst) +{ + if (inet_pton_setup_done == 0) inet_pton_setup(); + + if (inet_pton_setup_done == 1) + { + // Use the real IPv6 version + return (int)(inet_pton_ptr)(af, (PCTSTR)src, dst); + } + else + { + // Use the alternate IPv4 only version + char dst2[8]; + char dst3[4]; + int i; + + if (af != AF_INET) return 0; + sscanf_s(src, "%hu.%hu.%hu.%hu", (unsigned short*)(dst2), (unsigned short*)(dst2 + 2), (unsigned short*)(dst2 + 4), (unsigned short*)(dst2 + 6)); + + for (i = 0; i < 4; ++i) { ((unsigned char*)dst3)[i] = (unsigned char)((unsigned short*)dst2)[i]; } + + ((int*)dst)[0] = ((int*)dst3)[0]; + return 1; + } +} +#else +char* ILibInet_ntop(int af, const void *src, char *dst, size_t dstsize) +{ + return (char*)inet_ntop(af, src, dst, (socklen_t)dstsize); +} +int ILibInet_pton(int af, const char *src, void *dst) +{ + return inet_pton(af, src, dst); +} +#endif + +// Compare two IP addresses. Compare: 0 already true, 1 family only, 2 family and address, 3 familly, address and port. Return 0 if different, 1 if same. +int ILibInetCompare(struct sockaddr* addr1, struct sockaddr* addr2, int compare) +{ + if (addr1 == NULL || addr2 == NULL) return 0; + if (addr1 == addr2 || compare == 0) return 1; + if (addr1->sa_family != addr2->sa_family) return 0; + if (compare == 1) return 1; + if (addr1->sa_family != AF_INET && addr1->sa_family != AF_INET6) return 0; + if (addr1->sa_family == AF_INET) + { +#ifdef WIN32 + if (((struct sockaddr_in*)addr1)->sin_addr.S_un.S_addr != ((struct sockaddr_in*)addr2)->sin_addr.S_un.S_addr) return 0; +#else + if (((struct sockaddr_in*)addr1)->sin_addr.s_addr != ((struct sockaddr_in*)addr2)->sin_addr.s_addr) return 0; +#endif + } + else + { + if (memcmp(&((struct sockaddr_in6*)addr1)->sin6_addr, &((struct sockaddr_in6*)addr2)->sin6_addr, 16) != 0) return 0; + } + if (compare == 2) return 1; + if (addr1->sa_family == AF_INET) + { + if (((struct sockaddr_in*)addr1)->sin_port != ((struct sockaddr_in*)addr2)->sin_port) return 0; + } + else + { + if (((struct sockaddr_in6*)addr1)->sin6_port != ((struct sockaddr_in6*)addr2)->sin6_port) return 0; + } + return 1; +} + +int ILibResolve(char* hostname, char* service, struct sockaddr_in6* addr6) +{ + int r; + struct addrinfo *result = NULL; + struct addrinfo hints; + + memset(addr6, 0, sizeof(struct sockaddr_in6)); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + r = getaddrinfo(hostname, service, &hints, &result); + if (r != 0) { if (result != NULL) { freeaddrinfo(result); } return r; } + if (result == NULL) return -1; + if (result->ai_family == AF_INET) { memcpy_s(addr6, sizeof(struct sockaddr_in6), result->ai_addr, sizeof(struct sockaddr_in)); } + if (result->ai_family == AF_INET6) { memcpy_s(addr6, sizeof(struct sockaddr_in6), result->ai_addr, sizeof(struct sockaddr_in6)); } + freeaddrinfo(result); + return 0; +} +int ILibResolveEx(char* hostname, unsigned short port, struct sockaddr_in6* addr6) +{ + char service[16]; + if (sprintf_s(service, sizeof(service), "%u", port) > 0) + { + return(ILibResolve(hostname, service, addr6)); + } + else + { + return(1); + } +} +unsigned char ILib6to4Header[12]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; +void ILib6to4(struct sockaddr* addr) +{ + struct sockaddr_in* addr4 = (struct sockaddr_in*)addr; + struct sockaddr_in6* addr6 = (struct sockaddr_in6*)addr; + +#ifdef WIN32 + if (addr->sa_family != AF_INET6 || memcmp(addr6->sin6_addr.u.Byte, ILib6to4Header, 12)) return; + addr4->sin_addr.s_addr = ntohl(((int*)(addr6->sin6_addr.u.Byte + 12))[0]); + addr4->sin_family = AF_INET; +#else + if (addr->sa_family != AF_INET6 || memcmp(&(addr6->sin6_addr), ILib6to4Header, 12)) return; + addr4->sin_addr.s_addr = ntohl(((int*)(&(addr6->sin6_addr) + 12))[0]); + addr4->sin_family = AF_INET; +#endif +} + +// Log a critical error to file +char* ILibCriticalLog (const char* msg, const char* file, int line, int user1, int user2) +{ + char timeStamp[32]; + int len = ILibGetLocalTime((char*)timeStamp, (int)sizeof(timeStamp)); + if (file != NULL) + { + len = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "\r\n[%s] %s:%d (%d,%d) %s", timeStamp, file, line, user1, user2, msg); + } + else + { + len = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "\r\n[%s] %s", timeStamp, msg); + } + if (len > 0 && len < (int)sizeof(ILibScratchPad) && ILibCriticalLogFilename != NULL) ILibAppendStringToDiskEx(ILibCriticalLogFilename, ILibScratchPad, len); + if (file != NULL) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s:%d (%d,%d) %s", file, line, user1, user2, msg); + } + else + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s", msg); + } + return(ILibScratchPad); +} +//! Platform Agnostic method to Spawn a detached worker thread with normal priority/affinity +/*! + \param method Handler to dispatch on the new thread + \param arg Optional Parameter to dispatch [Can be NULL] + \return Thread Handle +*/ +void* ILibSpawnNormalThread(voidfp1 method, void* arg) +{ +#if defined (_POSIX) + intptr_t result; + void* (*fptr) (void* a); + pthread_t newThread; + fptr = (void*(*)(void*))method; + result = (intptr_t)pthread_create(&newThread, NULL, fptr, arg); + pthread_detach(newThread); + return (void*)result; +#endif +#ifdef WIN32 + return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)method, arg, 0, NULL ); +#endif + +#ifdef UNDER_CE + return CreateThread(NULL, 0, method, arg, 0, NULL ); +#endif +} +//! Platform Agnostic to end the currently executing thread +void ILibEndThisThread() +{ +#if defined (_POSIX) + pthread_exit(NULL); +#endif +#ifdef WIN32 + ExitThread(0); +#endif + +#ifdef UNDER_CE + ExitThread(0); +#endif +} +#ifdef WIN32 +void ILibHandle_DisableInherit(HANDLE * h) +{ + HANDLE tmpRead = *h; + if (tmpRead != NULL) + { + DuplicateHandle(GetCurrentProcess(), tmpRead, GetCurrentProcess(), h, 0, FALSE, DUPLICATE_SAME_ACCESS); + CloseHandle(tmpRead); + } +} +#endif +// Convert a block of data to HEX +// The "out" must have (len*2)+1 free space. +char ILibHexTable[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; +//! Convert a block of data to HEX +/*! + \param data Block of data to convert + \param len Data Length + \param[in,out] out Hex Result. Length must be at least (len*2) + 1 +*/ +char* ILibToHex(char* data, int len, char* out) +{ + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for(i = 0; i < len; i++) + { + *(p++) = ILibHexTable[((unsigned char)data[i]) >> 4]; + *(p++) = ILibHexTable[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} +//! Determine if the current thread is the Microstack thread +/*! + \param chain Microstack Thread to compare + \return 0 = NO, 1 = YES +*/ +int ILibIsRunningOnChainThread(void* chain) +{ + struct ILibBaseChain* c = (struct ILibBaseChain*)chain; + +#if defined(WIN32) + return(c->ChainThreadID == 0 || c->ChainThreadID == GetCurrentThreadId()); +#else + return(pthread_equal(c->ChainThreadID, pthread_self())); +#endif +} + +#define ILibLinkedList_FileBacked_Root_Raw_HEAD_OFFSET(root) ((unsigned int)((char*)&(root->head) - (char*)root)) +#define ILibLinkedList_FileBacked_Root_Raw_TAIL_OFFSET(root) ((unsigned int)((char*)&(root->tail) - (char*)root)) + +void ILibLinkedList_FileBacked_Close(ILibLinkedList_FileBacked_Root *root) +{ + FILE *source = ((FILE**)ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)))[0]; + if (source != NULL) { fclose(source); } + free(root); +} + +int ILibLinkedList_FileBacked_IsEmpty(ILibLinkedList_FileBacked_Root *root) +{ + return(root->head == 0 ? 1 : 0); +} + +void ILibLinkedList_FileBacked_Reset(ILibLinkedList_FileBacked_Root *root) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + fclose(source); + source = NULL; +#ifdef WIN32 + fopen_s(&source, (char*)ptr[1], "wb+N"); +#else + source = fopen((char*)ptr[1], "wb+"); +#endif + ptr[0] = source; + root->head = root->tail = 0; + ILibLinkedList_FileBacked_SaveRoot(root); +} +int ILibLinkedList_FileBacked_ReloadRoot(ILibLinkedList_FileBacked_Root* root) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + + fseek(source, 0, SEEK_SET); + return(fread((void*)root, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source) > 0 ? 0 : 1); +} +void ILibLinkedList_FileBacked_SaveRoot(ILibLinkedList_FileBacked_Root* root) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + + fseek(source, 0, SEEK_SET); + fwrite(root, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source); + fflush(source); +} +ILibLinkedList_FileBacked_Node* ILibLinkedList_FileBacked_ReadNext(ILibLinkedList_FileBacked_Root* root, ILibLinkedList_FileBacked_Node* current) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + char* buffer = (char*)&ptr[2]; + ILibLinkedList_FileBacked_Node *retVal = NULL; + size_t br; + + if (current == NULL) + { + if (root->head > 0) + { + retVal = (ILibLinkedList_FileBacked_Node*)(buffer); + fseek(source, root->head, SEEK_SET); + br = fread(buffer, sizeof(char), 2 * sizeof(unsigned int), source); + if (fread(buffer + br, sizeof(char), retVal->dataLen, source) != retVal->dataLen) { retVal = NULL; } + } + } + else + { + if (current->next > 0) + { + retVal = (ILibLinkedList_FileBacked_Node*)(buffer); + fseek(source, current->next, SEEK_SET); + br = fread(buffer, sizeof(char), 2 * sizeof(unsigned int), source); + if (fread(buffer + br, sizeof(char), retVal->dataLen, source) != retVal->dataLen) { retVal = NULL; } + } + } + + return(retVal); +} + +void ILibLinkedList_FileBacked_CopyPath(ILibLinkedList_FileBacked_Root *root, char* path) +{ + char *extraMemory = (char*)ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + int pathLen = (int)strnlen_s(path, _MAX_PATH); + int offset = ILibMemory_GetExtraMemorySize(extraMemory) - pathLen - 1; + + memcpy_s(extraMemory + offset, pathLen, path, pathLen); + (extraMemory + offset)[pathLen] = 0; + + ((void**)extraMemory)[1] = &extraMemory[offset]; +} +ILibLinkedList_FileBacked_Root* ILibLinkedList_FileBacked_Create(char* path, unsigned int maxFileSize, int maxRecordSize) +{ + FILE* source; + ILibLinkedList_FileBacked_Root *retVal = NULL; + +#ifdef WIN32 + if (fopen_s(&source, path, "rb+N") != 0) + { + fopen_s(&source, path, "wb+N"); + } +#else + if ((source = fopen(path, "rb+")) == NULL) + { + source = fopen(path, "wb+"); + } +#endif + + if (source == NULL) { return(NULL); } + + fseek(source, 0, SEEK_END); + if ((int)ftell(source) < (int)sizeof(ILibLinkedList_FileBacked_Root)) + { + // New File + retVal = (ILibLinkedList_FileBacked_Root*)ILibMemory_Allocate(sizeof(ILibLinkedList_FileBacked_Root), maxRecordSize + (int)strnlen_s(path, _MAX_PATH) + 1 + (int)(2*sizeof(void*)), NULL, NULL); + retVal->maxSize = maxFileSize; + ((FILE**)ILibMemory_GetExtraMemory(retVal, sizeof(ILibLinkedList_FileBacked_Root)))[0] = source; + ILibLinkedList_FileBacked_CopyPath(retVal, path); + fwrite((void*)retVal, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source); + fflush(source); + } + else + { + // File Exists + fseek(source, 0, SEEK_SET); + retVal = (ILibLinkedList_FileBacked_Root*)ILibMemory_Allocate(sizeof(ILibLinkedList_FileBacked_Root), maxRecordSize + (int)strnlen_s(path, _MAX_PATH) + 1 + (int)(2 * sizeof(void*)), NULL, NULL); + if (fread((void*)retVal, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source) != sizeof(ILibLinkedList_FileBacked_Root)) + { + free(retVal); + retVal = NULL; + fclose(source); + } + else + { + ((FILE**)ILibMemory_GetExtraMemory(retVal, sizeof(ILibLinkedList_FileBacked_Root)))[0] = source; + ILibLinkedList_FileBacked_CopyPath(retVal, path); + } + } + + return(retVal); +} + +int ILibLinkedList_FileBacked_AddTail(ILibLinkedList_FileBacked_Root* root, char* data, unsigned int dataLen) +{ + unsigned int sizeNeeded = dataLen + 8; + unsigned int i; + unsigned int tmp; + int headUpdated = 0; + + FILE* source = ((FILE**)ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)))[0]; + + fseek(source, 0, SEEK_SET); + if (fread((void*)root, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source) != sizeof(ILibLinkedList_FileBacked_Root)) { return(1); } + + // Grab a memory pointer that we can write to + if (root->tail == 0) + { + i = (unsigned int)sizeof(ILibLinkedList_FileBacked_Root); + } + else + { + // Need to calculate some things + fseek(source, root->tail + (unsigned int)sizeof(unsigned int), SEEK_SET); + if (fread(&i, sizeof(char), sizeof(unsigned int), source) != sizeof(unsigned int)) { return(1); } + i += ((unsigned int)2*sizeof(unsigned int) + root->tail); + + if (i + sizeNeeded > root->maxSize) { i = root->head; } + } + + // Check to see if we write to this memory location, do we need to move the head pointer + while (root->head != 0 && i <= root->head && (i + sizeNeeded > root->head)) + { + fseek(source, root->head, SEEK_SET); + if (fread(&(root->head), sizeof(char), sizeof(unsigned int), source) != sizeof(unsigned int)) { return(1); } + headUpdated = 1; + } + + if (root->head == 0) + { + root->head = i; + headUpdated = 1; + } + + // Write the new Head Pointer + if (headUpdated != 0) + { + fseek(source, ILibLinkedList_FileBacked_Root_Raw_HEAD_OFFSET(root), SEEK_SET); + fwrite(&(root->head), sizeof(char), sizeof(unsigned int), source); + fflush(source); + } + + // Write Record + fseek(source, i, SEEK_SET); + tmp = 0; + fwrite(&tmp, sizeof(char), sizeof(unsigned int), source); // Next + fwrite(&dataLen, sizeof(char), sizeof(unsigned int), source); // Data Length + if (dataLen > 0) + { + fwrite(data, sizeof(char), dataLen, source); // Data + } + fflush(source); + + // Update 'Next' of old Tail + if (root->tail != 0) + { + fseek(source, root->tail, SEEK_SET); + fwrite(&i, sizeof(char), sizeof(unsigned int), source); // New NEXT value + fflush(source); + } + + // Write new Tail Pointer + fseek(source, ILibLinkedList_FileBacked_Root_Raw_TAIL_OFFSET(root), SEEK_SET); + fwrite(&i, sizeof(char), sizeof(unsigned int), source); // NEXT + fflush(source); + return(0); +} diff --git a/MicroLMS/microstack/ILibParsers.h b/MicroLMS/microstack/ILibParsers.h new file mode 100644 index 0000000..60dee22 --- /dev/null +++ b/MicroLMS/microstack/ILibParsers.h @@ -0,0 +1,1302 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +/*! \file ILibParsers.h +\brief MicroStack APIs for various functions and tasks +*/ + +#ifndef __ILibParsers__ +#define __ILibParsers__ + +#ifdef __cplusplus +extern "C" { +#endif + + /*! \defgroup ILibParsers ILibParser Modules + * @{ + * @} + */ + + /*! \def MAX_HEADER_LENGTH + Specifies the maximum allowed length for an HTTP Header + */ +#define MAX_HEADER_LENGTH 800 + +#ifdef MEMORY_CHECK +#include +#define MEMCHECK(x) x +#else +#define MEMCHECK(x) +#endif + +#if !defined(WIN32) +#define HANDLE int +#define SOCKET int +#endif + +extern char* ILibCriticalLogFilename; + + +#ifndef WIN32 +#define REQUIRES_MEMORY_ALIGNMENT +#endif + +#if defined(WIN32) +#ifndef MICROSTACK_NO_STDAFX +#include "stdafx.h" +#endif +struct sockaddr_in6; +#elif defined(_POSIX) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(WIN32) && defined (_DEBUG) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif +#ifdef _POSIX +#include +#endif + +#include +#include +#include +#include + + +#if defined(WIN32) +#include +#include +#include +#include +#include +#endif + +#if defined(WIN32) +#include +#include +#endif + +#ifndef _MAX_PATH +#define _MAX_PATH PATH_MAX +#endif + +#include +static inline void ignore_result(uint64_t result) { (void)result; } + +int ILibGetLocalTime(char *dest, int destLen); +long ILibGetTimeStamp(); + +#ifdef ILibEXPORT +#ifdef WIN32 +#define ILibExportMethod __declspec(dllexport) +#else +#define ILibExportMethod +#endif +#else +#define ILibExportMethod +#endif + + +#if defined(WIN32) +#ifndef FD_SETSIZE +#define SEM_MAX_COUNT 64 +#else +#define SEM_MAX_COUNT FD_SETSIZE +#endif + void ILibGetTimeOfDay(struct timeval *tp); + +/* +// Implemented in Windows using events. +#define sem_t HANDLE +#define sem_init(x,pShared,InitValue) *x=CreateSemaphore(NULL,InitValue,SEM_MAX_COUNT,NULL) +#define sem_destroy(x) (CloseHandle(*x)==0?1:0) +#define sem_wait(x) WaitForSingleObject(*x,INFINITE) +#define sem_trywait(x) ((WaitForSingleObject(*x,0)==WAIT_OBJECT_0)?0:1) +#define sem_post(x) ReleaseSemaphore(*x,1,NULL) +*/ + +// Implemented in Windows using critical section. +#define sem_t CRITICAL_SECTION +#define sem_init(x,pShared,InitValue) InitializeCriticalSection(x); +#define sem_destroy(x) (DeleteCriticalSection(x)) +#define sem_wait(x) EnterCriticalSection(x) +#define sem_trywait(x) TryEnterCriticalSection(x) +#define sem_post(x) LeaveCriticalSection(x) + +#define strncasecmp(x,y,z) _strnicmp(x,y,z) +#define strcasecmp(x,y) _stricmp(x,y) +#define gettimeofday(tp,tzp) ILibGetTimeOfDay(tp) + +#define tzset() _tzset() +#define daylight _daylight +#define timezone _timezone + +#ifndef stricmp +#define stricmp(x,y) _stricmp(x,y) +#endif + +#ifndef strnicmp +#define strnicmp(x,y,z) _strnicmp(x,y,z) +#endif + +#ifndef strcmpi +#define strcmpi(x,y) _stricmp(x,y) +#endif +#endif + + +#ifndef WIN32 + +// Polyfills +#ifndef memcpy_s + int ILibMemory_Copy_s(void *destination, size_t destinationSize, const void *source, size_t sourceLength); + #define memcpy_s(dest, destSize, source, sourceLen) ILibMemory_Copy_s(dest, destSize, source, sourceLen) +#endif + +#ifndef memmove_s + int ILibMemory_Move_s(void *destination, size_t destinationSize, void *source, size_t sourceLength); + #define memmove_s(dest, destSize, source, sourceLen) ILibMemory_Move_s(dest, destSize, source, sourceLen) +#endif + +#ifndef strncpy_s +#define strncpy_s(dest, destSize, source, count) ILibString_n_Copy_s(dest, destSize, source, count) +#endif + +#ifndef strcpy_s +#define strcpy_s(dest, destSize, source) ILibString_Copy_s(dest, destSize, source) +#endif + +#ifndef strnlen_s +#define strnlen_s(source, maxCount) (strlen(source) < (maxCount) ? strlen(source) : (maxCount)) +#endif + +#ifndef sprintf_s +#define ILib_need_sprintf_s +#include + int sprintf_s(void *dest, size_t destSize, const char *format, ...); +#endif + +#ifndef strnlen_s +#define strnlen_s(source, maxLen) strnlen(source, maxLen) +#endif + +#endif + + + +#if !defined(WIN32) +#define __fastcall +#endif + + typedef void (*voidfp)(void); // Generic function pointer + typedef void(*voidfp1)(void*); // Generic function pointer + extern char ILibScratchPad[4096]; // General buffer + extern char ILibScratchPad2[65536]; // Often used for UDP packet processing + extern char ILibScratchPad_WWWAuth1[1024]; // Used to construct WWW auth digest request + extern char ILibScratchPad_WWWAuth2[4096]; // Used to construct WWW auth digest request + extern void* gILibChain; // Global Chain used for Remote Logging when a chain instance is not otherwise exposed + + /*! \def UPnPMIN(a,b) + Returns the minimum of \a a and \a b. + */ +#define UPnPMIN(a,b) (((a)<(b))?(a):(b)) + +/*! +\ingroup ChainGroup +*@{ +*/ + extern const int ILibMemory_CHAIN_CONTAINERSIZE; + +//! Determines if the specified chain is in the process of being disposed. +/*! + \param Chain Microstack Chain to query + \return 0 = NO, 1 = YES +*/ +#define ILibIsChainBeingDestroyed(Chain) (*((int*)Chain)) +//! Determines if the specified chain is running +/*! + \param Chain Microstack Chain to query + \return 0 = NO, 1 = YES +*/ +#define ILibIsChainRunning(Chain) (((int*)Chain)[1]) +int ILibIsRunningOnChainThread(void* chain); +/*! @} */ + + typedef enum ILibServerScope + { + ILibServerScope_All=0, + ILibServerScope_LocalLoopback=1, + ILibServerScope_LocalSegment=2 + }ILibServerScope; + + + typedef void(*ILibChain_PreSelect)(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime); + typedef void(*ILibChain_PostSelect)(void* object, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset); + typedef void(*ILibChain_Destroy)(void* object); + typedef void(*ILibChain_DestroyEvent)(void *chain, void *user); + typedef void(*ILibChain_StartEvent)(void *chain, void *user); + typedef void*(*ILibChain_Link_GetUserMemory)(void *ChainLinkObject, int *len); + typedef void* ILibChain_EventHookToken; + typedef void(*ILibChain_EventHookHandler)(void *hookedObject, ILibChain_EventHookToken token); + + typedef struct ILibChain_Link + { + ILibChain_PreSelect PreSelectHandler; + ILibChain_PostSelect PostSelectHandler; + ILibChain_Destroy DestroyHandler; + void* ParentChain; + void* ExtraMemoryPtr; + }ILibChain_Link; + + ILibChain_Link* ILibChain_Link_Allocate(int structSize, int extraMemorySize); + int ILibChain_Link_GetExtraMemorySize(ILibChain_Link* link); + + void* ILibMemory_Allocate(int containerSize, int extraMemorySize, void** allocatedContainer, void **extraMemory); + int ILibMemory_GetExtraMemorySize(void* extraMemory); + ILibExportMethod void* ILibMemory_GetExtraMemory(void *container, int containerSize); + ILibChain_EventHookToken ILibChain_SetEventHook(void* chainLinkObject, int maxTimeout, ILibChain_EventHookHandler handler); + void ILibChain_UpdateEventHook(ILibChain_EventHookToken token, int maxTimeout); + + /*! + \defgroup LoggingMethods Logging Methods + \ingroup ILibParsers + @{ + */ +#ifdef _REMOTELOGGING + //! Associate an ILibRemoteLogging module with a microstack Chain + /*! + \param chain Microstack Chain to associate + \param logger ILibRemoteLogging module to associate + */ + #define ILibChainSetLogger(chain, logger) ((void**)&((int*)chain)[2])[0] = logger + //! Gets the associated ILibRemoteLogging module from a microstack chain + /*! + \paran chain Microstack chain to get the association from + \return ILibRemoteLogging module associated (NULL if none associated) + */ + #define ILibChainGetLogger(chain) (chain!=NULL?(((void**)&((int*)chain)[2])[0]):NULL) + #ifdef _REMOTELOGGINGSERVER + ILibExportMethod unsigned short ILibStartDefaultLoggerEx(void* chain, unsigned short portNumber, char *path); + #define ILibStartDefaultLogger(chain, portNumber) ILibStartDefaultLoggerEx(chain, portNumber, NULL) + #else + #define ILibStartDefaultLogger(chain, portNumber) 0 + #define ILibStartDefaultLoggerEx(chain, portNumber, path) 0 + #endif +#else + #define ILibChainSetLogger(chain, logger) ; + #define ILibChainGetLogger(chain) NULL + #define ILibStartDefaultLogger(chain, portNumber) 0 + #define ILibStartDefaultLoggerEx(chain, portNumber, path) 0 +#endif + /*! @} */ + + /*! + \ingroup ChainGroup + *@{ + */ + void ILibChain_OnDestroyEvent_AddHandler(void *chain, ILibChain_DestroyEvent sink, void *user); + void ILibChain_OnStartEvent_AddHandler(void *chain, ILibChain_StartEvent sink, void *user); + /*! @} */ + +#ifdef MICROSTACK_NOTLS + #define NONCE_SIZE 32 + #define HALF_NONCE_SIZE 16 + + char* __fastcall util_tohex(char* data, int len, char* out); + int __fastcall util_hexToint(char *hexString, int hexStringLength); + + // Convert hex string to int + int __fastcall util_hexToBuf(char *hexString, int hexStringLength, char* output); +#endif + int ILibIsLittleEndian(); + int ILibGetCurrentTimezoneOffset_Minutes(); + int ILibIsDaylightSavingsTime(); + int ILibTime_ParseEx(char *timeString, time_t *val); + time_t ILibTime_Parse(char *timeString); + char* ILibTime_Serialize(time_t timeVal); + long long ILibGetUptime(); + + unsigned long long ILibHTONLL(unsigned long long v); + unsigned long long ILibNTOHLL(unsigned long long v); + + typedef void* ILibReaderWriterLock; + ILibReaderWriterLock ILibReaderWriterLock_Create(); + ILibReaderWriterLock ILibReaderWriterLock_CreateEx(void *chain); + int ILibReaderWriterLock_ReadLock(ILibReaderWriterLock rwLock); + int ILibReaderWriterLock_ReadUnLock(ILibReaderWriterLock rwLock); + int ILibReaderWriterLock_WriteLock(ILibReaderWriterLock rwLock); + int ILibReaderWriterLock_WriteUnLock(ILibReaderWriterLock rwLock); + void ILibReaderWriterLock_Destroy(ILibReaderWriterLock rwLock); + + /*! \defgroup ILibTransport ILibTransport Abstraction + \ingroup ILibParsers + * @{ + */ + //! Fetch the underlying chain from an ILibTransport object + /*! + \param transport ILibTransport object to query + \return Microstack Chain + */ + #define ILibTransportChain(transport) ((ILibTransport*)transport)->ChainLink.ParentChain + //! Memory Ownership of ILibTransport Data + typedef enum ILibTransport_MemoryOwnership + { + ILibTransport_MemoryOwnership_CHAIN = 0, /*!< The Microstack will own this memory, and free it when it is done with it */ + ILibTransport_MemoryOwnership_STATIC = 1, /*!< This memory is static, so the Microstack will not free it, and assume it will not go away, so it won't copy it either */ + ILibTransport_MemoryOwnership_USER = 2 /*!< The Microstack doesn't own this memory, so if necessary the memory will be copied */ + }ILibTransport_MemoryOwnership; + //! Operation Status + typedef enum ILibTransport_DoneState + { + ILibTransport_DoneState_INCOMPLETE = 0, /*!< Operation not complete [PENDING] */ + ILibTransport_DoneState_COMPLETE = 1, /*!< Operation complete */ + ILibTransport_DoneState_ERROR = -4 /*!< Operation failed */ + }ILibTransport_DoneState; + + typedef ILibTransport_DoneState(*ILibTransport_SendPtr)(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done); + typedef void(*ILibTransport_ClosePtr)(void *transport); + typedef unsigned int(*ILibTransport_PendingBytesToSendPtr)(void *transport); + + ILibTransport_DoneState ILibTransport_Send(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done); + void ILibTransport_Close(void *transport); + unsigned int ILibTransport_PendingBytesToSend(void *transport); + + //! ILibTransport Abstraction [DO NOT MODIFY STRUCT DEFINITION] + typedef void(*ILibTransport_OnSendOK)(void* sender); + typedef struct ILibTransport + { + ILibChain_Link ChainLink; + ILibTransport_SendPtr SendPtr; /*!< [RESERVED: Encapsulated Send Handler] */ + ILibTransport_ClosePtr ClosePtr; /*!< [RESERVED: Encapsulated Close Handler] */ + ILibTransport_PendingBytesToSendPtr PendingBytesPtr; /*!< [RESERVED: Encapsulated Pending Handler] */ + ILibTransport_OnSendOK SendOkPtr; /*!< [RESERVED: Encapsulated SendOK Callback] */ + unsigned int IdentifierFlags; /*!< ILibTransport Type Identifier */ + }ILibTransport; + /*! @} */ + + + /*! \defgroup DataStructures Data Structures + \ingroup ILibParsers + \{ + \} + */ + + + /*! \struct parser_result_field ILibParsers.h + \brief Data Elements of \a parser_result + \par + This structure represents individual tokens, resulting from a call to + \a ILibParseString and \a ILibParseStringAdv + */ + typedef struct parser_result_field + { + /*! \var data + \brief Pointer to string + */ + char* data; + + /*! \var datalength + \brief Length of \a data + */ + int datalength; + + /*! \var NextResult + \brief Pointer to next token + */ + struct parser_result_field *NextResult; + }parser_result_field; + + /*! \struct parser_result ILibParsers.h + \brief String Parsing Result Index + \par + This is returned from a successfull call to either \a ILibParseString or + \a ILibParseStringAdv. + */ + typedef struct parser_result + { + /*! \var FirstResult + \brief Pointer to the first token + */ + struct parser_result_field *FirstResult; + /*! \var LastResult + \brief Pointer to the last token + */ + struct parser_result_field *LastResult; + /*! \var NumResults + \brief The total number of tokens + */ + int NumResults; + }parser_result; + + /*! \struct packetheader_field_node ILibParsers.h + \brief HTTP Headers + \par + This structure represents an individual header element. A list of these + is referenced from a \a packetheader_field_node. + */ + typedef struct packetheader_field_node + { + /*! \var Field + \brief Header Name + */ + char* Field; + /*! \var FieldLength + \brief Length of \a Field + */ + int FieldLength; + /*! \var FieldData + \brief Header Value + */ + char* FieldData; + /*! \var FieldDataLength + \brief Length of \a FieldData + */ + int FieldDataLength; + /*! \var UserAllocStrings + \brief Boolean indicating if the above strings are non-static memory + */ + int UserAllocStrings; + /*! \var NextField + \brief Pointer to the Next Header entry. NULL if this is the last one + */ + struct packetheader_field_node* NextField; + }packetheader_field_node; + + /*! \struct packetheader ILibParsers.h + \brief Structure representing a packet formatted according to HTTP encoding rules + \par + This can be created manually by calling \a ILibCreateEmptyPacket(), or automatically via a call to \a ILibParsePacketHeader(...) + */ + typedef struct packetheader + { + /*! \var Directive + \brief HTTP Method + \par + eg: \b GET /index.html HTTP/1.1 + */ + char* Directive; + /*! \var DirectiveLength + \brief Length of \a Directive + */ + int DirectiveLength; + /*! \var DirectiveObj + \brief HTTP Method Path + \par + eg: GET \b /index.html HTTP/1.1 + */ + char* DirectiveObj; + /*! \var DirectiveObjLength + \brief Length of \a DirectiveObj + */ + + void *Reserved; + + int DirectiveObjLength; + /*! \var StatusCode + \brief HTTP Response Code + \par + eg: HTTP/1.1 \b 200 OK + */ + int StatusCode; + /* \var StatusData + \brief Status Meta Data + \par + eg: HTTP/1.1 200 \b OK + */ + char* StatusData; + /*! \var StatusDataLength + \brief Length of \a StatusData + */ + int StatusDataLength; + /*! \var Version + \brief HTTP Version + \par + eg: 1.1 + */ + char* Version; + /*! \var VersionLength + \brief Length of \a Version + */ + int VersionLength; + /*! \var Body + \brief Pointer to HTTP Body + */ + char* Body; + /*! \var BodyLength + \brief Length of \a Body + */ + int BodyLength; + /*! \var UserAllocStrings + \brief Boolean indicating if Directive/Obj are non-static + \par + This only needs to be set, if you manually populate \a Directive and \a DirectiveObj.
+ It is \b recommended that you use \a ILibSetDirective + */ + int UserAllocStrings; // Set flag if you allocate memory pointed to by Directive/Obj + /*! \var UserAllocVersion + \brief Boolean indicating if Version string is non-static + \par + This only needs to be set, if you manually populate \a Version.
+ It is \b recommended that you use \a ILibSetVersion + */ + int UserAllocVersion; // Set flag if you allocate memory pointed to by Version + int ClonedPacket; + + /*! \var FirstField + \brief Pointer to the first Header field + */ + struct packetheader_field_node* FirstField; + /*! \var LastField + \brief Pointer to the last Header field + */ + struct packetheader_field_node* LastField; + + /*! \var Source + \brief The origin of this packet + \par + This is only populated if you obtain this structure from either \a ILibWebServer or + \a ILibWebClient. + */ + char Source[30]; + /*! \var ReceivingAddress + \brief IP address that this packet was received on + \par + This is only populated if you obtain this structure from either \a ILibWebServer or + \a ILibWebClient. + */ + char ReceivingAddress[30]; + + void *HeaderTable; + }ILibHTTPPacket; + + /*! \struct ILibXMLNode + \brief An XML Tree + \par + This is obtained via a call to \a ILibParseXML. It is \b highly \b recommended + that you call \a ILibProcessXMLNodeList, so that the node pointers at the end of this + structure will be populated. That will greatly simplify node traversal.

+ In order for namespaces to be resolved, you must call \a ILibXML_BuildNamespaceLookupTable(...) + with root-most node that you would like to resolve namespaces for. It is recommended that you always use + the root node, obtained from the initial call to \a ILibParseXML.

+ For most intents and purposes, you only need to work with the "StartElements" + */ + struct ILibXMLNode + { + /*! \var Name + \brief Local Name of the current element + */ + char* Name; // Element Name + /*! \var NameLength + \brief Length of \a Name + */ + int NameLength; + + /*! \var NSTag + \brief Namespace Prefix of the current element + \par + This can be resolved using a call to \a ILibXML_LookupNamespace(...) + */ + char* NSTag; // Element Prefix + /*! \var NSLength + \brief Length of \a NSTag + */ + int NSLength; + + /*! \var StartTag + \brief boolean indicating if the current element is a start element + */ + int StartTag; // Non zero if this is a StartElement + /*! \var EmptyTag + \brief boolean indicating if this element is an empty element + */ + int EmptyTag; // Non zero if this is an EmptyElement + + void *Reserved; // DO NOT TOUCH + void *Reserved2; // DO NOT TOUCH + + /*! \var Next + \brief Pointer to the child of the current node + */ + struct ILibXMLNode *Next; // Next Node + /*! \var Parent + \brief Pointer to the Parent of the current node + */ + struct ILibXMLNode *Parent; // Parent Node + /*! \var Peer + \brief Pointer to the sibling of the current node + */ + struct ILibXMLNode *Peer; // Sibling Node + struct ILibXMLNode *ClosingTag; // Pointer to closing node of this element + struct ILibXMLNode *StartingTag; // Pointer to start node of this element + }; + + /*! \struct ILibXMLAttribute + \brief A list of XML Attributes for a specified XML node + \par + This can be obtained via a call to \a ILibGetXMLAttributes(...) + */ + struct ILibXMLAttribute + { + /*! \var Name + \brief Local name of Attribute + */ + char* Name; // Attribute Name + /*! \var NameLength + \brief Length of \a Name + */ + int NameLength; + + /*! \var Prefix + \brief Namespace Prefix of this attribute + \par + This can be resolved by calling \a ILibXML_LookupNamespace(...) and passing in \a Parent as the current node + */ + char* Prefix; // Attribute Namespace Prefix + /*! \var PrefixLength + \brief Lenth of \a Prefix + */ + int PrefixLength; + + /*! \var Parent + \brief Pointer to the XML Node that contains this attribute + */ + struct ILibXMLNode *Parent; // The XML Node this attribute belongs to + + /*! \var Value + \brief Attribute Value + */ + char* Value; // Attribute Value + /*! \var ValueLength + \brief Length of \a Value + */ + int ValueLength; + /*! \var Next + \brief Pointer to the next attribute + */ + struct ILibXMLAttribute *Next; // Next Attribute + }; + + char *ILibReadFileFromDisk(char *FileName); + int ILibReadFileFromDiskEx(char **Target, char *FileName); + void ILibWriteStringToDisk(char *FileName, char *data); + void ILibAppendStringToDiskEx(char *FileName, char *data, int dataLen); + void ILibWriteStringToDiskEx(char *FileName, char *data, int dataLen); + void ILibDeleteFileFromDisk(char *FileName); + void ILibGetDiskFreeSpace(void *i64FreeBytesToCaller, void *i64TotalBytes); + + + /*! \defgroup StackGroup Stack + \ingroup DataStructures + Stack Methods + \{ + */ + void ILibCreateStack(void **TheStack); + void ILibPushStack(void **TheStack, void *data); + void *ILibPopStack(void **TheStack); + void *ILibPeekStack(void **TheStack); + void ILibClearStack(void **TheStack); + /*! \} */ + + /*! \defgroup QueueGroup Queue + \ingroup DataStructures + Queue Methods + \{ + */ + typedef void* ILibQueue; + ILibQueue ILibQueue_Create(); + void ILibQueue_Destroy(ILibQueue q); + int ILibQueue_IsEmpty(ILibQueue q); + void ILibQueue_EnQueue(ILibQueue q, void *data); + void *ILibQueue_DeQueue(ILibQueue q); + #define ILibQueue_PeekTail(q) ILibLinkedList_GetDataFromNode(ILibLinkedList_GetNode_Tail(q)) + void *ILibQueue_PeekQueue(ILibQueue q); + void ILibQueue_Lock(ILibQueue q); + void ILibQueue_UnLock(ILibQueue q); + long ILibQueue_GetCount(ILibQueue q); + /* \} */ + + + /*! \defgroup XML XML Parsing Methods + \ingroup ILibParsers + MicroStack supplied XML Parsing Methods + \par + \b Note: None of the XML Parsing Methods copy or allocate memory + The lifetime of any/all strings is bound to the underlying string that was + parsed using ILibParseXML. If you wish to keep any of these strings for longer + then the lifetime of the underlying string, you must copy the string. + \{ + */ + + + // + // Parses an XML string. Returns a tree of ILibXMLNode elements. + // + struct ILibXMLNode *ILibParseXML(char *buffer, int offset, int length); + + // + // Preprocesses the tree of ILibXMLNode elements returned by ILibParseXML. + // This method populates all the node pointers in each node for easy traversal. + // In addition, this method will also determine if the given XML document was well formed. + // Returns 0 if processing succeeded. Specific Error Codes are returned otherwise. + // + int ILibProcessXMLNodeList(struct ILibXMLNode *nodeList); + + // + // Initalizes a namespace lookup table for a given parent node. + // If you want to resolve namespaces, you must call this method exactly once + // + void ILibXML_BuildNamespaceLookupTable(struct ILibXMLNode *node); + + // + // Resolves a namespace prefix. + // + char* ILibXML_LookupNamespace(struct ILibXMLNode *currentLocation, char *prefix, int prefixLength); + + // + // Fetches all the data for an element. Returns the length of the populated data + // + int ILibReadInnerXML(struct ILibXMLNode *node, char **RetVal); + + // + // Returns the attributes of an XML element. Returned as a linked list of ILibXMLAttribute. + // + struct ILibXMLAttribute *ILibGetXMLAttributes(struct ILibXMLNode *node); + + void ILibDestructXMLNodeList(struct ILibXMLNode *node); + void ILibDestructXMLAttributeList(struct ILibXMLAttribute *attribute); + + // + // Escapes an XML string. + // indata must be pre-allocated. + // + int ILibXmlEscapeEx(char* outdata, const char* indata, size_t indataLen); + #define ILibXmlEscape(outdata, indata) ILibXmlEscapeEx(outdata, indata, strnlen_s(indata, sizeof(ILibScratchPad2))) + // + // Returns the required size string necessary to escape this XML string + // + int ILibXmlEscapeLengthEx(const char* data, size_t dataLen); + #define ILibXmlEscapeLength(data) ILibXmlEscapeLengthEx(data, strnlen_s(data, sizeof(ILibScratchPad2))) + // + // Unescapes an XML string. + // Since Unescaped strings are always shorter than escaped strings, + // the resultant string will overwrite the supplied string, to save memory + // + int ILibInPlaceXmlUnEscapeEx(char* data, size_t dataLen); + #define ILibInPlaceXmlUnEscape(data) ILibInPlaceXmlUnEscapeEx(data, strnlen_s(data, sizeof(ILibScratchPad2))) + /*! \} */ + + /*! \defgroup ChainGroup Chain Methods + \ingroup ILibParsers + \brief Chaining Methods + \{ + */ + ILibExportMethod void *ILibCreateChain(); + void *ILibCreateChainEx(int extraMemorySize); + void ILibAddToChain(void *chain, void *object); + void *ILibGetBaseTimer(void *chain); + void ILibChain_SafeAdd(void *chain, void *object); + void ILibChain_SafeRemove(void *chain, void *object); + void ILibChain_SafeRemoveEx(void *chain, void *object); + void ILibChain_DestroyEx(void *chain); + ILibExportMethod void ILibStartChain(void *chain); + ILibExportMethod void ILibStopChain(void *chain); + ILibExportMethod void ILibChain_Continue(void *chain, ILibChain_Link **modules, int moduleCount, int maxTimeout); + ILibExportMethod void ILibChain_EndContinue(void *chain); + + void ILibForceUnBlockChain(void *Chain); + void ILibChain_RunOnMicrostackThreadEx(void *chain, ILibChain_StartEvent handler, void *user); + #define ILibChain_RunOnMicrostackThread(chain, handler, user) if(ILibIsRunningOnChainThread(chain)==0){ILibChain_RunOnMicrostackThreadEx(chain, handler, user);}else{handler(chain,user);} + /* \} */ + + /*! \defgroup SparseArrayGroup Sparse Array + \ingroup DataStructures + Sparse Array is a compact array where most of the elements will have the default value [NULL]. + *@{ + */ + + typedef void* ILibSparseArray; + //! Event handler triggered when an index needs to be hashed into a bucket value + /*! + \param value Index value to be hashed + \return Hashed Bucket value + */ + typedef int(*ILibSparseArray_Bucketizer)(int value); + //! Generic Event handler + /*! + \param sender The Sparse Array that generated the event + \param index Index Value + \param value Data Value + \param user Custom user state object + */ + typedef void(*ILibSparseArray_OnValue)(ILibSparseArray sender, int index, void *value, void *user); + extern const int ILibMemory_SparseArray_CONTAINERSIZE; + ILibSparseArray ILibSparseArray_CreateWithUserMemory(int numberOfBuckets, ILibSparseArray_Bucketizer bucketizer, int userMemorySize); + #define ILibSparseArray_Create(numberOfBuckets, bucketizer) ILibSparseArray_CreateWithUserMemory(numberOfBuckets, bucketizer, 0) + ILibSparseArray ILibSparseArray_CreateEx(ILibSparseArray source); + void* ILibSparseArray_Add(ILibSparseArray sarray, int index, void *data); + void* ILibSparseArray_Get(ILibSparseArray sarray, int index); + void* ILibSparseArray_Remove(ILibSparseArray sarray, int index); + void ILibSparseArray_Destroy(ILibSparseArray sarray); + void ILibSparseArray_DestroyEx(ILibSparseArray sarray, ILibSparseArray_OnValue onDestroy, void *user); + void ILibSparseArray_ClearEx2(ILibSparseArray sarray, ILibSparseArray_OnValue onClear, void *user, int nonZeroWillDelete); + #define ILibSparseArray_ClearEx(sarray, onClear, user) ILibSparseArray_ClearEx2(sarray, onClear, user, 1) + #define ILibSparseArray_Enumerate(sarray, onEnumerate, user) ILibSparseArray_ClearEx2(sarray, onEnumerate, user, 0) + ILibSparseArray ILibSparseArray_Move(ILibSparseArray sarray); + + void ILibSparseArray_Lock(ILibSparseArray sarray); + void ILibSparseArray_UnLock(ILibSparseArray sarray); + + /*! + *@} + */ + + /*! \defgroup AdvHashtableGroup Advanced Hashtable + \ingroup DataStructures + Advanced Hashtable is a customizable Hashtable data structure, that can be optimized as necessary + *@{ + */ + + typedef void* ILibHashtable; + //! Event handler for hash requests [Key1 or Key2 or Both] + /*! + \param Key1 Address Key + \param Key2 String Key + \param Key2Len String Key Length + \return Hash result + */ + typedef int(*ILibHashtable_Hash_Func)(void* Key1, char* Key2, int Key2Len); + //! Event handler for when elements in the hashtable are freed + /*! + \param sender Hashtable that generated the request + \param Key1 Address Key + \param Key2 String Key + \param Key2Len String Key Length + \param Data Element Value + \param user Custom User State data + */ + typedef void(*ILibHashtable_OnDestroy)(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user); + + ILibHashtable ILibHashtable_Create(); + + void ILibHashtable_DestroyEx(ILibHashtable table, ILibHashtable_OnDestroy onDestroy, void *user); + //! Free resources associated with Advanced Hashtable + /*! + \param hashtable Hashtable + */ + #define ILibHashtable_Destroy(hashtable) ILibHashtable_DestroyEx(hashtable, NULL, NULL) + void ILibHashtable_ChangeHashFunc(ILibHashtable table, ILibHashtable_Hash_Func hashFunc); + void ILibHashtable_ChangeBucketizer(ILibHashtable table, int bucketCount, ILibSparseArray_Bucketizer bucketizer); + void* ILibHashtable_Put(ILibHashtable table, void *Key1, char* Key2, int Key2Len, void* Data); + void* ILibHashtable_Get(ILibHashtable table, void *Key1, char* Key2, int Key2Len); + void* ILibHashtable_Remove(ILibHashtable table, void *Key1, char* Key2, int Key2Len); + void ILibHashtable_Clear(ILibHashtable table); + void ILibHashtable_ClearEx(ILibHashtable table, ILibHashtable_OnDestroy onClear, void *user); + void ILibHashtable_Enumerate(ILibHashtable table, ILibHashtable_OnDestroy onEnumerate, void *user); + void ILibHashtable_Lock(ILibHashtable table); + void ILibHashtable_UnLock(ILibHashtable table); + /*! + *@} + */ + + /*! \defgroup LinkedListGroup Linked List + \ingroup DataStructures + *@{ + */ + + typedef void* ILibLinkedList; + + /*! + \ingroup ChainGroup + *@{ + */ + + typedef struct ILibLinkedList_FileBacked_Root + { + unsigned int flags; + unsigned int maxSize; + unsigned int head; + unsigned int tail; + }ILibLinkedList_FileBacked_Root; +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4200 ) +#endif + typedef struct ILibLinkedList_FileBacked_Node + { + unsigned int next; + unsigned int dataLen; + char data[]; + }ILibLinkedList_FileBacked_Node; +#ifdef WIN32 +#pragma warning( pop ) +#endif + void ILibLinkedList_FileBacked_Close(ILibLinkedList_FileBacked_Root *root); + int ILibLinkedList_FileBacked_IsEmpty(ILibLinkedList_FileBacked_Root *root); + ILibLinkedList_FileBacked_Node* ILibLinkedList_FileBacked_ReadNext(ILibLinkedList_FileBacked_Root* root, ILibLinkedList_FileBacked_Node* current); + ILibLinkedList_FileBacked_Root* ILibLinkedList_FileBacked_Create(char* path, unsigned int maxFileSize, int maxRecordSize); + void ILibLinkedList_FileBacked_Reset(ILibLinkedList_FileBacked_Root *root); + int ILibLinkedList_FileBacked_AddTail(ILibLinkedList_FileBacked_Root* root, char* data, unsigned int dataLen); + int ILibLinkedList_FileBacked_ReloadRoot(ILibLinkedList_FileBacked_Root* root); + void ILibLinkedList_FileBacked_SaveRoot(ILibLinkedList_FileBacked_Root* root); + + + ILibLinkedList ILibChain_GetLinks(void *chain); + ILibHashtable ILibChain_GetBaseHashtable(void* chain); + #define ILibChain_Stash_Put(chain, key, value) ILibHashtable_Put(ILibChain_GetBaseHashtable(chain), NULL, key, strnlen_s(key, 255), value) + #define ILibChain_Stash_Get(chain, key) ILibHashtable_Get(ILibChain_GetBaseHashtable(chain), NULL, key, strnlen_s(key, 255)) + /*! @} */ + + + void* ILibLinkedList_Create(); + void* ILibLinkedList_CreateEx(int userMemorySize); + void ILibLinkedList_SetTag(ILibLinkedList list, void *tag); + void* ILibLinkedList_GetTag(ILibLinkedList list); + int ILibLinkedList_GetIndex(void *node); + + //! Comparer delegate is called to compare two values. Mimics behavior of .NET IComparer.. + //! obj2 == obj1 : 0 + //! obj2 < obj1 : -1 + //! obj2 > obj1 : +1 + /*! + \param obj1 Compare this object + \param obj2 with this object + \return Result [0: EQ, -1: LT, +1: GT] + */ + typedef int(*ILibLinkedList_Comparer)(void* obj1, void *obj2); + + //! Chooser handler is called when a new item is to be added to Linked List. The old value is passed in as well as the new value... The return value will be the designated added node + /*! + \param oldObject Current Element Value [NULL if it doesn't exist] + \param newObject Proposed Element Value + \param user Custom user state data + \return Element Value to be kept + */ + typedef void*(*ILibLinkedList_Chooser)(void* oldObject, void *newObject, void *user); + + void* ILibLinkedList_SortedInsert(void* LinkedList, ILibLinkedList_Comparer comparer, void *data); + void* ILibLinkedList_SortedInsertEx(void* LinkedList, ILibLinkedList_Comparer comparer, ILibLinkedList_Chooser chooser, void *data, void *user); + void* ILibLinkedList_GetNode_Search(void* LinkedList, ILibLinkedList_Comparer comparer, void *matchWith); + + void* ILibLinkedList_GetNode_Head(void *LinkedList); // Returns Node + void* ILibLinkedList_GetNode_Tail(void *LinkedList); // Returns Node + void* ILibLinkedList_GetNextNode(void *LinkedList_Node); // Returns Node + void* ILibLinkedList_GetPreviousNode(void *LinkedList_Node);// Returns Node + long ILibLinkedList_GetCount(void *LinkedList); + void* ILibLinkedList_ShallowCopy(void *LinkedList); + void *ILibLinkedList_GetDataFromNode(void *LinkedList_Node); + void* ILibLinkedList_InsertBefore(void *LinkedList_Node, void *data); + void* ILibLinkedList_InsertAfter(void *LinkedList_Node, void *data); + void* ILibLinkedList_Remove(void *LinkedList_Node); + int ILibLinkedList_Remove_ByData(void *LinkedList, void *data); + void* ILibLinkedList_AddHead(void *LinkedList, void *data); + void* ILibLinkedList_AddTail(void *LinkedList, void *data); + #define ILibLinkedList_Clear(LinkedList) while(ILibLinkedList_GetNode_Head(LinkedList)!=NULL){ILibLinkedList_Remove(ILibLinkedList_GetNode_Head(LinkedList));} + + void ILibLinkedList_Lock(void *LinkedList); + void ILibLinkedList_UnLock(void *LinkedList); + void ILibLinkedList_Destroy(void *LinkedList); + void* ILibLinkedList_GetExtendedMemory(void* LinkedList_Node); + /*! + *@} + */ + + + + /*! \defgroup HashTreeGroup Hash Table + \ingroup DataStructures + \b Note: Duplicate key entries will be overwritten. + *@{ + */ + + void* ILibInitHashTree(); + void* ILibInitHashTree_CaseInSensitive(); + void ILibDestroyHashTree(void *tree); + int ILibHasEntry(void *hashtree, char* key, int keylength); + void ILibAddEntry(void* hashtree, char* key, int keylength, void *value); + void ILibAddEntryEx(void* hashtree, char* key, int keylength, void *value, int valueEx); + void* ILibGetEntry(void *hashtree, char* key, int keylength); + ILibExportMethod void ILibGetEntryEx(void *hashtree, char* key, int keylength, void **value, int *valueEx); + void ILibDeleteEntry(void *hashtree, char* key, int keylength); + + void *ILibHashTree_GetEnumerator(void *tree); + void ILibHashTree_DestroyEnumerator(void *tree_enumerator); + int ILibHashTree_MoveNext(void *tree_enumerator); + + void ILibHashTree_GetValue(void *tree_enumerator, char **key, int *keyLength, void **data); + void ILibHashTree_GetValueEx(void *tree_enumerator, char **key, int *keyLength, void **data, int *dataEx); + void ILibHashTree_Lock(void *hashtree); + void ILibHashTree_UnLock(void *hashtree); + + /*! + *@} + */ + + /*! \defgroup LifeTimeMonitor LifeTimeMonitor + \ingroup ILibParsers + \brief Timed Callback Service + \par + These callbacks will always be triggered on the thread that calls ILibStartChain(). + \{ + */ + + typedef void(*ILibLifeTime_OnCallback)(void *obj); + + // + // Adds an event trigger to be called after the specified time elapses, with the + // specified data object + // +#define ILibLifeTime_Add(LifetimeMonitorObject, data, seconds, Callback, Destroy) ILibLifeTime_AddEx(LifetimeMonitorObject, data, seconds * 1000, Callback, Destroy) + void ILibLifeTime_AddEx(void *LifetimeMonitorObject,void *data, int milliseconds, ILibLifeTime_OnCallback Callback, ILibLifeTime_OnCallback Destroy); + + // + // Removes all event triggers that contain the specified data object. + // + void ILibLifeTime_Remove(void *LifeTimeToken, void *data); + + // + // Return the expiration time for an event + // + long long ILibLifeTime_GetExpiration(void *LifetimeMonitorObject, void *data); + + // + // Removes all events triggers + // + void ILibLifeTime_Flush(void *LifeTimeToken); + void *ILibCreateLifeTime(void *Chain); + long ILibLifeTime_Count(void* LifeTimeToken); + + /* \} */ + + + /*! \defgroup StringParsing String Parsing + \ingroup ILibParsers + \{ + */ + + int ILibFindEntryInTable(char *Entry, char **Table); + + + int ILibTrimString(char **theString, int length); + int ILibString_IndexOfFirstWhiteSpace(const char *inString, int inStringLength); + char* ILibString_Cat(const char *inString1, int inString1Len, const char *inString2, int inString2Len); + char* ILibString_Cat_s(char *destination, size_t destinationSize, char *source); + char *ILibString_Copy(const char *inString, int length); + int ILibString_Copy_s(char *destination, size_t destinationSize, char *source); + int ILibString_n_Copy_s(char *destination, size_t destinationSize, char *source, size_t count); + int ILibString_EndsWith(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength); + int ILibString_EndsWithEx(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength, int caseSensitive); + int ILibString_StartsWith(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength); + int ILibString_StartsWithEx(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength, int caseSensitive); + int ILibString_IndexOfEx(const char *inString, int inStringLength, const char *indexOf, int indexOfLength, int caseSensitive); + int ILibString_IndexOf(const char *inString, int inStringLength, const char *indexOf, int indexOfLength); + int ILibString_LastIndexOf(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength); + int ILibString_LastIndexOfEx(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength, int caseSensitive); + char *ILibString_Replace(const char *inString, int inStringLength, const char *replaceThis, int replaceThisLength, const char *replaceWithThis, int replaceWithThisLength); + char *ILibString_ToUpper(const char *inString, int length); + char *ILibString_ToLower(const char *inString, int length); + void ILibToUpper(const char *in, int inLength, char *out); + void ILibToLower(const char *in, int inLength, char *out); +#if !defined(WIN32) + #ifndef strcat_s + #define strcat_s(destination, destinationSize, source) ILibString_Cat_s(destination, destinationSize, source) + #endif + #ifndef strcpy_s + #define strcpy_s(destination, destinationSize, source) ILibString_Copy_s(destination, destinationSize, source); + #endif +#endif + + struct parser_result* ILibParseString (char* buffer, int offset, int length, const char* Delimiter, int DelimiterLength); + struct parser_result* ILibParseStringAdv (char* buffer, int offset, int length, const char* Delimiter, int DelimiterLength); + parser_result_field* ILibParseString_GetResultIndex(parser_result* r, int index); + void ILibDestructParserResults(struct parser_result *result); + + typedef enum ILibParseUriResult + { + ILibParseUriResult_UNKNOWN_SCHEME = 0, + ILibParseUriResult_NO_TLS = 1, + ILibParseUriResult_TLS = 2 + }ILibParseUriResult; + + ILibParseUriResult ILibParseUri(const char* URI, char** Addr, unsigned short* Port, char** Path, struct sockaddr_in6* AddrStruct); + + int ILibGetLong(char *TestValue, int TestValueLength, long* NumericValue); + int ILibGetULong(const char *TestValue, const int TestValueLength, unsigned long* NumericValue); + int ILibFragmentText(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength, char **RetVal); + int ILibFragmentTextLength(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength); + + + /* Base64 handling methods */ + int ILibBase64EncodeLength(const int inputLen); + int ILibBase64DecodeLength(const int inputLen); + int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output); + int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output); + + /* Compression Handling Methods */ + char* ILibDecompressString(unsigned char* CurrentCompressed, const int bufferLength, const int DecompressedLength); + + /* \} */ + + + /*! \defgroup PacketParsing Packet Parsing + \ingroup ILibParsers + \{ + */ + + /* Packet Methods */ + + struct packetheader *ILibCreateEmptyPacket(); + void ILibAddHeaderLine(struct packetheader *packet, const char* FieldName, int FieldNameLength, const char* FieldData, int FieldDataLength); + void ILibDeleteHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength); + void ILibHTTPPacket_Stash_Put(ILibHTTPPacket *packet, char* key, int keyLen, void *data); + int ILibHTTPPacket_Stash_HasKey(ILibHTTPPacket *packet, char* key, int keyLen); + void* ILibHTTPPacket_Stash_Get(ILibHTTPPacket *packet, char* key, int keyLen); + + char* ILibUrl_GetHost(char *url, int urlLen); + #define ILibGetHeaderLine(packet, FieldName, FieldNameLength) ILibGetHeaderLineEx(packet, FieldName, FieldNameLength, NULL) + char* ILibGetHeaderLineEx(struct packetheader *packet, char* FieldName, int FieldNameLength, int *len); + char* ILibGetHeaderLineSP(struct packetheader *packet, char* FieldName, int FieldNameLength); + char* ILibGetHeaderLineSP_Next(char* PreviousValue, char* FieldName, int FieldNameLength); + void ILibSetVersion(struct packetheader *packet, char* Version, int VersionLength); + void ILibSetStatusCode(struct packetheader *packet, int StatusCode, char* StatusData, int StatusDataLength); + void ILibSetDirective(struct packetheader *packet, char* Directive, int DirectiveLength, char* DirectiveObj, int DirectiveObjLength); + void ILibDestructPacket(struct packetheader *packet); + struct packetheader* ILibParsePacketHeader(char* buffer, int offset, int length); + int ILibGetRawPacket(struct packetheader *packet,char **buffer); + struct packetheader* ILibClonePacket(struct packetheader *packet); + + int ILibHTTPEscapeEx(char* outdata, const char* indata, size_t indataLen); + int ILibHTTPEscapeLengthEx(const char* data, size_t dataLen); + #define ILibHTTPEscape(outdata, indata) ILibHTTPEscapeEx(outdata, indata, strnlen_s(indata, sizeof(ILibScratchPad))) + #define ILibHTTPEscapeLength(data) ILibHTTPEscapeLengthEx(data, strnlen_s(data, sizeof(ILibScratchPad))) + + int ILibInPlaceHTTPUnEscapeEx(char* data, int length); + #define ILibInPlaceHTTPUnEscape(data) ILibInPlaceHTTPUnEscapeEx(data, (int)strnlen_s(data, sizeof(ILibScratchPad))) + /* \} */ + + void* dbg_malloc(int sz); + void dbg_free(void* ptr); + int dbg_GetCount(); + + /*! \defgroup NetworkHelper Network Helper + \ingroup ILibParsers + *@{ + */ + + int ILibGetLocalIPv6IndexList(int** indexlist); + int ILibGetLocalIPv6List(struct sockaddr_in6** list); + int ILibGetLocalIPAddressList(int** pp_int); + int ILibGetLocalIPv4AddressList(struct sockaddr_in** addresslist, int includeloopback); + //int ILibGetLocalIPv6AddressList(struct sockaddr_in6** addresslist); + +#if defined(WINSOCK2) + int ILibGetLocalIPAddressNetMask(unsigned int address); +#endif + + SOCKET ILibGetSocket(struct sockaddr *localif, int type, int protocol); + + // + // IPv6 helper methods + // + void ILibMakeIPv6Addr(struct sockaddr *addr4, struct sockaddr_in6* addr6); + int ILibMakeHttpHeaderAddr(struct sockaddr *addr, char** str); + int ILibIsIPv4MappedAddr(struct sockaddr *addr); + int ILibIsLoopback(struct sockaddr *addr); + int ILibGetAddrBlob(struct sockaddr *addr, char** ptr); + void ILibGetAddrFromBlob(char* ptr, int len, unsigned short port, struct sockaddr_in6 *addr); + int ILibDetectIPv6Support(); + extern int g_ILibDetectIPv6Support; + char* ILibInet_ntop2(struct sockaddr* addr, char *dst, size_t dstsize); + char* ILibInet_ntop(int af, const void *src, char *dst, size_t dstsize); + int ILibInet_pton(int af, const char *src, void *dst); + int ILibInetCompare(struct sockaddr* addr1, struct sockaddr* addr2, int compare); // Compare=1 family only, 2 family and address, 3 familly, address and port. + int ILibResolve(char* hostname, char* service, struct sockaddr_in6* addr6); + int ILibResolveEx(char* hostname, unsigned short port, struct sockaddr_in6* addr6); + void ILib6to4(struct sockaddr* addr); + #define ILibInet_StructSize(addr) ((((struct sockaddr*)(addr))->sa_family == AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) + /*! @} */ + // + // Used to log critical problems + // + char* ILibChain_Debug(void *chain, char* buffer, int bufferLen); + void ILibChain_DebugOffset(char *buffer, int bufferLen, uint64_t addrOffset); + +#if defined(WIN32) + int ILib_WindowsExceptionFilter(DWORD exceptionCode, void *exceptionInfo, CONTEXT *exceptionContext); + void ILib_WindowsExceptionDebug(CONTEXT *exceptionContext); +#elif defined(_POSIX) + char* ILib_POSIX_InstallCrashHandler(char *exename); +#endif + +#define ILIBCRITICALEXITMSG(code, msg) {printf("%s", ILibCriticalLog(msg, NULL, 0, 0, 0)); exit(code);} +#if defined(WIN32) +#define ILIBCRITICALERREXIT(ex) { ILibCriticalLog(NULL, __FILE__, __LINE__, GetLastError(), 0); exit(ex); } +#define ILIBCRITICALEXIT(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, GetLastError());printf("CRITICALEXIT, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__); exit(ex);} +#define ILIBCRITICALEXIT2(ex,u) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT2, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__); exit(ex);} +#define ILIBCRITICALEXIT3(ex,m,u) {ILibCriticalLog(m, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT3, FILE: %s, LINE: %d\r\nMessage: %s\r\n", __FILE__, __LINE__, m); exit(ex);} +#define ILIBMARKPOSITION(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, GetLastError());} +#define ILIBMESSAGE(m) {ILibCriticalLog(m, __FILE__, __LINE__, 0, GetLastError());printf("ILIBMSG: %s (%d).\r\n", m, (int)GetLastError());} +#define ILIBMESSAGE2(m,u) {ILibCriticalLog(m, __FILE__, __LINE__, u, GetLastError());printf("ILIBMSG: %s (%d,%d).\r\n", m, (int)u, (int)GetLastError());} +#else +extern void ILib_POSIX_CrashHandler(int code); +#define ILIBCRITICALERREXIT(ex) { ILibCriticalLog(NULL, __FILE__, __LINE__, errno, 0); fflush(stdout); exit(ex); } +#ifdef _POSIX +#define ILIBCRITICALEXIT(ex) ILib_POSIX_CrashHandler(ex); +#else +#define ILIBCRITICALEXIT(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, errno); printf("CRITICALEXIT, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__); fflush(stdout); exit(ex); } +#endif +#define ILIBCRITICALEXIT2(ex,u) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT2, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__);fflush(stdout); exit(ex);} +#define ILIBCRITICALEXIT3(ex,m,u) {ILibCriticalLog(m, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT3, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__);fflush(stdout); exit(ex);} +#define ILIBMARKPOSITION(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, errno);} +#define ILIBMESSAGE(m) {ILibCriticalLog(m, __FILE__, __LINE__, 0, errno);printf("ILIBMSG: %s\r\n", m);fflush(stdout);} +#define ILIBMESSAGE2(m,u) {ILibCriticalLog(m, __FILE__, __LINE__, u, errno);printf("ILIBMSG: %s (%d)\r\n", m, u);fflush(stdout);} +#endif + + char* ILibCriticalLog(const char* msg, const char* file, int line, int user1, int user2); + + /*! \defgroup ThreadingHelper Threading Helper + \ingroup ILibParsers + *@{ + */ + void* ILibSpawnNormalThread(voidfp1 method, void* arg); + void ILibEndThisThread(); +#ifdef WIN32 + void ILibHandle_DisableInherit(HANDLE *h); +#endif + /*! @} */ + + + /*! \defgroup ConversionGroup Conversion Utilities + \ingroup ILibParsers + *@{ + */ + char* ILibToHex(char* data, int len, char* out); + int ILibWhichPowerOfTwo(int number); +#define ILibPowerOfTwo(exponent) (0x01 << exponent) + /*! @} */ +#ifdef __cplusplus +} +#endif + +/* \} */ // End of ILibParser Group +#endif + diff --git a/MicroLMS/microstack/ILibRemoteLogging.h b/MicroLMS/microstack/ILibRemoteLogging.h new file mode 100644 index 0000000..9092f05 --- /dev/null +++ b/MicroLMS/microstack/ILibRemoteLogging.h @@ -0,0 +1,95 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef __ILIBREMOTELOGGING__ +#define __ILIBREMOTELOGGING__ + +#include "ILibParsers.h" + +/*! \defgroup ILibRemoteLogging ILibRemoteLogging Module +@{ +*/ +//! Module Types +typedef enum ILibRemoteLogging_Modules +{ + ILibRemoteLogging_Modules_UNKNOWN = 0x00, //!< UNKNOWN Module + ILibRemoteLogging_Modules_Logger = 0x01, //!< RESERVED: Logger + ILibRemoteLogging_Modules_WebRTC_STUN_ICE = 0x02, //!< WebRTC: STUN/ICE + ILibRemoteLogging_Modules_WebRTC_DTLS = 0x04, //!< WebRTC: DTLS + ILibRemoteLogging_Modules_WebRTC_SCTP = 0x08, //!< WebRTC: SCTP + ILibRemoteLogging_Modules_Agent_GuardPost = 0x10, //!< Mesh Agent: Guard Post + ILibRemoteLogging_Modules_Agent_P2P = 0x20, //!< Mesh Agent: Peer to Peer + ILibRemoteLogging_Modules_Agent_KVM = 0x200, //!< Mesh AGent: KVM + ILibRemoteLogging_Modules_Microstack_AsyncSocket = 0x40, //!< Microstack: AsyncSocket, AsyncServerSocket, AsyncUDPSocket + ILibRemoteLogging_Modules_Microstack_Web = 0x80, //!< Microstack: WebServer, WebSocket, WebClient + ILibRemoteLogging_Modules_Microstack_Pipe = 0x400,//!< Microstack: Pipe + ILibRemoteLogging_Modules_Microstack_Generic = 0x100,//!< Microstack: Generic + ILibRemoteLogging_Modules_ConsolePrint = 0x4000 +}ILibRemoteLogging_Modules; +//! Logging Flags +typedef enum ILibRemoteLogging_Flags +{ + ILibRemoteLogging_Flags_NONE = 0x00, //!< NONE + ILibRemoteLogging_Flags_DisableLogging = 0x01, //!< DISABLED + ILibRemoteLogging_Flags_VerbosityLevel_1 = 0x02, //!< Verbosity Level 1 + ILibRemoteLogging_Flags_VerbosityLevel_2 = 0x04, //!< Verbosity Level 2 + ILibRemoteLogging_Flags_VerbosityLevel_3 = 0x08, //!< Verbosity Level 3 + ILibRemoteLogging_Flags_VerbosityLevel_4 = 0x10, //!< Verbosity Level 4 + ILibRemoteLogging_Flags_VerbosityLevel_5 = 0x20, //!< Verbosity Level 5 +}ILibRemoteLogging_Flags; + +typedef enum ILibRemoteLogging_Command_Logger_Flags +{ + ILibRemoteLogging_Command_Logger_Flags_ENABLE = 0x100, //!< Enables/Disables File Logging + ILibRemoteLogging_Command_Logger_Flags_RESET_FILE = 0x200, //!< Erases the log file + ILibRemoteLogging_Command_Logger_Flags_READ_FILE = 0x400, //!< Reads the log file + ILibRemoteLogging_Command_Logger_Flags_RESET_FLAGS = 0x800, //!< Sets the Module/Flags to log to file +}ILibRemoteLogging_Command_Logger_Flags; + +#define ILibTransports_RemoteLogging_FileTransport 0x70 +typedef void* ILibRemoteLogging; +typedef void (*ILibRemoteLogging_OnWrite)(ILibRemoteLogging module, char* data, int dataLen, void *userContext); +typedef void (*ILibRemoteLogging_OnCommand)(ILibRemoteLogging sender, ILibRemoteLogging_Modules module, unsigned short flags, char* data, int dataLen, void *userContext); +typedef void(*ILibRemoteLogging_OnRawForward)(ILibRemoteLogging sender, ILibRemoteLogging_Modules module, ILibRemoteLogging_Flags flags, char *buffer, int bufferLen); + +#ifdef _REMOTELOGGING + char* ILibRemoteLogging_ConvertAddress(struct sockaddr* addr); + char* ILibRemoteLogging_ConvertToHex(char* inVal, int inValLength); + void ILibRemoteLogging_printf(ILibRemoteLogging loggingModule, ILibRemoteLogging_Modules module, ILibRemoteLogging_Flags flags, char* format, ...); + + ILibRemoteLogging ILibRemoteLogging_Create(ILibRemoteLogging_OnWrite onOutput); + ILibTransport* ILibRemoteLogging_CreateFileTransport(ILibRemoteLogging loggingModule, ILibRemoteLogging_Modules modules, ILibRemoteLogging_Flags flags, char* path, int pathLen); + void ILibRemoteLogging_Destroy(ILibRemoteLogging logger); + void ILibRemoteLogging_SetRawForward(ILibRemoteLogging logger, int bufferOffset, ILibRemoteLogging_OnRawForward onRawForward); + + void ILibRemoteLogging_DeleteUserContext(ILibRemoteLogging logger, void *userContext); + void ILibRemoteLogging_RegisterCommandSink(ILibRemoteLogging logger, ILibRemoteLogging_Modules module, ILibRemoteLogging_OnCommand sink); + int ILibRemoteLogging_Dispatch(ILibRemoteLogging loggingModule, char* data, int dataLen, void *userContext); + #define ILibRemoteLogging_ReadModuleType(data) ((ILibRemoteLogging_Modules)ntohs(((unsigned short*)data)[0])) + #define ILibRemoteLogging_ReadFlags(data) ((ILibRemoteLogging_Flags)ntohs(((unsigned short*)data)[1])) + int ILibRemoteLogging_IsModuleSet(ILibRemoteLogging loggingModule, ILibRemoteLogging_Modules module); + void ILibRemoteLogging_Forward(ILibRemoteLogging loggingModule, char* data, int dataLen); +#else + #define ILibRemoteLogging_ConvertAddress(...) ; + #define ILibRemoteLogging_ConvertToHex(...) ; + #define ILibRemoteLogging_printf(...) ; + #define ILibRemoteLogging_Create(...) NULL; + #define ILibRemoteLogging_SetRawForward(...) ; + #define ILibRemoteLogging_CreateFileTransport(...) NULL; + #define ILibRemoteLogging_Destroy(...) ; + #define ILibRemoteLogging_DeleteUserContext(...) ; + #define ILibRemoteLogging_RegisterCommandSink(...) ; + #define ILibRemoteLogging_Dispatch(...) ; + #define ILibRemoteLogging_ReadModuleType(data) ILibRemoteLogging_Modules_UNKNOWN + #define ILibRemoteLogging_ReadFlags(data) ILibRemoteLogging_Flags_NONE + #define ILibRemoteLogging_IsModuleSet(...) 0 + #define ILibRemoteLogging_Forward(...) ; +#endif + +/*! @} */ + +#endif + diff --git a/README.md b/README.md index e9cc75c..8400006 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +The Default ("master") branch is our release branch that is for production use. All other branches are pre-production and should not be used for production deployments. + # 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). @@ -9,67 +11,77 @@ As a prerequisite, a Local Management Service (LMS) must be installed and runnin 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 -- RPC requires CMake version 3.17. - - Please use "cmake --version" to check version. - - CMake be downloaded from . +``` +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 . +``` +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +cmake --build . +``` + +To build debug: +``` +cmake -DCMAKE_BUILD_TYPE=Debug .. +``` ### Run -- See ./rpc --help for details. -- Example - - sudo ./rpc --url wss://localhost:8080 --profile profile1 +``` +sudo ./rpc --url wss://localhost:8080 --cmd "-t activate --profile profile1" +``` + +Use --help for more options. ## Windows Steps below are for Windows 10 and Visual Studio 2019 Professional. -### Dependencies - -- RPC requires CMake version 3.17, which is included with Visual Studio 2019. - - Please use "cmake --version" to check version. - - CMake can be downloaded from . - ### 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 +``` +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 +``` +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 +``` -- 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 +To 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 +``` +cd build\Release +rpc.exe --url wss://localhost:8080 --cmd "-t activate --profile profile1" +``` + +Use --help for more options. diff --git a/activation.cpp b/activation.cpp new file mode 100644 index 0000000..59eed9d --- /dev/null +++ b/activation.cpp @@ -0,0 +1,321 @@ +/* +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 "activation.h" +#include +#include +#include +#include +#include +#include "version.h" +#include "commands.h" +#include "network.h" +#include "utils.h" + +bool get_certificate_hashes(web::json::value& hashes) +{ + std::vector hashValues; + std::vector certHashes; + if (!cmd_get_certificate_hashes(certHashes)) + { + return false; + } + + for (std::string hashString : certHashes) + { + hashValues.push_back(web::json::value::string(utility::conversions::convertstring(hashString))); + } + + hashes = web::json::value::array(hashValues); + + return true; +} + +bool get_uuid(web::json::value& value) +{ + int i = 0; + std::vector uuidValue; + std::vector uuid; + + if (!cmd_get_uuid(uuid)) return false; + + for (unsigned char value : uuid) + { + uuidValue.push_back(web::json::value(uuid[i++])); + } + + value = web::json::value::array(uuidValue); + + return true; +} + +std::string get_dns_info() +{ + std::string dnsSuffix; + + // get DNS according to AMT + cmd_get_dns_suffix(dnsSuffix); + + if (!dnsSuffix.length()) + { + std::vector address; + cmd_get_wired_mac_address(address); + + if (address.size() == 6) + { + char macAddress[6]; + macAddress[0] = address[0]; + macAddress[1] = address[1]; + macAddress[2] = address[2]; + macAddress[3] = address[3]; + macAddress[4] = address[4]; + macAddress[5] = address[5]; + + // get DNS from OS + dnsSuffix = net_get_dns(macAddress); + } + } + + return dnsSuffix; +} + +web::json::value get_dns() +{ + utility::string_t tmp; + + std::string dnsSuffix = get_dns_info(); + tmp = utility::conversions::convertstring(dnsSuffix); + + return web::json::value::string(tmp); +} + +bool getVersion(web::json::value& value) +{ + std::string version; + utility::string_t tmp; + + if (!cmd_get_version(version)) return false; + + tmp = utility::conversions::convertstring(version); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_sku(web::json::value& value) +{ + std::string version; + utility::string_t tmp; + + if (!cmd_get_sku(version)) return false; + tmp = utility::conversions::convertstring(version); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_build_number(web::json::value& value) +{ + std::string version; + utility::string_t tmp; + + if (!cmd_get_build_number(version)) return false; + tmp = utility::conversions::convertstring(version); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_local_system_account_username(web::json::value& value) +{ + std::string username; + std::string password; + utility::string_t tmp; + + if (!cmd_get_local_system_account(username, password)) return false; + tmp = utility::conversions::convertstring(username); + + value = web::json::value::string(tmp); + + return true; +} + + bool get_local_system_account_password(web::json::value& value) +{ + std::string username; + std::string password; + utility::string_t tmp; + + if (!cmd_get_local_system_account(username, password)) return false; + tmp = utility::conversions::convertstring(password); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_control_mode(web::json::value& value) +{ + int controlMode; + utility::string_t tmp; + + if (!cmd_get_control_mode(controlMode)) return false; + + value = web::json::value::number(controlMode); + + return true; +} + +bool get_client_string(web::json::value& value) +{ + int controlMode; + utility::string_t tmp; + + tmp = utility::conversions::convertstring("PPC"); + value = web::json::value::string(tmp); + + return true; +} + +bool get_activation_payload(web::json::value& payload) +{ + web::json::value value; + utility::string_t tmp; + web::json::value activationParams; + + // get code version + if (!getVersion(value)) return false; + activationParams[U("ver")] = value; + + if (!get_build_number(value)) return false; + activationParams[U("build")] = value; + + if (!get_sku(value)) return false; + activationParams[U("sku")] = value; + + // get UUID + if (!get_uuid(value)) return false; + activationParams[U("uuid")] = value; + + // get local system account + if (!get_local_system_account_username(value)) return false; + activationParams[U("username")] = value; + + if (!get_local_system_account_password(value)) return false; + activationParams[U("password")] = value; + + // get Control Mode + if (!get_control_mode(value)) return false; + activationParams[U("currentMode")] = value; + + // get DNS Info + activationParams[U("fqdn")] = get_dns(); + + // get client string + if (!get_client_string(value)) return false; + activationParams[U("client")] = value; + + // get certificate hashes + if (!get_certificate_hashes(value)) return false; + activationParams[U("certHashes")] = value; + + payload = activationParams; + + return true; +} + +bool act_create_request(std::string commands, std::string dns_suffix, std::string& request) +{ + web::json::value msg; + + // get the activation info + utility::string_t tmp = utility::conversions::convertstring(commands); + msg[U("method")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("key"); + msg[U("apiKey")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROJECT_VER); + msg[U("appVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROTOCOL_VERSION); + msg[U("protocolVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("status")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("message")] = web::json::value::string(tmp); + + // get the activation payload + web::json::value activationPayload; + if (!get_activation_payload(activationPayload)) return false; + + // override dns value if passed in + if (!dns_suffix.empty()) + { + utility::string_t tmp = utility::conversions::convertstring(dns_suffix); + activationPayload[U("fqdn")] = web::json::value::string(tmp); + } + + // serialize payload + std::string serializedPayload = utility::conversions::to_utf8string(activationPayload.serialize()); + std::string encodedPayload = util_encode_base64(serializedPayload); + utility::string_t payload = utility::conversions::to_string_t(encodedPayload); + msg[U("payload")] = web::json::value::string(payload); + +#ifdef DEBUG + std::cout << "Activation info payload:" << serializedPayload << std::endl; +#endif + + // serialize the entire message + request = utility::conversions::to_utf8string(msg.serialize()); + + return true; +} + +bool act_create_response(std::string payload, std::string& response) +{ + web::json::value msg; + + utility::string_t tmp = utility::conversions::convertstring("response"); + msg[U("method")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("key"); + msg[U("apiKey")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROJECT_VER); + msg[U("appVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROTOCOL_VERSION); + msg[U("protocolVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("status")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("message")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(util_encode_base64(payload)); + msg[U("payload")] = web::json::value::string(tmp); + + response = utility::conversions::to_utf8string(msg.serialize()); + + return true; +} diff --git a/activation.h b/activation.h new file mode 100644 index 0000000..86006dd --- /dev/null +++ b/activation.h @@ -0,0 +1,33 @@ +/* +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 __ACTIVATION_H__ +#define __ACTIVATION_H__ + +#include + +#define PROTOCOL_VERSION "3.0.0" + +#ifdef _WIN32 +#define convertstring to_utf16string +#else +#define convertstring to_utf8string +#endif + +bool act_create_request(std::string commands, std::string dns_suffix, std::string& request); +bool act_create_response(std::string payload, std::string& response); + +#endif \ No newline at end of file diff --git a/args.cpp b/args.cpp new file mode 100644 index 0000000..bdf3d0f --- /dev/null +++ b/args.cpp @@ -0,0 +1,95 @@ +/* +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 "args.h" +#include +#include "port.h" +#include "utils.h" + +bool get_arg_exists(int argc, char* argv[], const char* long_opt, const char* short_opt) +{ + for (int i = 1; i < argc; i++) + { + if ((strcasecmp(argv[i], long_opt) == 0) || (strcasecmp(argv[i], short_opt) == 0)) + { + return true; + } + } + + return false; +} + +bool get_arg_string(int argc, char* argv[], const char* long_opt, const char* short_opt, std::string& value) +{ + value = ""; + + for (int i = 1; i < argc; i++) + { + if ((strcasecmp(argv[i], long_opt) == 0) || (strcasecmp(argv[i], short_opt) == 0)) + { + if (i + 1 < argc) + { + std::string tmp = argv[++i]; + + if (!util_is_printable(tmp)) + { + return false; + } + + value = tmp; + + return true; + } + } + } + + return false; +} + +bool args_get_help(int argc, char* argv[]) +{ + return get_arg_exists(argc, argv, "--help", "-h"); +} + +bool args_get_version(int argc, char* argv[]) +{ + return get_arg_exists(argc, argv, "--version", "-v"); +} + +bool args_get_url(int argc, char* argv[], std::string& url) +{ + return get_arg_string(argc, argv, "--url", "-u", url); +} + +bool args_get_proxy(int argc, char* argv[], std::string& proxy) +{ + return get_arg_string(argc, argv, "--proxy", "-x", proxy); +} + +bool args_get_cmd(int argc, char* argv[], std::string& cmd) +{ + return get_arg_string(argc, argv, "--cmd", "-c", cmd); +} + +bool args_get_dns(int argc, char* argv[], std::string& dns) +{ + return get_arg_string(argc, argv, "--dns", "-d", dns); +} + +bool args_get_info(int argc, char* argv[], std::string& info) +{ + return get_arg_string(argc, argv, "--info", "-i", info); +} diff --git a/args.h b/args.h new file mode 100644 index 0000000..7fd005a --- /dev/null +++ b/args.h @@ -0,0 +1,30 @@ +/* +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 __ARGS_H__ +#define __ARGS_H__ + +#include + +bool args_get_help(int argc, char* argv[]); +bool args_get_version(int argc, char* argv[]); +bool args_get_url(int argc, char* argv[], std::string& url); +bool args_get_proxy(int argc, char* argv[], std::string& proxy); +bool args_get_cmd(int argc, char* argv[], std::string& cmd); +bool args_get_dns(int argc, char* argv[], std::string& dns); +bool args_get_info(int argc, char* argv[], std::string& info); + +#endif diff --git a/commands.cpp b/commands.cpp index 43145f5..208e9fb 100644 --- a/commands.cpp +++ b/commands.cpp @@ -15,225 +15,245 @@ limitations under the License. */ #include "commands.h" - -#ifdef _WIN32 -#include -#include -#include -#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 - -#include -#include -#include -#include -#include #include -#include "lms.h" +#include "port.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; -} +#ifndef _WIN32 +#include #endif +extern "C" { +#ifndef _WIN32 + #include "HECILinux.h" +#endif + #include "PTHICommand.h" +#ifdef bool + #undef bool +#endif +} +#include "version.h" -json::value getCertificateHashes() +bool cmd_get_version(std::string& version) { - json::value certHashes; - vector hashValues; + version.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get code version + CODE_VERSIONS codeVersion; + AMT_STATUS status = pthi_GetCodeVersions(&codeVersion); + + // additional versions + if (status == 0) + { + for (int i = 0; i < (int) codeVersion.VersionsCount; i++) + { + if (strcmp((char*)(codeVersion.Versions[i].Description.String), "AMT") == 0) + { + version = ((char*)codeVersion.Versions[i].Version.String); + + return true; + } + } + } + + return false; +} + +bool cmd_get_build_number(std::string& version) +{ + version.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get code version + CODE_VERSIONS codeVersion; + AMT_STATUS status = pthi_GetCodeVersions(&codeVersion); + + // additional versions + if (status == 0) + { + for (int i = 0; i < (int) codeVersion.VersionsCount; i++) + { + if (strcmp((char*)(codeVersion.Versions[i].Description.String), "Build Number") == 0) + { + version = ((char*)codeVersion.Versions[i].Version.String); + + return true; + } + } + } + + return false; +} + +bool cmd_get_sku(std::string& version) +{ + version.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get code version + CODE_VERSIONS codeVersion; + AMT_STATUS status = pthi_GetCodeVersions(&codeVersion); + + // additional versions + if (status == 0) + { + for (int i = 0; i < (int) codeVersion.VersionsCount; i++) + { + if (strcmp((char*)(codeVersion.Versions[i].Description.String), "Sku") == 0) + { + version = ((char*)codeVersion.Versions[i].Version.String); + + return true; + } + } + } + + return false; +} + +bool cmd_get_uuid(std::vector& uuid) +{ + uuid.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get UUID + AMT_UUID amt_uuid; + AMT_STATUS amt_status = pthi_GetUUID(&amt_uuid); + if (amt_status == 0) + { + for (int i = 0; i < 16; i++) + { + uuid.push_back(amt_uuid[i]); + } + + return true; + } + + return false; +} + +bool cmd_get_local_system_account(std::string& username, std::string& password) +{ + username.clear(); + password.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get Local System Account + LOCAL_SYSTEM_ACCOUNT local_system_account; + AMT_STATUS amt_status = pthi_GetLocalSystemAccount(&local_system_account); + if (amt_status == 0) + { + username = local_system_account.username; + password = local_system_account.password; + + return true; + } + + return false; +} + +bool cmd_get_control_mode(int& mode) +{ + mode = 0; + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get Control Mode + int controlMode; + AMT_STATUS amt_status = pthi_GetControlMode(&controlMode); + if (amt_status == 0) + { + mode = controlMode; + + return true; + } + + return false; +} + +bool cmd_get_dns_suffix(std::string& suffix) +{ + suffix.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get DNS according to AMT + AMT_ANSI_STRING amt_dns_suffix; + AMT_STATUS amt_status = pthi_GetDnsSuffix(&amt_dns_suffix); + + if (amt_status == 0) + { + std::string tmp(amt_dns_suffix.Buffer, amt_dns_suffix.Length); + suffix = tmp; + + return true; + } + + return false; +} + +bool cmd_get_wired_mac_address(std::vector& address) +{ + address.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get wired interface + LAN_SETTINGS lan_settings; + UINT32 interface_settings = 0; // wired=0, wireless=1 + AMT_STATUS amt_status = pthi_GetLanInterfaceSettings(interface_settings, &lan_settings); + if (amt_status == 0) + { + if (!lan_settings.Enabled) + { + return false; + } + + for (int i = 0; i < 6; i++) + { + address.push_back(lan_settings.MacAddress[i]); + } + + return true; + } + + return false; +} + +bool cmd_get_certificate_hashes(std::vector& hashes) +{ + hashes.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; // get the hash handles - EnumerateHashHandlesCommand command; - ENUMERATE_HASH_HANDLES_RESPONSE response = command.getResponse(); + AMT_HASH_HANDLES amt_hash_handles; + CERTHASH_ENTRY certhash_entry; - vector::iterator itr = response.HashHandles.begin(); - vector::iterator endItr = response.HashHandles.end(); - for (; itr != endItr; ++itr) + memset(&amt_hash_handles, 0, sizeof(AMT_HASH_HANDLES)); + if (pthi_EnumerateHashHandles(&amt_hash_handles) == 0) { - // get each entry - GetCertificateHashEntryCommand command(*itr); - GET_CERTIFICATE_HASH_ENTRY_RESPONSE response = command.getResponse(); + for (int i = 0; i < (int) amt_hash_handles.Length; i++) + { + // get each entry + AMT_STATUS status = pthi_GetCertificateHashEntry(amt_hash_handles.Handles[i], &certhash_entry); - int hashSize; - switch (response.HashAlgorithm) { + int hashSize; + switch (certhash_entry.HashAlgorithm) { case 0: // MD5 hashSize = 16; break; @@ -249,261 +269,25 @@ json::value getCertificateHashes() 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) ) ); + if (certhash_entry.IsActive == 1) + { + std::string hashString; + hashString.clear(); + for (int i = 0; i < hashSize; i++) + { + char hex[10]; + snprintf(hex, 10, "%02x", certhash_entry.CertificateHash[i]); + hashString += hex; + } + + hashes.push_back(hashString); + } } + + return true; } - return json::value::array(hashValues); + return false; } - -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, string dnssuffixcmd) -{ - 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::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 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 = ""; - if (dnssuffixcmd.length() > 0) - { - // use what's passed in - dnsSuffix = dnssuffixcmd; - } - else - { - // get it from AMT or OS - 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 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 strVector = utility::conversions::from_base64(serializedData); - string decodedString(strVector.begin(), strVector.end()); - - return decodedString; -} - -string createActivationRequest(string profile, string dnssuffixcmd) -{ - // 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, dnssuffixcmd); - 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; -} \ No newline at end of file diff --git a/commands.h b/commands.h index 25a806b..614ef30 100644 --- a/commands.h +++ b/commands.h @@ -17,32 +17,17 @@ limitations under the License. #ifndef __COMMANDS_H__ #define __COMMANDS_H__ -#include +#include #include -#include -#include -#include - -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, string dnssuffixcmd); -json::value getCertificateHashes(); -string createResponse(string payload); -string getActivateInfo(string profile, string dnssuffixcmd); -string encodeBase64(string str); -string decodeBase64(string str); -void dumpMessage(string tmp); +bool cmd_get_version(std::string& version); +bool cmd_get_build_number(std::string& version); +bool cmd_get_sku(std::string& version); +bool cmd_get_uuid(std::vector& uuid); +bool cmd_get_local_system_account(std::string& username, std::string& password); +bool cmd_get_control_mode(int& mode); +bool cmd_get_dns_suffix(std::string& suffix); +bool cmd_get_wired_mac_address(std::vector& address); +bool cmd_get_certificate_hashes(std::vector& hashes); #endif \ No newline at end of file diff --git a/info.cpp b/info.cpp new file mode 100644 index 0000000..814bebb --- /dev/null +++ b/info.cpp @@ -0,0 +1,217 @@ +/* +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 "info.h" +#include +#include +#include +#include "commands.h" +#include "utils.h" + +void out_text(const std::string name, const std::vector value, const unsigned char delimeter=' ') +{ + std::cout << name << ": "; + int char_count = 1; + for (unsigned char tmp : value) + { + std::cout << std::setfill('0') << std::setw(2) << std::hex << (unsigned int) tmp; + + if (char_count++ < value.size()) + { + std::cout << delimeter; + } + } + + std::cout << std::endl; +} + +void out_text(const std::string name, const std::string value) +{ + std::cout << name << ": "; + std::cout << value; + std::cout << std::endl; +} + +void out_text(const std::string name, const int value) +{ + std::cout << name << ": "; + std::cout << value; + std::cout << std::endl; +} + +void out_text(const std::string name, const std::vector value) +{ + int count = 1; + + std::cout << name << std::endl; + for (std::string tmp : value) + { + std::cout << std::setfill('0') << std::setw(2) << count++ << ":"; + std::cout << tmp << std::endl; + } +} + +bool info_get_version() +{ + std::string tmp; + + if (!cmd_get_version(tmp)) return false; + + out_text("Version", tmp); + + return true; +} + +bool info_get_build_number() +{ + std::string tmp; + + if (!cmd_get_build_number(tmp)) return false; + + out_text("Build number", tmp); + + return true; +} + +bool info_get_sku() +{ + std::string tmp; + + if (!cmd_get_sku(tmp)) return false; + + out_text("SKU", tmp); + + return true; +} + +bool info_get_uuid() +{ + std::string uuid_string; + std::vector tmp; + + if (!cmd_get_uuid(tmp)) return false; + + if (!util_format_uuid(tmp, uuid_string)) return false; + + out_text("UUID", uuid_string); + + return true; +} + +bool info_get_control_mode() +{ + int tmp; + + if (!cmd_get_control_mode(tmp)) return false; + + std::string control_mode; + if (tmp == 0) control_mode = "pre-provisioning state"; + else if (tmp == 1) control_mode = "activated in client control mode"; + else if (tmp == 2) control_mode = "activated in admin control mode"; + + out_text("Control mode", control_mode); + + return true; +} + +bool info_get_dns_suffix() +{ + std::string tmp; + + if (!cmd_get_dns_suffix(tmp)) return false; + + out_text("DNS suffix", tmp); + + return true; +} + +bool info_get_wired_mac_address() +{ + std::vector tmp; + + if (!cmd_get_wired_mac_address(tmp)) return false; + + out_text("Wired MAC address", tmp, '-'); + + return true; +} + +bool info_get_certificate_hashes() +{ + std::vector tmp; + + if (!cmd_get_certificate_hashes(tmp)) return false; + + out_text("Certificate hashes", tmp); + + return true; +} + +bool info_get_all() +{ + std::vector tmp; + + if (info_get_version() && info_get_build_number() && info_get_sku() && + info_get_uuid() && info_get_control_mode() && info_get_dns_suffix() && + info_get_wired_mac_address() && info_get_certificate_hashes()) + { + return true; + } + + return true; +} + +bool info_get(const std::string info) +{ + if (info.compare("ver") == 0) + { + return info_get_version(); + } + else if (info.compare("build") == 0) + { + return info_get_build_number(); + } + else if (info.compare("sku") == 0) + { + return info_get_sku(); + } + else if (info.compare("uuid") == 0) + { + return info_get_uuid(); + } + else if (info.compare("mode") == 0) + { + return info_get_control_mode(); + } + else if (info.compare("dns") == 0) + { + return info_get_dns_suffix(); + } + else if (info.compare("mac") == 0) + { + return info_get_wired_mac_address(); + } + else if (info.compare("cert") == 0) + { + return info_get_certificate_hashes(); + } + else if (info.compare("all") == 0) + { + return info_get_all(); + } + + return false; +} diff --git a/info.h b/info.h new file mode 100644 index 0000000..0157882 --- /dev/null +++ b/info.h @@ -0,0 +1,32 @@ +/* +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 __INFO_H__ +#define __INFO_H__ + +#include + +bool info_get(const std::string info); +bool info_get_version(); +bool info_get_build_number(); +bool info_get_sku(); +bool info_get_uuid(); +bool info_get_control_mode(); +bool info_get_dns_suffix(); +bool info_get_wired_mac_address(); +bool info_get_all(); + +#endif diff --git a/lms.cmake.in b/lms.cmake.in deleted file mode 100644 index 950ada4..0000000 --- a/lms.cmake.in +++ /dev/null @@ -1,15 +0,0 @@ -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 "" -) \ No newline at end of file diff --git a/lms.cpp b/lms.cpp index 53724a9..f4971c5 100644 --- a/lms.cpp +++ b/lms.cpp @@ -26,7 +26,7 @@ limitations under the License. #include #endif -SOCKET lmsConnect() +SOCKET lms_connect() { std::string lmsAddress = "localhost"; std::string lmsPort = "16992"; diff --git a/lms.h b/lms.h index 85a219c..e28c6c7 100644 --- a/lms.h +++ b/lms.h @@ -39,6 +39,6 @@ static inline int closesocket(int fd) #define SD_BOTH SHUT_RDWR #endif -SOCKET lmsConnect(); +SOCKET lms_connect(); #endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 4b12c94..c0dcef2 100644 --- a/main.cpp +++ b/main.cpp @@ -14,54 +14,45 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include -#include -#include #include #include -#include -#include -#include "commands.h" -#include "lms.h" -#include "version.h" - -using namespace std; -using namespace web; -using namespace web::websockets::client; - +#include #include +#include "port.h" +#include "lms.h" +#include "commands.h" +#include "activation.h" +#include "utils.h" +#include "usage.h" +#include "args.h" +#include "info.h" -void showUsage(); -bool isPrintable(std::string str); +// timer thread globals +long long g_timeout_val = 0; +const int g_timeout_max = 10; +bool g_thread_alive = true; -string websocket_address = ""; -string server_profile = ""; -string websocket_proxy = ""; -string dns_suffix = ""; - -long long timeoutTimer = 0; -const int MAX_TIMEOUT = 10; // seconds -bool timeoutThreadAlive = true; - -void timeoutFunc(std::condition_variable *cv, std::mutex *mx) +// timeout thread function +// used to exit application in case a timeout occurs +void timeout_thread_function(std::condition_variable *cv, std::mutex *mx) { - while (timeoutThreadAlive) + while (g_thread_alive) { std::chrono::time_point now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); long long currTime = std::chrono::duration_cast(duration).count(); - if (currTime > timeoutTimer) + if (currTime > g_timeout_val) { - if (currTime - timeoutTimer >= MAX_TIMEOUT) + if (currTime - g_timeout_val >= g_timeout_max) { 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) + if (g_timeout_val) { - cout << endl << "Timed-out due to inactivity." < now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); - timeoutTimer = std::chrono::duration_cast(duration).count(); + g_timeout_val = std::chrono::duration_cast(duration).count(); try { - // handle message from server... - string rcv_websocket_msg = ret_msg.extract_string().get(); + // handle message from server + std::string rcv_websocket_msg = ret_msg.extract_string().get(); #ifdef DEBUG - cout << endl << "<<<<< Received Message " << endl; - cout << rcv_websocket_msg << endl; + std::cout << std::endl << "<<<<< Received Message " << std::endl; + std::cout << rcv_websocket_msg << std::endl; #endif - cout << "." << std::flush; // dot status output + std::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 = ""; + std::string msgMethod = ""; + std::string msgApiKey = ""; + std::string msgAppVersion = ""; + std::string msgProtocolVersion = ""; + std::string msgStatus = ""; + std::string msgMessage = ""; + std::string msgPayload = ""; + std::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; + std::cerr << std::endl << "Received incorrectly formatted message." << std::endl; cv.notify_all(); - timeoutThreadAlive = false; + g_thread_alive = false; return; } @@ -282,35 +231,35 @@ int main(int argc, char *argv[]) } catch (...) { - std::cerr << endl << "Received message parse error." << endl; + std::cerr << std::endl << "Received message parse error." << std::endl; return; } #ifdef DEBUG - cout << msgMethod << ", " << msgStatus << ", " << msgMessage << endl; - cout << rcv_websocket_msg << endl; + std::cout << msgMethod << ", " << msgStatus << ", " << msgMessage << std::endl; + std::cout << rcv_websocket_msg << std::endl; #endif // process any messages we can // - if success, done // - if error, get out - if (boost::iequals(msgMethod, "success")) + if (msgMethod.compare("success")==0) { // cleanup - timeoutTimer = 0; + g_timeout_val = 0; // exit - cout << endl << msgMessage << endl; + std::cout << std::endl << msgMessage << std::endl; return; } - else if (boost::iequals(msgMethod, "error")) + else if (msgMethod.compare("error")==0) { // cleanup - timeoutTimer = 0; + g_timeout_val = 0; // exit - cout << endl << msgMessage << endl; + std::cout << std::endl << msgMessage << std::endl; return; } @@ -325,7 +274,7 @@ int main(int argc, char *argv[]) msgPayload = utility::conversions::to_utf8string(out); // decode payload - payloadDecoded = decodeBase64(msgPayload); + payloadDecoded = util_decode_base64(msgPayload); } else { @@ -335,24 +284,24 @@ int main(int argc, char *argv[]) } catch (...) { - std::cerr << endl << "JSON format error. Unable to parse message." << endl; + std::cerr << std::endl << "JSON format error. Unable to parse message." << std::endl; return; } try { // conntect to lms - s = lmsConnect(); + s = lms_connect(); } catch (...) { - std::cerr << endl << "Unable to connect to Local Management Service (LMS). Please ensure LMS is running." << endl; + std::cerr << std::endl << "Unable to connect to Local Management Service (LMS). Please ensure LMS is running." << std::endl; return; } #ifdef DEBUG - cout << endl << "vvvvv Sending Message " << endl; - cout << payloadDecoded << endl; + std::cout << std::endl << "vvvvv Sending Message " << std::endl; + std::cout << payloadDecoded << std::endl; #endif // send message to LMS @@ -375,7 +324,7 @@ int main(int argc, char *argv[]) // read until connection is closed by LMS while (1) { - string superBuffer = ""; + std::string superBuffer = ""; while (1) { int res = select(fd, &rset, NULL, NULL, &timeout); @@ -394,8 +343,8 @@ int main(int argc, char *argv[]) if (res > 0) { #ifdef DEBUG - cout << endl << "^^^^^ Received Message" << endl; - cout << recv_buffer << endl; + std::cout << std::endl << "^^^^^ Received Message" << std::endl; + std::cout << recv_buffer << std::endl; #endif superBuffer += recv_buffer; } @@ -406,9 +355,8 @@ int main(int argc, char *argv[]) } 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 + // case where res is zero bytes, select returns 1 + // with recv returning 0 to indicate close break; } } // while select() @@ -416,14 +364,16 @@ int main(int argc, char *argv[]) // 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); + std::string response; + if (!act_create_response(superBuffer.c_str(), response)) return; + + web::websockets::client::websocket_outgoing_message send_websocket_msg; + std::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; + std::cout << std::endl << ">>>>> Sending Message" << std::endl; + std::cout << superBuffer << std::endl; + std::cout << send_websocket_buffer << std::endl; #endif client.send(send_websocket_msg).wait(); @@ -437,13 +387,13 @@ int main(int argc, char *argv[]) } catch (...) { - std::cerr << endl << "Communication error in receive handler." << endl; + std::cerr << std::endl << "Communication error in receive handler." << std::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) + client.set_close_handler([&mx,&cv](web::websockets::client::websocket_close_status status, const utility::string_t &reason, const std::error_code &code) { // websocket closed by server, notify main thread cv.notify_all(); @@ -451,37 +401,37 @@ int main(int argc, char *argv[]) try { - // Connect to web socket server; AMT activation server - client.connect(utility::conversions::to_string_t(websocket_address)).wait(); + // connect to web socket server; AMT activation server + client.connect(utility::conversions::to_string_t(arg_url)).wait(); } catch (...) { - std::cerr << "Unable to connect to websocket server. Please check url." << endl; + std::cerr << "Unable to connect to websocket server. Please check url." << std::endl; exit(1); } try { - // Send activationParams to websocket - websocket_outgoing_message out_msg; - out_msg.set_utf8_message(activationInfo); + // send activationParams to websocket + web::websockets::client::websocket_outgoing_message out_msg; + out_msg.set_utf8_message(activation_info); #ifdef DEBUG - cout << endl << ">>>>> Sending Activiation Info" << endl; - cout << activationInfo << endl; + std::cout << std::endl << ">>>>> Sending Activiation Info" << std::endl; + std::cout << activation_info << std::endl; #endif client.send(out_msg).wait(); } catch (...) { - std::cerr << endl << "Unable to send message to websocket server." << endl; + std::cerr << std::endl << "Unable to send message to websocket server." << std::endl; exit(1); } std::chrono::time_point now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); - timeoutTimer = std::chrono::duration_cast(duration).count(); - std::thread timeoutThread(timeoutFunc, &cv, &mx); + g_timeout_val = std::chrono::duration_cast(duration).count(); + std::thread timeoutThread(timeout_thread_function, &cv, &mx); // wait for server to send success/failure command std::unique_lock lock(mx); @@ -502,32 +452,3 @@ int main(int argc, char *argv[]) exit(0); } -bool isPrintable(std::string str) -{ - for (char c : str) - { - if (!std::isprint(c)) - { - return false; - } - } - - return true; -} - -void showUsage() -{ - cout << "Usage: " << PROJECT_NAME << " --url --profile [--proxy ]" << endl; - cout << "Required:" << endl; - cout << " -u, --url websocket server" << endl; - cout << " -p, --profile server profile" << endl; - cout << "Optional:" << endl; - cout << " -x, --proxy proxy address and port" << endl; - cout << " -d, --dns dns suffix" << 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; -} diff --git a/network.cpp b/network.cpp new file mode 100644 index 0000000..efa4940 --- /dev/null +++ b/network.cpp @@ -0,0 +1,196 @@ +/* +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 "network.h" +#include + +#ifdef _WIN32 +#include +#include +#define WORKING_BUFFER_SIZE 15000 +#define MAX_TRIES 3 +#else +#include +#include +#include +#include +#include +#include +#include +#include +typedef int SOCKET; +#endif + +#ifdef _WIN32 +std::string net_get_dns(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) { + std::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 net_get_dns(char* macAddress) +{ + std::string dnsSuffix = ""; + + // get socket + SOCKET s = 0; + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + std::cout << "couldn't get socket" << std::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) + { + std::cout << "ioctl SIOCGIFCONF failed" << std::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) + { + std::cout << "ioctl SIOCGIFADDR failed" << std::endl; + continue; + } + + if (inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), + ip, sizeof(ip)) == NULL) + { + std::cout << "inet_ntop" << std::endl; + continue; + } + + // get MAC address + if (ioctl(s, SIOCGIFHWADDR, item) < 0) + { + std::cout << "ioctl SIOCGIFHWADDR failed" << std::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) + { + std::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 \ No newline at end of file diff --git a/network.h b/network.h new file mode 100644 index 0000000..8d014f4 --- /dev/null +++ b/network.h @@ -0,0 +1,24 @@ +/* +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 __NETWORK_H__ +#define __NETWORK_H__ + +#include + +std::string net_get_dns(char* macAddress); + +#endif \ No newline at end of file diff --git a/port.h b/port.h new file mode 100644 index 0000000..8ee9651 --- /dev/null +++ b/port.h @@ -0,0 +1,37 @@ +/* +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 __PORT_H__ +#define __PORT_H__ + +#include + +extern "C" +{ + // main entry into microlms + extern int main_micro_lms(); +} + + +#ifdef _WIN32 +// Windows +#define strncpy strncpy_s +#define strcasecmp _stricmp +#else +// Linux +#endif + +#endif diff --git a/usage.cpp b/usage.cpp new file mode 100644 index 0000000..21a7703 --- /dev/null +++ b/usage.cpp @@ -0,0 +1,61 @@ +/* +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 "usage.h" +#include +#include "version.h" +#include "activation.h" + +void usage_show_help() +{ + std::cout << "Usage: " << PROJECT_NAME << " --help | --version | --info | --url --cmd [--proxy ] [--dns ]" << std::endl; + std::cout << "Required:" << std::endl; + std::cout << " -u, --url websocket server" << std::endl; + std::cout << " -c, --cmd server command" << std::endl; + std::cout << "Optional:" << std::endl; + std::cout << " -x, --proxy proxy address and port" << std::endl; + std::cout << " -d, --dns dns suffix" << std::endl; + std::cout << "Informational:" << std::endl; + std::cout << " -h, --help this help text" << std::endl; + std::cout << " -v, --version version" << std::endl; + std::cout << " -i, --info info on an " << std::endl; + std::cout << std::endl; + std::cout << "Info :" << std::endl; + std::cout << " all all items" << std::endl; + std::cout << " ver bios version" << std::endl; + std::cout << " build build number" << std::endl; + std::cout << " sku product sku" << std::endl; + std::cout << " uuid unique identifier" << std::endl; + std::cout << " mode current control mode {0=none, 1=client, 2=admin}" << std::endl; + std::cout << " dns domain name suffix" << std::endl; + std::cout << " mac wired MAC address" << std::endl; + std::cout << " cert certificate hashes" << std::endl; + std::cout << std::endl; + std::cout << "Examples:" << std::endl; + std::cout << " " << PROJECT_NAME << " --url wss://localhost:8080 --cmd \"-t activate --profile profile1\"" << std::endl; + std::cout << " " << PROJECT_NAME << " -u wss://localhost:8080 -c \"-t deactivate --password P@ssw0rd\" -x http://proxy.com:1000" << std::endl; + std::cout << " " << PROJECT_NAME << " -u wss://localhost:8080 -c \"-e [encrypted-command-text] -h [signature-hash]\"" << std::endl; + std::cout << std::endl; + std::cout << "Note:" << std::endl; + std::cout << " Since can contain multiple options and arguments, the entire must be in quotes as shown" << std::endl; + std::cout << " in the examples." << std::endl; +} + +void usage_show_version() +{ + std::cout << PROJECT_NAME << " " << PROJECT_VER << std::endl; + std::cout << "protocol " << PROTOCOL_VERSION << std::endl; +} \ No newline at end of file diff --git a/usage.h b/usage.h new file mode 100644 index 0000000..0ee549e --- /dev/null +++ b/usage.h @@ -0,0 +1,23 @@ +/* +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 __USAGE_H__ +#define __USAGE_H__ + +void usage_show_help(); +void usage_show_version(); + +#endif \ No newline at end of file diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..d1e7b81 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,70 @@ +/* +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 "utils.h" +#include +#include +#include + +std::string util_encode_base64(std::string str) +{ + std::vector strVector(str.begin(), str.end()); + utility::string_t base64 = utility::conversions::to_base64(strVector); + std::string encodedString = utility::conversions::to_utf8string(base64); + + return encodedString; +} + +std::string util_decode_base64(std::string str) +{ + utility::string_t serializedData = utility::conversions::to_string_t(str); + std::vector strVector = utility::conversions::from_base64(serializedData); + std::string decodedString(strVector.begin(), strVector.end()); + + return decodedString; +} + +bool util_is_printable(std::string str) +{ + for (char c : str) + { + if (!std::isprint(c)) + { + return false; + } + } + + return true; +} + +bool util_format_uuid(std::vector uuid_bytes, std::string& uuid_string) +{ + if (uuid_bytes.size() != 16) return false; + + char tmp[100]; + snprintf(tmp, 100, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid_bytes[3], uuid_bytes[2], uuid_bytes[1], uuid_bytes[0], + uuid_bytes[5], uuid_bytes[4], + uuid_bytes[7], uuid_bytes[6], + uuid_bytes[8], uuid_bytes[9], + uuid_bytes[10], uuid_bytes[11], uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]); + + uuid_string = tmp; + + return true; +} + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..1d445e8 --- /dev/null +++ b/utils.h @@ -0,0 +1,28 @@ +/* +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 __UTILS_H__ +#define __UTILS_H__ + +#include +#include + +std::string util_encode_base64(std::string str); +std::string util_decode_base64(std::string str); +bool util_is_printable(std::string str); +bool util_format_uuid(std::vector uuid_bytes, std::string& uuid_string); + +#endif \ No newline at end of file