Initial adding of files that are included in the project

This commit is contained in:
Jelani 2015-10-30 12:42:59 -07:00
Родитель c4e4d47267
Коммит dc5b99d230
319 изменённых файлов: 91436 добавлений и 0 удалений

167
c/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,167 @@
#Copyright (c) Microsoft. All rights reserved.
#Licensed under the MIT license. See LICENSE file in the project root for full license information.
cmake_minimum_required(VERSION 3.0)
project(azure_c_shared_utility)
#the following variables are project-wide and can be used with cmake-gui
option(skip_unittests "set skip_unittests to ON to skip unittests (default is OFF)[if possible, they are always build]" OFF)
option(compileOption_C "passes a string to the command line of the C compiler" OFF)
option(compileOption_CXX "passes a string to the command line of the C++ compiler" OFF)
#Use solution folders.
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
enable_testing()
#if any compiler has a command line switch called "OFF" then it will need special care
if(NOT "${compileOption_C}" STREQUAL "OFF")
set(CMAKE_C_FLAGS "${compileOption_C} ${CMAKE_C_FLAGS}")
endif()
if(NOT "${compileOption_CXX}" STREQUAL "OFF")
set(CMAKE_CXX_FLAGS "${compileOption_CXX} ${CMAKE_CXX_FLAGS}")
endif()
#this project uses several other projects that are build not by these CMakeFiles
#this project also targets several OSes
#this function takes care of three things:
#1. copying some shared libraries(.dll or .so) to the location of the output executable
macro(compileAsC99)
if (CMAKE_VERSION VERSION_LESS "3.1")
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
set (CMAKE_C_FLAGS "--std=c99 ${CMAKE_C_FLAGS}")
set (CMAKE_CXX_FLAGS "--std=c++11 ${CMAKE_CXX_FLAGS}")
endif()
else()
set (CMAKE_C_STANDARD 99)
set (CMAKE_CXX_STANDARD 11)
endif()
endmacro(compileAsC99)
function(windows_unittests_add_dll whatIsBuilding)
link_directories(${whatIsBuilding}_dll $ENV{VCInstallDir}UnitTest/lib)
add_library(${whatIsBuilding}_dll SHARED
${${whatIsBuilding}_cpp_files}
${${whatIsBuilding}_h_files}
${${whatIsBuilding}_c_files}
)
set_target_properties(${whatIsBuilding}_dll
PROPERTIES
FOLDER "UnitTests")
target_include_directories(${whatIsBuilding}_dll PUBLIC ${sharedutil_include_directories} $ENV{VCInstallDir}UnitTest/include)
target_compile_definitions(${whatIsBuilding}_dll PUBLIC -DCPP_UNITTEST)
target_link_libraries(${whatIsBuilding}_dll micromock_cpp_unittest ctest ${ARGN})
endfunction()
function(windows_unittests_add_lib whatIsBuilding)
link_directories(${whatIsBuilding}_lib $ENV{VCInstallDir}UnitTest/lib)
add_library(${whatIsBuilding}_lib STATIC
${${whatIsBuilding}_cpp_files}
${${whatIsBuilding}_h_files}
${${whatIsBuilding}_c_files}
)
target_include_directories(${whatIsBuilding}_lib PUBLIC ${sharedutil_include_directories})
target_compile_definitions(${whatIsBuilding}_lib PUBLIC -DUSE_CTEST)
target_link_libraries(${whatIsBuilding}_lib micromock_ctest ctest ${ARGN})
endfunction()
function(windows_unittests_add_exe whatIsBuilding)
add_executable(${whatIsBuilding}_exe
${${whatIsBuilding}_cpp_files}
${${whatIsBuilding}_h_files}
${${whatIsBuilding}_c_files}
${CMAKE_CURRENT_LIST_DIR}/main.c
)
set_target_properties(${whatIsBuilding}_exe
PROPERTIES
FOLDER "UnitTests")
target_compile_definitions(${whatIsBuilding}_exe PUBLIC -DUSE_CTEST)
target_include_directories(${whatIsBuilding}_exe PUBLIC ${sharedutil_include_directories})
target_link_libraries(${whatIsBuilding}_exe micromock_ctest ctest ${ARGN})
add_test(NAME ${whatIsBuilding} COMMAND ${whatIsBuilding}_exe)
endfunction()
function(build_test_artifacts whatIsBuilding use_gballoc)
#the first argument is what is building
#the second argument is whether the tests should be build with gballoc #defines or not
#the following arguments are a list of libraries to link with
if(${use_gballoc})
add_definitions(-DGB_MEASURE_MEMORY_FOR_THIS -DGB_DEBUG_ALLOC)
else()
endif()
#setting #defines
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
endif()
#setting includes
set(sharedutil_include_directories ${MICROMOCK_INC_FOLDER} ${TESTRUNNERSWITCHER_INC_FOLDER} ${CTEST_INC_FOLDER} ${SAL_INC_FOLDER} ${SHARED_UTIL_INC_FOLDER} ${SHARED_UTIL_SRC_FOLDER})
if(WIN32)
else()
include_directories(${sharedutil_include_directories})
endif()
#setting output type
if(WIN32)
#to disable running e2e or longhaul or unittests tests for windows, we build the the same thing as "static library" so it is not picked up by visual studio
if(
(("${whatIsBuilding}" MATCHES ".*longhaul.*") AND NOT ${run_longhaul_tests}) OR
(("${whatIsBuilding}" MATCHES ".*unittests.*") AND ${skip_unittests})
)
windows_unittests_add_lib(${whatIsBuilding} ${ARGN})
else()
windows_unittests_add_exe(${whatIsBuilding} ${ARGN})
windows_unittests_add_dll(${whatIsBuilding} ${ARGN})
endif()
else()
if(
(("${whatIsBuilding}" MATCHES ".*longhaul.*") AND NOT ${run_longhaul_tests}) OR
(("${whatIsBuilding}" MATCHES ".*unittests.*") AND ${skip_unittests})
)
windows_unittests_add_lib(${whatIsBuilding} ${ARGN})
else()
windows_unittests_add_exe(${whatIsBuilding} ${ARGN})
endif()
endif()
endfunction(build_test_artifacts)
function(set_platform_files)
if(WIN32)
set(PLATFORM_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/platform_win32.c PARENT_SCOPE)
set(TLSIO_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/tlsio_win32.c PARENT_SCOPE)
set(SOCKETIO_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/socketio_win32.c PARENT_SCOPE)
set(LOCK_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/lock_c11.c PARENT_SCOPE)
set(THREAD_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/threadapi_c11.c PARENT_SCOPE)
set(HTTP_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/httpapi_winhttp.c PARENT_SCOPE)
#set(CONDITION_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/condition_c11.c PARENT_SCOPE)
else()
set(LOCK_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/lock_pthreads.c PARENT_SCOPE)
#set(CONDITION_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/condition_pthreads.c PARENT_SCOPE)
set(THREAD_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/threadapi_pthreads.c PARENT_SCOPE)
set(HTTP_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/httpapi_curl.c PARENT_SCOPE)
set(PLATFORM_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/platform_linux.c PARENT_SCOPE)
set(TLSIO_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/tlsio_linux.c PARENT_SCOPE)
set(SOCKETIO_C_FILE ${CMAKE_CURRENT_LIST_DIR}/sharedutil/adapters/socketio_linux.c PARENT_SCOPE)
endif()
endfunction(set_platform_files)
set_platform_files()
add_subdirectory(testtools)
add_subdirectory(sharedutil)

104
c/sharedutil/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,104 @@
#Copyright (c) Microsoft. All rights reserved.
#Licensed under the MIT license. See LICENSE file in the project root for full license information.
cmake_minimum_required(VERSION 3.0)
compileAsC99()
#these are the C source files
set(source_c_files
./src/base64.c
./src/buffer.c
./src/crt_abstractions.c
./src/doublylinkedlist.c
./src/gballoc.c
./src/hmac.c
./src/hmacsha256.c
./src/io.c
./src/list.c
./src/map.c
./src/sastoken.c
./src/sha1.c
./src/sha224.c
./src/sha384-512.c
./src/strings.c
./src/string_tokenizer.c
./src/urlencode.c
./src/usha.c
./src/vector.c
./adapters/agenttime.c
)
if(${use_http})
set(source_c_files ${source_c_files}
./src/httpapiex.c
./src/httpapiexsas.c
./src/httpheaders.c
)
endif()
#these are the C headers
set(source_h_files
./inc/agenttime.h
./inc/base64.h
./inc/buffer_.h
./inc/crt_abstractions.h
#./inc/condition.h
./inc/doublylinkedlist.h
./inc/gballoc.h
./inc/hmac.h
./inc/hmacsha256.h
./inc/io.h
./inc/iot_logging.h
./inc/list.h
./inc/lock.h
./inc/macro_utils.h
./inc/map.h
./inc/platform.h
./inc/sastoken.h
./inc/sha-private.h
./inc/sha.h
./inc/socketio.h
./inc/stdint_ce6.h
./inc/strings.h
./inc/string_tokenizer.h
./inc/threadapi.h
./inc/tlsio.h
./inc/urlencode.h
./inc/vector.h
)
if(${use_http})
set(source_h_files ${source_h_files}
./inc/httpapi.h
./inc/httpapiex.h
./inc/httpapiexsas.h
./inc/httpheaders.h
)
endif()
#these are the include folders
#the following "set" statetement exports across the project a global variable called COMMON_INC_FOLDER that expands to whatever needs to included when using COMMON library
set(SHARED_UTIL_INC_FOLDER ${CMAKE_CURRENT_LIST_DIR}/inc CACHE INTERNAL "this is what needs to be included if using sharedLib lib" FORCE)
set(SHARED_UTIL_SRC_FOLDER ${CMAKE_CURRENT_LIST_DIR}/src CACHE INTERNAL "this is what needs to be included when doing include sources" FORCE)
include_directories(${SHARED_UTIL_INC_FOLDER})
IF(WIN32)
#windows needs this define
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
ENDIF(WIN32)
#Setup the platform files
set(source_h_files ${source_h_files}
${PLATFORM_C_FILE}
${TLSIO_C_FILE}
${SOCKETIO_C_FILE}
${LOCK_C_FILE}
${THREAD_C_FILE}
${HTTP_C_FILE}
)
#this is the product (a library)
add_library(aziotsharedlib ${source_c_files} ${source_h_files})
add_subdirectory(tests)
add_subdirectory(testtools)

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "time.h"
#include "agenttime.h"
time_t get_time(time_t* p)
{
return time(p);
}
struct tm* get_gmtime(time_t* currentTime)
{
return gmtime(currentTime);
}
char* get_ctime(time_t* timeToGet)
{
return ctime(timeToGet);
}
double get_difftime(time_t stopTime, time_t startTime)
{
return difftime(stopTime, startTime);
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "agenttime.h"
// mbed version of gmtime() returns NULL.
// system RTC should be set to UTC as its localtime
time_t get_time(time_t* currentTime)
{
return time(currentTime);
}
double get_difftime(time_t stopTime, time_t startTime)
{
return difftime(stopTime, startTime);
}
struct tm* get_gmtime(time_t* currentTime)
{
return localtime(currentTime);
}
char* get_ctime(time_t* timeToGet)
{
return ctime(timeToGet);
}

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "stdlib.h"
#include "macro_utils.h"
#include "condition.h"
#include "iot_logging.h"
#include <threads.h>
DEFINE_ENUM_STRINGS(COND_RESULT, COND_RESULT_VALUES);
COND_HANDLE Condition_Init(void)
{
cnd_t* cond = (cnd_t*)malloc(sizeof(cnd_t));
cnd_init(cond);
return cond;
}
COND_RESULT Condition_Post(COND_HANDLE handle)
{
cnd_broadcast((cnd_t*)handle);
return COND_OK;
}
COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds)
{
if (timeout_milliseconds > 0)
{
struct xtime tm;
tm.sec = timeout_milliseconds / 1000;
tm.nsec = (timeout_milliseconds % 1000) * 1000000L;
int wait_result = cnd_timedwait((cnd_t *)handle, (mtx_t *)lock, &tm);
if (wait_result == ETIMEDOUT)
{
return COND_TIMEOUT;
}
else
{
LogError("Failed to Condition_Wait\r\n");
return COND_ERROR;
}
}
else
{
if (cnd_wait((cnd_t*)handle, (mtx_t *)lock) != 0)
{
LogError("Failed to cnd_wait\r\n");
return COND_ERROR;
}
}
return COND_OK;
}
COND_RESULT Condition_Deinit(COND_HANDLE handle)
{
cnd_destroy((cnd_t*)handle);
return COND_OK;
}

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "lock.h"
#include <stdlib.h>
#include <iot_logging.h>
#include <errno.h>
DEFINE_ENUM_STRINGS(COND_RESULT, COND_RESULT_VALUES);
COND_HANDLE Condition_Init(void)
{
pthread_cond_t * cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
pthread_cond_init(cond, NULL);
return cond;
}
COND_RESULT Condition_Post(COND_HANDLE handle)
{
pthread_cond_broadcast((pthread_cond_t*)handle);
return COND_OK;
}
COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds)
{
if ( timeout_milliseconds > 0)
{
struct timespec tm;
tm.tv_sec = timeout_milliseconds / 1000;
tm.tv_nsec = (timeout_milliseconds % 1000) * 1000000L;
int wait_result = pthread_cond_timedwait((pthread_cond_t *)handle, (pthread_mutex_t *)lock, &tm);
if ( wait_result == ETIMEDOUT)
{
return COND_TIMEOUT;
}
else
{
LogError("Failed to pthread_cond_timedwait\r\n");
return COND_ERROR;
}
}
else
{
if (pthread_cond_wait((pthread_cond_t*)handle, (pthread_mutex_t *)lock) != 0 )
{
LogError("Failed to pthread_cond_wait\r\n");
return COND_ERROR;
}
}
return COND_OK;
}
COND_RESULT Condition_Deinit(COND_HANDLE handle)
{
pthread_cond_destroy((pthread_cond_t*)handle);
return COND_OK;
}

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <cstdlib>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "lock.h"
#include "iot_logging.h"
#include "rtos.h"
COND_HANDLE Condition_Init(void)
{
return NULL;
}
COND_RESULT Condition_Post(COND_HANDLE handle)
{
return COND_ERROR;
}
COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds)
{
return COND_ERROR;
}
COND_RESULT Condition_Deinit(COND_HANDLE handle)
{
return COND_ERROR;
}

Просмотреть файл

@ -0,0 +1,676 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include "strings.h"
#include "httpapi.h"
#include "httpheaders.h"
#include "crt_abstractions.h"
#include "curl/curl.h"
#include "iot_logging.h"
#define TEMP_BUFFER_SIZE 1024
DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES);
typedef struct HTTP_HANDLE_DATA_TAG
{
CURL* curl;
char* hostURL;
long timeout;
long lowSpeedLimit;
long lowSpeedTime;
long forbidReuse;
long freshConnect;
long verbose;
} HTTP_HANDLE_DATA;
typedef struct HTTP_RESPONSE_CONTENT_BUFFER_TAG
{
unsigned char* buffer;
size_t bufferSize;
unsigned char error;
} HTTP_RESPONSE_CONTENT_BUFFER;
static size_t nUsersOfHTTPAPI = 0; /*used for reference counting (a weak one)*/
HTTPAPI_RESULT HTTPAPI_Init(void)
{
HTTPAPI_RESULT result;
if (nUsersOfHTTPAPI == 0)
{
if (curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
{
result = HTTPAPI_INIT_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
nUsersOfHTTPAPI++;
result = HTTPAPI_OK;
}
}
else
{
nUsersOfHTTPAPI++;
result = HTTPAPI_OK;
}
return result;
}
void HTTPAPI_Deinit(void)
{
if (nUsersOfHTTPAPI > 0)
{
nUsersOfHTTPAPI--;
if (nUsersOfHTTPAPI == 0)
{
curl_global_cleanup();
}
}
}
HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
HTTP_HANDLE_DATA* httpHandleData = NULL;
if (hostName != NULL)
{
httpHandleData = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA));
if (httpHandleData != NULL)
{
size_t hostURL_size = strlen("https://") + strlen(hostName) + 1;
httpHandleData->hostURL = malloc(hostURL_size);
if (httpHandleData->hostURL == NULL)
{
LogError("unable to malloc\r\n");
}
else
{
if ((strcpy_s(httpHandleData->hostURL, hostURL_size, "https://") == 0) &&
(strcat_s(httpHandleData->hostURL, hostURL_size, hostName) == 0))
{
httpHandleData->curl = curl_easy_init();
if (httpHandleData->curl == NULL)
{
free(httpHandleData->hostURL);
free(httpHandleData);
httpHandleData = NULL;
}
else
{
httpHandleData->timeout = 242 * 1000; /*242 seconds seems like a nice enough time. Reasone for 242:
1. http://curl.haxx.se/libcurl/c/CURLOPT_TIMEOUT.html says Normally, name lookups can take a considerable time and limiting operations to less than a few minutes risk aborting perfectly normal operations.
2. 256KB of data... at 9600 bps transfers in about 218 seconds. Add to that a buffer of 10%... round it up to 242 :)*/
httpHandleData->lowSpeedTime = 0;
httpHandleData->lowSpeedLimit = 0;
httpHandleData->forbidReuse = 0;
httpHandleData->freshConnect = 0;
httpHandleData->verbose = 0;
}
}
else
{
free(httpHandleData->hostURL);
free(httpHandleData);
httpHandleData = NULL;
}
}
}
}
return (HTTP_HANDLE)httpHandleData;
}
void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
HTTP_HANDLE_DATA* httpHandleData = (HTTP_HANDLE_DATA*)handle;
if (httpHandleData != NULL)
{
free(httpHandleData->hostURL);
curl_easy_cleanup(httpHandleData->curl);
free(httpHandleData);
}
}
static size_t HeadersWriteFunction(void *ptr, size_t size, size_t nmemb, void *userdata)
{
HTTP_HEADERS_HANDLE responseHeadersHandle = (HTTP_HEADERS_HANDLE)userdata;
char* headerLine = (char*)ptr;
if (headerLine != NULL)
{
char* token = strtok(headerLine, "\r\n");
while ((token != NULL) &&
(token[0] != '\0'))
{
char* whereIsColon = strchr(token, ':');
if(whereIsColon!=NULL)
{
*whereIsColon='\0';
HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, token, whereIsColon+1);
*whereIsColon=':';
}
else
{
/*not a header, maybe a status-line*/
}
token = strtok(NULL, "\r\n");
}
}
return size * nmemb;
}
static size_t ContentWriteFunction(void *ptr, size_t size, size_t nmemb, void *userdata)
{
HTTP_RESPONSE_CONTENT_BUFFER* responseContentBuffer = (HTTP_RESPONSE_CONTENT_BUFFER*)userdata;
if ((userdata != NULL) &&
(ptr != NULL) &&
(size * nmemb > 0))
{
void* newBuffer = realloc(responseContentBuffer->buffer, responseContentBuffer->bufferSize + (size * nmemb));
if (newBuffer != NULL)
{
responseContentBuffer->buffer = newBuffer;
memcpy(responseContentBuffer->buffer + responseContentBuffer->bufferSize, ptr, size * nmemb);
responseContentBuffer->bufferSize += size * nmemb;
}
else
{
LogError("Could not allocate buffer of size %zu\r\n", (size_t)(responseContentBuffer->bufferSize + (size * nmemb)));
responseContentBuffer->error = 1;
if (responseContentBuffer->buffer != NULL)
{
free(responseContentBuffer->buffer);
}
}
}
return size * nmemb;
}
HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
size_t contentLength, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent)
{
HTTPAPI_RESULT result;
HTTP_HANDLE_DATA* httpHandleData = (HTTP_HANDLE_DATA*)handle;
size_t headersCount;
HTTP_RESPONSE_CONTENT_BUFFER responseContentBuffer;
if ((httpHandleData == NULL) ||
(relativePath == NULL) ||
(httpHeadersHandle == NULL) ||
((content == NULL) && (contentLength > 0))
)
{
result = HTTPAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK)
{
result = HTTPAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
char* tempHostURL;
size_t tempHostURL_size = strlen(httpHandleData->hostURL) + strlen(relativePath) + 1;
tempHostURL = malloc(tempHostURL_size);
if (tempHostURL == NULL)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_VERBOSE, httpHandleData->verbose) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_VERBOSE (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if ((strcpy_s(tempHostURL, tempHostURL_size, httpHandleData->hostURL) != 0) ||
(strcat_s(tempHostURL, tempHostURL_size, relativePath) != 0))
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
/* set the URL */
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_URL, tempHostURL) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_URL (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_TIMEOUT_MS, httpHandleData->timeout) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_TIMEOUT_MS (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_LOW_SPEED_LIMIT, httpHandleData->lowSpeedLimit) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_LOW_SPEED_LIMIT (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_LOW_SPEED_TIME, httpHandleData->lowSpeedTime) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_LOW_SPEED_TIME (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_FRESH_CONNECT, httpHandleData->freshConnect) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_FRESH_CONNECT (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_FORBID_REUSE, httpHandleData->forbidReuse) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_FORBID_REUSE (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (curl_easy_setopt(httpHandleData->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("failed to set CURLOPT_HTTP_VERSION (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
result = HTTPAPI_OK;
switch (requestType)
{
default:
result = HTTPAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
break;
case HTTPAPI_REQUEST_GET:
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_HTTPGET, 1L) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_CUSTOMREQUEST, NULL) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
break;
case HTTPAPI_REQUEST_POST:
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_POST, 1L) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_CUSTOMREQUEST, NULL) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
break;
case HTTPAPI_REQUEST_PUT:
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_POST, 1L))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_CUSTOMREQUEST, "PUT") != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
break;
case HTTPAPI_REQUEST_DELETE:
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_POST, 1L) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_CUSTOMREQUEST, "DELETE") != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
break;
case HTTPAPI_REQUEST_PATCH:
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_POST, 1L) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_CUSTOMREQUEST, "PATCH") != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
break;
}
if (result == HTTPAPI_OK)
{
/* add headers */
struct curl_slist* headers = NULL;
size_t i;
for (i = 0; i < headersCount; i++)
{
char *tempBuffer;
if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &tempBuffer) != HTTP_HEADERS_OK)
{
/* error */
result = HTTPAPI_HTTP_HEADERS_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
break;
}
else
{
struct curl_slist* newHeaders = curl_slist_append(headers, tempBuffer);
if (newHeaders == NULL)
{
result = HTTPAPI_ALLOC_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
free(tempBuffer);
break;
}
else
{
free(tempBuffer);
headers = newHeaders;
}
}
}
if (result == HTTPAPI_OK)
{
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_HTTPHEADER, headers) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
/* add content */
if ((content != NULL) &&
(contentLength > 0))
{
if ((curl_easy_setopt(httpHandleData->curl, CURLOPT_POSTFIELDS, (void*)content) != CURLE_OK) ||
(curl_easy_setopt(httpHandleData->curl, CURLOPT_POSTFIELDSIZE, contentLength) != CURLE_OK))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
else
{
if (requestType != HTTPAPI_REQUEST_GET)
{
if ((curl_easy_setopt(httpHandleData->curl, CURLOPT_POSTFIELDS, (void*)NULL) != CURLE_OK) ||
(curl_easy_setopt(httpHandleData->curl, CURLOPT_POSTFIELDSIZE, 0) != CURLE_OK))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
else
{
/*GET request cannot POST, so "do nothing*/
}
}
if (result == HTTPAPI_OK)
{
if ((curl_easy_setopt(httpHandleData->curl, CURLOPT_WRITEHEADER, NULL) != CURLE_OK) ||
(curl_easy_setopt(httpHandleData->curl, CURLOPT_HEADERFUNCTION, NULL) != CURLE_OK) ||
(curl_easy_setopt(httpHandleData->curl, CURLOPT_WRITEFUNCTION, ContentWriteFunction) != CURLE_OK))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (responseHeadersHandle != NULL)
{
/* setup the code to get the response headers */
if ((curl_easy_setopt(httpHandleData->curl, CURLOPT_WRITEHEADER, responseHeadersHandle) != CURLE_OK) ||
(curl_easy_setopt(httpHandleData->curl, CURLOPT_HEADERFUNCTION, HeadersWriteFunction) != CURLE_OK))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
}
if (result == HTTPAPI_OK)
{
responseContentBuffer.buffer = NULL;
responseContentBuffer.bufferSize = 0;
responseContentBuffer.error = 0;
if (curl_easy_setopt(httpHandleData->curl, CURLOPT_WRITEDATA, &responseContentBuffer) != CURLE_OK)
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
if (result == HTTPAPI_OK)
{
/* Execute request */
CURLcode curlRes = curl_easy_perform(httpHandleData->curl);
if (curlRes != CURLE_OK)
{
LogError("curl_easy_perform() failed: %s\n", curl_easy_strerror(curlRes));
result = HTTPAPI_OPEN_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
long httpCode;
/* get the status code */
if (curl_easy_getinfo(httpHandleData->curl, CURLINFO_RESPONSE_CODE, &httpCode) != CURLE_OK)
{
result = HTTPAPI_QUERY_HEADERS_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (responseContentBuffer.error)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (statusCode != NULL)
{
*statusCode = httpCode;
}
/* fill response content length */
if (responseContent != NULL)
{
if ((responseContentBuffer.bufferSize > 0) && (BUFFER_build(responseContent, responseContentBuffer.buffer, responseContentBuffer.bufferSize) != 0))
{
result = HTTPAPI_INSUFFICIENT_RESPONSE_BUFFER;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
/*all nice*/
}
}
if (httpCode >= 300)
{
LogError("Failure in HTTP communication: server reply code is %ld\r\n", httpCode);
LogInfo("HTTP Response:\r\n%*.*s\r\n", (int)responseContentBuffer.bufferSize,
(int)responseContentBuffer.bufferSize, responseContentBuffer.buffer);
}
else
{
result = HTTPAPI_OK;
}
}
}
}
if (responseContentBuffer.buffer != NULL)
{
free(responseContentBuffer.buffer);
}
}
}
}
}
}
curl_slist_free_all(headers);
}
}
free(tempHostURL);
}
}
return result;
}
HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value)
{
HTTPAPI_RESULT result;
if (
(handle == NULL) ||
(optionName == NULL) ||
(value == NULL)
)
{
result = HTTPAPI_INVALID_ARG;
LogError("invalid parameter (NULL) passed to HTTPAPI_SetOption\r\n");
}
else
{
HTTP_HANDLE_DATA* httpHandleData = (HTTP_HANDLE_DATA*)handle;
if (strcmp("timeout", optionName) == 0)
{
long timeout = (long)(*(unsigned int*)value);
httpHandleData->timeout = timeout;
result = HTTPAPI_OK;
}
else if (strcmp("CURLOPT_LOW_SPEED_LIMIT", optionName) == 0)
{
httpHandleData->lowSpeedLimit = *(const long*)value;
result = HTTPAPI_OK;
}
else if (strcmp("CURLOPT_LOW_SPEED_TIME", optionName) == 0)
{
httpHandleData->lowSpeedTime = *(const long*)value;
result = HTTPAPI_OK;
}
else if (strcmp("CURLOPT_FRESH_CONNECT", optionName) == 0)
{
httpHandleData->freshConnect = *(const long*)value;
result = HTTPAPI_OK;
}
else if (strcmp("CURLOPT_FORBID_REUSE", optionName) == 0)
{
httpHandleData->forbidReuse = *(const long*)value;
result = HTTPAPI_OK;
}
else if (strcmp("CURLOPT_VERBOSE", optionName) == 0)
{
httpHandleData->verbose = *(const long*)value;
result = HTTPAPI_OK;
}
else
{
result = HTTPAPI_INVALID_ARG;
LogError("unknown option %s\r\n", optionName);
}
}
return result;
}
HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue)
{
HTTPAPI_RESULT result;
if (
(optionName == NULL) ||
(value == NULL) ||
(savedValue == NULL)
)
{
result = HTTPAPI_INVALID_ARG;
LogError("invalid argument(NULL) passed to HTTPAPI_CloneOption\r\n");
}
else
{
if (strcmp("timeout", optionName) == 0)
{
/*by convention value is pointing to an unsigned int */
unsigned int* temp = malloc(sizeof(unsigned int)); /*shall be freed by HTTPAPIEX*/
if (temp == NULL)
{
result = HTTPAPI_ERROR;
LogError("malloc failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
*temp = *(const unsigned int*)value;
*savedValue = temp;
result = HTTPAPI_OK;
}
}
/*all "long" options are cloned in the same way*/
else if (
(strcmp("CURLOPT_LOW_SPEED_LIMIT", optionName) == 0) ||
(strcmp("CURLOPT_LOW_SPEED_TIME", optionName) == 0) ||
(strcmp("CURLOPT_FRESH_CONNECT", optionName) == 0) ||
(strcmp("CURLOPT_FORBID_REUSE", optionName) == 0) ||
(strcmp("CURLOPT_VERBOSE", optionName) == 0)
)
{
/*by convention value is pointing to an long */
long* temp = malloc(sizeof(long)); /*shall be freed by HTTPAPIEX*/
if (temp == NULL)
{
result = HTTPAPI_ERROR;
LogError("malloc failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
*temp = *(const long*)value;
*savedValue = temp;
result = HTTPAPI_OK;
}
}
else
{
result = HTTPAPI_INVALID_ARG;
LogError("unknown option %s\r\n", optionName);
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,589 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <cstdlib>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include "httpapi.h"
#include "httpheaders.h"
#include "crt_abstractions.h"
#include "mbed.h"
#include "EthernetInterface.h"
#include "wolfssl_connection.h"
#include "iot_logging.h"
#include "string.h"
#include "certs.h"
#define MAX_HOSTNAME 64
#define TEMP_BUFFER_SIZE 4096
#define CHAR_COUNT(A) (sizeof(A) - 1)
DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES)
class HTTP_HANDLE_DATA
{
public:
char host[MAX_HOSTNAME];
char* certificate;
WolfSSLConnection con;
};
HTTPAPI_RESULT HTTPAPI_Init(void)
{
LogInfo("HTTPAPI_Init::Start\r\n");
time_t ctTime;
ctTime = time(NULL);
HTTPAPI_RESULT result;
LogInfo("HTTAPI_Init::Time is now (UTC) %s\r\n", ctime(&ctTime));
if (EthernetInterface::connect(30000))
{
LogError("HTTPAPI_Init::Error with connecting.\r\n");
result = HTTPAPI_INIT_FAILED;
}
else
{
LogInfo("HTTAPI_Init::Ethernet interface was connected (brought up)!\r\n");
LogInfo("HTTAPI_Init::MAC address %s\r\n", EthernetInterface::getMACAddress());
LogInfo("HTTAPI_Init::IP address %s\r\n", EthernetInterface::getIPAddress());
result = HTTPAPI_OK;
}
LogInfo("HTTPAPI_Init::End\r\n");
return result;
}
void HTTPAPI_Deinit(void)
{
(void)EthernetInterface::disconnect();
}
HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
LogInfo("HTTPAPI_CreateConnection::Start\r\n");
HTTP_HANDLE_DATA* handle = NULL;
if (hostName)
{
LogInfo("HTTPAPI_CreateConnection::Connecting to %s\r\n", hostName);
handle = new HTTP_HANDLE_DATA();
if (strcpy_s(handle->host, MAX_HOSTNAME, hostName) != 0)
{
LogError("HTTPAPI_CreateConnection::Could not strcpy_s\r\n");
delete handle;
handle = NULL;
}
else
{
handle->certificate = NULL;
}
}
else
{
LogInfo("HTTPAPI_CreateConnection:: null hostName parameter\r\n");
}
LogInfo("HTTPAPI_CreateConnection::End\r\n");
return (HTTP_HANDLE)handle;
}
void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)handle;
if (h)
{
LogInfo("HTTPAPI_CloseConnection to %s\r\n", h->host);
if (h->con.is_connected())
{
LogInfo("HTTPAPI_CloseConnection h->con.close(); to %s\r\n", h->host);
h->con.close();
}
if (h->certificate)
{
delete[] h->certificate;
}
LogInfo("HTTPAPI_CloseConnection (delete h) to %s\r\n", h->host);
delete h;
}
}
static int readLine(WolfSSLConnection* con, char* buf, const size_t size)
{
// reads until \r\n is encountered. writes in buf all the characters
// read until \r\n and returns the number of characters in the buffer.
char* p = buf;
char c;
if (con->receive(&c, 1) < 0)
return -1;
while (c != '\r') {
if ((p - buf + 1) >= (int)size)
return -1;
*p++ = c;
if (con->receive(&c, 1) < 0)
return -1;
}
*p = 0;
if (con->receive(&c, 1) < 0 || c != '\n') // skip \n
return -1;
return p - buf;
}
static int readChunk(WolfSSLConnection* con, char* buf, size_t size)
{
size_t cur, offset;
// read content with specified length, even if it is received
// only in chunks due to fragmentation in the networking layer.
// returns -1 in case of error.
offset = 0;
while (size > 0)
{
cur = con->receive(buf + offset, size);
// end of stream reached
if (cur == 0)
return offset;
// read cur bytes (might be less than requested)
size -= cur;
offset += cur;
}
return offset;
}
static int skipN(WolfSSLConnection* con, size_t n, char* buf, size_t size)
{
size_t org = n;
// read and abandon response content with specified length
// returns -1 in case of error.
while (n > size)
{
if (readChunk(con, (char*)buf, size) < 0)
return -1;
n -= size;
}
if (readChunk(con, (char*)buf, n) < 0)
return -1;
return org;
}
//Note: This function assumes that "Host:" and "Content-Length:" headers are setup
// by the caller of HTTPAPI_ExecuteRequest() (which is true for httptransport.c).
HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
size_t contentLength, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent)
{
LogInfo("HTTPAPI_ExecuteRequest::Start\r\n");
HTTPAPI_RESULT result;
size_t headersCount;
char buf[TEMP_BUFFER_SIZE];
int ret;
WolfSSLConnection* con = NULL;
size_t bodyLength = 0;
bool chunked = false;
const unsigned char* receivedContent;
const char* method = (requestType == HTTPAPI_REQUEST_GET) ? "GET"
: (requestType == HTTPAPI_REQUEST_POST) ? "POST"
: (requestType == HTTPAPI_REQUEST_PUT) ? "PUT"
: (requestType == HTTPAPI_REQUEST_DELETE) ? "DELETE"
: (requestType == HTTPAPI_REQUEST_PATCH) ? "PATCH"
: NULL;
if (handle == NULL ||
relativePath == NULL ||
httpHeadersHandle == NULL ||
method == NULL ||
HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK)
{
result = HTTPAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)handle;
con = &(httpHandle->con);
if (!con->is_connected())
{
// Load the certificate
if ((httpHandle->certificate != NULL) &&
(!con->load_certificate((const unsigned char*)httpHandle->certificate, strlen(httpHandle->certificate) + 1)))
{
result = HTTPAPI_ERROR;
LogError("Could not load certificate (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
// Make the connection
if (con->connect(httpHandle->host, 443) != 0)
{
result = HTTPAPI_ERROR;
LogError("Could not connect (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
else
{
LogInfo("HTTPAPI_CreateConnection::Connection to %s successful!\r\n", httpHandle->host);
}
}
//Send request
if ((ret = snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\n", method, relativePath)) < 0
|| ret >= sizeof(buf))
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
LogInfo("HTTPAPI_ExecuteRequest::Sending=%*.*s\r\n", strlen(buf), strlen(buf), buf);
if (con->send_all(buf, strlen(buf)) < 0)
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
//Send default headers
for (size_t i = 0; i < headersCount; i++)
{
char* header;
if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &header) != HTTP_HEADERS_OK)
{
result = HTTPAPI_HTTP_HEADERS_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
LogInfo("HTTPAPI_ExecuteRequest::Sending=%*.*s\r\n", strlen(header), strlen(header), header);
if (con->send_all(header, strlen(header)) < 0)
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
free(header);
goto exit;
}
if (con->send_all("\r\n", 2) < 0)
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
free(header);
goto exit;
}
free(header);
}
//Close headers
if (con->send_all("\r\n", 2) < 0)
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
//Send data (if available)
if (content && contentLength > 0)
{
LogInfo("HTTPAPI_ExecuteRequest::Sending data=%*.*s\r\n", contentLength, contentLength, content);
if (con->send_all((char*)content, contentLength) < 0)
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
}
//Receive response
if (readLine(con, buf, sizeof(buf)) < 0)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
//Parse HTTP response
if (sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) != 1)
{
//Cannot match string, error
LogInfo("HTTPAPI_ExecuteRequest::Not a correct HTTP answer=%s\r\n", buf);
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
if (statusCode)
*statusCode = ret;
LogInfo("HTTPAPI_ExecuteRequest::Received response=%*.*s\r\n", strlen(buf), strlen(buf), buf);
//Read HTTP response headers
if (readLine(con, buf, sizeof(buf)) < 0)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
while (buf[0])
{
const char ContentLength[] = "content-length:";
const char TransferEncoding[] = "transfer-encoding:";
LogInfo("Receiving header=%*.*s\r\n", strlen(buf), strlen(buf), buf);
if (strncasecmp(buf, ContentLength, CHAR_COUNT(ContentLength)) == 0)
{
if (sscanf(buf + CHAR_COUNT(ContentLength), " %d", &bodyLength) != 1)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
}
else if (strncasecmp(buf, TransferEncoding, CHAR_COUNT(TransferEncoding)) == 0)
{
const char* p = buf + CHAR_COUNT(TransferEncoding);
while (isspace(*p)) p++;
if (strcasecmp(p, "chunked") == 0)
chunked = true;
}
char* whereIsColon = strchr((char*)buf, ':');
if (whereIsColon && responseHeadersHandle != NULL)
{
*whereIsColon = '\0';
HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1);
}
if (readLine(con, buf, sizeof(buf)) < 0)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
}
//Read HTTP response body
LogInfo("HTTPAPI_ExecuteRequest::Receiving body=%d,%x\r\n", bodyLength, responseContent);
if (!chunked)
{
if (bodyLength)
{
if (responseContent != NULL)
{
if (BUFFER_pre_build(responseContent, bodyLength) != 0)
{
result = HTTPAPI_ALLOC_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (BUFFER_content(responseContent, &receivedContent) != 0)
{
(void)BUFFER_unbuild(responseContent);
result = HTTPAPI_ALLOC_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
if (readChunk(con, (char*)receivedContent, bodyLength) < 0)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
else
{
LogInfo("HTTPAPI_ExecuteRequest::Received response body=%*.*s\r\n", bodyLength, bodyLength, receivedContent);
result = HTTPAPI_OK;
}
}
else
{
(void)skipN(con, bodyLength, buf, sizeof(buf));
result = HTTPAPI_OK;
}
}
else
{
result = HTTPAPI_OK;
}
}
else
{
size_t size = 0;
result = HTTPAPI_OK;
for (;;)
{
int chunkSize;
if (readLine(con, buf, sizeof(buf)) < 0) // read [length in hex]/r/n
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
if (sscanf(buf, "%x", &chunkSize) != 1) // chunkSize is length of next line (/r/n is not counted)
{
//Cannot match string, error
result = HTTPAPI_RECEIVE_RESPONSE_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
if (chunkSize == 0)
{
// 0 length means next line is just '\r\n' and end of chunks
if (readChunk(con, (char*)buf, 2) < 0
|| buf[0] != '\r' || buf[1] != '\n') // skip /r/n
{
(void)BUFFER_unbuild(responseContent);
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
break;
}
else
{
if (responseContent != NULL)
{
if (BUFFER_enlarge(responseContent, chunkSize) != 0)
{
(void)BUFFER_unbuild(responseContent);
result = HTTPAPI_ALLOC_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else if (BUFFER_content(responseContent, &receivedContent) != 0)
{
(void)BUFFER_unbuild(responseContent);
result = HTTPAPI_ALLOC_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
if (readChunk(con, (char*)receivedContent + size, chunkSize) < 0)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
}
else
{
if (skipN(con, chunkSize, buf, sizeof(buf)) < 0)
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
}
if (readChunk(con, (char*)buf, 2) < 0
|| buf[0] != '\r' || buf[1] != '\n') // skip /r/n
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goto exit;
}
size += chunkSize;
}
}
if (size > 0)
{
LogInfo("HTTPAPI_ExecuteRequest::Received chunk body=%*.*s\r\n", (int)size, (int)size, (const char*)responseContent);
}
}
exit:
LogInfo("HTTPAPI_ExecuteRequest::End=%d\r\n", result);
return result;
}
HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value)
{
HTTPAPI_RESULT result;
if (
(handle == NULL) ||
(optionName == NULL) ||
(value == NULL)
)
{
result = HTTPAPI_INVALID_ARG;
LogError("invalid parameter (NULL) passed to HTTPAPI_SetOption\r\n");
}
else if (strcmp("TrustedCerts", optionName) == 0)
{
HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)handle;
if (h->certificate)
{
delete[] h->certificate;
}
int len = strlen((char*)value);
h->certificate = new char[len + 1];
if (h->certificate == NULL)
{
result = HTTPAPI_ERROR;
LogError("unable to allocate certificate memory in HTTPAPI_SetOption\r\n");
}
else
{
(void)strcpy(h->certificate, (const char*)value);
result = HTTPAPI_OK;
}
}
else
{
result = HTTPAPI_INVALID_ARG;
LogError("unknown option %s\r\n", optionName);
}
return result;
}
HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue)
{
HTTPAPI_RESULT result;
if (
(optionName == NULL) ||
(value == NULL) ||
(savedValue == NULL)
)
{
result = HTTPAPI_INVALID_ARG;
LogError("invalid argument(NULL) passed to HTTPAPI_CloneOption\r\n");
}
else if (strcmp("TrustedCerts", optionName) == 0)
{
size_t certLen = strlen((const char*)value);
char* tempCert = (char*)malloc(certLen+1);
if (tempCert == NULL)
{
result = HTTPAPI_INVALID_ARG;
LogError("unable to allocate certificate memory in HTTPAPI_CloneOption\r\n");
}
else
{
(void)strcpy(tempCert, (const char*)value);
*savedValue = tempCert;
result = HTTPAPI_OK;
}
}
else
{
result = HTTPAPI_INVALID_ARG;
LogError("unknown option %s\r\n", optionName);
}
return result;
}

Просмотреть файл

@ -0,0 +1,278 @@
// Copyright (c) Texas Instruments. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ti/net/http/httpcli.h>
#include "httpapi.h"
#include "strings.h"
#define CONTENT_BUF_LEN 128
static const char* getHttpMethod(HTTPAPI_REQUEST_TYPE requestType)
{
switch (requestType) {
case HTTPAPI_REQUEST_GET:
return (HTTPStd_GET);
case HTTPAPI_REQUEST_POST:
return (HTTPStd_POST);
case HTTPAPI_REQUEST_PUT:
return (HTTPStd_PUT);
case HTTPAPI_REQUEST_DELETE:
return (HTTPStd_DELETE);
case HTTPAPI_REQUEST_PATCH:
return (HTTPStd_PATCH);
default:
return (NULL);
}
}
static int splitHeader(char *headerName, char **headerValue)
{
*headerValue = strchr(headerName, ':');
if (*headerValue == NULL) {
return (-1);
}
**headerValue = '\0';
(*headerValue)++;
while (**headerValue == ' ') {
(*headerValue)++;
}
return (0);
}
HTTPAPI_RESULT HTTPAPI_Init(void)
{
return (HTTPAPI_OK);
}
void HTTPAPI_Deinit(void)
{
}
HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
int ret;
struct sockaddr addr;
HTTPCli_Handle cli;
ret = HTTPCli_initSockAddr(&addr, hostName, 0);
if (ret < 0) {
return (NULL);
}
((struct sockaddr_in *) (&addr))->sin_port = htons(HTTPStd_SECURE_PORT);
cli = HTTPCli_create();
if (cli == NULL) {
return (NULL);
}
ret = HTTPCli_connect(cli, &addr, HTTPCli_TYPE_TLS, NULL);
if (ret < 0) {
HTTPCli_delete(&cli);
return (NULL);
}
return ((HTTP_HANDLE) cli);
}
void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
HTTPCli_Handle cli = (HTTPCli_Handle) handle;
if (cli) {
HTTPCli_disconnect(cli);
HTTPCli_delete(&cli);
}
}
HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle,
HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
size_t contentLength, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHeadersHandle,
BUFFER_HANDLE responseContent)
{
HTTPCli_Handle cli = (HTTPCli_Handle) handle;
int ret;
int offset;
size_t cnt;
char contentBuf[CONTENT_BUF_LEN] = {0};
char *hname;
char *hvalue;
const char *method;
bool moreFlag;
method = getHttpMethod(requestType);
if ((cli == NULL) || (method == NULL) || (relativePath == NULL)
|| (statusCode == NULL) || (responseHeadersHandle == NULL)) {
return (HTTPAPI_INVALID_ARG);
}
else if (HTTPHeaders_GetHeaderCount(httpHeadersHandle, &cnt)
!= HTTP_HEADERS_OK) {
return (HTTPAPI_QUERY_HEADERS_FAILED);
}
/* Send the request line */
ret = HTTPCli_sendRequest(cli, method, relativePath, true);
if (ret < 0) {
return (HTTPAPI_SEND_REQUEST_FAILED);
}
/* Send the request headers */
while (cnt--) {
ret = HTTPHeaders_GetHeader(httpHeadersHandle, cnt, &hname);
if (ret != HTTP_HEADERS_OK) {
return (HTTPAPI_QUERY_HEADERS_FAILED);
}
ret = splitHeader(hname, &hvalue);
if (ret == 0) {
ret = HTTPCli_sendField(cli, hname, hvalue, false);
}
free(hname);
hname = NULL;
if (ret < 0) {
return (HTTPAPI_SEND_REQUEST_FAILED);
}
}
/* Send the last header and request body */
ret = HTTPCli_sendField(cli, NULL, NULL, true);
if (ret < 0) {
return (HTTPAPI_SEND_REQUEST_FAILED);
}
if (content && contentLength != 0) {
ret = HTTPCli_sendRequestBody(cli, (const char *)content,
contentLength);
if (ret < 0) {
return (HTTPAPI_SEND_REQUEST_FAILED);
}
}
/* Get the response status code */
ret = HTTPCli_getResponseStatus(cli);
if (ret < 0) {
return (HTTPAPI_RECEIVE_RESPONSE_FAILED);
}
*statusCode = (unsigned int)ret;
/* Get the response headers */
hname = NULL;
cnt = 0;
offset = 0;
do {
ret = HTTPCli_readResponseHeader(cli, contentBuf, CONTENT_BUF_LEN,
&moreFlag);
if (ret < 0) {
ret = HTTPAPI_RECEIVE_RESPONSE_FAILED;
goto headersDone;
}
else if (ret == 0) {
/* All headers read */
goto headersDone;
}
if (cnt < offset + ret) {
hname = (char *)realloc(hname, offset + ret);
if (hname == NULL) {
ret = HTTPAPI_ALLOC_FAILED;
goto headersDone;
}
cnt = offset + ret;
}
memcpy(hname + offset, contentBuf, ret);
offset += ret;
if (moreFlag) {
continue;
}
ret = splitHeader(hname, &hvalue);
if (ret < 0) {
ret = HTTPAPI_HTTP_HEADERS_FAILED;
goto headersDone;
}
ret = HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle,
hname, hvalue);
if (ret != HTTP_HEADERS_OK) {
ret = HTTPAPI_HTTP_HEADERS_FAILED;
goto headersDone;
}
offset = 0;
} while (1);
headersDone:
free(hname);
hname = NULL;
if (ret != 0) {
return ((HTTPAPI_RESULT)ret);
}
/* Get response body */
if (responseContent != NULL) {
offset = 0;
cnt = 0;
do {
ret = HTTPCli_readResponseBody(cli, contentBuf, CONTENT_BUF_LEN,
&moreFlag);
if (ret < 0) {
ret = HTTPAPI_RECEIVE_RESPONSE_FAILED;
goto contentDone;
}
if (ret != 0) {
cnt = ret;
ret = BUFFER_enlarge(responseContent, cnt);
if (ret != 0) {
ret = HTTPAPI_ALLOC_FAILED;
goto contentDone;
}
ret = BUFFER_content(responseContent,
(const unsigned char **)&hname);
if (ret != 0) {
ret = HTTPAPI_ALLOC_FAILED;
goto contentDone;
}
memcpy(hname + offset, contentBuf, cnt);
offset += cnt;
}
} while (moreFlag);
contentDone:
if (ret < 0) {
BUFFER_unbuild(responseContent);
return ((HTTPAPI_RESULT)ret);
}
}
return (HTTPAPI_OK);
}
HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName,
const void* value)
{
return (HTTPAPI_INVALID_ARG);
}
HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value,
const void** savedValue)
{
return (HTTPAPI_INVALID_ARG);
}

Просмотреть файл

@ -0,0 +1,769 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <stddef.h>
#include "windows.h"
#include "winhttp.h"
#include "string.h"
#include "httpapi.h"
#include "httpheaders.h"
#include "iot_logging.h"
#include "strings.h"
#define TEMP_BUFFER_SIZE 1024
#define MESSAGE_BUFFER_SIZE 260
#define LogErrorWinHTTPWithGetLastErrorAsString(FORMAT, ...) { \
DWORD errorMessageID = GetLastError(); \
LogError(FORMAT, __VA_ARGS__); \
CHAR messageBuffer[MESSAGE_BUFFER_SIZE]; \
if (errorMessageID == 0) \
{\
LogError("GetLastError() returned 0. Make sure you are calling this right after the code that failed. \r\n"); \
} \
else\
{\
int size = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, \
GetModuleHandle("WinHttp"), errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, MESSAGE_BUFFER_SIZE, NULL); \
if (size == 0)\
{\
size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, MESSAGE_BUFFER_SIZE, NULL); \
if (size == 0)\
{\
LogError("GetLastError Code: %d. \r\n", errorMessageID); \
}\
else\
{\
LogError("GetLastError: %s.\r\n", messageBuffer); \
}\
}\
else\
{\
LogError("GetLastError: %s.\r\n", messageBuffer); \
}\
}\
} \
DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES)
typedef enum HTTPAPI_STATE_TAG
{
HTTPAPI_NOT_INITIALIZED,
HTTPAPI_INITIALIZED
} HTTPAPI_STATE;
typedef struct HTTP_HANDLE_DATA_TAG
{
HINTERNET ConnectionHandle;
unsigned int timeout;
} HTTP_HANDLE_DATA;
static HTTPAPI_STATE g_HTTPAPIState = HTTPAPI_NOT_INITIALIZED;
/*There's a global SessionHandle for all the connections*/
static HINTERNET g_SessionHandle;
static size_t nUsersOfHTTPAPI = 0; /*used for reference counting (a weak one)*/
/*returns NULL if it failed to construct the headers*/
static const char* ConstructHeadersString(HTTP_HEADERS_HANDLE httpHeadersHandle)
{
char* result;
size_t headersCount;
if (HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK)
{
result = NULL;
LogError("HTTPHeaders_GetHeaderCount failed.\r\n");
}
else
{
size_t i;
/*the total size of all the headers is given by sumof(lengthof(everyheader)+2)*/
size_t toAlloc = 0;
for (i = 0; i < headersCount; i++)
{
char *temp;
if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &temp) == HTTP_HEADERS_OK)
{
toAlloc += strlen(temp);
toAlloc += 2;
free(temp);
}
else
{
LogError("HTTPHeaders_GetHeader failed\r\n");
break;
}
}
if (i < headersCount)
{
result = NULL;
}
else
{
result = malloc(toAlloc*sizeof(char) + 1 );
if (result == NULL)
{
LogError("unable to malloc\r\n");
/*let it be returned*/
}
else
{
result[0] = '\0';
for (i = 0; i < headersCount; i++)
{
char* temp;
if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &temp) != HTTP_HEADERS_OK)
{
LogError("unable to HTTPHeaders_GetHeader");
break;
}
else
{
(void)strcat(result, temp);
(void)strcat(result, "\r\n");
free(temp);
}
}
if (i < headersCount)
{
free(result);
result = NULL;
}
else
{
/*all is good*/
}
}
}
}
return result;
}
HTTPAPI_RESULT HTTPAPI_Init(void)
{
HTTPAPI_RESULT result;
if (nUsersOfHTTPAPI == 0)
{
LogInfo("HTTP_API first init.\r\n");
if ((g_SessionHandle = WinHttpOpen(
NULL,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0)) == NULL)
{
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpOpen failed.\r\n");
result = HTTPAPI_INIT_FAILED;
}
else
{
nUsersOfHTTPAPI++;
g_HTTPAPIState = HTTPAPI_INITIALIZED;
result = HTTPAPI_OK;
LogInfo("HTTP_API has now %d users\r\n", (int)nUsersOfHTTPAPI);
}
}
else
{
nUsersOfHTTPAPI++;
result = HTTPAPI_OK;
LogInfo("HTTP_API has now %d users\r\n", (int)nUsersOfHTTPAPI);
}
return result;
}
void HTTPAPI_Deinit(void)
{
if (nUsersOfHTTPAPI > 0)
{
nUsersOfHTTPAPI--;
LogInfo("HTTP_API has now %d users\r\n", (int)nUsersOfHTTPAPI);
if (nUsersOfHTTPAPI == 0)
{
LogInfo("Deinitializing HTTP_API\r\n");
if (g_SessionHandle != NULL)
{
(void)WinHttpCloseHandle(g_SessionHandle);
g_SessionHandle = NULL;
g_HTTPAPIState = HTTPAPI_NOT_INITIALIZED;
}
}
}
}
HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
HTTP_HANDLE_DATA* result;
if (g_HTTPAPIState != HTTPAPI_INITIALIZED)
{
LogError("g_HTTPAPIState not HTTPAPI_INITIALIZED\r\n");
result = NULL;
}
else
{
result = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA));
if (result == NULL)
{
LogError("malloc returned NULL.\r\n");
}
else
{
wchar_t* hostNameTemp;
size_t hostNameTemp_size = MultiByteToWideChar(CP_ACP, 0, hostName, -1, NULL, 0);
if (hostNameTemp_size == 0)
{
LogError("MultiByteToWideChar failed\r\n");
}
else
{
hostNameTemp = malloc(sizeof(wchar_t)*hostNameTemp_size);
if (hostNameTemp == NULL)
{
LogError("malloc failed\r\n");
}
else
{
if (MultiByteToWideChar(CP_ACP, 0, hostName, -1, hostNameTemp, hostNameTemp_size) == 0)
{
LogError("MultiByteToWideChar failed\r\n");
free(result);
result = NULL;
}
else
{
result->ConnectionHandle = WinHttpConnect(
g_SessionHandle,
hostNameTemp,
INTERNET_DEFAULT_HTTPS_PORT,
0);
if (result->ConnectionHandle == NULL)
{
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpConnect returned NULL.\r\n");
free(result);
result = NULL;
}
else
{
result->timeout = 30000; /*default from MSDN ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx )*/
}
}
free(hostNameTemp);
}
}
}
}
return (HTTP_HANDLE)result;
}
void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
if (g_HTTPAPIState != HTTPAPI_INITIALIZED)
{
LogError("g_HTTPAPIState not HTTPAPI_INITIALIZED\r\n");
}
else
{
HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;
if (handleData != NULL)
{
if (handleData->ConnectionHandle != NULL)
{
(void)WinHttpCloseHandle(handleData->ConnectionHandle);
handleData->ConnectionHandle = NULL;
}
free(handleData);
}
}
}
HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
size_t contentLength, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent)
{
HTTPAPI_RESULT result;
if (g_HTTPAPIState != HTTPAPI_INITIALIZED)
{
LogError("g_HTTPAPIState not HTTPAPI_INITIALIZED\r\n");
result = HTTPAPI_NOT_INIT;
}
else
{
HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;
if ((handleData == NULL) ||
(relativePath == NULL) ||
(httpHeadersHandle == NULL))
{
result = HTTPAPI_INVALID_ARG;
LogError("NULL parameter detected (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
wchar_t* requestTypeString = NULL;
switch (requestType)
{
default:
break;
case HTTPAPI_REQUEST_GET:
requestTypeString = L"GET";
break;
case HTTPAPI_REQUEST_POST:
requestTypeString = L"POST";
break;
case HTTPAPI_REQUEST_PUT:
requestTypeString = L"PUT";
break;
case HTTPAPI_REQUEST_DELETE:
requestTypeString = L"DELETE";
break;
case HTTPAPI_REQUEST_PATCH:
requestTypeString = L"PATCH";
break;
}
if (requestTypeString == NULL)
{
result = HTTPAPI_INVALID_ARG;
LogError("requestTypeString was NULL (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
const char* headers2;
headers2 = ConstructHeadersString(httpHeadersHandle);
if (headers2 != NULL)
{
size_t requiredCharactersForRelativePath = MultiByteToWideChar(CP_ACP, 0, relativePath, -1, NULL, 0);
wchar_t* relativePathTemp = malloc((requiredCharactersForRelativePath+1) * sizeof(wchar_t));
result = HTTPAPI_OK; /*legacy code*/
if (relativePathTemp == NULL)
{
result = HTTPAPI_ALLOC_FAILED;
LogError("malloc failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (MultiByteToWideChar(CP_ACP, 0, relativePath, -1, relativePathTemp, requiredCharactersForRelativePath) == 0)
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("MultiByteToWideChar was 0. (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
size_t requiredCharactersForHeaders = MultiByteToWideChar(CP_ACP, 0, headers2, -1, NULL, 0);
wchar_t* headersTemp = malloc((requiredCharactersForHeaders +1) * sizeof(wchar_t) );
if (headersTemp == NULL)
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("MultiByteToWideChar was 0. (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (MultiByteToWideChar(CP_ACP, 0, headers2, -1, headersTemp, requiredCharactersForHeaders) == 0)
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("MultiByteToWideChar was 0(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
HINTERNET requestHandle = WinHttpOpenRequest(
handleData->ConnectionHandle,
requestTypeString,
relativePathTemp,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE);
if (requestHandle == NULL)
{
result = HTTPAPI_OPEN_REQUEST_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpOpenRequest failed (result = %s).\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (WinHttpSetTimeouts(requestHandle,
0, /*_In_ int dwResolveTimeout - The initial value is zero, meaning no time-out (infinite). */
60000, /*_In_ int dwConnectTimeout, - The initial value is 60,000 (60 seconds).*/
handleData->timeout, /*_In_ int dwSendTimeout, - The initial value is 30,000 (30 seconds).*/
handleData->timeout /* int dwReceiveTimeout The initial value is 30,000 (30 seconds).*/
) == FALSE)
{
result = HTTPAPI_SET_TIMEOUTS_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpOpenRequest failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
DWORD dwSecurityFlags = 0;
if (!WinHttpSetOption(
requestHandle,
WINHTTP_OPTION_SECURITY_FLAGS,
&dwSecurityFlags,
sizeof(dwSecurityFlags)))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpSetOption failed (result = %s).\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (!WinHttpSendRequest(
requestHandle,
headersTemp,
(DWORD)-1L, /*An unsigned long integer value that contains the length, in characters, of the additional headers. If this parameter is -1L ... */
(void*)content,
(DWORD)contentLength,
(DWORD)contentLength,
0))
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpSendRequest: (result = %s).\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (!WinHttpReceiveResponse(
requestHandle,
0))
{
result = HTTPAPI_RECEIVE_RESPONSE_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpReceiveResponse: (result = %s).\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
DWORD dwStatusCode = 0;
DWORD dwBufferLength = sizeof(DWORD);
DWORD responseBytesAvailable;
if (!WinHttpQueryHeaders(
requestHandle,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&dwStatusCode,
&dwBufferLength,
WINHTTP_NO_HEADER_INDEX))
{
result = HTTPAPI_QUERY_HEADERS_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpQueryHeaders failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
BUFFER_HANDLE useToReadAllResponse = (responseContent != NULL) ? responseContent : BUFFER_new();
if (statusCode != NULL)
{
*statusCode = dwStatusCode;
}
if (useToReadAllResponse == NULL)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
int goOnAndReadEverything = 1;
do
{
/*from MSDN: If no data is available and the end of the file has not been reached, one of two things happens. If the session is synchronous, the request waits until data becomes available.*/
if (!WinHttpQueryDataAvailable(requestHandle, &responseBytesAvailable))
{
result = HTTPAPI_QUERY_DATA_AVAILABLE_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpQueryDataAvailable failed (result = %s).\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else if (responseBytesAvailable == 0)
{
/*end of the stream, go out*/
if (dwStatusCode >= HTTP_STATUS_AMBIGUOUS)
{
LogError("HTTP status code was: %d \r\n", (int)dwStatusCode);
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
result = HTTPAPI_OK;
goOnAndReadEverything = 0;
}
else
{
if (BUFFER_enlarge(useToReadAllResponse, responseBytesAvailable) != 0)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
/*Add the read bytes to the response buffer*/
size_t bufferSize;
const unsigned char* bufferContent;
if (BUFFER_content(useToReadAllResponse, &bufferContent) != 0)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else if (BUFFER_size(useToReadAllResponse, &bufferSize) != 0)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
DWORD bytesReceived;
if (!WinHttpReadData(requestHandle, (LPVOID)(bufferContent + bufferSize - responseBytesAvailable), responseBytesAvailable, &bytesReceived))
{
result = HTTPAPI_READ_DATA_FAILED;
LogErrorWinHTTPWithGetLastErrorAsString("WinHttpReadData failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
/*if for some reason bytesReceived is zero If you are using WinHttpReadData synchronously, and the return value is TRUE and the number of bytes read is zero, the transfer has been completed and there are no more bytes to read on the handle.*/
if (bytesReceived == 0)
{
/*end of everything, but this looks like an error still, or a non-conformance between WinHttpQueryDataAvailable and WinHttpReadData*/
result = HTTPAPI_READ_DATA_FAILED;
LogError("bytesReceived was unexpectedly zero (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
/*all is fine, keep going*/
}
}
}
}
}
} while (goOnAndReadEverything != 0);
if (responseContent != NULL)
{
if (BUFFER_u_char(responseContent) != NULL)
{
LogInfo("Buffer Content:\r\n %.*s\r\n", BUFFER_length(responseContent), BUFFER_u_char(responseContent));
}
}
else if (useToReadAllResponse != NULL)
{
if (BUFFER_u_char(useToReadAllResponse) != NULL)
{
LogInfo("Buffer Content:\r\n %.*s\r\n", BUFFER_length(useToReadAllResponse), BUFFER_u_char(useToReadAllResponse));
}
BUFFER_delete(useToReadAllResponse);
}
}
}
if (result == HTTPAPI_OK && responseHeadersHandle != NULL)
{
wchar_t* responseHeadersTemp;
DWORD responseHeadersTempLength = sizeof(responseHeadersTemp);
(void)WinHttpQueryHeaders(
requestHandle,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_NO_OUTPUT_BUFFER,
&responseHeadersTempLength,
WINHTTP_NO_HEADER_INDEX);
responseHeadersTemp = (wchar_t*)malloc(responseHeadersTempLength + 2);
if (responseHeadersTemp == NULL)
{
result = HTTPAPI_ALLOC_FAILED;
LogError("malloc failed: (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (WinHttpQueryHeaders(
requestHandle,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
responseHeadersTemp,
&responseHeadersTempLength,
WINHTTP_NO_HEADER_INDEX))
{
wchar_t *next_token;
wchar_t* token = wcstok_s(responseHeadersTemp, L"\r\n", &next_token);
while ((token != NULL) &&
(token[0] != L'\0'))
{
char* tokenTemp;
size_t tokenTemp_size;
tokenTemp_size = WideCharToMultiByte(CP_ACP, 0, token, -1, NULL, 0, NULL, NULL);
if (tokenTemp_size == 0)
{
LogError("WideCharToMultiByte failed\r\n");
}
else
{
tokenTemp = malloc(sizeof(char)*tokenTemp_size);
if (tokenTemp == NULL)
{
LogError("malloc failed\r\n");
}
else
{
if (WideCharToMultiByte(CP_ACP, 0, token, -1, tokenTemp, tokenTemp_size, NULL, NULL) > 0)
{
/*breaking the token in 2 parts: everything before the first ":" and everything after the first ":"*/
/* if there is no such character, then skip it*/
/*if there is a : then replace is by a '\0' and so it breaks the original string in name and value*/
char* whereIsColon = strchr(tokenTemp, ':');
if (whereIsColon != NULL)
{
*whereIsColon = '\0';
if (HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, tokenTemp, whereIsColon + 1) != HTTP_HEADERS_OK)
{
LogError("HTTPHeaders_AddHeaderNameValuePair failed\r\n");
result = HTTPAPI_HTTP_HEADERS_FAILED;
break;
}
}
}
else
{
LogError("WideCharToMultiByte failed\r\n");
}
free(tokenTemp);
}
}
token = wcstok_s(NULL, L"\r\n", &next_token);
}
}
else
{
LogError("WinHttpQueryHeaders failed\r\n");
}
free(responseHeadersTemp);
}
}
}
}
}
}
(void)WinHttpCloseHandle(requestHandle);
}
}
free(headersTemp);
}
}
free(relativePathTemp);
}
free((void*)headers2);
}
else
{
result = HTTPAPI_ALLOC_FAILED; /*likely*/
LogError("ConstructHeadersString failed\r\n");
}
}
}
}
return result;
}
HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value)
{
HTTPAPI_RESULT result;
if (
(handle == NULL) ||
(optionName == NULL) ||
(value == NULL)
)
{
result = HTTPAPI_INVALID_ARG;
LogError("invalid parameter (NULL) passed to HTTPAPI_SetOption\r\n");
}
else
{
HTTP_HANDLE_DATA* httpHandleData = (HTTP_HANDLE_DATA*)handle;
if (strcmp("timeout", optionName) == 0)
{
long timeout = (long)(*(unsigned int*)value);
httpHandleData->timeout = timeout;
result = HTTPAPI_OK;
}
else
{
result = HTTPAPI_INVALID_ARG;
LogError("unknown option %s\r\n", optionName);
}
}
return result;
}
HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue)
{
HTTPAPI_RESULT result;
if (
(optionName == NULL) ||
(value == NULL) ||
(savedValue == NULL)
)
{
result = HTTPAPI_INVALID_ARG;
LogError("invalid argument(NULL) passed to HTTPAPI_CloneOption\r\n");
}
else
{
if (strcmp("timeout", optionName) == 0)
{
/*by convention value is pointing to an unsigned int */
unsigned int* temp = malloc(sizeof(unsigned int)); /*shall be freed by HTTPAPIEX*/
if (temp == NULL)
{
result = HTTPAPI_ERROR;
LogError("malloc failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
*temp = *(const unsigned int*)value;
*savedValue = temp;
result = HTTPAPI_OK;
}
}
else
{
result = HTTPAPI_INVALID_ARG;
LogError("unknown option %s\r\n", optionName);
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,459 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "malloc.h"
#include "stddef.h"
#include "windows.h"
#include "wininet.h"
#include "string.h"
#include "httpapi.h"
#include "httpheaders.h"
#include "iot_logging.h"
#include "macro_utils.h"
#include "strings.h"
#define TEMP_BUFFER_SIZE 1024
DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES);
typedef enum HTTPAPI_STATE_TAG
{
HTTPAPI_NOT_INITIALIZED,
HTTPAPI_INITIALIZED
} HTTPAPI_STATE;
typedef struct HTTP_HANDLE_DATA_TAG
{
HINTERNET SessionHandle;
HINTERNET ConnectionHandle;
} HTTP_HANDLE_DATA;
static HTTPAPI_STATE g_HTTPAPIState;
static HTTPAPI_RESULT ConstructHeadersString(HTTP_HEADERS_HANDLE httpHeadersHandle, char* headers, size_t headersBufferSize)
{
HTTPAPI_RESULT result;
size_t headersCount;
headers[0] = '\0';
if (HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK)
{
result = HTTPAPI_HTTP_HEADERS_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
size_t i;
for (i = 0; i < headersCount; i++)
{
char tempBuffer[TEMP_BUFFER_SIZE];
if (HTTPHeaders_GetHeader(httpHeadersHandle, i, tempBuffer, TEMP_BUFFER_SIZE) == HTTP_HEADERS_OK)
{
strcat_s(headers, headersBufferSize, tempBuffer);
strcat_s(headers, headersBufferSize, "\r\n");
}
}
result = HTTPAPI_OK;
}
return result;
}
static size_t nUsersOfHTTPAPI = 0; /*used for reference counting (a weak one)*/
HTTPAPI_RESULT HTTPAPI_Init(void)
{
HTTPAPI_RESULT result;
if (nUsersOfHTTPAPI == 0)
{
LogInfo("HTTP_API first init.\r\n");
/*do internetOpen here... TFS202146*/
nUsersOfHTTPAPI++;
g_HTTPAPIState = HTTPAPI_INITIALIZED;
result = HTTPAPI_OK;
LogInfo("HTTP_API has now %d users\r\n", (int)nUsersOfHTTPAPI);
}
else
{
nUsersOfHTTPAPI++;
result = HTTPAPI_OK;
LogInfo("HTTP_API has now %d users\r\n", (int)nUsersOfHTTPAPI);
}
return result;
}
void HTTPAPI_Deinit(void)
{
if (nUsersOfHTTPAPI > 0)
{
nUsersOfHTTPAPI--;
LogInfo("HTTP_API has now %d users\r\n", (int)nUsersOfHTTPAPI);
if (nUsersOfHTTPAPI == 0)
{
LogInfo("Deinitializing HTTP_API\r\n");
g_HTTPAPIState = HTTPAPI_NOT_INITIALIZED;
}
}
}
HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
HTTP_HANDLE_DATA* result = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA));
if (result == NULL)
{
/* error */
}
else
{
wchar_t hostNameTemp[1024];
if (MultiByteToWideChar(CP_ACP, 0, hostName, -1, hostNameTemp, sizeof(hostNameTemp) / sizeof(hostNameTemp[0])) == 0)
{
/* error */
}
else
{
result->SessionHandle = InternetOpen(
NULL,
INTERNET_OPEN_TYPE_DIRECT,
NULL,
NULL,
0);
if (result->SessionHandle != NULL)
{
result->ConnectionHandle = InternetConnectW(
result->SessionHandle,
hostNameTemp,
INTERNET_DEFAULT_HTTPS_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0);
}
if ((result->SessionHandle == NULL) ||
(result->ConnectionHandle == NULL))
{
HTTPAPI_CloseConnection(result);
result = NULL;
}
}
}
return (HTTP_HANDLE)result;
}
void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;
if (handleData != NULL)
{
if (handleData->ConnectionHandle != NULL)
{
(void)InternetCloseHandle(handleData->ConnectionHandle);
handleData->ConnectionHandle = NULL;
}
if (handleData->SessionHandle != NULL)
{
(void)InternetCloseHandle(handleData->SessionHandle);
handleData->SessionHandle = NULL;
}
free(handleData);
}
}
HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
size_t contentLength, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent)
{
HTTPAPI_RESULT result;
HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;
if ((handleData == NULL) ||
(relativePath == NULL) ||
(httpHeadersHandle == NULL))
{
result = HTTPAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
wchar_t* requestTypeString = NULL;
switch (requestType)
{
default:
break;
case HTTPAPI_REQUEST_GET:
requestTypeString = L"GET";
break;
case HTTPAPI_REQUEST_POST:
requestTypeString = L"POST";
break;
case HTTPAPI_REQUEST_PUT:
requestTypeString = L"PUT";
break;
case HTTPAPI_REQUEST_DELETE:
requestTypeString = L"DELETE";
break;
case HTTPAPI_REQUEST_PATCH:
requestTypeString = L"PATCH";
break;
}
if (requestTypeString == NULL)
{
result = HTTPAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
wchar_t relativePathTemp[1024];
char headers[1024];
wchar_t headersTemp[1024];
result = ConstructHeadersString(httpHeadersHandle, headers, sizeof(headers));
if (result == HTTPAPI_OK)
{
if (MultiByteToWideChar(CP_ACP, 0, relativePath, -1, relativePathTemp, sizeof(relativePathTemp) / sizeof(relativePathTemp[0])) == 0)
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (MultiByteToWideChar(CP_ACP, 0, headers, -1, headersTemp, sizeof(headersTemp) / sizeof(headersTemp[0])) == 0)
{
result = HTTPAPI_STRING_PROCESSING_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
PCWSTR rgpszAcceptTypes[] = { L"text/*", NULL };
HINTERNET requestHandle = HttpOpenRequestW(
handleData->ConnectionHandle,
requestTypeString,
relativePathTemp,
NULL,
NULL,
rgpszAcceptTypes,
INTERNET_FLAG_SECURE,
0);
if (requestHandle == NULL)
{
result = HTTPAPI_OPEN_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
unsigned long int timeout = 55000;
if (!InternetSetOption(
requestHandle,
INTERNET_OPTION_RECEIVE_TIMEOUT, /*Sets or retrieves an unsigned long integer value that contains the time-out value, in milliseconds, to receive a response to a request.*/
&timeout,
sizeof(timeout)))
{
result = HTTPAPI_SET_TIMEOUTS_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
DWORD dwSecurityFlags = 0;
if (!InternetSetOption(
requestHandle,
INTERNET_OPTION_SECURITY_FLAGS,
&dwSecurityFlags,
sizeof(dwSecurityFlags)))
{
result = HTTPAPI_SET_OPTION_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
if (!HttpSendRequestW(
requestHandle,
headersTemp,
-1,
(void*)content,
contentLength))
{
result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
DWORD dwStatusCode = 0;
DWORD dwBufferLength = sizeof(DWORD);
DWORD responseBytesAvailable;
if (responseHeadersHandle != NULL)
{
wchar_t responseHeadersTemp[16384];
DWORD responseHeadersTempLength = sizeof(responseHeadersTemp);
if (HttpQueryInfo(
requestHandle,
HTTP_QUERY_RAW_HEADERS_CRLF,
responseHeadersTemp,
&responseHeadersTempLength,
0))
{
wchar_t *next_token;
wchar_t* token = wcstok_s(responseHeadersTemp, L"\r\n", &next_token);
while ((token != NULL) &&
(token[0] != L'\0'))
{
char tokenTemp[1024];
if (WideCharToMultiByte(CP_ACP, 0, token, -1, tokenTemp, sizeof(tokenTemp), NULL, NULL) > 0)
{
/*breaking the token in 2 parts: everything before the first ":" and everything after the first ":"*/
/* if there is no such character, then skip it*/
/*if there is a : then replace is by a '\0' and so it breaks the original string in name and value*/
char* whereIsColon = strchr(tokenTemp, ':');
if (whereIsColon != NULL)
{
*whereIsColon = '\0';
HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, tokenTemp, whereIsColon + 1);
}
}
token = wcstok_s(NULL, L"\r\n", &next_token);
}
}
}
if (!HttpQueryInfo(
requestHandle,
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
&dwStatusCode,
&dwBufferLength,
0))
{
result = HTTPAPI_QUERY_HEADERS_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
BUFFER_HANDLE useToReadAllResponse = (responseContent != NULL) ? responseContent : BUFFER_new();
/*HTTP status code (dwStatusCode) can be either ok (<HTTP_STATUS_AMBIGUOUS) or "not ok (>=HTTP_STATUS_AMBIGUOUS)*/
if (useToReadAllResponse == NULL)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
else
{
int goOnAndReadEverything = 1;
/*set the response code*/
if (statusCode != NULL)
{
*statusCode = dwStatusCode;
}
do
{
/*from MSDN: If there is currently no data available and the end of the file has not been reached, the request waits until data becomes available*/
if (!InternetQueryDataAvailable(requestHandle, &responseBytesAvailable, 0, 0))
{
result = HTTPAPI_QUERY_DATA_AVAILABLE_FAILED;
LogError("InternetQueryDataAvailable failed (result = %s) GetLastError = %d\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result), GetLastError());
goOnAndReadEverything = 0;
}
else if (responseBytesAvailable == 0)
{
/*end of the stream, go out*/
if (dwStatusCode >= HTTP_STATUS_AMBIGUOUS)
{
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
}
result = HTTPAPI_OK;
goOnAndReadEverything = 0;
}
else
{
/*add the needed space to the buffer*/
if (BUFFER_enlarge(useToReadAllResponse, responseBytesAvailable) != 0)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
unsigned char* bufferContent;
size_t bufferSize;
/*add the data to the buffer*/
if (BUFFER_content(useToReadAllResponse, &bufferContent) != 0)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else if (BUFFER_size(useToReadAllResponse, &bufferSize) != 0)
{
result = HTTPAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
DWORD bytesReceived;
if (!InternetReadFile(requestHandle, bufferContent + bufferSize - responseBytesAvailable, responseBytesAvailable, &bytesReceived))
{
result = HTTPAPI_READ_DATA_FAILED;
LogError("InternetReadFile failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
/*if for some reason bytesReceived is zero, MSDN says To ensure all data is retrieved, an application must continue to call the InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero*/
if (bytesReceived == 0)
{
/*end of everything, but this looks like an error still, or a non-conformance between InternetQueryDataAvailable and InternetReadFile*/
result = HTTPAPI_READ_DATA_FAILED;
LogError("InternetReadFile failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
goOnAndReadEverything = 0;
}
else
{
/*all is fine, keep going*/
}
}
}
}
}
} while (goOnAndReadEverything != 0);
if (responseContent == NULL)
{
BUFFER_delete(useToReadAllResponse);
}
}
}
}
}
}
(void)InternetCloseHandle(requestHandle);
}
}
}
}
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,105 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "stdlib.h"
#include "macro_utils.h"
#include "lock.h"
#include <thr/threads.h>
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(LOCK_RESULT, LOCK_RESULT_VALUES);
/*SRS_LOCK_99_002:[ This API on success will return a valid lock handle which should be a non NULL value]*/
LOCK_HANDLE Lock_Init(void)
{
mtx_t* lock_mtx = (mtx_t*)malloc(sizeof(mtx_t));
if (NULL != lock_mtx)
{
if (mtx_init(lock_mtx, mtx_plain) != thrd_success)
{
/*SRS_LOCK_99_003:[ On Error Should return NULL]*/
free(lock_mtx);
lock_mtx = NULL;
LogError("Failed to initialize mutex\r\n");
}
}
return (LOCK_HANDLE)lock_mtx;
}
LOCK_RESULT Lock(LOCK_HANDLE handle)
{
LOCK_RESULT result;
if (handle == NULL)
{
/*SRS_LOCK_99_007:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
if (mtx_lock((mtx_t*)handle) == thrd_success)
{
/*SRS_LOCK_99_005:[ This API on success should return LOCK_OK]*/
result = LOCK_OK;
}
else
{
/*SRS_LOCK_99_006:[ This API on error should return LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}
LOCK_RESULT Unlock(LOCK_HANDLE handle)
{
LOCK_RESULT result;
if (handle == NULL)
{
/*SRS_LOCK_99_011:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
if (mtx_unlock((mtx_t*)handle) == thrd_success)
{
/*SRS_LOCK_99_009:[ This API on success should return LOCK_OK]*/
result = LOCK_OK;
}
else
{
/*SRS_LOCK_99_010:[ This API on error should return LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}
LOCK_RESULT Lock_Deinit(LOCK_HANDLE handle)
{
LOCK_RESULT result=LOCK_OK ;
if (NULL == handle)
{
/*SRS_LOCK_99_013:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
/*SRS_LOCK_99_012:[ This API frees the memory pointed by handle]*/
(void)mtx_destroy((mtx_t*)handle);
free(handle);
handle = NULL;
}
return result;
}

Просмотреть файл

@ -0,0 +1,108 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "lock.h"
#include<pthread.h>
#include<stdlib.h>
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(LOCK_RESULT, LOCK_RESULT_VALUES);
/*SRS_LOCK_99_002:[ This API on success will return a valid lock handle which should be a non NULL value]*/
LOCK_HANDLE Lock_Init(void)
{
pthread_mutex_t* lock_mtx = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
if (NULL != lock_mtx)
{
if (pthread_mutex_init(lock_mtx, NULL) != 0)
{
/*SRS_LOCK_99_003:[ On Error Should return NULL]*/
free(lock_mtx);
lock_mtx = NULL;
LogError("Failed to initialize mutex\r\n");
}
}
return (LOCK_HANDLE)lock_mtx;
}
LOCK_RESULT Lock(LOCK_HANDLE handle)
{
LOCK_RESULT result;
if (handle == NULL)
{
/*SRS_LOCK_99_007:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
if ( pthread_mutex_lock((pthread_mutex_t*)handle) == 0)
{
/*SRS_LOCK_99_005:[ This API on success should return LOCK_OK]*/
result = LOCK_OK;
}
else
{
/*SRS_LOCK_99_006:[ This API on error should return LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}
LOCK_RESULT Unlock(LOCK_HANDLE handle)
{
LOCK_RESULT result;
if (handle == NULL)
{
/*SRS_LOCK_99_011:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
if (pthread_mutex_unlock((pthread_mutex_t*)handle) == 0)
{
/*SRS_LOCK_99_009:[ This API on success should return LOCK_OK]*/
result = LOCK_OK;
}
else
{
/*SRS_LOCK_99_010:[ This API on error should return LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}
LOCK_RESULT Lock_Deinit(LOCK_HANDLE handle)
{
LOCK_RESULT result=LOCK_OK ;
if (NULL == handle)
{
/*SRS_LOCK_99_013:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
/*SRS_LOCK_99_012:[ This API frees the memory pointed by handle]*/
if(pthread_mutex_destroy((pthread_mutex_t*)handle)==0)
{
free(handle);
handle = NULL;
}
else
{
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,94 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <cstdlib>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "lock.h"
#include "iot_logging.h"
#include "rtos.h"
DEFINE_ENUM_STRINGS(LOCK_RESULT, LOCK_RESULT_VALUES);
/*Tests_SRS_LOCK_99_002:[ This API on success will return a valid lock handle which should be a non NULL value]*/
LOCK_HANDLE Lock_Init(void)
{
Mutex* lock_mtx = new Mutex();
return (LOCK_HANDLE)lock_mtx;
}
LOCK_RESULT Lock(LOCK_HANDLE handle)
{
LOCK_RESULT result;
if (handle == NULL)
{
/*Tests_SRS_LOCK_99_007:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
Mutex* lock_mtx = (Mutex*)handle;
if (lock_mtx->lock() == osOK)
{
/*Tests_SRS_LOCK_99_005:[ This API on success should return LOCK_OK]*/
result = LOCK_OK;
}
else
{
/*Tests_SRS_LOCK_99_006:[ This API on error should return LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}
LOCK_RESULT Unlock(LOCK_HANDLE handle)
{
LOCK_RESULT result;
if (handle == NULL)
{
/*Tests_SRS_LOCK_99_011:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
Mutex* lock_mtx = (Mutex*)handle;
if (lock_mtx->unlock() == osOK)
{
/*Tests_SRS_LOCK_99_009:[ This API on success should return LOCK_OK]*/
result = LOCK_OK;
}
else
{
/*Tests_SRS_LOCK_99_010:[ This API on error should return LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
}
return result;
}
LOCK_RESULT Lock_Deinit(LOCK_HANDLE handle)
{
LOCK_RESULT result=LOCK_OK ;
if (NULL == handle)
{
/*Tests_SRS_LOCK_99_013:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(LOCK_RESULT, result));
}
else
{
/*Tests_SRS_LOCK_99_012:[ This API frees the memory pointed by handle]*/
Mutex* lock_mtx = (Mutex*)handle;
delete lock_mtx;
}
return result;
}

Просмотреть файл

@ -0,0 +1,76 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "lock.h"
#include <windows.h>
#include "malloc.h"
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(LOCK_RESULT, LOCK_RESULT_VALUES);
/*SRS_LOCK_99_002:[ This API on success will return a valid lock handle which should be a non NULL value]*/
LOCK_HANDLE Lock_Init(void)
{
LPCRITICAL_SECTION lpCriticalSection = (LPCRITICAL_SECTION) malloc(sizeof(CRITICAL_SECTION));
if (!lpCriticalSection)
{
LogError("Could not allocate memory for Critical Section");
}
else
{
//In low-memory situations, InitializeCriticalSection can raise a STATUS_NO_MEMORY exception.
InitializeCriticalSection( lpCriticalSection );
}
return (LOCK_HANDLE) lpCriticalSection;
}
LOCK_RESULT Lock(LOCK_HANDLE handle)
{
LOCK_RESULT result = LOCK_OK;
if (handle == NULL)
{
/*SRS_LOCK_99_007:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", Lock_ResultAsString[result]);
}
else
{
EnterCriticalSection( (LPCRITICAL_SECTION) handle );
}
return result;
}
LOCK_RESULT Unlock(LOCK_HANDLE handle)
{
LOCK_RESULT result = LOCK_OK;
if (handle == NULL)
{
/*SRS_LOCK_99_007:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", Lock_ResultAsString[result]);
}
else
{
LeaveCriticalSection( (LPCRITICAL_SECTION) handle );
}
return result;
}
LOCK_RESULT Lock_Deinit(LOCK_HANDLE handle)
{
LOCK_RESULT result = LOCK_OK;
if (handle == NULL)
{
/*SRS_LOCK_99_007:[ This API on NULL handle passed returns LOCK_ERROR]*/
result = LOCK_ERROR;
LogError("(result = %s)\r\n", Lock_ResultAsString[result]);
}
else
{
DeleteCriticalSection( (LPCRITICAL_SECTION) handle );
free( (LPCRITICAL_SECTION) handle );
}
return result;
}

Просмотреть файл

@ -0,0 +1,28 @@
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "platform.h"
#include "winsock2.h"
int platform_init(void)
{
int result;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
result = __LINE__;
}
else
{
result = 0;
}
return result;
}
void platform_deinit(void)
{
(void)WSACleanup();
}

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "platform.h"
#include "winsock2.h"
int platform_init(void)
{
int result;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
result = __LINE__;
}
else
{
result = 0;
}
return result;
}
void platform_deinit(void)
{
(void)WSACleanup();
}

Просмотреть файл

@ -0,0 +1,306 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include <stddef.h>
#include <stdio.h>
#include "socketio.h"
#include "winsock2.h"
#include "ws2tcpip.h"
#include "windows.h"
typedef struct SOCKET_IO_INSTANCE_TAG
{
SOCKET socket;
ON_BYTES_RECEIVED on_bytes_received;
ON_IO_STATE_CHANGED on_io_state_changed;
LOGGER_LOG logger_log;
void* callback_context;
char* hostname;
int port;
IO_STATE io_state;
unsigned char* pending_send_bytes;
size_t pending_send_byte_count;
} SOCKET_IO_INSTANCE;
static const IO_INTERFACE_DESCRIPTION socket_io_interface_description =
{
socketio_create,
socketio_destroy,
socketio_open,
socketio_close,
socketio_send,
socketio_dowork
};
static void set_io_state(SOCKET_IO_INSTANCE* socket_io_instance, IO_STATE io_state)
{
IO_STATE previous_state = socket_io_instance->io_state;
socket_io_instance->io_state = io_state;
if (socket_io_instance->on_io_state_changed != NULL)
{
socket_io_instance->on_io_state_changed(socket_io_instance->callback_context, io_state, previous_state);
}
}
IO_HANDLE socketio_create(void* io_create_parameters, LOGGER_LOG logger_log)
{
SOCKETIO_CONFIG* socket_io_config = io_create_parameters;
SOCKET_IO_INSTANCE* result;
if (socket_io_config == NULL)
{
result = NULL;
}
else
{
result = malloc(sizeof(SOCKET_IO_INSTANCE));
if (result != NULL)
{
result->hostname = (char*)malloc(strlen(socket_io_config->hostname) + 1);
if (result->hostname == NULL)
{
free(result);
result = NULL;
}
else
{
strcpy(result->hostname, socket_io_config->hostname);
result->port = socket_io_config->port;
result->on_bytes_received = NULL;
result->on_io_state_changed = NULL;
result->logger_log = logger_log;
result->socket = INVALID_SOCKET;
result->callback_context = NULL;
result->pending_send_byte_count = 0;
result->pending_send_bytes = NULL;
result->io_state = IO_STATE_NOT_OPEN;
}
}
}
return result;
}
void socketio_destroy(IO_HANDLE socket_io)
{
if (socket_io != NULL)
{
SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io;
/* we cannot do much if the close fails, so just ignore the result */
(void)closesocket(socket_io_instance->socket);
free(socket_io_instance->hostname);
free(socket_io);
}
}
int socketio_open(IO_HANDLE socket_io, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context)
{
int result;
SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io;
if (socket_io == NULL)
{
result = __LINE__;
}
else
{
ADDRINFO* addrInfo;
char portString[16];
socket_io_instance->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket_io_instance->socket == INVALID_SOCKET)
{
set_io_state(socket_io_instance, IO_STATE_ERROR);
result = __LINE__;
}
else
{
sprintf(portString, "%u", socket_io_instance->port);
if (getaddrinfo(socket_io_instance->hostname, portString, NULL, &addrInfo) != 0)
{
closesocket(socket_io_instance->socket);
set_io_state(socket_io_instance, IO_STATE_ERROR);
socket_io_instance->socket = INVALID_SOCKET;
result = __LINE__;
}
else
{
u_long iMode = 1;
if (connect(socket_io_instance->socket, addrInfo->ai_addr, sizeof(*addrInfo->ai_addr)) != 0)
{
closesocket(socket_io_instance->socket);
set_io_state(socket_io_instance, IO_STATE_ERROR);
socket_io_instance->socket = INVALID_SOCKET;
result = __LINE__;
}
else if (ioctlsocket(socket_io_instance->socket, FIONBIO, &iMode))
{
closesocket(socket_io_instance->socket);
set_io_state(socket_io_instance, IO_STATE_ERROR);
socket_io_instance->socket = INVALID_SOCKET;
result = __LINE__;
}
else
{
socket_io_instance->on_bytes_received = on_bytes_received;
socket_io_instance->on_io_state_changed = on_io_state_changed;
socket_io_instance->callback_context = callback_context;
set_io_state(socket_io_instance, IO_STATE_OPEN);
result = 0;
}
}
}
}
return result;
}
int socketio_close(IO_HANDLE socket_io)
{
int result = 0;
if (socket_io == NULL)
{
result = __LINE__;
}
else
{
SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io;
closesocket(socket_io_instance->socket);
socket_io_instance->socket = INVALID_SOCKET;
set_io_state(socket_io_instance, IO_STATE_NOT_OPEN);
result = 0;
}
return result;
}
int socketio_send(IO_HANDLE socket_io, const void* buffer, size_t size)
{
int result;
if ((socket_io == NULL) ||
(buffer == NULL) ||
(size == 0))
{
/* Invalid arguments */
result = __LINE__;
}
else
{
SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io;
if (socket_io_instance->io_state != IO_STATE_OPEN)
{
result = __LINE__;
}
else
{
int send_result = send(socket_io_instance->socket, buffer, size, 0);
if (send_result != size)
{
int last_error = WSAGetLastError();
if (last_error != WSAEWOULDBLOCK)
{
result = __LINE__;
}
else
{
/* queue data */
unsigned char* new_pending_send_bytes = realloc(socket_io_instance->pending_send_bytes, socket_io_instance->pending_send_byte_count + size);
if (new_pending_send_bytes == NULL)
{
result = __LINE__;
}
else
{
socket_io_instance->pending_send_bytes = new_pending_send_bytes;
(void)memcpy(socket_io_instance->pending_send_bytes + socket_io_instance->pending_send_byte_count, buffer, size);
socket_io_instance->pending_send_byte_count += size;
result = 0;
}
}
}
else
{
size_t i;
for (i = 0; i < size; i++)
{
LOG(socket_io_instance->logger_log, 0, "%02x-> ", ((unsigned char*)buffer)[i]);
}
result = 0;
}
}
}
return result;
}
void socketio_dowork(IO_HANDLE socket_io)
{
if (socket_io != NULL)
{
SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io;
if (socket_io_instance->io_state == IO_STATE_OPEN)
{
int received = 1;
if (socket_io_instance->pending_send_byte_count > 0)
{
int send_result = send(socket_io_instance->socket, socket_io_instance->pending_send_bytes, socket_io_instance->pending_send_byte_count, 0);
if (send_result != socket_io_instance->pending_send_byte_count)
{
int last_error = WSAGetLastError();
if (last_error != WSAEWOULDBLOCK)
{
/* error */
}
else
{
/* simply wait */
}
}
else
{
free(socket_io_instance->pending_send_bytes);
socket_io_instance->pending_send_bytes = NULL;
socket_io_instance->pending_send_byte_count = 0;
}
}
while (received > 0)
{
unsigned char recv_bytes[1];
received = recv(socket_io_instance->socket, recv_bytes, sizeof(recv_bytes), 0);
if (received > 0)
{
int i;
for (i = 0; i < received; i++)
{
LOG(socket_io_instance->logger_log, 0, "<-%02x ", (unsigned char)recv_bytes[i]);
}
if (socket_io_instance->on_bytes_received != NULL)
{
/* explictly ignoring here the result of the callback */
(void)socket_io_instance->on_bytes_received(socket_io_instance->callback_context, recv_bytes, received);
}
}
}
}
}
}
const IO_INTERFACE_DESCRIPTION* socketio_get_interface_description(void)
{
return &socket_io_interface_description;
}

Просмотреть файл

@ -0,0 +1,114 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#if defined(__cplusplus)
#include <cstdint>
#else
#include <stdint.h>
#endif
#include <thr/threads.h>
#include "threadapi.h"
#include "windows.h"
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(THREADAPI_RESULT, THREADAPI_RESULT_VALUES);
THREADAPI_RESULT ThreadAPI_Create(THREAD_HANDLE* threadHandle, THREAD_START_FUNC func, void* arg)
{
THREADAPI_RESULT result;
int thrd_create_result = thrd_success;
if ((threadHandle == NULL) ||
(func == NULL))
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
thrd_t* thrd_t_ptr = (thrd_t*)malloc(sizeof(thrd_t));
if (thrd_t_ptr == NULL)
{
result = THREADAPI_NO_MEMORY;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
thrd_create_result = thrd_create(thrd_t_ptr, func, arg);
if (thrd_create_result != thrd_success)
{
free(thrd_t_ptr);
result = THREADAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
result = THREADAPI_OK;
*threadHandle = (THREAD_HANDLE)thrd_t_ptr;
}
}
}
return result;
}
THREADAPI_RESULT ThreadAPI_Join(THREAD_HANDLE threadHandle, int *res)
{
THREADAPI_RESULT result;
thrd_t* thrd_t_ptr = (thrd_t*)threadHandle;
if (threadHandle == NULL)
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
switch (thrd_join(*thrd_t_ptr, res))
{
default:
case thrd_error:
result = THREADAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
break;
case thrd_success:
result = THREADAPI_OK;
break;
case thrd_nomem:
result = THREADAPI_NO_MEMORY;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
break;
}
free(thrd_t_ptr);
}
return result;
}
void ThreadAPI_Exit(int res)
{
thrd_exit(res);
}
void ThreadAPI_Sleep(unsigned int milliseconds)
{
HANDLE handle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
if (handle != NULL)
{
/*
* Have to use at least 1 to cause a thread yield in case 0 is passed
*/
(void)WaitForSingleObjectEx(handle, milliseconds == 0 ? 1 : milliseconds, FALSE);
(void)CloseHandle(handle);
}
}

Просмотреть файл

@ -0,0 +1,136 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#define _DEFAULT_SOURCE
#include "threadapi.h"
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#ifdef TI_RTOS
#include <ti/sysbios/knl/Task.h>
#else
#include <unistd.h>
#endif
#include <pthread.h>
#include <time.h>
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(THREADAPI_RESULT, THREADAPI_RESULT_VALUES);
typedef struct THREAD_INSTANCE_TAG
{
pthread_t Pthread_handle;
THREAD_START_FUNC ThreadStartFunc;
void* Arg;
} THREAD_INSTANCE;
static void* ThreadWrapper(void* threadInstanceArg)
{
THREAD_INSTANCE* threadInstance = (THREAD_INSTANCE*)threadInstanceArg;
int result = threadInstance->ThreadStartFunc(threadInstance->Arg);
return (void*)(intptr_t)result;
}
THREADAPI_RESULT ThreadAPI_Create(THREAD_HANDLE* threadHandle, THREAD_START_FUNC func, void* arg)
{
THREADAPI_RESULT result;
if ((threadHandle == NULL) ||
(func == NULL))
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
THREAD_INSTANCE* threadInstance = malloc(sizeof(THREAD_INSTANCE));
if (threadInstance == NULL)
{
result = THREADAPI_NO_MEMORY;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
threadInstance->ThreadStartFunc = func;
threadInstance->Arg = arg;
int createResult = pthread_create(&threadInstance->Pthread_handle, NULL, ThreadWrapper, threadInstance);
switch (createResult)
{
default:
free(threadInstance);
result = THREADAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
break;
case 0:
*threadHandle = threadInstance;
result = THREADAPI_OK;
break;
case EAGAIN:
free(threadInstance);
result = THREADAPI_NO_MEMORY;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
break;
}
}
}
return result;
}
THREADAPI_RESULT ThreadAPI_Join(THREAD_HANDLE threadHandle, int* res)
{
THREADAPI_RESULT result;
THREAD_INSTANCE* threadInstance = (THREAD_INSTANCE*)threadHandle;
if (threadInstance == NULL)
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
void* threadResult;
if (pthread_join(threadInstance->Pthread_handle, &threadResult) != 0)
{
result = THREADAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
if (res != NULL)
{
*res = (int)(intptr_t)threadResult;
}
result = THREADAPI_OK;
}
free(threadInstance);
}
return result;
}
void ThreadAPI_Exit(int res)
{
pthread_exit((void*)(intptr_t)res);
}
void ThreadAPI_Sleep(unsigned int milliseconds)
{
#ifdef TI_RTOS
Task_sleep(milliseconds);
#else
time_t seconds = milliseconds / 1000;
long nsRemainder = (milliseconds % 1000) * 1000000;
struct timespec timeToSleep = { seconds, nsRemainder };
(void)nanosleep(&timeToSleep, NULL);
#endif
}

Просмотреть файл

@ -0,0 +1,145 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <cstdlib>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "stdlib.h"
#include "threadapi.h"
#include "rtos.h"
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(THREADAPI_RESULT, THREADAPI_RESULT_VALUES);
#define MAX_THREADS 4
#define STACK_SIZE 0x4000
typedef struct _thread
{
Thread* thrd;
osThreadId id;
Queue<int, 1> result;
} mbedThread;
static mbedThread threads[MAX_THREADS] = { 0 };
typedef struct _create_param
{
THREAD_START_FUNC func;
const void* arg;
mbedThread *p_thread;
} create_param;
static void thread_wrapper(const void* createParamArg)
{
const create_param* p = (const create_param*)createParamArg;
p->p_thread->id = Thread::gettid();
(*(p->func))((void*)p->arg);
free((void*)p);
}
THREADAPI_RESULT ThreadAPI_Create(THREAD_HANDLE* threadHandle, THREAD_START_FUNC func, void* arg)
{
THREADAPI_RESULT result;
if ((threadHandle == NULL) ||
(func == NULL))
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
size_t slot;
for (slot = 0; slot < MAX_THREADS; slot++)
{
if (threads[slot].id == NULL)
break;
}
if (slot < MAX_THREADS)
{
create_param* param = (create_param*)malloc(sizeof(create_param));
if (param != NULL)
{
param->func = func;
param->arg = arg;
param->p_thread = threads + slot;
threads[slot].thrd = new Thread(thread_wrapper, param, osPriorityNormal, STACK_SIZE);
*threadHandle = (THREAD_HANDLE)(threads + slot);
result = THREADAPI_OK;
}
else
{
result = THREADAPI_NO_MEMORY;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
}
else
{
result = THREADAPI_NO_MEMORY;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
}
return result;
}
THREADAPI_RESULT ThreadAPI_Join(THREAD_HANDLE thr, int *res)
{
THREADAPI_RESULT result = THREADAPI_OK;
mbedThread* p = (mbedThread*)thr;
if (p)
{
osEvent evt = p->result.get();
if (evt.status == osEventMessage) {
Thread* t = p->thrd;
if (res)
{
*res = (int)evt.value.p;
}
(void)t->terminate();
}
else
{
result = THREADAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
}
else
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
return result;
}
void ThreadAPI_Exit(int res)
{
mbedThread* p;
for (p = threads; p < &threads[MAX_THREADS]; p++)
{
if (p->id == Thread::gettid())
{
p->result.put((int*)res);
break;
}
}
}
void ThreadAPI_Sleep(unsigned int millisec)
{
//
// The timer on mbed seems to wrap around 65 seconds. Hmmm.
// So we will do our waits in increments of 30 seconds.
//
const int thirtySeconds = 30000;
int numberOfThirtySecondWaits = millisec / thirtySeconds;
int remainderOfThirtySeconds = millisec % thirtySeconds;
int i;
for (i = 1; i <= numberOfThirtySecondWaits; i++)
{
Thread::wait(thirtySeconds);
}
Thread::wait(remainderOfThirtySeconds);
}

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "threadapi.h"
#include "windows.h"
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(THREADAPI_RESULT, THREADAPI_RESULT_VALUES);
THREADAPI_RESULT ThreadAPI_Create(THREAD_HANDLE* threadHandle, THREAD_START_FUNC func, void* arg)
{
THREADAPI_RESULT result;
if ((threadHandle == NULL) ||
(func == NULL))
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
*threadHandle = CreateThread(NULL, 0, func, arg, 0, NULL);
if(threadHandle == NULL)
{
result = THREADAPI_ERROR;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
result = THREADAPI_OK;
}
}
return result;
}
THREADAPI_RESULT ThreadAPI_Join(THREAD_HANDLE threadHandle, int *res)
{
THREADAPI_RESULT result = THREADAPI_OK;
if (threadHandle == NULL)
{
result = THREADAPI_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(THREADAPI_RESULT, result));
}
else
{
DWORD returnCode = WaitForSingleObject(threadHandle, INFINITE);
if( returnCode != WAIT_OBJECT_0)
{
result = THREADAPI_ERROR;
LogError("Error waiting for Single Object. Return Code: %d. Error Code: %d\r\n", returnCode);
}
else if((res != NULL) && !GetExitCodeThread(threadHandle, res)) //If thread end is signaled we need to get the Thread Exit Code;
{
DWORD errorCode = GetLastError();
result = THREADAPI_ERROR;
LogError("Error Getting Exit Code. Error Code: %d.\r\n", errorCode);
}
CloseHandle(threadHandle);
}
return result;
}
void ThreadAPI_Exit(int res)
{
ExitThread(res);
}
void ThreadAPI_Sleep(unsigned int milliseconds)
{
Sleep(milliseconds);
}

Просмотреть файл

@ -0,0 +1,692 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#define SECURITY_WIN32
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdbool.h>
#include "tlsio.h"
#include "socketio.h"
#include "windows.h"
#include "sspi.h"
#include "schannel.h"
#include "iot_logging.h"
typedef enum TLS_STATE_TAG
{
TLS_STATE_HANDSHAKE_NOT_STARTED,
TLS_STATE_HANDSHAKE_CLIENT_HELLO_SENT,
TLS_STATE_HANDSHAKE_SERVER_HELLO_RECEIVED,
TLS_STATE_HANDSHAKE_DONE,
TLS_STATE_ERROR
} TLS_STATE;
typedef struct TLS_IO_INSTANCE_TAG
{
IO_HANDLE socket_io;
ON_BYTES_RECEIVED on_bytes_received;
ON_IO_STATE_CHANGED on_io_state_changed;
void* callback_context;
LOGGER_LOG logger_log;
CtxtHandle security_context;
TLS_STATE tls_state;
SEC_CHAR* host_name;
CredHandle credential_handle;
bool credential_handle_allocated;
unsigned char* received_bytes;
size_t received_byte_count;
size_t buffer_size;
size_t needed_bytes;
size_t consumed_bytes;
IO_STATE io_state;
} TLS_IO_INSTANCE;
static const IO_INTERFACE_DESCRIPTION tls_io_interface_description =
{
tlsio_create,
tlsio_destroy,
tlsio_open,
tlsio_close,
tlsio_send,
tlsio_dowork
};
static void set_io_state(TLS_IO_INSTANCE* tls_io_instance, IO_STATE io_state)
{
IO_STATE previous_state = tls_io_instance->io_state;
tls_io_instance->io_state = io_state;
if (tls_io_instance->on_io_state_changed != NULL)
{
tls_io_instance->on_io_state_changed(tls_io_instance->callback_context, io_state, previous_state);
}
}
static int resize_receive_buffer(TLS_IO_INSTANCE* tls_io_instance, size_t needed_buffer_size)
{
int result;
if (needed_buffer_size > tls_io_instance->buffer_size)
{
unsigned char* new_buffer = realloc(tls_io_instance->received_bytes, needed_buffer_size);
if (new_buffer == NULL)
{
result = __LINE__;
}
else
{
tls_io_instance->received_bytes = new_buffer;
tls_io_instance->buffer_size = needed_buffer_size;
result = 0;
}
}
else
{
result = 0;
}
return result;
}
static int set_receive_buffer(TLS_IO_INSTANCE* tls_io_instance, size_t buffer_size)
{
int result;
unsigned char* new_buffer = realloc(tls_io_instance->received_bytes, buffer_size);
if (new_buffer == NULL)
{
result = __LINE__;
}
else
{
tls_io_instance->received_bytes = new_buffer;
tls_io_instance->buffer_size = buffer_size;
result = 0;
}
return result;
}
static void tlsio_on_bytes_received(void* context, const void* buffer, size_t size)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->received_byte_count + size) == 0)
{
memcpy(tls_io_instance->received_bytes + tls_io_instance->received_byte_count, buffer, size);
tls_io_instance->received_byte_count += size;
if (size > tls_io_instance->needed_bytes)
{
tls_io_instance->needed_bytes = 0;
}
else
{
tls_io_instance->needed_bytes -= size;
}
switch (tls_io_instance->tls_state)
{
default:
break;
case TLS_STATE_ERROR:
break;
case TLS_STATE_HANDSHAKE_CLIENT_HELLO_SENT:
{
if (tls_io_instance->needed_bytes == 0)
{
SecBuffer input_buffers[2];
SecBuffer output_buffers[2];
ULONG context_attributes;
/* we need to try and perform the second (next) step of the init */
input_buffers[0].cbBuffer = tls_io_instance->received_byte_count;
input_buffers[0].BufferType = SECBUFFER_TOKEN;
input_buffers[0].pvBuffer = (void*)tls_io_instance->received_bytes;
input_buffers[1].cbBuffer = 0;
input_buffers[1].BufferType = SECBUFFER_EMPTY;
input_buffers[1].pvBuffer = 0;
SecBufferDesc input_buffers_desc;
input_buffers_desc.cBuffers = 2;
input_buffers_desc.pBuffers = input_buffers;
input_buffers_desc.ulVersion = SECBUFFER_VERSION;
output_buffers[0].cbBuffer = 0;
output_buffers[0].BufferType = SECBUFFER_TOKEN;
output_buffers[0].pvBuffer = NULL;
output_buffers[1].cbBuffer = 0;
output_buffers[1].BufferType = SECBUFFER_EMPTY;
output_buffers[1].pvBuffer = 0;
SecBufferDesc output_buffers_desc;
output_buffers_desc.cBuffers = 2;
output_buffers_desc.pBuffers = output_buffers;
output_buffers_desc.ulVersion = SECBUFFER_VERSION;
SECURITY_STATUS status = InitializeSecurityContext(&tls_io_instance->credential_handle,
&tls_io_instance->security_context, (SEC_CHAR*)tls_io_instance->host_name, ISC_REQ_EXTENDED_ERROR | ISC_REQ_STREAM | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &input_buffers_desc, 0,
&tls_io_instance->security_context, &output_buffers_desc,
&context_attributes, NULL);
switch (status)
{
case SEC_E_INCOMPLETE_MESSAGE:
if (input_buffers[1].BufferType != SECBUFFER_MISSING)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
tls_io_instance->needed_bytes = input_buffers[1].cbBuffer;
tls_io_instance->consumed_bytes += tls_io_instance->needed_bytes;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->received_byte_count + tls_io_instance->needed_bytes) != 0)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
}
break;
case SEC_E_OK:
memmove(tls_io_instance->received_bytes, tls_io_instance->received_bytes + tls_io_instance->consumed_bytes, tls_io_instance->received_byte_count - tls_io_instance->consumed_bytes);
tls_io_instance->received_byte_count -= tls_io_instance->consumed_bytes;
tls_io_instance->needed_bytes = 1;
tls_io_instance->consumed_bytes = tls_io_instance->needed_bytes;
if (set_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
tls_io_instance->tls_state = TLS_STATE_HANDSHAKE_DONE;
set_io_state(tls_io_instance, IO_STATE_OPEN);
}
break;
case SEC_I_COMPLETE_NEEDED:
case SEC_I_CONTINUE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
if ((output_buffers[0].cbBuffer > 0) && io_send(tls_io_instance->socket_io, output_buffers[0].pvBuffer, output_buffers[0].cbBuffer) != 0)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
memmove(tls_io_instance->received_bytes, tls_io_instance->received_bytes + tls_io_instance->consumed_bytes, tls_io_instance->received_byte_count - tls_io_instance->consumed_bytes);
tls_io_instance->received_byte_count -= tls_io_instance->consumed_bytes;
/* set the needed bytes to 1, to get on the next byte how many we actually need */
tls_io_instance->needed_bytes = 1;
tls_io_instance->consumed_bytes = tls_io_instance->needed_bytes;
if (set_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
FreeCredentialHandle(&tls_io_instance->credential_handle);
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
tls_io_instance->tls_state = TLS_STATE_HANDSHAKE_CLIENT_HELLO_SENT;
}
}
break;
}
}
break;
case TLS_STATE_HANDSHAKE_DONE:
{
if (tls_io_instance->needed_bytes == 0)
{
SecBuffer security_buffers[4];
SecBufferDesc security_buffers_desc;
security_buffers[0].BufferType = SECBUFFER_DATA;
security_buffers[0].pvBuffer = tls_io_instance->received_bytes;
security_buffers[0].cbBuffer = tls_io_instance->received_byte_count;
security_buffers[1].BufferType = SECBUFFER_EMPTY;
security_buffers[2].BufferType = SECBUFFER_EMPTY;
security_buffers[3].BufferType = SECBUFFER_EMPTY;
security_buffers_desc.cBuffers = sizeof(security_buffers) / sizeof(security_buffers[0]);
security_buffers_desc.pBuffers = security_buffers;
security_buffers_desc.ulVersion = SECBUFFER_VERSION;
SECURITY_STATUS status = DecryptMessage(&tls_io_instance->security_context, &security_buffers_desc, 0, NULL);
switch (status)
{
case SEC_E_INCOMPLETE_MESSAGE:
if (security_buffers[1].BufferType != SECBUFFER_MISSING)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
tls_io_instance->needed_bytes = security_buffers[1].cbBuffer;
tls_io_instance->consumed_bytes += tls_io_instance->needed_bytes;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->received_byte_count + tls_io_instance->needed_bytes) != 0)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
}
break;
case SEC_E_OK:
if (security_buffers[1].BufferType != SECBUFFER_DATA)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
size_t i;
for (i = 0; i < security_buffers[1].cbBuffer; i++)
{
LOG(tls_io_instance->logger_log, 0, "<-%02x ", ((unsigned char*)security_buffers[1].pvBuffer)[i]);
}
/* notify of the received data */
if (tls_io_instance->on_bytes_received != NULL)
{
tls_io_instance->on_bytes_received(tls_io_instance->callback_context, security_buffers[1].pvBuffer, security_buffers[1].cbBuffer);
}
memmove(tls_io_instance->received_bytes, tls_io_instance->received_bytes + tls_io_instance->consumed_bytes, tls_io_instance->received_byte_count - tls_io_instance->consumed_bytes);
tls_io_instance->received_byte_count -= tls_io_instance->consumed_bytes;
tls_io_instance->needed_bytes = 1;
tls_io_instance->consumed_bytes = tls_io_instance->needed_bytes;
if (set_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
tls_io_instance->tls_state = TLS_STATE_HANDSHAKE_DONE;
set_io_state(tls_io_instance, IO_STATE_OPEN);
}
}
break;
}
}
break;
}
}
}
}
}
static void tlsio_on_io_state_changed(void* context, IO_STATE new_io_state, IO_STATE previous_io_state)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
if (tls_io_instance->io_state == IO_STATE_OPENING)
{
switch (tls_io_instance->tls_state)
{
default:
break;
case TLS_STATE_HANDSHAKE_NOT_STARTED:
{
SecBuffer init_security_buffers[2];
ULONG context_attributes;
SECURITY_STATUS status;
SCHANNEL_CRED auth_data;
auth_data.dwVersion = SCHANNEL_CRED_VERSION;
auth_data.cCreds = 0;
auth_data.paCred = NULL;
auth_data.hRootStore = NULL;
auth_data.cSupportedAlgs = 0;
auth_data.palgSupportedAlgs = NULL;
auth_data.grbitEnabledProtocols = 0;
auth_data.dwMinimumCipherStrength = 0;
auth_data.dwMaximumCipherStrength = 0;
auth_data.dwSessionLifespan = 0;
auth_data.dwFlags = SCH_USE_STRONG_CRYPTO;
auth_data.dwCredFormat = 0;
status = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL,
&auth_data, NULL, NULL, &tls_io_instance->credential_handle, NULL);
if (status != SEC_E_OK)
{
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
init_security_buffers[0].cbBuffer = 0;
init_security_buffers[0].BufferType = SECBUFFER_TOKEN;
init_security_buffers[0].pvBuffer = NULL;
init_security_buffers[1].cbBuffer = 0;
init_security_buffers[1].BufferType = SECBUFFER_EMPTY;
init_security_buffers[1].pvBuffer = 0;
SecBufferDesc security_buffers_desc;
security_buffers_desc.cBuffers = 2;
security_buffers_desc.pBuffers = init_security_buffers;
security_buffers_desc.ulVersion = SECBUFFER_VERSION;
status = InitializeSecurityContext(&tls_io_instance->credential_handle,
NULL, (SEC_CHAR*)tls_io_instance->host_name, ISC_REQ_EXTENDED_ERROR | ISC_REQ_STREAM | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, NULL, 0,
&tls_io_instance->security_context, &security_buffers_desc,
&context_attributes, NULL);
if ((status == SEC_I_COMPLETE_NEEDED) || (status == SEC_I_CONTINUE_NEEDED) || (status == SEC_I_COMPLETE_AND_CONTINUE))
{
if (io_send(tls_io_instance->socket_io, init_security_buffers[0].pvBuffer, init_security_buffers[0].cbBuffer) != 0)
{
FreeCredentialHandle(&tls_io_instance->credential_handle);
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
/* set the needed bytes to 1, to get on the next byte how many we actually need */
tls_io_instance->needed_bytes = 1;
tls_io_instance->consumed_bytes = tls_io_instance->needed_bytes;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
FreeCredentialHandle(&tls_io_instance->credential_handle);
tls_io_instance->tls_state = TLS_STATE_ERROR;
}
else
{
tls_io_instance->tls_state = TLS_STATE_HANDSHAKE_CLIENT_HELLO_SENT;
}
}
}
}
break;
}
}
}
}
IO_HANDLE tlsio_create(void* io_create_parameters, LOGGER_LOG logger_log)
{
TLSIO_CONFIG* tls_io_config = io_create_parameters;
TLS_IO_INSTANCE* result;
if (tls_io_config == NULL)
{
result = NULL;
}
else
{
result = malloc(sizeof(TLS_IO_INSTANCE));
if (result != NULL)
{
SOCKETIO_CONFIG socketio_config;
socketio_config.hostname = tls_io_config->hostname;
socketio_config.port = tls_io_config->port;
result->on_bytes_received = NULL;
result->on_io_state_changed = NULL;
result->logger_log = logger_log;
result->callback_context = NULL;
result->host_name = (SEC_CHAR*)malloc(sizeof(SEC_CHAR) * (1 + strlen(tls_io_config->hostname)));
if (result->host_name == NULL)
{
free(result);
result = NULL;
}
else
{
(void)strcpy(result->host_name, tls_io_config->hostname);
const IO_INTERFACE_DESCRIPTION* socket_io_interface = socketio_get_interface_description();
if (socket_io_interface == NULL)
{
free(result->host_name);
free(result);
result = NULL;
}
else
{
result->socket_io = io_create(socket_io_interface, &socketio_config, logger_log);
if (result->socket_io == NULL)
{
free(result->host_name);
free(result);
result = NULL;
}
else
{
result->received_bytes = NULL;
result->received_byte_count = 0;
result->buffer_size = 0;
result->consumed_bytes = 0;
result->tls_state = TLS_STATE_HANDSHAKE_NOT_STARTED;
set_io_state(result, IO_STATE_NOT_OPEN);
}
}
}
}
}
return result;
}
void tlsio_destroy(IO_HANDLE tls_io)
{
if (tls_io != NULL)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (tls_io_instance->credential_handle_allocated)
{
(void)FreeCredentialHandle(&tls_io_instance->credential_handle);
}
if (tls_io_instance->received_bytes != NULL)
{
free(tls_io_instance->received_bytes);
}
io_destroy(tls_io_instance->socket_io);
free(tls_io_instance->host_name);
free(tls_io);
}
}
int tlsio_open(IO_HANDLE tls_io, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context)
{
int result;
if (tls_io == NULL)
{
result = __LINE__;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (tls_io_instance->io_state != IO_STATE_NOT_OPEN)
{
result = __LINE__;
}
else
{
tls_io_instance->on_bytes_received = on_bytes_received;
tls_io_instance->on_io_state_changed = on_io_state_changed;
tls_io_instance->callback_context = callback_context;
set_io_state(tls_io_instance, IO_STATE_OPENING);
if (io_open(tls_io_instance->socket_io, tlsio_on_bytes_received, tlsio_on_io_state_changed, tls_io_instance) != 0)
{
set_io_state(tls_io_instance, IO_STATE_ERROR);
result = __LINE__;
}
else
{
result = 0;
}
}
}
return result;
}
int tlsio_close(IO_HANDLE tls_io)
{
int result = 0;
if (tls_io == NULL)
{
result = __LINE__;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
(void)io_close(tls_io_instance->socket_io);
set_io_state(tls_io_instance, IO_STATE_NOT_OPEN);
}
return result;
}
int send_chunk(IO_HANDLE tls_io, const void* buffer, size_t size)
{
int result;
if ((tls_io == NULL) ||
(buffer == NULL) ||
(size == 0))
{
/* Invalid arguments */
result = __LINE__;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (tls_io_instance->io_state != IO_STATE_OPEN)
{
result = __LINE__;
}
else
{
SecPkgContext_StreamSizes sizes;
SECURITY_STATUS status = QueryContextAttributes(&tls_io_instance->security_context, SECPKG_ATTR_STREAM_SIZES, &sizes);
if (status != SEC_E_OK)
{
result = __LINE__;
}
else
{
SecBuffer security_buffers[4];
SecBufferDesc security_buffers_desc;
size_t needed_buffer = sizes.cbHeader + size + sizes.cbTrailer;
unsigned char* out_buffer = (unsigned char*)malloc(needed_buffer);
if (out_buffer == NULL)
{
result = __LINE__;
}
else
{
memcpy(out_buffer + sizes.cbHeader, buffer, size);
security_buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
security_buffers[0].cbBuffer = sizes.cbHeader;
security_buffers[0].pvBuffer = out_buffer;
security_buffers[1].BufferType = SECBUFFER_DATA;
security_buffers[1].cbBuffer = size;
security_buffers[1].pvBuffer = out_buffer + sizes.cbHeader;
security_buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
security_buffers[2].cbBuffer = sizes.cbTrailer;
security_buffers[2].pvBuffer = out_buffer + sizes.cbHeader + size;
security_buffers[3].cbBuffer = 0;
security_buffers[3].BufferType = SECBUFFER_EMPTY;
security_buffers[3].pvBuffer = 0;
security_buffers_desc.cBuffers = sizeof(security_buffers) / sizeof(security_buffers[0]);
security_buffers_desc.pBuffers = security_buffers;
security_buffers_desc.ulVersion = SECBUFFER_VERSION;
status = EncryptMessage(&tls_io_instance->security_context, 0, &security_buffers_desc, 0);
if (FAILED(status))
{
result = __LINE__;
}
else
{
if (io_send(tls_io_instance->socket_io, out_buffer, security_buffers[0].cbBuffer + security_buffers[1].cbBuffer + security_buffers[2].cbBuffer) != 0)
{
result = __LINE__;
}
else
{
size_t i;
for (i = 0; i < size; i++)
{
LOG(tls_io_instance->logger_log, 0, "%02x-> ", ((unsigned char*)buffer)[i]);
}
result = 0;
}
}
free(out_buffer);
}
}
}
}
return result;
}
int tlsio_send(IO_HANDLE tls_io, const void* buffer, size_t size)
{
int result;
while (size > 0)
{
size_t to_send = 16 * 1024;
if (to_send > size)
{
to_send = size;
}
if (send_chunk(tls_io, buffer, to_send) != 0)
{
break;
}
size -= to_send;
buffer = ((const unsigned char*)buffer) + to_send;
}
if (size > 0)
{
result = __LINE__;
}
else
{
result = 0;
}
return result;
}
void tlsio_dowork(IO_HANDLE tls_io)
{
if (tls_io != NULL)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
io_dowork(tls_io_instance->socket_io);
}
}
const IO_INTERFACE_DESCRIPTION* tlsio_get_interface_description(void)
{
return &tls_io_interface_description;
}

Просмотреть файл

@ -0,0 +1,227 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <memory.h>
#include "wolfssl/ssl.h"
#include "wolfssl_connection.h"
#include "iot_logging.h"
WolfSSLConnection::WolfSSLConnection()
{
wolfSSL_Init();
WOLFSSL_METHOD* method = wolfTLSv1_2_client_method();
if(method != NULL)
{
sslContext = wolfSSL_CTX_new(method);
}
else
{
sslContext = NULL;
}
isConnected = false;
}
WolfSSLConnection::~WolfSSLConnection()
{
if (sslContext != NULL)
{
wolfSSL_CTX_free(sslContext);
sslContext = NULL;
}
wolfSSL_Cleanup();
}
static int receiveCallback(WOLFSSL* ssl, char *buf, int sz, void *ctx)
{
int fd = *(int*)ctx;
int result;
(void)ssl;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if (lwip_select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0)
{
result = -1;
}
else
{
result = lwip_recv(fd, buf, sz, 0);
}
return result;
}
static int sendCallback(WOLFSSL* ssl, char *buf, int sz, void *ctx)
{
int fd = *(int*)ctx;
int result;
(void)ssl;
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
if (lwip_select(FD_SETSIZE, NULL, &wfds, NULL, NULL) < 0)
{
return -1;
}
else
{
result = lwip_send(fd, buf, sz, 0);
}
return result;
}
int WolfSSLConnection::connect(const char* host, const int port)
{
int result;
if(sslContext == NULL)
{
LogError("NULL SSL context\r\n");
result = __LINE__;
}
else
{
if (init_socket(SOCK_STREAM) < 0)
{
LogError("init_socket failed\r\n");
result = __LINE__;
}
else
{
if (set_address(host, port) != 0)
{
LogError("set_address failed\r\n");
result = __LINE__;
}
else if (lwip_connect(_sock_fd, (const struct sockaddr *) &_remoteHost, sizeof(_remoteHost)) < 0)
{
close();
LogError("lwip_connect failed\r\n");
result = __LINE__;
}
else
{
wolfSSL_SetIOSend(sslContext, &sendCallback);
wolfSSL_SetIORecv(sslContext, &receiveCallback);
ssl = wolfSSL_new(sslContext);
if(ssl == NULL)
{
LogError("wolfssl new error\r\n");
result = __LINE__;
}
else
{
wolfSSL_set_fd(ssl, _sock_fd);
result = wolfSSL_connect(ssl);
if (result != SSL_SUCCESS)
{
LogError("wolfssl connect error=%d\r\n", result);
result = __LINE__;
}
else
{
result = 0;
isConnected = true;
}
}
}
}
}
return result;
};
bool WolfSSLConnection::is_connected(void)
{
return isConnected;
}
int WolfSSLConnection::send(char* data, int length)
{
int result;
if (!isConnected)
{
result = 0;
}
else
{
result = wolfSSL_write(ssl, data, length);
}
return result;
}
int WolfSSLConnection::send_all(char* data, int length)
{
return send(data, length);
}
int WolfSSLConnection::receive(char* data, int length)
{
int result;
if (!isConnected)
{
result = 0;
}
else
{
result = wolfSSL_read(ssl, data, length);
}
return result;
}
int WolfSSLConnection::receive_all(char* data, int length)
{
return receive(data, length);
}
bool WolfSSLConnection::close(bool shutdown)
{
bool result;
if (!isConnected)
{
result = true;
}
else
{
isConnected = false;
wolfSSL_CTX_free(sslContext);
result = Socket::close(shutdown) == 0;
}
return result;
}
bool WolfSSLConnection::load_certificate(const unsigned char* certificate, size_t size)
{
bool result;
if (sslContext == NULL)
{
LogError("NULL SSL context\r\n");
result = false;
}
else
{
result = (wolfSSL_CTX_load_verify_buffer(sslContext,(unsigned char*)certificate, size, SSL_FILETYPE_PEM) == SSL_SUCCESS);
}
return result;
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef WOLFSSL_CONNECTION_H
#define WOLFSSL_CONNECTION_H
#include "wolfssl/ssl.h"
#include "TCPSocketConnection.h"
class WolfSSLConnection : public TCPSocketConnection
{
public:
WolfSSLConnection();
virtual ~WolfSSLConnection();
public:
int connect(const char* host, const int port);
bool is_connected(void);
int send(char* data, int length);
int send_all(char* data, int length);
int receive(char* data, int length);
int receive_all(char* data, int length);
bool close(bool shutdown = true);
bool load_certificate(const unsigned char* certificate, size_t size);
protected:
WOLFSSL_CTX* sslContext;
WOLFSSL* ssl;
bool isConnected;
};
#endif /* WOLFSSL_CONNECTION_H */

Просмотреть файл

@ -0,0 +1,8 @@
..\inc\*.h
..\src\*.c
..\adapters\agenttime_mbed.c
..\adapters\httpapi_mbed.cpp
..\adapters\lock_rtx_mbed.cpp
..\adapters\threadapi_rtx_mbed.cpp
..\adapters\wolfssl_connection.h
..\adapters\wolfssl_connection.cpp

Просмотреть файл

@ -0,0 +1,137 @@
/*
* ======== package.bld ========
*/
var Build = xdc.useModule('xdc.bld.BuildEnvironment');
var Pkg = xdc.useModule('xdc.bld.PackageContents');
/* make command to search for the srcs */
Pkg.makePrologue = "vpath %.c ../../src ../../adapters";
/* lib/ is a generated directory that 'xdc clean' should remove */
Pkg.generatedFiles.$add("lib/");
var SRCS = [
"httpapi_tirtos.c",
"threadapi_pthreads.c",
"lock_pthreads.c",
"agenttime.c",
"base64.c",
"buffer.c",
"certs.c",
"crt_abstractions.c",
"doublylinkedlist.c",
"gballoc.c",
"hmac.c",
"hmacsha256.c",
"httpapiex.c",
"httpapiexsas.c",
"httpheaders.c",
"map.c",
"sastoken.c",
"sha1.c",
"sha224.c",
"sha384-512.c",
"strings.c",
"string_tokenizer.c",
"urlencode.c",
"usha.c",
"vector.c"
];
/* Paths to external source libraries */
xdcargs = java.lang.System.getenv("XDCARGS").split(" ");
var slIncs = "";
var ndkIncs = "";
var wolfsslIncs = "";
var commonIncs = "-I../../inc ";
var pthreadIncs = "";
/* Parse out the XDCARGS options for the library source paths */
for (x = 0; x < xdcargs.length; x++) {
if (xdcargs[x].match(/^CC3200SDK=/)) {
slIncs = (xdcargs[x] + "").replace(/'/g, '');
slIncs = slIncs.substring(slIncs.indexOf('=') + 1);
if (slIncs != "") {
slIncs += "/simplelink";
slIncs = "-I" + slIncs + " -I" + slIncs + "/include ";
}
}
if (xdcargs[x].match(/^NDK=/)) {
ndkIncs = (xdcargs[x] + "").replace(/'/g, '');
ndkIncs = ndkIncs.substring(ndkIncs.indexOf('=') + 1);
if (ndkIncs != "") {
ndkIncs = "-I" + ndkIncs + "/packages/ti/ndk/inc/bsd ";
}
}
if (xdcargs[x].match(/^BIOS=/)) {
pthreadIncs = (xdcargs[x] + "").replace(/'/g, '');
pthreadIncs = pthreadIncs.substring(pthreadIncs.indexOf('=') + 1);
if (pthreadIncs != "") {
pthreadIncs = "-I" + pthreadIncs + "/packages/ti/sysbios/posix ";
}
}
if (xdcargs[x].match(/^WOLFSSL=/)) {
wolfsslIncs = (xdcargs[x] + "").replace(/'/g, '');
wolfsslIncs = wolfsslIncs.substring(wolfsslIncs.indexOf('=') + 1);
if (wolfsslIncs != "") {
wolfsslIncs = "-I" + wolfsslIncs;
}
}
}
if (ndkIncs != "") {
if (wolfsslIncs == "") {
print("Warning: Cannot find WolfSSL directory: '" + wolfsslPath
+ "'. Skipping TLS support build.");
wolfsslIncs = "";
}
var wolfsslPath = wolfsslIncs.substring(2);
try {
var f = new java.io.File(wolfsslPath);
if (!f.exists()) {
print("Warning: Cannot find WolfSSL directory: '" + wolfsslPath
+ "'. Skipping TLS support build.");
wolfsslIncs = "";
}
}
catch(e) {
print("Warning: Cannot find WolfSSL directory: '" + wolfsslPath
+ "'. Skipping TLS support build.");
wolfsslIncs = "";
}
}
for (var i = 0; i < Build.targets.length; i++) {
var profile = "release";
var target = Build.targets[i];
var extraOpts = commonIncs + pthreadIncs;
if (slIncs != "") {
Pkg.addLibrary("lib/common_sl", target, { profile: profile,
defs: "-DNET_SL " + extraOpts, incs: slIncs
}).addObjects(SRCS);
}
if (ndkIncs != "") {
if (wolfsslIncs != "") {
Pkg.addLibrary("lib/common_ndk_wolfssl", target, {
profile: profile,
defs: "-DNET_NDK -DNET_WOLFSSL -DWOLFSSL_TIRTOS "
+ extraOpts,
incs: ndkIncs + wolfsslIncs
}).addObjects(SRCS)
}
Pkg.addLibrary("lib/common_ndk", target, { profile: profile,
defs: "-DNET_NDK " + extraOpts, incs: ndkIncs
}).addObjects(SRCS);
}
}

Просмотреть файл

@ -0,0 +1,5 @@
/*
* ======== package.xdc ========
*/
package common.build.tirtos {
}

Двоичные данные
c/sharedutil/devdoc/agenttime_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/base64_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/buffer_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/crt_abstractions_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/doublylinkedlist_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/gballoc_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/httpapiex_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/httpapiex_retry_mechanism.vsdx Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/httpapiexsas.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/httpheaders requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/io_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/list_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/lock_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/map_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/sastoken.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/string_tokenizer_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/strings_requirements.docm Normal file

Двоичный файл не отображается.

Двоичные данные
c/sharedutil/devdoc/url_encode_requirements.docm Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file agenttime.h
* @brief Function prototypes for time related functions.
*
* @details These functions are implemented with C standard functions,
* and therefore they are platform independent. But then a platform
* can replace these functions with its own implementation as necessary.
*/
#ifndef AGENTTIME_H
#define AGENTTIME_H
#include <time.h>
#ifdef __cplusplus
extern "C"
{
#endif
/** @brief Get current calendar time.
*
* @details This function provides the same functionality as the
* standard C @c time() function.
*/
extern time_t get_time(time_t* currentTime);
/** @brief Get UTC in @c tm struct.
*
* @details This function provides the same functionality as the
* standard C @c gmtime() function.
*/
extern struct tm* get_gmtime(time_t* currentTime);
/** @brief Gets a C-string representation of the given time.
*
* @details This function provides the same functionality as the
* standard C @c ctime() function.
*/
extern char* get_ctime(time_t* timeToGet);
/** @brief Gets the difference in seconds between @c stopTime and
* @c startTime.
*
* @details This function provides the same functionality as the
* standard C @c difftime() function.
*/
extern double get_difftime(time_t stopTime, time_t startTime);
#ifdef __cplusplus
}
#endif
#endif // AGENTTIME_H

80
c/sharedutil/inc/base64.h Normal file
Просмотреть файл

@ -0,0 +1,80 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file base64.h
* @brief Prototypes for functions related to encoding/decoding
* a @c buffer using standard base64 encoding.
*/
#ifndef BASE64_H
#define BASE64_H
#include "strings.h"
#include "buffer_.h"
#ifdef __cplusplus
#include <cstddef>
extern "C" {
#else
#include <stddef.h>
#endif
/**
* @brief Base64 encodes a buffer and returns the resulting string.
*
* @param input The buffer that needs to be base64 encoded.
*
* Base64_Encode takes as a parameter a pointer to a BUFFER. If @p input is @c NULL then
* @c Base64_Encode returns @c NULL. The size of the BUFFER pointed to by @p input may
* be zero. If when allocating memory to produce the encoding a failure occurs, then @c
* Base64_Encode returns @c NULL. Otherwise
* @c Base64_Encode returns a pointer to a STRING. That string contains the
* base 64 encoding of the @p input. This encoding of @p input will not contain embedded
* line feeds.
*
* @return A @c STRING_HANDLE containing the base64 encoding of @p input.
*/
extern STRING_HANDLE Base64_Encode(BUFFER_HANDLE input);
/**
* @brief Base64 encodes the buffer pointed to by @p source and returns the resulting string.
*
* @param source The buffer that needs to be base64 encoded.
* @param size The size.
*
* This function produces a @c STRING_HANDLE containing the base64 encoding of the
* buffer pointed to by @p source, having the size as given by
* @p size. If @p source is @c NULL then @c Base64_Encode_Bytes returns @c NULL
* If @p source is not @c NULL and @p size is zero, then @c Base64_Encode_Bytes produces
* an empty @c STRING_HANDLE. Otherwise, @c Base64_Encode_Bytes produces a
* @c STRING_HANDLE containing the Base64 representation of the buffer. In case of
* any errors, @c Base64_Encode_Bytes returns @c NULL.].
*
* @return @c NULL in case an error occurs or a @c STRING_HANDLE containing the base64 encoding
* of @p input.
*
*/
extern STRING_HANDLE Base64_Encode_Bytes(const unsigned char* source, size_t size);
/**
* @brief Base64 decodes the buffer pointed to by @p source and returns the resulting buffer.
*
* @param source A base64 encoded string buffer.
*
* This function decodes the string pointed at by @p source using base64 decoding and
* returns the resulting buffer. If @p source is @c NULL then
* @c Base64_Decoder returns NULL. If the string pointed to by @p source is zero
* length then the handle returned refers to a zero length buffer. If there is any
* memory allocation failure during the decode or if the source string has an invalid
* length for a base 64 encoded string then @c Base64_Decoder returns @c NULL.
*
* @return A @c BUFFER_HANDLE pointing to a buffer containing the result of base64 decoding @p
* source.
*/
extern BUFFER_HANDLE Base64_Decoder(const char* source);
#ifdef __cplusplus
}
#endif
#endif /* BASE64_H */

Просмотреть файл

@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef BUFFER_H
#define BUFFER_H
#ifdef __cplusplus
#include <cstddef>
extern "C"
{
#else
#include <stddef.h>
#endif
typedef void* BUFFER_HANDLE;
extern BUFFER_HANDLE BUFFER_new(void);
extern BUFFER_HANDLE BUFFER_create(const unsigned char* source, size_t size);
extern void BUFFER_delete(BUFFER_HANDLE handle);
extern int BUFFER_pre_build(BUFFER_HANDLE handle, size_t size);
extern int BUFFER_build(BUFFER_HANDLE handle, const unsigned char* source, size_t size);
extern int BUFFER_unbuild(BUFFER_HANDLE handle);
extern int BUFFER_enlarge(BUFFER_HANDLE handle, size_t enlargeSize);
extern int BUFFER_content(BUFFER_HANDLE handle, const unsigned char** content);
extern int BUFFER_size(BUFFER_HANDLE handle, size_t* size);
extern int BUFFER_append(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2);
extern unsigned char* BUFFER_u_char(BUFFER_HANDLE handle);
extern size_t BUFFER_length(BUFFER_HANDLE handle);
extern BUFFER_HANDLE BUFFER_clone(BUFFER_HANDLE handle);
#ifdef __cplusplus
}
#endif
#endif /* BUFFER_H */

Просмотреть файл

@ -0,0 +1,69 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef CONDITION_H
#define CONDITION_H
#include "macro_utils.h"
#include "lock.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void* COND_HANDLE;
#define COND_RESULT_VALUES \
COND_OK, \
COND_ERROR, \
COND_TIMEOUT \
/** @brief Enumeration specifying the lock status.
*/
DEFINE_ENUM(COND_RESULT, COND_RESULT_VALUES);
/**
* @brief This API creates and returns a valid condition handle.
*
* @return A valid @c COND_HANDLE when successful or @c NULL otherwise.
*/
extern COND_HANDLE Condition_Init(void);
/**
* @brief unblock all currently working condition.
*
* @param handle A valid handle to the lock.
*
* @return Returns @c COND_OK when the condition object has been
* destroyed and @c COND_ERROR when an error occurs
* and @c COND_TIMEOUT when the handle times out.
*/
extern COND_RESULT Condition_Post(COND_HANDLE handle);
/**
* @brief block on the condition handle unti the thread is signalled
* or until the timeout_milliseconds is reached.
*
* @param handle A valid handle to the lock.
*
* @return Returns @c COND_OK when the condition object has been
* destroyed and @c COND_ERROR when an error occurs
* and @c COND_TIMEOUT when the handle times out.
*/
extern COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds);
/**
* @brief The condition instance is deinitialized.
*
* @param handle A valid handle to the condition.
*
* @return Returns @c COND_OK when the condition object has been
* destroyed and @c COND_ERROR when an error occurs.
*/
extern COND_RESULT Condition_Deinit(COND_HANDLE handle);
#ifdef __cplusplus
}
#endif
#endif /* LOCK_H */

Просмотреть файл

@ -0,0 +1,176 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef CRT_ABSTRACTIONS_H
#define CRT_ABSTRACTIONS_H
#ifdef __cplusplus
#include <cstdio>
#include <cstring>
extern "C" {
#else
#include <stdio.h>
#include <string.h>
#endif
#ifdef _MSC_VER
#ifdef QUARKGALILEO
#define HAS_STDBOOL
#ifdef __cplusplus
typedef bool _Bool;
#else
/*galileo apparently has _Bool and bool as built in types*/
#endif
#endif
#ifndef _WIN32_WCE
#define HAS_STDBOOL
#ifdef __cplusplus
#include <cstdbool>
/*because C++ doesn't do anything about _Bool... */
#define _Bool bool
#else
#include <stdbool.h>
#endif
#endif
#else
#if defined __STDC_VERSION__
#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L))
/*C99 compiler or C11*/
#define HAS_STDBOOL
#include <stdbool.h>
#endif
#endif
#endif
#ifndef HAS_STDBOOL
#ifdef __cplusplus
#define _Bool bool
#else
typedef unsigned char _Bool;
typedef unsigned char bool;
#define false 0
#define true 1
#endif
#endif
/* Codes_SRS_CRT_ABSTRACTIONS_99_001:[The module shall not redefine the secure functions implemented by Microsoft CRT.] */
/* Codes_SRS_CRT_ABSTRACTIONS_99_040 : [The module shall still compile when building on a Microsoft platform.] */
/* Codes_SRS_CRT_ABSTRACTIONS_99_002: [CRTAbstractions module shall expose the following API]*/
#ifdef _MSC_VER
#else
#include "inttypes.h"
/* Adding definitions from errno.h & crtdefs.h */
#if !defined (_TRUNCATE)
#define _TRUNCATE ((size_t)-1)
#endif /* !defined (_TRUNCATE) */
#if !defined STRUNCATE
#define STRUNCATE 80
#endif /* !defined (STRUNCATE) */
typedef int errno_t;
extern int strcpy_s(char* dst, size_t dstSizeInBytes, const char* src);
extern int strcat_s(char* dst, size_t dstSizeInBytes, const char* src);
extern int strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t maxCount);
extern int sprintf_s(char* dst, size_t dstSizeInBytes, const char* format, ...);
#endif
extern int mallocAndStrcpy_s(char** destination, const char*source);
extern int unsignedIntToString(char* destination, size_t destinationSize, unsigned int value);
extern int size_tToString(char* destination, size_t destinationSize, size_t value);
/*following logic shall define the ISNAN macro*/
/*if runing on Microsoft Visual C compiler, than ISNAN shall be _isnan*/
/*else if running on C99 or C11, ISNAN shall be isnan*/
/*else if running on C89 ... #error and inform user*/
#ifdef _MSC_VER
#define ISNAN _isnan
#else
#if defined __STDC_VERSION__
#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L))
/*C99 compiler or C11*/
#define ISNAN isnan
#else
#error update this file to contain the latest C standard.
#endif
#else
#ifdef __cplusplus
/*C++ defines isnan... in C11*/
#define ISNAN std::isnan
#else
#error unknown (or C89) compiler, provide ISNAN with the same meaning as isnan in C99 standard
#endif
#endif
#endif
/*ispositiveinfinity*/
#ifdef _MSC_VER
#define ISPOSITIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_PINF) == _FPCLASS_PINF))
#else
#if defined __STDC_VERSION__
#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L))
/*C99 compiler or C11*/
#define ISPOSITIVEINFINITY(x) (isinf((x)) && (signbit((x))==0))
#else
#error update this file to contain the latest C standard.
#endif
#else
#ifdef __cplusplus
#define ISPOSITIVEINFINITY(x) (std::isinf((x)) && (signbit((x))==0))
#else
#error unknown (or C89) compiler, must provide a definition for ISPOSITIVEINFINITY
#endif
#endif
#endif
#ifdef _MSC_VER
/*not exactly signbit*/
#define ISNEGATIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_NINF) == _FPCLASS_NINF))
#else
#if defined __STDC_VERSION__
#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L))
/*C99 compiler or C11*/
#define ISNEGATIVEINFINITY(x) (isinf((x)) && (signbit((x))!=0))
#else
#error update this file to contain the latest C standard.
#endif
#else
#ifdef __cplusplus
#define ISNEGATIVEINFINITY(x) (std::isinf((x)) && (signbit((x)) != 0))
#else
#error unknown (or C89) compiler, must provide a definition for ISNEGATIVEINFINITY
#endif
#endif
#endif
#ifdef _MSC_VER
#define INT64_PRINTF "%I64d"
#else
#if defined __STDC_VERSION__
#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L))
/*C99 compiler or C11*/
#define INT64_PRINTF "%" PRId64 ""
#else
#error update this file to contain the latest C standard.
#endif
#else
#ifdef __cplusplus
#define INT64_PRINTF "%" PRId64 ""
#else
#error unknown (or C89) compiler, provide INT64_PRINTF with the same meaning as PRIdN in C99 standard
#endif
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* CRT_ABSTRACTIONS_H */

Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef DOUBLYLINKEDLIST_H
#define DOUBLYLINKEDLIST_H
#ifdef __cplusplus
#include <cstddef>
extern "C"
{
#else
#include <stddef.h>
#endif
typedef struct DLIST_ENTRY_TAG
{
struct DLIST_ENTRY_TAG *Flink;
struct DLIST_ENTRY_TAG *Blink;
} DLIST_ENTRY, *PDLIST_ENTRY;
extern void DList_InitializeListHead(PDLIST_ENTRY listHead);
extern int DList_IsListEmpty(const PDLIST_ENTRY listHead);
extern void DList_InsertTailList(PDLIST_ENTRY listHead, PDLIST_ENTRY listEntry);
extern void DList_InsertHeadList(PDLIST_ENTRY listHead, PDLIST_ENTRY listEntry);
extern void DList_AppendTailList(PDLIST_ENTRY listHead, PDLIST_ENTRY ListToAppend);
extern int DList_RemoveEntryList(PDLIST_ENTRY listEntry);
extern PDLIST_ENTRY DList_RemoveHeadList(PDLIST_ENTRY listHead);
//
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//
#define containingRecord(address, type, field) ((type *)((uintptr_t)(address) - offsetof(type,field)))
#ifdef __cplusplus
}
#else
#endif
#endif /* DOUBLYLINKEDLIST_H */

Просмотреть файл

@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef GBALLOC_H
#define GBALLOC_H
#ifdef __cplusplus
#include <cstdlib>
extern "C"
{
#else
#include <stdlib.h>
#endif
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
/* all translation units that need memory measurement need to have GB_MEASURE_MEMORY_FOR_THIS defined */
/* GB_DEBUG_ALLOC is the switch that turns the measurement on/off, so that it is not on always */
#if defined(GB_DEBUG_ALLOC)
extern int gballoc_init(void);
extern void gballoc_deinit(void);
extern void* gballoc_malloc(size_t size);
extern void* gballoc_calloc(size_t nmemb, size_t size);
extern void* gballoc_realloc(void* ptr, size_t size);
extern void gballoc_free(void* ptr);
extern size_t gballoc_getMaximumMemoryUsed(void);
extern size_t gballoc_getCurrentMemoryUsed(void);
/* if GB_MEASURE_MEMORY_FOR_THIS is defined then we want to redirect memory allocation functions to gballoc_xxx functions */
#ifdef GB_MEASURE_MEMORY_FOR_THIS
#if defined(_CRTDBG_MAP_ALLOC) && defined(_DEBUG)
#undef _malloc_dbg
#undef _calloc_dbg
#undef _realloc_dbg
#undef _free_dbg
#define _malloc_dbg(size, ...) gballoc_malloc(size)
#define _calloc_dbg(nmemb, size, ...) gballoc_calloc(nmemb, size)
#define _realloc_dbg(ptr, size, ...) gballoc_realloc(ptr, size)
#define _free_dbg(ptr, ...) gballoc_free(ptr)
#else
#define malloc gballoc_malloc
#define calloc gballoc_calloc
#define realloc gballoc_realloc
#define free gballoc_free
#endif
#endif
#else /* GB_DEBUG_ALLOC */
#define gballoc_init() 0
#define gballoc_deinit() ((void)0)
#define gballoc_getMaximumMemoryUsed() SIZE_MAX
#define gballoc_getCurrentMemoryUsed() SIZE_MAX
#endif /* GB_DEBUG_ALLOC */
#ifdef __cplusplus
}
#endif
#endif /* GBALLOC_H */

21
c/sharedutil/inc/hmac.h Normal file
Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef HMAC_H
#define HMAC_H
#ifdef __cplusplus
extern "C" {
#endif
#include "sha.h"
extern int hmac(SHAversion whichSha, const unsigned char *text, int text_len,
const unsigned char *key, int key_len,
uint8_t digest[USHAMaxHashSize]);
#ifdef __cplusplus
}
#endif
#endif /* HMAC_H */

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef HMACSHA256_H
#define HMACSHA256_H
#include "macro_utils.h"
#include "buffer_.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HMACSHA256_RESULT_VALUES \
HMACSHA256_OK, \
HMACSHA256_INVALID_ARG, \
HMACSHA256_ERROR
DEFINE_ENUM(HMACSHA256_RESULT, HMACSHA256_RESULT_VALUES)
extern HMACSHA256_RESULT HMACSHA256_ComputeHash(const unsigned char* key, size_t keyLen, const unsigned char* payload, size_t payloadLen, BUFFER_HANDLE hash);
#ifdef __cplusplus
}
#endif
#endif /* HMACSHA256_H */

194
c/sharedutil/inc/httpapi.h Normal file
Просмотреть файл

@ -0,0 +1,194 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file httpapi.h
* @brief This module implements the standard HTTP API used by the C IoT client
* library.
*
* @details For example, on the Windows platform the HTTP API code uses
* WinHTTP and for Linux it uses curl and so forth. HTTPAPI must support
* HTTPs (HTTP+SSL).
*/
#ifndef HTTPAPI_H
#define HTTPAPI_H
#include "httpheaders.h"
#include "macro_utils.h"
#include "buffer_.h"
#ifdef __cplusplus
#include <cstddef>
extern "C" {
#else
#include <stddef.h>
#endif
typedef void* HTTP_HANDLE;
#define AMBIGUOUS_STATUS_CODE (300)
#define HTTPAPI_RESULT_VALUES \
HTTPAPI_OK, \
HTTPAPI_INVALID_ARG, \
HTTPAPI_ERROR, \
HTTPAPI_OPEN_REQUEST_FAILED, \
HTTPAPI_SET_OPTION_FAILED, \
HTTPAPI_SEND_REQUEST_FAILED, \
HTTPAPI_RECEIVE_RESPONSE_FAILED, \
HTTPAPI_QUERY_HEADERS_FAILED, \
HTTPAPI_QUERY_DATA_AVAILABLE_FAILED, \
HTTPAPI_READ_DATA_FAILED, \
HTTPAPI_ALREADY_INIT, \
HTTPAPI_NOT_INIT, \
HTTPAPI_HTTP_HEADERS_FAILED, \
HTTPAPI_STRING_PROCESSING_ERROR, \
HTTPAPI_ALLOC_FAILED, \
HTTPAPI_INIT_FAILED, \
HTTPAPI_INSUFFICIENT_RESPONSE_BUFFER, \
HTTPAPI_SET_TIMEOUTS_FAILED \
/** @brief Enumeration specifying the possible return values for the APIs in
* this module.
*/
DEFINE_ENUM(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES);
#define HTTPAPI_REQUEST_TYPE_VALUES\
HTTPAPI_REQUEST_GET, \
HTTPAPI_REQUEST_POST, \
HTTPAPI_REQUEST_PUT, \
HTTPAPI_REQUEST_DELETE, \
HTTPAPI_REQUEST_PATCH \
/** @brief Enumeration specifying the HTTP request verbs accepted by
* the HTTPAPI module.
*/
DEFINE_ENUM(HTTPAPI_REQUEST_TYPE, HTTPAPI_REQUEST_TYPE_VALUES);
/**
* @brief Global initialization for the HTTP API component.
*
* Platform specific implementations are expected to initialize
* the underlying HTTP API stacks.
*
* @return @c HTTPAPI_OK if initialization is successful or an error
* code in case it fails.
*/
extern HTTPAPI_RESULT HTTPAPI_Init(void);
/** @brief Free resources allocated in ::HTTPAPI_Init. */
extern void HTTPAPI_Deinit(void);
/**
* @brief Creates an HTTPS connection to the host specified by the @p
* hostName parameter.
*
* @param hostName Name of the host.
*
* This function returns a handle to the newly created connection.
* You can use the handle in subsequent calls to execute specific
* HTTP calls using ::HTTPAPI_ExecuteRequest.
*
* @return A @c HTTP_HANDLE to the newly created connection or @c NULL in
* case an error occurs.
*/
extern HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName);
/**
* @brief Closes a connection created with ::HTTPAPI_CreateConnection.
*
* @param handle The handle to the HTTP connection created via ::HTTPAPI_CreateConnection.
*
* All resources allocated by ::HTTPAPI_CreateConnection should be
* freed in ::HTTPAPI_CloseConnection.
*/
extern void HTTPAPI_CloseConnection(HTTP_HANDLE handle);
/**
* @brief Sends the HTTP request to the host and handles the response for
* the HTTP call.
*
* @param handle The handle to the HTTP connection created
* via ::HTTPAPI_CreateConnection.
* @param requestType Specifies which HTTP method is used (GET,
* POST, DELETE, PUT, PATCH).
* @param relativePath Specifies the relative path of the URL
* excluding the host name.
* @param httpHeadersHandle Specifies a set of HTTP headers (name-value
* pairs) to be added to the
* HTTP request. The @p httpHeadersHandle
* handle can be created and setup with
* the proper name-value pairs by using the
* HTTPHeaders APIs available in @c
* HTTPHeaders.h.
* @param content Specifies a pointer to the request body.
* This value is optional and can be @c NULL.
* @param contentLength Specifies the request body size (this is
* typically added into the HTTP headers as
* the Content-Length header). This value is
* optional and can be 0.
* @param statusCode This is an out parameter, where
* ::HTTPAPI_ExecuteRequest returns the status
* code from the HTTP response (200, 201, 400,
* 401, etc.)
* @param responseHeadersHandle This is an HTTP headers handle to which
* ::HTTPAPI_ExecuteRequest must add all the
* HTTP response headers so that the caller of
* ::HTTPAPI_ExecuteRequest can inspect them.
* You can manipulate @p responseHeadersHandle
* by using the HTTPHeaders APIs available in
* @c HTTPHeaders.h
* @param responseContent This is a buffer that must be filled by
* ::HTTPAPI_ExecuteRequest with the contents
* of the HTTP response body. The buffer size
* must be increased by the
* ::HTTPAPI_ExecuteRequest implementation in
* order to fit the response body.
* ::HTTPAPI_ExecuteRequest must also handle
* chunked transfer encoding for HTTP responses.
* To manipulate the @p responseContent buffer,
* use the APIs available in @c Strings.h.
*
* @return @c HTTPAPI_OK if the API call is successful or an error
* code in case it fails.
*/
extern HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
size_t contentLength, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent);
/**
* @brief Sets the option named @p optionName bearing the value
* @p value for the HTTP_HANDLE @p handle.
*
* @param handle The handle to the HTTP connection created via
* ::HTTPAPI_CreateConnection.
* @param optionName A @c NULL terminated string representing the name
* of the option.
* @param value A pointer to the value for the option.
*
* @return @c HTTPAPI_OK if initialization is successful or an error
* code in case it fails.
*/
extern HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value);
/**
* @brief Clones the option named @p optionName bearing the value @p value
* into the pointer @p savedValue.
*
* @param optionName A @c NULL terminated string representing the name of
* the option
* @param value A pointer to the value of the option.
* @param savedValue This pointer receives the copy of the value of the
* option. The copy needs to be free-able.
*
* @return @c HTTPAPI_OK if initialization is successful or an error
* code in case it fails.
*/
extern HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue);
#ifdef __cplusplus
}
#endif
#endif /* HTTPAPI_H */

Просмотреть файл

@ -0,0 +1,105 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file httpapiex.h
* @brief This is a utility module that provides HTTP requests with
* build-in retry capabilities.
*
* @details HTTAPIEX is a utility module that provides HTTP requests with build-in
* retry capability to an HTTP server. Features over "regular" HTTPAPI include:
* - Optional parameters
* - Implementation independent
* - Retry mechanism
* - Persistent options
*/
#ifndef HTTPAPIEX_H
#define HTTPAPIEX_H
#include "macro_utils.h"
#include "httpapi.h"
#ifdef __cplusplus
#include <cstddef>
extern "C" {
#else
#include <stddef.h>
#endif
typedef void* HTTPAPIEX_HANDLE;
#define HTTPAPIEX_RESULT_VALUES \
HTTPAPIEX_OK, \
HTTPAPIEX_ERROR, \
HTTPAPIEX_INVALID_ARG, \
HTTPAPIEX_RECOVERYFAILED
/*to be continued*/
/** @brief Enumeration specifying the status of calls to various APIs in this module.
*/
DEFINE_ENUM(HTTPAPIEX_RESULT, HTTPAPIEX_RESULT_VALUES);
/**
* @brief Creates an @c HTTPAPIEX_HANDLE that can be used in further calls.
*
* @param hostName Pointer to a null-terminated string that contains the host name
* of an HTTP server.
*
* If @p hostName is @c NULL then @c HTTPAPIEX_Create returns @c NULL. The @p
* hostName value is saved and associated with the returned handle. If creating
* the handle fails for any reason, then @c HTTAPIEX_Create returns @c NULL.
* Otherwise, @c HTTPAPIEX_Create returns an @c HTTAPIEX_HANDLE suitable for
* further calls to the module.
*
* @return An @c HTTAPIEX_HANDLE suitable for further calls to the module.
*/
extern HTTPAPIEX_HANDLE HTTPAPIEX_Create(const char* hostName);
/**
* @brief Tries to execute an HTTP request.
*
* @param handle A valid @c HTTPAPIEX_HANDLE value.
* @param requestType A value from the ::HTTPAPI_REQUEST_TYPE enum.
* @param relativePath Relative path to send the request to on the server.
* @param requestHttpHeadersHandle Handle to the request HTTP headers.
* @param requestContent The request content.
* @param statusCode If non-null, the HTTP status code is written to this
* pointer.
* @param responseHttpHeadersHandle Handle to the response HTTP headers.
* @param responseContent The response content.
*
* @c HTTPAPIEX_ExecuteRequest tries to execute an HTTP request of type @p
* requestType, on the server's @p relativePath, pushing the request HTTP
* headers @p requestHttpHeadersHandle, having the content of the request
* as pointed to by @p requestContent. If successful, @c HTTAPIEX_ExecuteRequest
* writes in the out @p parameter statusCode the HTTP status, populates the @p
* responseHeadersHandle with the response headers and copies the response body
* to @p responseContent.
*
* @return An @c HTTAPIEX_HANDLE suitable for further calls to the module.
*/
extern HTTPAPIEX_RESULT HTTPAPIEX_ExecuteRequest(HTTPAPIEX_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode, HTTP_HEADERS_HANDLE responseHttpHeadersHandle, BUFFER_HANDLE responseContent);
/**
* @brief Frees all resources used by the @c HTTPAPIEX_HANDLE object.
*
* @param handle The @c HTTPAPIEX_HANDLE object to be freed.
*/
extern void HTTPAPIEX_Destroy(HTTPAPIEX_HANDLE handle);
/**
* @brief Sets the option @p optionName to the value pointed to by @p value.
*
* @param handle The @c HTTPAPIEX_HANDLE representing this session.
* @param optionName Name of the option.
* @param value The value to be set for the option.
*
* @return An @c HTTPAPIEX_RESULT indicating the status of the call.
*/
extern HTTPAPIEX_RESULT HTTPAPIEX_SetOption(HTTPAPIEX_HANDLE handle, const char* optionName, const void* value);
#ifdef __cplusplus
}
#endif
#endif /* HTTPAPIEX_H */

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef HTTPAPIEX_SAS_H
#define HTTPAPIEX_SAS_H
#include "strings.h"
#include "buffer_.h"
#include "httpheaders.h"
#include "httpapiex.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void* HTTPAPIEX_SAS_HANDLE;
extern HTTPAPIEX_SAS_HANDLE HTTPAPIEX_SAS_Create(STRING_HANDLE key, STRING_HANDLE uriResource, STRING_HANDLE keyName);
extern void HTTPAPIEX_SAS_Destroy(HTTPAPIEX_SAS_HANDLE handle);
extern HTTPAPIEX_RESULT HTTPAPIEX_SAS_ExecuteRequest(HTTPAPIEX_SAS_HANDLE sasHandle, HTTPAPIEX_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode, HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent);
#ifdef __cplusplus
}
#endif
#endif /* HTTPAPIEX_SAS_H */

Просмотреть файл

@ -0,0 +1,157 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file httpheaders.h
* @brief This is a utility module that handles HTTP message-headers.
*
* @details An application would use ::HTTPHeaders_Alloc to create a new set of HTTP headers.
* After getting the handle, the application would build in several headers by
* consecutive calls to ::HTTPHeaders_AddHeaderNameValuePair. When the headers are
* constructed, the application can retrieve the stored data by calling one of the
* following functions:
* - ::HTTPHeaders_FindHeaderValue - when the name of the header is known and it
* wants to know the value of that header
* - ::HTTPHeaders_GetHeaderCount - when the application needs to know the count
* of all the headers
* - ::HTTPHeaders_GetHeader - when the application needs to retrieve the
* <code>name + ": " + value</code> string based on an index.
*/
#ifndef HTTPHEADERS_H
#define HTTPHEADERS_H
#include "macro_utils.h"
#ifdef __cplusplus
#include <cstddef>
extern "C" {
#else
#include <stddef.h>
#endif
/*Codes_SRS_HTTP_HEADERS_99_001:[ HttpHeaders shall have the following interface]*/
#define HTTP_HEADERS_RESULT_VALUES \
HTTP_HEADERS_OK, \
HTTP_HEADERS_INVALID_ARG, \
HTTP_HEADERS_ALLOC_FAILED, \
HTTP_HEADERS_INSUFFICIENT_BUFFER, \
HTTP_HEADERS_ERROR \
/** @brief Enumeration specifying the status of calls to various APIs in this module.
*/
DEFINE_ENUM(HTTP_HEADERS_RESULT, HTTP_HEADERS_RESULT_VALUES);
typedef void* HTTP_HEADERS_HANDLE;
/**
* @brief Produces a @c HTTP_HANDLE that can later be used in subsequent calls to the module.
*
* This function returns @c NULL in case an error occurs. After successful execution
* ::HTTPHeaders_GetHeaderCount will report @c 0 existing headers.
*
* @return A HTTP_HEADERS_HANDLE representing the newly created collection of HTTP headers.
*/
extern HTTP_HEADERS_HANDLE HTTPHeaders_Alloc(void);
/**
* @brief De-allocates the data structures allocated by previous API calls to the same handle.
*
* @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value.
*/
extern void HTTPHeaders_Free(HTTP_HEADERS_HANDLE httpHeadersHandle);
/**
* @brief Adds a header record from the @p name and @p value parameters.
*
* @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value.
* @param name The name of the HTTP header to add. It is invalid for
* the name to include the ':' character or character codes
* outside the range 33-126.
* @param value The value to be assigned to the header.
*
* The function stores the @c name:value pair in such a way that when later
* retrieved by a call to ::HTTPHeaders_GetHeader it will return a string
* that is @c strcmp equal to @c name+": "+value. If the name already exists
* in the collection of headers, the function concatenates the new value
* after the existing value, separated by a comma and a space as in:
* <code>old-value+", "+new-value</code>.
*
* @return Returns @c HTTP_HEADERS_OK when execution is successful or an error code from
* the ::HTTPAPIEX_RESULT enum.
*/
extern HTTP_HEADERS_RESULT HTTPHeaders_AddHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value);
/**
* @brief This API performs exactly the same as ::HTTPHeaders_AddHeaderNameValuePair
* except that if the header name already exists then the already existing value
* will be replaced as opposed to being concatenated to.
*
* @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value.
* @param name The name of the HTTP header to add/replace. It is invalid for
* the name to include the ':' character or character codes
* outside the range 33-126.
* @param value The value to be assigned to the header.
*
* @return Returns @c HTTP_HEADERS_OK when execution is successful or an error code from
* the ::HTTPAPIEX_RESULT enum.
*/
extern HTTP_HEADERS_RESULT HTTPHeaders_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value);
/**
* @brief Retrieves the value for a previously stored name.
*
* @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value.
* @param name The name of the HTTP header to find.
*
* @return The return value points to a string that shall be @c strcmp equal
* to the original stored string.
*/
extern const char* HTTPHeaders_FindHeaderValue(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name);
/**
* @brief This API retrieves the number of stored headers.
*
* @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value.
* @param headersCount If non-null, the API writes the number of
* into the memory pointed at by this parameter.
*
* @return Returns @c HTTP_HEADERS_OK when execution is successful or
* @c HTTP_HEADERS_ERROR when an error occurs.
*/
extern HTTP_HEADERS_RESULT HTTPHeaders_GetHeaderCount(HTTP_HEADERS_HANDLE httpHeadersHandle, size_t* headersCount);
/**
* @brief This API retrieves the string name+": "+value for the header
* element at the given @p index.
*
* @param handle A valid @c HTTP_HEADERS_HANDLE value.
* @param index Zero-based index of the item in the
* headers collection.
* @param destination If non-null, the header value is written into a
* new string a pointer to which is written into this
* parameters. It is the caller's responsibility to free
* this memory.
*
* @return Returns @c HTTP_HEADERS_OK when execution is successful or
* @c HTTP_HEADERS_ERROR when an error occurs.
*/
extern HTTP_HEADERS_RESULT HTTPHeaders_GetHeader(HTTP_HEADERS_HANDLE handle, size_t index, char** destination);
/**
* @brief This API produces a clone of the @p handle parameter.
*
* @param handle A valid @c HTTP_HEADERS_HANDLE value.
*
* If @p handle is not @c NULL this function clones the content
* of the handle to a new handle and returns it.
*
* @return A @c HTTP_HEADERS_HANDLE containing a cloned copy of the
* contents of @p handle.
*/
extern HTTP_HEADERS_HANDLE HTTPHeaders_Clone(HTTP_HEADERS_HANDLE handle);
#ifdef __cplusplus
}
#endif
#endif /* HTTPHEADERS_H */

58
c/sharedutil/inc/io.h Normal file
Просмотреть файл

@ -0,0 +1,58 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef IO_H
#define IO_H
#include "iot_logging.h"
#ifdef __cplusplus
#include <cstddef>
extern "C" {
#else
#include <stddef.h>
#endif /* __cplusplus */
typedef void* IO_HANDLE;
typedef void* CONCRETE_IO_HANDLE;
typedef enum IO_STATE_TAG
{
IO_STATE_NOT_OPEN,
IO_STATE_OPENING,
IO_STATE_OPEN,
IO_STATE_ERROR
} IO_STATE;
typedef void(*ON_BYTES_RECEIVED)(void* context, const void* buffer, size_t size);
typedef void(*ON_IO_STATE_CHANGED)(void* context, IO_STATE new_io_state, IO_STATE previous_io_state);
typedef CONCRETE_IO_HANDLE(*IO_CREATE)(void* io_create_parameters, LOGGER_LOG logger_log);
typedef void(*IO_DESTROY)(CONCRETE_IO_HANDLE handle);
typedef int(*IO_OPEN)(CONCRETE_IO_HANDLE handle, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context);
typedef int(*IO_CLOSE)(CONCRETE_IO_HANDLE handle);
typedef int(*IO_SEND)(CONCRETE_IO_HANDLE handle, const void* buffer, size_t size);
typedef void(*IO_DOWORK)(CONCRETE_IO_HANDLE handle);
typedef struct IO_INTERFACE_DESCRIPTION_TAG
{
IO_CREATE concrete_io_create;
IO_DESTROY concrete_io_destroy;
IO_OPEN concrete_io_open;
IO_CLOSE concrete_io_close;
IO_SEND concrete_io_send;
IO_DOWORK concrete_io_dowork;
} IO_INTERFACE_DESCRIPTION;
extern IO_HANDLE io_create(const IO_INTERFACE_DESCRIPTION* io_interface_description, const void* io_create_parameters, LOGGER_LOG logger_log);
extern void io_destroy(IO_HANDLE handle);
extern int io_open(IO_HANDLE handle, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context);
extern int io_close(IO_HANDLE handle);
extern int io_send(IO_HANDLE handle, const void* buffer, size_t size);
extern void io_dowork(IO_HANDLE handle);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* IO_H */

Просмотреть файл

@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef LOGGING_H
#define LOGGING_H
#include <stdio.h>
#include "agenttime.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define STRINGIFY(a) (#a)
typedef void(*LOGGER_LOG)(unsigned int options, char* format, ...);
#define LOG_LINE 0x01
#define LogInfo(...) (void)printf("Info: " __VA_ARGS__)
#define LOG(logger, ...) if (logger != NULL) logger(__VA_ARGS__)
#if defined _MSC_VER
#define LogError(FORMAT, ...) { time_t t = time(NULL); (void)fprintf(stderr,"Error: Time:%.24s File:%s Func:%s Line:%d " FORMAT, ctime(&t), __FILE__, __FUNCDNAME__, __LINE__, __VA_ARGS__); }
#else
#define LogError(FORMAT, ...) { time_t t = time(NULL); (void)fprintf(stderr,"Error: Time:%.24s File:%s Func:%s Line:%d " FORMAT, ctime(&t), __FILE__, __func__, __LINE__, ##__VA_ARGS__); }
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LOGGING_H */

31
c/sharedutil/inc/list.h Normal file
Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef LIST_H
#define LIST_H
#ifdef __cplusplus
extern "C" {
#include "cstdbool"
#else
#include "stdbool.h"
#endif /* __cplusplus */
typedef void* LIST_HANDLE;
typedef void* LIST_ITEM_HANDLE;
typedef bool (*LIST_MATCH_FUNCTION)(LIST_ITEM_HANDLE list_item, const void* match_context);
extern LIST_HANDLE list_create(void);
extern void list_destroy(LIST_HANDLE handle);
extern int list_add(LIST_HANDLE handle, const void* item);
extern LIST_ITEM_HANDLE list_get_head_item(LIST_HANDLE handle);
extern LIST_ITEM_HANDLE list_get_next_item(LIST_ITEM_HANDLE item_handle);
extern const void* list_item_get_value(LIST_ITEM_HANDLE item_handle);
extern LIST_ITEM_HANDLE list_find(LIST_HANDLE handle, LIST_MATCH_FUNCTION match_function, const void* match_context);
extern int list_remove_matching_item(LIST_HANDLE handle, LIST_MATCH_FUNCTION match_function, const void* match_context);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LIST_H */

76
c/sharedutil/inc/lock.h Normal file
Просмотреть файл

@ -0,0 +1,76 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file lock.h
* @brief A minimalistic platform agnostic lock abstraction for thread
* synchronization.
* @details The Lock component is implemented in order to achieve thread
* synchronization, as we may have a requirement to consume locks
* across different platforms. This component exposes some generic
* APIs so that it can be extended for platform specific
* implementations.
*/
#ifndef LOCK_H
#define LOCK_H
#include "macro_utils.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void* LOCK_HANDLE;
#define LOCK_RESULT_VALUES \
LOCK_OK, \
LOCK_ERROR \
/** @brief Enumeration specifying the lock status.
*/
DEFINE_ENUM(LOCK_RESULT, LOCK_RESULT_VALUES);
/**
* @brief This API creates and returns a valid lock handle.
*
* @return A valid @c LOCK_HANDLE when successful or @c NULL otherwise.
*/
extern LOCK_HANDLE Lock_Init(void);
/**
* @brief Acquires a lock on the given lock handle. Uses platform
* specific mutex primitives in its implementation.
*
* @param handle A valid handle to the lock.
*
* @return Returns @c LOCK_OK when a lock has been acquired and
* @c LOCK_ERROR when an error occurs.
*/
extern LOCK_RESULT Lock(LOCK_HANDLE handle);
/**
* @brief Releases the lock on the given lock handle. Uses platform
* specific mutex primitives in its implementation.
*
* @param handle A valid handle to the lock.
*
* @return Returns @c LOCK_OK when the lock has been released and
* @c LOCK_ERROR when an error occurs.
*/
extern LOCK_RESULT Unlock(LOCK_HANDLE handle);
/**
* @brief The lock instance is destroyed.
*
* @param handle A valid handle to the lock.
*
* @return Returns @c LOCK_OK when the lock object has been
* destroyed and @c LOCK_ERROR when an error occurs.
*/
extern LOCK_RESULT Lock_Deinit(LOCK_HANDLE handle);
#ifdef __cplusplus
}
#endif
#endif /* LOCK_H */

10996
c/sharedutil/inc/macro_utils.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

199
c/sharedutil/inc/map.h Normal file
Просмотреть файл

@ -0,0 +1,199 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file map.h
* @brief Map is a module that implements a dictionary of @c STRING_HANDLE
* keys to @c STRING_HANDLE values.
*/
#ifndef MAP_H
#define MAP_H
#ifdef __cplusplus
#include <cstddef>
extern "C"
{
#else
#include <stddef.h>
#endif
#include "macro_utils.h"
#include "strings.h"
#include "crt_abstractions.h"
#define MAP_RESULT_VALUES \
MAP_OK, \
MAP_ERROR, \
MAP_INVALIDARG, \
MAP_KEYEXISTS, \
MAP_KEYNOTFOUND, \
MAP_FILTER_REJECT
/** @brief Enumeration specifying the status of calls to various APIs in this
* module.
*/
DEFINE_ENUM(MAP_RESULT, MAP_RESULT_VALUES);
typedef void* MAP_HANDLE;
typedef int (*MAP_FILTER_CALLBACK)(const char* mapProperty, const char* mapValue);
/**
* @brief Creates a new, empty map.
*
* @param mapFilterFunc A callback function supplied by the caller that is
* invoked during calls to ::Map_Add and
* ::Map_AddOrUpdate to provide the caller an
* opportunity to indicate whether the new entry or
* the update to an existing entry can be made or not.
* The callback function can request that the operation
* be canceled by returning a non-zero value from the
* callback.
*
* @return A valid @c MAP_HANDLE or @c NULL in case an error occurs.
*/
extern MAP_HANDLE Map_Create(MAP_FILTER_CALLBACK mapFilterFunc);
/**
* @brief Release all resources associated with the map.
*
* @param handle The handle to an existing map.
*/
extern void Map_Destroy(MAP_HANDLE handle);
/**
* @brief Creates a copy of the map indicated by @p handle and returns a
* handle to it.
*
* @param handle The handle to an existing map.
*
* @return A valid @c MAP_HANDLE to the cloned copy of the map or @c NULL
* in case an error occurs.
*/
extern MAP_HANDLE Map_Clone(MAP_HANDLE handle);
/**
* @brief Adds a key/value pair to the map.
*
* @param handle The handle to an existing map.
* @param key The @c key to be used for this map entry.
* @param value The @c value to be associated with @p key.
*
* If a non-NULL pointer to a callback function was supplied
* via the @c mapFilterFunc parameter when ::Map_Create was
* called then that callback is invoked when a new entry is
* added and if the callback returns a non-zero value then
* the function cancels the add operation and returns
* @c MAP_FILTER_REJECT.
*
* @return If any of the input parameters are @c NULL then this function
* returns @c MAP_INVALID_ARG. If the key already exists in the
* map then @c MAP_KEYEXISTS is returned. If the filter function
* associated with the map rejects the entry then
* @c MAP_FILTER_REJECT is returned. In case an error occurs when
* the new key is added to the map the function returns @c MAP_ERROR.
* If everything goes well then @c MAP_OK is returned.
*/
extern MAP_RESULT Map_Add(MAP_HANDLE handle, const char* key, const char* value);
/**
* @brief Adds/updates a key/value pair to the map.
*
* @param handle The handle to an existing map.
* @param key The @c key to be used for this map entry.
* @param value The @c value to be associated with @p key.
*
* This function behaves exactly like ::Map_Add except that if the key
* already exists in the map then it overwrites the value with the
* supplied value instead of returning an error. If a non-NULL pointer
* to a callback function was supplied via the @c mapFilterFunc parameter
* when ::Map_Create was called then that callback is invoked when a new
* entry is added or when an existing entry is updated and if the
* callback returns a non-zero value then the function cancels the
* add/update operation and returns @c MAP_FILTER_REJECT.
*
* @return If any of the input parameters are @c NULL then this function
* returns @c MAP_INVALID_ARG. If the filter function associated
* with the map rejects the entry then @c MAP_FILTER_REJECT is
* returned. In case an error occurs when the new key is
* added/updated in the map the function returns @c MAP_ERROR. If
* everything goes well then @c MAP_OK is returned.
*/
extern MAP_RESULT Map_AddOrUpdate(MAP_HANDLE handle, const char* key, const char* value);
/**
* @brief Removes a key and its associated value from the map.
*
* @param handle The handle to an existing map.
* @param key The @c key of the item to be deleted.
*
* @return Returns @c MAP_OK if the key was deleted successfully or an
* error code otherwise.
*/
extern MAP_RESULT Map_Delete(MAP_HANDLE handle, const char* key);
/**
* @brief This function returns a boolean value in @p keyExists if the map
* contains a key with the same value the parameter @p key.
*
* @param handle The handle to an existing map.
* @param key The key that the caller wants checked.
* @param keyExists The function writes @c true at the address pointed at
* by this parameter if the key exists in the map and
* @c false otherwise.
*
* @return Returns @c MAP_OK if the check was performed successfully or an
* error code otherwise.
*/
extern MAP_RESULT Map_ContainsKey(MAP_HANDLE handle, const char* key, bool* keyExists);
/**
* @brief This function returns @c true in @p valueExists if at
* least one <key,value> pair exists in the map where the entry's
* value is equal to the parameter @c value.
*
* @param handle The handle to an existing map.
* @param value The value that the caller wants checked.
* @param valueExists The function writes @c true at the address pointed at
* by this parameter if the value exists in the map and
* @c false otherwise.
*
* @return Returns @c MAP_OK if the check was performed successfully or an
* error code otherwise.
*/
extern MAP_RESULT Map_ContainsValue(MAP_HANDLE handle, const char* value, bool* valueExists);
/**
* @brief Retrieves the value of a stored key.
*
* @param handle The handle to an existing map.
* @param key The key to be looked up in the map.
*
* @return Returns @c NULL in case the input arguments are @c NULL or if the
* requested key is not found in the map. Returns a pointer to the
* key's value otherwise.
*/
extern const char* Map_GetValueFromKey(MAP_HANDLE handle, const char* key);
/**
* @brief Retrieves the complete list of keys and values from the map
* in @p values and @p keys. Also writes the size of the list
* in @p count.
*
* @param handle The handle to an existing map.
* @param keys The location where the list of keys is to be written.
* @param values The location where the list of values is to be written.
* @param count The number of stored keys and values is written at the
* location indicated by this pointer.
*
* @return Returns @c MAP_OK if the keys and values are retrieved and written
* successfully or an error code otherwise.
*/
extern MAP_RESULT Map_GetInternals(MAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count);
#ifdef __cplusplus
}
#endif
#endif /* MAP_H */

Просмотреть файл

@ -0,0 +1,77 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef MQTTAPI_H
#define MQTTAPI_H
#ifdef __cplusplus
extern "C" {
#include <cstddef>
#else
#include <stddef.h>
#endif
#include "macro_utils.h"
#include "crt_abstractions.h"
typedef void* MQTTAPI_HANDLE;
typedef void* MQTTAPI_TOPIC_HANDLE;
#define MQTTAPI_RESULT_VALUES \
MQTTAPI_OK, \
MQTTAPI_INVALID_ARG, \
MQTTAPI_ERROR_CONNECTED_INSTANCE, \
MQTTAPI_ERROR \
DEFINE_ENUM(MQTTAPI_RESULT, MQTTAPI_RESULT_VALUES);
#define MQTTAPI_CONFIRMATION_RESULT_VALUES \
MQTTAPI_CONFIRMATION_OK, \
MQTTAPI_CONFIRMATION_BECAUSE_DESTROY, \
MQTTAPI_CONFIRMATION_ERROR \
DEFINE_ENUM(MQTTAPI_CONFIRMATION_RESULT, MQTTAPI_CONFIRMATION_RESULT_VALUES);
typedef struct
{
/** The length of the MQTT message payload in bytes. */
size_t payloadlen;
/** A pointer to the payload of the MQTT message. */
unsigned char* payload;
} MQTTAPI_Message;
typedef struct
{
const char* deviceId;
const char* deviceKey;
/**
* String used to connect with a sas token. Format is <IOTHUBNAME>.<IOTHUBSUFFIX>\devices\<DEVICEID>
*/
const char* sasTokenSr;
/**
* String with the Server URI to be connected. Format required <i>protocol://host:port</i>
*/
const char* serverURI;
} MQTTAPI_ConnectOptions;
typedef bool MQTTAPI_MessageArrived(void* context, const MQTTAPI_Message* message);
typedef void MQTTAPI_DeliveryComplete(void* context, MQTTAPI_CONFIRMATION_RESULT result);
extern MQTTAPI_HANDLE MQTTAPI_Create(const MQTTAPI_ConnectOptions* options);
extern MQTTAPI_RESULT MQTTAPI_SetMessageCallback(MQTTAPI_HANDLE instance, void* context, MQTTAPI_MessageArrived * ma);
extern MQTTAPI_RESULT MQTTAPI_SetDeliveryCompletedCallback(MQTTAPI_HANDLE instance, MQTTAPI_DeliveryComplete* dc);
extern void MQTTAPI_Destroy(MQTTAPI_HANDLE instance);
extern MQTTAPI_RESULT MQTTAPI_PublishMessage(MQTTAPI_HANDLE instance, const char* topicName, const MQTTAPI_Message* msg, void* context);
extern MQTTAPI_TOPIC_HANDLE MQTTAPI_Subscribe(MQTTAPI_HANDLE instance, const char* topic);
extern void MQTTAPI_Unsubscribe(MQTTAPI_TOPIC_HANDLE topicInstance);
extern void MQTTAPI_DoWork(MQTTAPI_HANDLE instance);
#ifdef __cplusplus
}
#endif
#endif /* MQTTAPI_H*/

Просмотреть файл

@ -0,0 +1,15 @@
#ifndef PLATFORM_H
#define PLATFORM_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
extern int platform_init(void);
extern void platform_deinit(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PLATFORM_H */

Просмотреть файл

@ -0,0 +1,20 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef SASTOKEN_H
#define SASTOKEN_H
#include "strings.h"
#include "buffer_.h"
#ifdef __cplusplus
extern "C" {
#endif
extern STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, size_t expiry);
#ifdef __cplusplus
}
#endif
#endif /* SASTOKEN_H */

Просмотреть файл

@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/*************************** sha-private.h ***************************/
/********************** See RFC 4634 for details *********************/
#ifndef _SHA_PRIVATE__H
#define _SHA_PRIVATE__H
/*
* These definitions are defined in FIPS-180-2, section 4.1.
* Ch() and Maj() are defined identically in sections 4.1.1,
* 4.1.2 and 4.1.3.
*
* The definitions used in FIPS-180-2 are as follows:
*/
#ifndef USE_MODIFIED_MACROS
#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#else /* USE_MODIFIED_MACROS */
/*
* The following definitions are equivalent and potentially faster.
*/
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#endif /* USE_MODIFIED_MACROS */
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
#endif /* _SHA_PRIVATE__H */

267
c/sharedutil/inc/sha.h Normal file
Просмотреть файл

@ -0,0 +1,267 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/**************************** sha.h ****************************/
/******************* See RFC 4634 for details ******************/
#ifndef _SHA_H_
#define _SHA_H_
/*
* Description:
* This file implements the Secure Hash Signature Standard
* algorithms as defined in the National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
* published on August 1, 2002, and the FIPS PUB 180-2 Change
* Notice published on February 28, 2004.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-2/fips180-2withchangenotice.pdf
*
* The five hashes are defined in these sizes:
* SHA-1 20 byte / 160 bit
* SHA-224 28 byte / 224 bit
* SHA-256 32 byte / 256 bit
* SHA-384 48 byte / 384 bit
* SHA-512 64 byte / 512 bit
*/
#include <stdint.h>
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typedef the following:
* name meaning
* uint64_t unsigned 64 bit integer
* uint32_t unsigned 32 bit integer
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
*/
#ifndef _SHA_enum_
#define _SHA_enum_
/*
* All SHA functions return one of these values.
*/
enum {
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError, /* called Input after FinalBits or Result */
shaBadParam /* passed a bad parameter */
};
#endif /* _SHA_enum_ */
/*
* These constants hold size information for each of the SHA
* hashing operations
*/
enum {
SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128,
SHA512_Message_Block_Size = 128,
USHA_Max_Message_Block_Size = SHA512_Message_Block_Size,
SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
SHA384HashSize = 48, SHA512HashSize = 64,
USHAMaxHashSize = SHA512HashSize,
SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
SHA256HashSizeBits = 256, SHA384HashSizeBits = 384,
SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits
};
/*
* These constants are used in the USHA (unified sha) functions.
*/
typedef enum SHAversion {
SHA1, SHA224, SHA256, SHA384, SHA512
} SHAversion;
/*
* This structure will hold context information for the SHA-1
* hashing operation.
*/
typedef struct SHA1Context {
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
uint32_t Length_Low; /* Message length in bits */
uint32_t Length_High; /* Message length in bits */
int_least16_t Message_Block_Index; /* Message_Block array index */
/* 512-bit message blocks */
uint8_t Message_Block[SHA1_Message_Block_Size];
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the digest corrupted? */
} SHA1Context;
/*
* This structure will hold context information for the SHA-256
* hashing operation.
*/
typedef struct SHA256Context {
uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
uint32_t Length_Low; /* Message length in bits */
uint32_t Length_High; /* Message length in bits */
int_least16_t Message_Block_Index; /* Message_Block array index */
/* 512-bit message blocks */
uint8_t Message_Block[SHA256_Message_Block_Size];
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the digest corrupted? */
} SHA256Context;
/*
* This structure will hold context information for the SHA-512
* hashing operation.
*/
typedef struct SHA512Context {
#ifdef USE_32BIT_ONLY
uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */
uint32_t Length[4]; /* Message length in bits */
#else /* !USE_32BIT_ONLY */
uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */
uint64_t Length_Low, Length_High; /* Message length in bits */
#endif /* USE_32BIT_ONLY */
int_least16_t Message_Block_Index; /* Message_Block array index */
/* 1024-bit message blocks */
uint8_t Message_Block[SHA512_Message_Block_Size];
int Computed; /* Is the digest computed?*/
int Corrupted; /* Is the digest corrupted? */
} SHA512Context;
/*
* This structure will hold context information for the SHA-224
* hashing operation. It uses the SHA-256 structure for computation.
*/
typedef struct SHA256Context SHA224Context;
/*
* This structure will hold context information for the SHA-384
* hashing operation. It uses the SHA-512 structure for computation.
*/
typedef struct SHA512Context SHA384Context;
/*
* This structure holds context information for all SHA
* hashing operations.
*/
typedef struct USHAContext {
int whichSha; /* which SHA is being used */
union {
SHA1Context sha1Context;
SHA224Context sha224Context; SHA256Context sha256Context;
SHA384Context sha384Context; SHA512Context sha512Context;
} ctx;
} USHAContext;
/*
* This structure will hold context information for the HMAC
* keyed hashing operation.
*/
typedef struct HMACContext {
int whichSha; /* which SHA is being used */
int hashSize; /* hash size of SHA being used */
int blockSize; /* block size of SHA being used */
USHAContext shaContext; /* SHA context */
unsigned char k_opad[USHA_Max_Message_Block_Size];
/* outer padding - key XORd with opad */
} HMACContext;
/*
* Function Prototypes
*/
/* SHA-1 */
extern int SHA1Reset(SHA1Context *);
extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
unsigned int bytecount);
extern int SHA1FinalBits(SHA1Context *, const uint8_t bits,
unsigned int bitcount);
extern int SHA1Result(SHA1Context *,
uint8_t Message_Digest[SHA1HashSize]);
/* SHA-224 */
extern int SHA224Reset(SHA224Context *);
extern int SHA224Input(SHA224Context *, const uint8_t *bytes,
unsigned int bytecount);
extern int SHA224FinalBits(SHA224Context *, const uint8_t bits,
unsigned int bitcount);
extern int SHA224Result(SHA224Context *,
uint8_t Message_Digest[SHA224HashSize]);
/* SHA-256 */
extern int SHA256Reset(SHA256Context *);
extern int SHA256Input(SHA256Context *, const uint8_t *bytes,
unsigned int bytecount);
extern int SHA256FinalBits(SHA256Context *, const uint8_t bits,
unsigned int bitcount);
extern int SHA256Result(SHA256Context *,
uint8_t Message_Digest[SHA256HashSize]);
/* SHA-384 */
extern int SHA384Reset(SHA384Context *);
extern int SHA384Input(SHA384Context *, const uint8_t *bytes,
unsigned int bytecount);
extern int SHA384FinalBits(SHA384Context *, const uint8_t bits,
unsigned int bitcount);
extern int SHA384Result(SHA384Context *,
uint8_t Message_Digest[SHA384HashSize]);
/* SHA-512 */
extern int SHA512Reset(SHA512Context *);
extern int SHA512Input(SHA512Context *, const uint8_t *bytes,
unsigned int bytecount);
extern int SHA512FinalBits(SHA512Context *, const uint8_t bits,
unsigned int bitcount);
extern int SHA512Result(SHA512Context *,
uint8_t Message_Digest[SHA512HashSize]);
/* Unified SHA functions, chosen by whichSha */
extern int USHAReset(USHAContext *, SHAversion whichSha);
extern int USHAInput(USHAContext *,
const uint8_t *bytes, unsigned int bytecount);
extern int USHAFinalBits(USHAContext *,
const uint8_t bits, unsigned int bitcount);
extern int USHAResult(USHAContext *,
uint8_t Message_Digest[USHAMaxHashSize]);
extern int USHABlockSize(enum SHAversion whichSha);
extern int USHAHashSize(enum SHAversion whichSha);
extern int USHAHashSizeBits(enum SHAversion whichSha);
/*
* HMAC Keyed-Hashing for Message Authentication, RFC2104,
* for all SHAs.
* This interface allows a fixed-length text input to be used.
*/
extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */
const unsigned char *text, /* pointer to data stream */
int text_len, /* length of data stream */
const unsigned char *key, /* pointer to authentication key */
int key_len, /* length of authentication key */
uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
/*
* HMAC Keyed-Hashing for Message Authentication, RFC2104,
* for all SHAs.
* This interface allows any length of text input to be used.
*/
extern int hmacReset(HMACContext *ctx, enum SHAversion whichSha,
const unsigned char *key, int key_len);
extern int hmacInput(HMACContext *ctx, const unsigned char *text,
int text_len);
extern int hmacFinalBits(HMACContext *ctx, const uint8_t bits,
unsigned int bitcount);
extern int hmacResult(HMACContext *ctx,
uint8_t digest[USHAMaxHashSize]);
#endif /* _SHA_H_ */

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef SOCKETIO_H
#define SOCKETIO_H
#ifdef __cplusplus
extern "C" {
#include <cstddef>
#else
#include <stddef.h>
#endif /* __cplusplus */
#include "io.h"
#include "iot_logging.h"
typedef struct SOCKETIO_CONFIG_TAG
{
const char* hostname;
int port;
} SOCKETIO_CONFIG;
extern IO_HANDLE socketio_create(void* io_create_parameters, LOGGER_LOG logger_log);
extern void socketio_destroy(IO_HANDLE socket_io);
extern int socketio_open(IO_HANDLE socket_io, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context);
extern int socketio_close(IO_HANDLE socket_io);
extern int socketio_send(IO_HANDLE socket_io, const void* buffer, size_t size);
extern void socketio_dowork(IO_HANDLE socket_io);
extern const IO_INTERFACE_DESCRIPTION* socketio_get_interface_description(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SOCKETIO_H */

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;

Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef STRING_TOKENIZER_H
#define STRING_TOKENIZER_H
#include "strings.h"
#ifdef __cplusplus
extern "C"
{
#else
#endif
typedef void* STRING_TOKENIZER_HANDLE;
extern STRING_TOKENIZER_HANDLE STRING_TOKENIZER_create(STRING_HANDLE handle);
extern int STRING_TOKENIZER_get_next_token(STRING_TOKENIZER_HANDLE t, STRING_HANDLE output, const char* delimiters);
extern void STRING_TOKENIZER_destroy(STRING_TOKENIZER_HANDLE t);
#ifdef __cplusplus
}
#else
#endif
#endif /*STRING_TOKENIZER_H*/

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef STRINGS_H
#define STRINGS_H
#ifdef __cplusplus
#include <cstddef>
extern "C"
{
#else
#include <stddef.h>
#endif
typedef void* STRING_HANDLE;
extern STRING_HANDLE STRING_new(void);
extern STRING_HANDLE STRING_clone(STRING_HANDLE handle);
extern STRING_HANDLE STRING_construct(const char* psz);
extern STRING_HANDLE STRING_construct_n(const char* psz, size_t n);
extern STRING_HANDLE STRING_new_with_memory(const char* memory);
extern STRING_HANDLE STRING_new_quoted(const char* source);
extern STRING_HANDLE STRING_new_JSON(const char* source);
extern void STRING_delete(STRING_HANDLE handle);
extern int STRING_concat(STRING_HANDLE handle, const char* s2);
extern int STRING_concat_with_STRING(STRING_HANDLE s1, STRING_HANDLE s2);
extern int STRING_quote(STRING_HANDLE handle);
extern int STRING_copy(STRING_HANDLE s1, const char* s2);
extern int STRING_copy_n(STRING_HANDLE s1, const char* s2, size_t n);
extern const char* STRING_c_str(STRING_HANDLE handle);
extern int STRING_empty(STRING_HANDLE handle);
extern size_t STRING_length(STRING_HANDLE handle);
extern int STRING_compare(STRING_HANDLE s1, STRING_HANDLE s2);
#ifdef __cplusplus
}
#else
#endif
#endif /*STRINGS_H*/

Просмотреть файл

@ -0,0 +1,89 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/** @file threadapi.h
* @brief This module implements support for creating new threads,
* terminating threads and sleeping threads.
*/
#ifndef THREADAPI_H
#define THREADAPI_H
#ifdef __cplusplus
extern "C" {
#endif
#include "macro_utils.h"
typedef int(*THREAD_START_FUNC)(void *);
#define THREADAPI_RESULT_VALUES \
THREADAPI_OK, \
THREADAPI_INVALID_ARG, \
THREADAPI_NO_MEMORY, \
THREADAPI_ERROR
/** @brief Enumeration specifying the possible return values for the APIs in
* this module.
*/
DEFINE_ENUM(THREADAPI_RESULT, THREADAPI_RESULT_VALUES);
typedef void* THREAD_HANDLE;
/**
* @brief Creates a thread with the entry point specified by the @p func
* argument.
*
* @param threadHandle The handle to the new thread is returned in this
* pointer.
* @param func A function pointer that indicates the entry point
* to the new thread.
* @param arg A void pointer that must be passed to the function
* pointed to by @p func.
*
* @return @c THREADAPI_OK if the API call is successful or an error
* code in case it fails.
*/
extern THREADAPI_RESULT ThreadAPI_Create(THREAD_HANDLE* threadHandle, THREAD_START_FUNC func, void* arg);
/**
* @brief Blocks the calling thread by waiting on the thread identified by
* the @p threadHandle argument to complete.
*
* @param threadHandle The handle of the thread to wait for completion.
* @param res The result returned by the thread which is passed
* to the ::ThreadAPI_Exit function.
*
* When the @p threadHandle thread completes, all resources associated
* with the thread must be released and the thread handle will no
* longer be valid.
*
* @return @c THREADAPI_OK if the API call is successful or an error
* code in case it fails.
*/
extern THREADAPI_RESULT ThreadAPI_Join(THREAD_HANDLE threadHandle, int* res);
/**
* @brief This function is called by a thread when the thread exits.
*
* @param res An integer that represents the exit status of the thread.
*
* This function is called by a thread when the thread exits in order
* to return a result value to the caller of the ::ThreadAPI_Join
* function. The @p res value must be copied into the @p res out
* argument passed to the ::ThreadAPI_Join function.
*/
extern void ThreadAPI_Exit(int res);
/**
* @brief Sleeps the current thread for the given number of milliseconds.
*
* @param milliseconds The number of milliseconds to sleep.
*/
extern void ThreadAPI_Sleep(unsigned int milliseconds);
#ifdef __cplusplus
}
#endif
#endif /* THREADAPI_H */

35
c/sharedutil/inc/tlsio.h Normal file
Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef TLSIO_H
#define TLSIO_H
#ifdef __cplusplus
extern "C" {
#include <cstddef>
#else
#include <stddef.h>
#endif /* __cplusplus */
#include "io.h"
#include "iot_logging.h"
typedef struct TLSIO_CONFIG_TAG
{
const char* hostname;
int port;
} TLSIO_CONFIG;
extern IO_HANDLE tlsio_create(void* io_create_parameters, LOGGER_LOG logger_log);
extern void tlsio_destroy(IO_HANDLE tls_io);
extern int tlsio_open(IO_HANDLE tls_io, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context);
extern int tlsio_close(IO_HANDLE tls_io);
extern int tlsio_send(IO_HANDLE tls_io, const void* buffer, size_t size);
extern void tlsio_dowork(IO_HANDLE tls_io);
extern const IO_INTERFACE_DESCRIPTION* tlsio_get_interface_description(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* TLSIO_H */

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef URLENCODE_H
#define URLENCODE_H
#include "strings.h"
#ifdef __cplusplus
extern "C" {
#endif
extern STRING_HANDLE URL_EncodeString(const char* textEncode);
extern STRING_HANDLE URL_Encode(STRING_HANDLE input);
#ifdef __cplusplus
}
#endif
#endif /* URLENCODE_H */

46
c/sharedutil/inc/vector.h Normal file
Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef VECTOR_H
#define VECTOR_H
#include "crt_abstractions.h"
#ifdef __cplusplus
#include <cstddef>
extern "C"
{
#else
#include <stddef.h>
#endif
typedef void* VECTOR_HANDLE;
typedef bool(*PREDICATE_FUNCTION)(const void* element, const void* value);
/* creation */
extern VECTOR_HANDLE VECTOR_create(size_t elementSize);
extern void VECTOR_destroy(VECTOR_HANDLE handle);
/* insertion */
extern int VECTOR_push_back(VECTOR_HANDLE handle, const void* elements, size_t numElements);
/* removal */
extern void VECTOR_erase(VECTOR_HANDLE handle, void* elements, size_t numElements);
extern void VECTOR_clear(VECTOR_HANDLE handle);
/* access */
extern void* VECTOR_element(const VECTOR_HANDLE handle, size_t index);
extern void* VECTOR_front(const VECTOR_HANDLE handle);
extern void* VECTOR_back(const VECTOR_HANDLE handle);
extern void* VECTOR_find_if(const VECTOR_HANDLE handle, PREDICATE_FUNCTION pred, const void* value);
/* capacity */
extern size_t VECTOR_size(const VECTOR_HANDLE handle);
#ifdef __cplusplus
}
#else
#endif
#endif /* VECTOR_H */

1
c/sharedutil/readme.txt Normal file
Просмотреть файл

@ -0,0 +1 @@
This folder contains the azure shared utility library used in all the clients.

349
c/sharedutil/src/base64.c Normal file
Просмотреть файл

@ -0,0 +1,349 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// PUT NO INCLUDES BEFORE HERE !!!!
//
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <stddef.h>
#include <string.h>
//
// PUT NO CLIENT LIBRARY INCLUDES BEFORE HERE !!!!
//
#include "base64.h"
#include "iot_logging.h"
static const char base64char[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '+', '/'
};
static const char base64b16[16] = {
'A', 'E', 'I', 'M', 'Q', 'U', 'Y', 'c', 'g', 'k',
'o', 's', 'w', '0', '4', '8'
};
static const char base64b8[4] = {
'A', 'Q', 'g', 'w'
};
static int base64toValue(char base64character, unsigned char* value)
{
int result = 0;
if (('A' <= base64character) && (base64character <= 'Z'))
{
*value = base64character - 'A';
}
else if (('a' <= base64character) && (base64character <= 'z'))
{
*value = ('Z' - 'A') + 1 + (base64character - 'a');
}
else if (('0' <= base64character) && (base64character <= '9'))
{
*value = ('Z' - 'A') + 1 + ('z' - 'a') + 1 + (base64character - '0');
}
else if ('+' == base64character)
{
*value = 62;
}
else if ('/' == base64character)
{
*value = 63;
}
else
{
*value = 0;
result = -1;
}
return result;
}
static size_t numberOfBase64Characters(const char* encodedString)
{
size_t length = 0;
unsigned char junkChar;
while (base64toValue(encodedString[length],&junkChar) != -1)
{
length++;
}
return length;
}
static size_t Base64decode_len(const char *encodedString)
{
size_t absoluteLengthOfString;
size_t numberOfBytesToAdd = 0;
absoluteLengthOfString = strlen(encodedString);
if (absoluteLengthOfString)
{
if (encodedString[absoluteLengthOfString - 1] == '=')
{
// See if there are two.
absoluteLengthOfString--;
if (absoluteLengthOfString)
{
if (encodedString[absoluteLengthOfString - 1] == '=')
{
absoluteLengthOfString--;
numberOfBytesToAdd = 1;
}
else
{
numberOfBytesToAdd = 2;
}
}
else
{
numberOfBytesToAdd = 2;
}
}
}
return ((absoluteLengthOfString / 4)*3) + numberOfBytesToAdd;
}
static void Base64decode(unsigned char *decodedString, const char *base64String)
{
size_t numberOfEncodedChars;
size_t indexOfFirstEncodedChar;
size_t decodedIndex;
//
// We can only operate on individual bytes. If we attempt to work
// on anything larger we could get an alignment fault on some
// architectures
//
numberOfEncodedChars = numberOfBase64Characters(base64String);
indexOfFirstEncodedChar = 0;
decodedIndex = 0;
while (numberOfEncodedChars >= 4)
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
(void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
(void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
(void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3);
(void)base64toValue(base64String[indexOfFirstEncodedChar + 3], &c4);
decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
decodedIndex++;
decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2);
decodedIndex++;
decodedString[decodedIndex] = ((c3 & 0x03) << 6) | c4;
decodedIndex++;
numberOfEncodedChars -= 4;
indexOfFirstEncodedChar += 4;
}
if (numberOfEncodedChars == 2)
{
unsigned char c1;
unsigned char c2;
(void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
(void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
}
else if (numberOfEncodedChars == 3)
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
(void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
(void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
(void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3);
decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
decodedIndex++;
decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2);
}
}
BUFFER_HANDLE Base64_Decoder(const char* source)
{
BUFFER_HANDLE result = NULL;
/*Codes_SRS_BASE64_06_008: [If source is NULL then Base64_Decode shall return NULL.]*/
if (source)
{
size_t lengthOfSource = numberOfBase64Characters(source);
if ((lengthOfSource % 4) == 1)
{
/*Codes_SRS_BASE64_06_011: [If the source string has an invalid length for a base 64 encoded string then Base64_Decode shall return NULL.]*/
LogError("Invalid length Base64 string!\r\n");
}
else
{
if ((result = BUFFER_new()) == NULL)
{
/*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Base64_Decode shall return NULL.]*/
LogError("Could not create a buffer to decoding.\r\n");
}
else
{
size_t sizeOfOutputBuffer = Base64decode_len(source);
/*Codes_SRS_BASE64_06_009: [If the string pointed to by source is zero length then the handle returned shall refer to a zero length buffer.]*/
if (sizeOfOutputBuffer > 0)
{
if (BUFFER_pre_build(result, sizeOfOutputBuffer) != 0)
{
/*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Base64_Decode shall return NULL.]*/
LogError("Could not prebuild a buffer for base 64 decoding.\r\n");
BUFFER_delete(result);
result = NULL;
}
else
{
Base64decode(BUFFER_u_char(result), source);
}
}
}
}
}
return result;
}
static STRING_HANDLE Base64_Encode_Internal(const unsigned char* source, size_t size)
{
STRING_HANDLE result;
size_t neededSize = 0;
char* encoded;
size_t currentPosition = 0;
neededSize += (size == 0) ? (0) : ((((size - 1) / 3) + 1) * 4);
neededSize += 1; /*+1 because \0 at the end of the string*/
/*Codes_SRS_BASE64_06_006: [If when allocating memory to produce the encoding a failure occurs then Base64_Encode shall return NULL.]*/
encoded = (char*)malloc(neededSize);
if (encoded == NULL)
{
result = NULL;
LogError("Base64_Encode:: Allocation failed.\r\n");
}
else
{
/*b0 b1(+1) b2(+2)
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|----c1---| |----c2---| |----c3---| |----c4---|
*/
size_t destinationPosition = 0;
while (size - currentPosition >= 3)
{
char c1 = base64char[source[currentPosition] >> 2];
char c2 = base64char[
((source[currentPosition] & 3) << 4) |
(source[currentPosition + 1] >> 4)
];
char c3 = base64char[
((source[currentPosition + 1] & 0x0F) << 2) |
((source[currentPosition + 2] >> 6) & 3)
];
char c4 = base64char[
source[currentPosition + 2] & 0x3F
];
currentPosition += 3;
encoded[destinationPosition++] = c1;
encoded[destinationPosition++] = c2;
encoded[destinationPosition++] = c3;
encoded[destinationPosition++] = c4;
}
if (size - currentPosition == 2)
{
char c1 = base64char[source[currentPosition] >> 2];
char c2 = base64char[
((source[currentPosition] & 0x03) << 4) |
(source[currentPosition + 1] >> 4)
];
char c3 = base64b16[source[currentPosition + 1] & 0x0F];
encoded[destinationPosition++] = c1;
encoded[destinationPosition++] = c2;
encoded[destinationPosition++] = c3;
encoded[destinationPosition++] = '=';
}
else if (size - currentPosition == 1)
{
char c1 = base64char[source[currentPosition] >> 2];
char c2 = base64b8[source[currentPosition] & 0x03];
encoded[destinationPosition++] = c1;
encoded[destinationPosition++] = c2;
encoded[destinationPosition++] = '=';
encoded[destinationPosition++] = '=';
}
/*null terminating the string*/
encoded[destinationPosition] = '\0';
/*Codes_SRS_BASE64_06_007: [Otherwise Base64_Encode shall return a pointer to STRING, that string contains the base 64 encoding of input.]*/
result = STRING_new_with_memory(encoded);
if (result == NULL)
{
free(encoded);
LogError("Base64_Encode:: Allocation failed for return value.\r\n");
}
}
return result;
}
STRING_HANDLE Base64_Encode_Bytes(const unsigned char* source, size_t size)
{
STRING_HANDLE result;
/*Codes_SRS_BASE64_02_001: [If source is NULL then Base64_Encode_Bytes shall return NULL.] */
if (source == NULL)
{
result = NULL;
}
/*Codes_SRS_BASE64_02_002: [If source is not NULL and size is zero, then Base64_Encode_Bytes shall produce an empty STRING_HANDLE.] */
else if (size == 0)
{
result = STRING_new(); /*empty string*/
}
else
{
result = Base64_Encode_Internal(source, size);
}
return result;
}
STRING_HANDLE Base64_Encode(BUFFER_HANDLE input)
{
STRING_HANDLE result;
/*the following will happen*/
/*1. the "data" of the binary shall be "eaten" 3 characters at a time and produce 4 base64 encoded characters for as long as there are more than 3 characters still to process*/
/*2. the remaining characters (1 or 2) shall be encoded.*/
/*there's a level of assumption that 'a' corresponds to 0b000000 and that '_' corresponds to 0b111111*/
/*the encoding will use the optional [=] or [==] at the end of the encoded string, so that other less standard aware libraries can do their work*/
/*these are the bits of the 3 normal bytes to be encoded*/
/*Codes_SRS_BASE64_06_001: [If input is NULL then Base64_Encode shall return NULL.]*/
if (input == NULL)
{
result = NULL;
LogError("Base64_Encode:: NULL input\r\n");
}
else
{
size_t inputSize;
const unsigned char* inputBinary;
if ((BUFFER_content(input, &inputBinary) != 0) ||
(BUFFER_size(input, &inputSize) != 0))
{
result = NULL;
LogError("Base64_Encode:: BUFFER_routines failure.\r\n");
}
else
{
result = Base64_Encode_Internal(inputBinary, inputSize);
}
}
return result;
}

396
c/sharedutil/src/buffer.c Normal file
Просмотреть файл

@ -0,0 +1,396 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// PUT NO INCLUDES BEFORE HERE
//
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <stddef.h>
#include <string.h>
//
// PUT NO CLIENT LIBRARY INCLUDES BEFORE HERE
//
#include "buffer_.h"
#include "iot_logging.h"
typedef struct BUFFER_TAG
{
unsigned char* buffer;
size_t size;
}BUFFER;
/* Codes_SRS_BUFFER_07_001: [BUFFER_new shall allocate a BUFFER_HANDLE that will contain a NULL unsigned char*.] */
BUFFER_HANDLE BUFFER_new(void)
{
BUFFER* temp = (BUFFER*)malloc(sizeof(BUFFER));
/* Codes_SRS_BUFFER_07_002: [BUFFER_new shall return NULL on any error that occurs.] */
if (temp != NULL)
{
temp->buffer = NULL;
temp->size = 0;
}
return (BUFFER_HANDLE)temp;
}
BUFFER_HANDLE BUFFER_create(const unsigned char* source, size_t size)
{
BUFFER* result;
/*Codes_SRS_BUFFER_02_001: [If source is NULL then BUFFER_create shall return NULL.]*/
if (source == NULL)
{
result = NULL;
}
else
{
/*Codes_SRS_BUFFER_02_002: [Otherwise, BUFFER_create shall allocate memory to hold size bytes and shall copy from source size bytes into the newly allocated memory.] */
result = (BUFFER*)malloc(sizeof(BUFFER));
if (result == NULL)
{
/*Codes_SRS_BUFFER_02_003: [If allocating memory fails, then BUFFER_create shall return NULL.] */
/*fallthrough*/
}
else
{
result->buffer = (unsigned char*)malloc(size);
if (result->buffer == NULL)
{
/*Codes_SRS_BUFFER_02_003: [If allocating memory fails, then BUFFER_create shall return NULL.]*/
free(result);
result = NULL;
}
else
{
/*Codes_SRS_BUFFER_02_004: [Otherwise, BUFFER_create shall return a non-NULL handle.] */
memcpy(result->buffer, source, size);
result->size = size;
}
}
}
return (BUFFER_HANDLE)result;
}
/* Codes_SRS_BUFFER_07_003: [BUFFER_delete shall delete the data associated with the BUFFER_HANDLE along with the Buffer.] */
void BUFFER_delete(BUFFER_HANDLE handle)
{
/* Codes_SRS_BUFFER_07_004: [BUFFER_delete shall not delete any BUFFER_HANDLE that is NULL.] */
if (handle != NULL)
{
BUFFER* b = (BUFFER*)handle;
if (b->buffer != NULL)
{
/* Codes_SRS_BUFFER_07_003: [BUFFER_delete shall delete the data associated with the BUFFER_HANDLE along with the Buffer.] */
free(b->buffer);
}
free(b);
}
}
/*return 0 if the buffer was copied*/
/*else return different than zero*/
/* Codes_SRS_BUFFER_07_008: [BUFFER_build allocates size_t bytes, copies the unsigned char* into the buffer and returns zero on success.] */
int BUFFER_build(BUFFER_HANDLE handle, const unsigned char* source, size_t size)
{
int result;
if (handle == NULL)
{
/* Codes_SRS_BUFFER_07_009: [BUFFER_build shall return nonzero if handle is NULL ] */
result = __LINE__;
}
/* Codes_SRS_BUFFER_01_002: [The size argument can be zero, in which case the underlying buffer held by the buffer instance shall be freed.] */
else if (size == 0)
{
/* Codes_SRS_BUFFER_01_003: [If size is zero, source can be NULL.] */
BUFFER* b = (BUFFER*)handle;
free(b->buffer);
b->buffer = NULL;
b->size = 0;
result = 0;
}
else
{
if (source == NULL)
{
/* Codes_SRS_BUFFER_01_001: [If size is positive and source is NULL, BUFFER_build shall return nonzero] */
result = __LINE__;
}
else
{
BUFFER* b = (BUFFER*)handle;
/* Codes_SRS_BUFFER_07_011: [BUFFER_build shall overwrite previous contents if the buffer has been previously allocated.] */
unsigned char* newBuffer = (unsigned char*)realloc(b->buffer, size);
if (newBuffer == NULL)
{
/* Codes_SRS_BUFFER_07_010: [BUFFER_build shall return nonzero if any error is encountered.] */
result = __LINE__;
}
else
{
b->buffer = newBuffer;
b->size = size;
/* Codes_SRS_BUFFER_01_002: [The size argument can be zero, in which case nothing shall be copied from source.] */
(void)memcpy(b->buffer, source, size);
result = 0;
}
}
}
return result;
}
/*return 0 if the buffer was pre-build(that is, had its space allocated)*/
/*else return different than zero*/
/* Codes_SRS_BUFFER_07_005: [BUFFER_pre_build allocates size_t bytes of BUFFER_HANDLE and returns zero on success.] */
int BUFFER_pre_build(BUFFER_HANDLE handle, size_t size)
{
int result;
if (handle == NULL)
{
/* Codes_SRS_BUFFER_07_006: [If handle is NULL or size is 0 then BUFFER_pre_build shall return a nonzero value.] */
result = __LINE__;
}
else if (size == 0)
{
/* Codes_SRS_BUFFER_07_006: [If handle is NULL or size is 0 then BUFFER_pre_build shall return a nonzero value.] */
result = __LINE__;
}
else
{
BUFFER* b = (BUFFER*)handle;
if (b->buffer != NULL)
{
/* Codes_SRS_BUFFER_07_007: [BUFFER_pre_build shall return nonzero if the buffer has been previously allocated and is not NULL.] */
result = __LINE__;
}
else
{
if ((b->buffer = (unsigned char*)malloc(size)) == NULL)
{
/* Codes_SRS_BUFFER_07_013: [BUFFER_pre_build shall return nonzero if any error is encountered.] */
result = __LINE__;
}
else
{
b->size = size;
result = 0;
}
}
}
return result;
}
/* Codes_SRS_BUFFER_07_019: [BUFFER_content shall return the data contained within the BUFFER_HANDLE.] */
int BUFFER_content(BUFFER_HANDLE handle, const unsigned char** content)
{
int result;
if ((handle == NULL) || (content == NULL))
{
/* Codes_SRS_BUFFER_07_020: [If the handle and/or content*is NULL BUFFER_content shall return nonzero.] */
result = __LINE__;
}
else
{
BUFFER* b = (BUFFER*)handle;
*content = b->buffer;
result = 0;
}
return result;
}
/*return 0 if everything went ok and whatever was built in the buffer was unbuilt*/
/* Codes_SRS_BUFFER_07_012: [BUFFER_unbuild shall clear the underlying unsigned char* data associated with the BUFFER_HANDLE this will return zero on success.] */
extern int BUFFER_unbuild(BUFFER_HANDLE handle)
{
int result;
if (handle == NULL)
{
/* Codes_SRS_BUFFER_07_014: [BUFFER_unbuild shall return a nonzero value if BUFFER_HANDLE is NULL.] */
result = __LINE__;
}
else
{
BUFFER* b = (BUFFER*)handle;
if (b->buffer != NULL)
{
free(b->buffer);
b->buffer = NULL;
b->size = 0;
result = 0;
}
else
{
/* Codes_SRS_BUFFER_07_015: [BUFFER_unbuild shall return a nonzero value if the unsigned char* referenced by BUFFER_HANDLE is NULL.] */
result = __LINE__;
}
}
return result;
}
/* Codes_SRS_BUFFER_07_016: [BUFFER_enlarge shall increase the size of the unsigned char* referenced by BUFFER_HANDLE.] */
int BUFFER_enlarge(BUFFER_HANDLE handle, size_t enlargeSize)
{
int result;
if (handle == NULL)
{
/* Codes_SRS_BUFFER_07_017: [BUFFER_enlarge shall return a nonzero result if any parameters are NULL or zero.] */
result = __LINE__;
}
else if (enlargeSize == 0)
{
/* Codes_SRS_BUFFER_07_017: [BUFFER_enlarge shall return a nonzero result if any parameters are NULL or zero.] */
result = __LINE__;
}
else
{
BUFFER* b = (BUFFER*)handle;
unsigned char* temp = (unsigned char*)realloc(b->buffer, b->size + enlargeSize);
if (temp == NULL)
{
/* Codes_SRS_BUFFER_07_018: [BUFFER_enlarge shall return a nonzero result if any error is encountered.] */
result = __LINE__;
}
else
{
b->buffer = temp;
b->size += enlargeSize;
result = 0;
}
}
return result;
}
/* Codes_SRS_BUFFER_07_021: [BUFFER_size shall place the size of the associated buffer in the size variable and return zero on success.] */
int BUFFER_size(BUFFER_HANDLE handle, size_t* size)
{
int result;
if ((handle == NULL) || (size == NULL))
{
/* Codes_SRS_BUFFER_07_022: [BUFFER_size shall return a nonzero value for any error that is encountered.] */
result = __LINE__;
}
else
{
BUFFER* b = (BUFFER*)handle;
*size = b->size;
result = 0;
}
return result;
}
/* Codes_SRS_BUFFER_07_024: [BUFFER_append concatenates b2 onto b1 without modifying b2 and shall return zero on success.] */
int BUFFER_append(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2)
{
int result;
if ( (handle1 == NULL) || (handle2 == NULL) || (handle1 == handle2) )
{
/* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */
result = __LINE__;
}
else
{
BUFFER* b1 = (BUFFER*)handle1;
BUFFER* b2 = (BUFFER*)handle2;
if (b1->buffer == NULL)
{
/* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */
result = __LINE__;
}
else if (b2->buffer == NULL)
{
/* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */
result = __LINE__;
}
else
{
unsigned char* temp = (unsigned char*)realloc(b1->buffer, b1->size + b2->size);
if (temp == NULL)
{
/* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */
result = __LINE__;
}
else
{
b1->buffer = temp;
// Append the BUFFER
memcpy(&b1->buffer[b1->size], b2->buffer, b2->size);
b1->size += b2->size;
result = 0;
}
}
}
return result;
}
/* Codes_SRS_BUFFER_07_025: [BUFFER_u_char shall return a pointer to the underlying unsigned char*.] */
unsigned char* BUFFER_u_char(BUFFER_HANDLE handle)
{
unsigned char* result;
if (handle == NULL)
{
/* Codes_SRS_BUFFER_07_026: [BUFFER_u_char shall return NULL for any error that is encountered.] */
result = NULL;
}
else
{
BUFFER* b = (BUFFER*)handle;
result = b->buffer;
}
return result;
}
/* Codes_SRS_BUFFER_07_027: [BUFFER_length shall return the size of the underlying buffer.] */
size_t BUFFER_length(BUFFER_HANDLE handle)
{
size_t result;
if (handle == NULL)
{
/* Codes_SRS_BUFFER_07_028: [BUFFER_length shall return zero for any error that is encountered.] */
result = 0;
}
else
{
BUFFER* b = (BUFFER*)handle;
result = b->size;
}
return result;
}
BUFFER_HANDLE BUFFER_clone(BUFFER_HANDLE handle)
{
BUFFER_HANDLE result;
if (handle == NULL)
{
result = NULL;
}
else
{
BUFFER* suppliedBuff = (BUFFER*)handle;
BUFFER* b = (BUFFER*)malloc(sizeof(BUFFER) );
if (b != NULL)
{
if ( (b->buffer = (unsigned char*)malloc(suppliedBuff->size) ) == NULL)
{
free(b);
result = NULL;
}
else
{
memcpy(b->buffer, suppliedBuff->buffer, suppliedBuff->size);
b->size = suppliedBuff->size;
result = (BUFFER_HANDLE)b;
}
}
else
{
result = NULL;
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,390 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "crt_abstractions.h"
#include "errno.h"
#include <stddef.h>
#include <limits.h>
#ifdef _MSC_VER
#else
#include "stdarg.h"
/*Codes_SRS_CRT_ABSTRACTIONS_99_008: [strcat_s shall append the src to dst and terminates the resulting string with a null character.]*/
int strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
{
int result;
/*Codes_SRS_CRT_ABSTRACTIONS_99_004: [If dst is NULL or unterminated, the error code returned shall be EINVAL & dst shall not be modified.]*/
if (dst == NULL)
{
result = EINVAL;
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_005: [If src is NULL, the error code returned shall be EINVAL and dst[0] shall be set to 0.]*/
else if (src == NULL)
{
dst[0] = '\0';
result = EINVAL;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_006: [If the dstSizeInBytes is 0 or smaller than the required size for dst & src, the error code returned shall be ERANGE & dst[0] set to 0.]*/
if (dstSizeInBytes == 0)
{
result = ERANGE;
dst[0] = '\0';
}
else
{
size_t dstStrLen = 0;
#ifdef __STDC_LIB_EXT1__
dstStrLen = strnlen_s(dst, dstSizeInBytes);
#else
size_t i;
for(i=0; (i < dstSizeInBytes) && (dst[i]!= '\0'); i++)
{
}
dstStrLen = i;
#endif
/*Codes_SRS_CRT_ABSTRACTIONS_99_004: [If dst is NULL or unterminated, the error code returned shall be EINVAL & dst shall not be modified.]*/
if (dstSizeInBytes == dstStrLen) /* this means the dst string is not terminated*/
{
result = EINVAL;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_009: [The initial character of src shall overwrite the terminating null character of dst.]*/
(void)strncpy(&dst[dstStrLen], src, dstSizeInBytes - dstStrLen);
/*Codes_SRS_CRT_ABSTRACTIONS_99_006: [If the dstSizeInBytes is 0 or smaller than the required size for dst & src, the error code returned shall be ERANGE & dst[0] set to 0.]*/
if (dst[dstSizeInBytes-1] != '\0')
{
dst[0] = '\0';
result = ERANGE;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_003: [strcat_s shall return Zero upon success.]*/
result = 0;
}
}
}
}
return result;
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_025: [strncpy_s shall copy the first N characters of src to dst, where N is the lesser of MaxCount and the length of src.]*/
int strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t maxCount)
{
int result;
int truncationFlag = 0;
/*Codes_SRS_CRT_ABSTRACTIONS_99_020: [If dst is NULL, the error code returned shall be EINVAL and dst shall not be modified.]*/
if (dst == NULL)
{
result = EINVAL;
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_021: [If src is NULL, the error code returned shall be EINVAL and dst[0] shall be set to 0.]*/
else if (src == NULL)
{
dst[0] = '\0';
result = EINVAL;
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_022: [If the dstSizeInBytes is 0, the error code returned shall be EINVAL and dst shall not be modified.]*/
else if (dstSizeInBytes == 0)
{
result = EINVAL;
}
else
{
size_t srcLength = strlen(src);
if (maxCount != _TRUNCATE)
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_041: [If those N characters will fit within dst (whose size is given as dstSizeInBytes) and still leave room for a null terminator, then those characters shall be copied and a terminating null is appended; otherwise, strDest[0] is set to the null character and ERANGE error code returned.]*/
if (srcLength > maxCount)
{
srcLength = maxCount;
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_023: [If dst is not NULL & dstSizeInBytes is smaller than the required size for the src string, the error code returned shall be ERANGE and dst[0] shall be set to 0.]*/
if (srcLength + 1 > dstSizeInBytes)
{
dst[0] = '\0';
result = ERANGE;
}
else
{
(void)strncpy(dst, src, srcLength);
dst[srcLength] = '\0';
/*Codes_SRS_CRT_ABSTRACTIONS_99_018: [strncpy_s shall return Zero upon success]*/
result = 0;
}
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_026: [If MaxCount is _TRUNCATE (defined as -1), then as much of src as will fit into dst shall be copied while still leaving room for the terminating null to be appended.]*/
else
{
if (srcLength + 1 > dstSizeInBytes )
{
srcLength = dstSizeInBytes - 1;
truncationFlag = 1;
}
(void)strncpy(dst, src, srcLength);
dst[srcLength] = '\0';
result = 0;
}
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_019: [If truncation occurred as a result of the copy, the error code returned shall be STRUNCATE.]*/
if (truncationFlag == 1)
{
result = STRUNCATE;
}
return result;
}
/* Codes_SRS_CRT_ABSTRACTIONS_99_016: [strcpy_s shall copy the contents in the address of src, including the terminating null character, to the location that's specified by dst.]*/
int strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
{
int result;
/* Codes_SRS_CRT_ABSTRACTIONS_99_012: [If dst is NULL, the error code returned shall be EINVAL & dst shall not be modified.]*/
if (dst == NULL)
{
result = EINVAL;
}
/* Codes_SRS_CRT_ABSTRACTIONS_99_013: [If src is NULL, the error code returned shall be EINVAL and dst[0] shall be set to 0.]*/
else if (src == NULL)
{
dst[0] = '\0';
result = EINVAL;
}
/* Codes_SRS_CRT_ABSTRACTIONS_99_014: [If the dstSizeInBytes is 0 or smaller than the required size for the src string, the error code returned shall be ERANGE & dst[0] set to 0.]*/
else if (dstSizeInBytes == 0)
{
dst[0] = '\0';
result = ERANGE;
}
else
{
size_t neededBuffer = strlen(src);
/* Codes_SRS_CRT_ABSTRACTIONS_99_014: [If the dstSizeInBytes is 0 or smaller than the required size for the src string, the error code returned shall be ERANGE & dst[0] set to 0.]*/
if (neededBuffer + 1 > dstSizeInBytes)
{
dst[0] = '\0';
result = ERANGE;
}
else
{
memcpy(dst, src, neededBuffer + 1);
/*Codes_SRS_CRT_ABSTRACTIONS_99_011: [strcpy_s shall return Zero upon success]*/
result = 0;
}
}
return result;
}
/*Codes_SRS_CRT_ABSTRACTIONS_99_029: [The sprintf_s function shall format and store series of characters and values in dst. Each argument (if any) is converted and output according to the corresponding Format Specification in the format variable.]*/
/*Codes_SRS_CRT_ABSTRACTIONS_99_031: [A null character is appended after the last character written.]*/
int sprintf_s(char* dst, size_t dstSizeInBytes, const char* format, ...)
{
int result;
/*Codes_SRS_CRT_ABSTRACTIONS_99_028: [If dst or format is a null pointer, sprintf_s shall return -1 and set errno to EINVAL]*/
if ((dst == NULL) ||
(format == NULL))
{
errno = EINVAL;
result = -1;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_033: [sprintf_s shall check the format string for valid formatting characters. If the check fails, the function returns -1.]*/
#if defined _MSC_VER
#error crt_abstractions is not provided for Microsoft Compilers
#else
/*not Microsoft compiler... */
#if defined __STDC_VERSION__
#if ((__STDC_VERSION__ == 199901L) || ( __STDC_VERSION__ == 201112L))
/*C99 compiler*/
va_list args;
va_start(args, format);
/*Codes_SRS_CRT_ABSTRACTIONS_99_027: [sprintf_s shall return the number of characters stored in dst upon success. This number shall not include the terminating null character.]*/
result = vsnprintf(dst, dstSizeInBytes, format, args);
va_end(args);
/*C99: Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n*/
if (result < 0)
{
result = -1;
}
else if ((size_t)result >= dstSizeInBytes)
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_034: [If the dst buffer is too small for the text being printed, then dst is set to an empty string and the function shall return -1.]*/
dst[0] = '\0';
result = -1;
}
else
{
/*do nothing, all is fine*/
}
#else
#error STDC_VERSION defined, but of unknown value; unable to sprinf_s, or provide own implementation
#endif
#else
#error for STDC_VERSION undefined (assumed C89), provide own implementation of sprintf_s
#endif
#endif
}
return result;
}
#endif /* _MSC_VER */
/*Codes_SRS_CRT_ABSTRACTIONS_99_038: [mallocAndstrcpy_s shall allocate memory for destination buffer to fit the string in the source parameter.]*/
int mallocAndStrcpy_s(char** destination, const char* source)
{
int result;
/*Codes_SRS_CRT_ABSTRACTIONS_99_036: [destination parameter or source parameter is NULL, the error code returned shall be EINVAL and destination shall not be modified.]*/
if ((destination == NULL) || (source == NULL))
{
/*If strDestination or strSource is a null pointer[...]these functions return EINVAL */
result = EINVAL;
}
else
{
size_t l = strlen(source);
*destination = (char*)malloc(l + 1);
/*Codes_SRS_CRT_ABSTRACTIONS_99_037: [Upon failure to allocate memory for the destination, the function will return ENOMEM.]*/
if (*destination == NULL)
{
result = ENOMEM;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_039: [mallocAndstrcpy_s shall copy the contents in the address source, including the terminating null character into location specified by the destination pointer after the memory allocation.]*/
int temp = strcpy_s(*destination, l + 1, source);
if (temp < 0) /*strcpy_s error*/
{
free(*destination);
*destination = NULL;
result = temp;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_99_035: [mallocAndstrcpy_s shall return Zero upon success]*/
result = 0;
}
}
}
return result;
}
/*takes "value" and transforms it into a decimal string*/
/*10 => "10"*/
/*return 0 when everything went ok*/
/*Codes_SRS_CRT_ABSTRACTIONS_02_001: [unsignedIntToString shall convert the parameter value to its decimal representation as a string in the buffer indicated by parameter destination having the size indicated by parameter destinationSize.] */
int unsignedIntToString(char* destination, size_t destinationSize, unsigned int value)
{
int result;
size_t pos;
/*the below loop gets the number in reverse order*/
/*Codes_SRS_CRT_ABSTRACTIONS_02_003: [If destination is NULL then unsignedIntToString shall fail.] */
/*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */
if (
(destination == NULL) ||
(destinationSize < 2) /*because the smallest number is '0\0' which requires 2 characters*/
)
{
result = __LINE__;
}
else
{
pos = 0;
do
{
destination[pos++] = '0' + (value % 10);
value /= 10;
} while ((value > 0) && (pos < (destinationSize-1)));
if (value == 0)
{
size_t w;
destination[pos] = '\0';
/*all converted and they fit*/
for (w = 0; w <= (pos-1) >> 1; w++)
{
char temp;
temp = destination[w];
destination[w] = destination[pos - 1 - w];
destination[pos -1 - w] = temp;
}
/*Codes_SRS_CRT_ABSTRACTIONS_02_004: [If the conversion has been successfull then unsignedIntToString shall return 0.] */
result = 0;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */
result = __LINE__;
}
}
return result;
}
/*takes "value" and transforms it into a decimal string*/
/*10 => "10"*/
/*return 0 when everything went ok*/
/*Codes_SRS_CRT_ABSTRACTIONS_02_001: [unsignedIntToString shall convert the parameter value to its decimal representation as a string in the buffer indicated by parameter destination having the size indicated by parameter destinationSize.] */
int size_tToString(char* destination, size_t destinationSize, size_t value)
{
int result;
size_t pos;
/*the below loop gets the number in reverse order*/
/*Codes_SRS_CRT_ABSTRACTIONS_02_003: [If destination is NULL then unsignedIntToString shall fail.] */
/*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */
if (
(destination == NULL) ||
(destinationSize < 2) /*because the smallest number is '0\0' which requires 2 characters*/
)
{
result = __LINE__;
}
else
{
pos = 0;
do
{
destination[pos++] = '0' + (value % 10);
value /= 10;
} while ((value > 0) && (pos < (destinationSize - 1)));
if (value == 0)
{
size_t w;
destination[pos] = '\0';
/*all converted and they fit*/
for (w = 0; w <= (pos - 1) >> 1; w++)
{
char temp;
temp = destination[w];
destination[w] = destination[pos - 1 - w];
destination[pos - 1 - w] = temp;
}
/*Codes_SRS_CRT_ABSTRACTIONS_02_004: [If the conversion has been successfull then unsignedIntToString shall return 0.] */
result = 0;
}
else
{
/*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */
result = __LINE__;
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,107 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "doublylinkedlist.h"
void
DList_InitializeListHead(
PDLIST_ENTRY ListHead
)
{
/* Codes_SRS_DLIST_06_005: [DList_InitializeListHead will initialize the Flink & Blink to the address of the DLIST_ENTRY.] */
ListHead->Flink = ListHead->Blink = ListHead;
return;
}
int
DList_IsListEmpty(
const PDLIST_ENTRY ListHead
)
{
/* Codes_SRS_DLIST_06_003: [DList_IsListEmpty shall return a non-zero value if there are no DLIST_ENTRY's on this list other than the list head.] */
/* Codes_SRS_DLIST_06_004: [DList_IsListEmpty shall return 0 if there is one or more items in the list.] */
return (ListHead->Flink == ListHead);
}
int
DList_RemoveEntryList(
PDLIST_ENTRY Entry
)
{
/* Codes_SRS_DLIST_06_008: [DList_RemoveEntryList shall remove a listEntry from whatever list it is properly part of.] */
/* Codes_SRS_DLIST_06_009: [The remaining list is properly formed.] */
/* Codes_SRS_DLIST_06_010: [DList_RemoveEntryList shall return non-zero if the remaining list is empty.] */
/* Codes_SRS_DLIST_06_011: [DList_RemoveEntryList shall return zero if the remaining list is NOT empty.] */
PDLIST_ENTRY Blink;
PDLIST_ENTRY Flink;
Flink = Entry->Flink;
Blink = Entry->Blink;
Blink->Flink = Flink;
Flink->Blink = Blink;
return (Flink == Blink);
}
PDLIST_ENTRY
DList_RemoveHeadList(
PDLIST_ENTRY ListHead
)
{
/* Codes_SRS_DLIST_06_012: [DList_RemoveHeadList removes the oldest entry from the list defined by the listHead parameter and returns a pointer to that entry.] */
/* Codes_SRS_DLIST_06_013: [DList_RemoveHeadList shall return listHead if that's the only item in the list.] */
PDLIST_ENTRY Flink;
PDLIST_ENTRY Entry;
Entry = ListHead->Flink;
Flink = Entry->Flink;
ListHead->Flink = Flink;
Flink->Blink = ListHead;
return Entry;
}
void
DList_InsertTailList(
PDLIST_ENTRY ListHead,
PDLIST_ENTRY Entry
)
{
PDLIST_ENTRY Blink;
/* Codes_SRS_DLIST_06_006: [DListInsertTailList shall place the DLIST_ENTRY at the end of the list defined by the listHead parameter.] */
Blink = ListHead->Blink;
Entry->Flink = ListHead;
Entry->Blink = Blink;
Blink->Flink = Entry;
ListHead->Blink = Entry;
return;
}
void
DList_AppendTailList(
PDLIST_ENTRY ListHead,
PDLIST_ENTRY ListToAppend
)
{
/* Codes_SRS_DLIST_06_007: [DList_AppendTailList shall place the list defined by listToAppend at the end of the list defined by the listHead parameter.] */
PDLIST_ENTRY ListEnd = ListHead->Blink;
ListHead->Blink->Flink = ListToAppend;
ListHead->Blink = ListToAppend->Blink;
ListToAppend->Blink->Flink = ListHead;
ListToAppend->Blink = ListEnd;
return;
}
/*Codes_SRS_DLIST_02_002: [DList_InsertHeadList inserts a singular entry in the list having as head listHead after "head".]*/
void DList_InsertHeadList(PDLIST_ENTRY listHead, PDLIST_ENTRY entry)
{
entry->Blink = listHead;
entry->Flink = listHead->Flink;
listHead->Flink->Blink = entry;
listHead->Flink = entry;
}

387
c/sharedutil/src/gballoc.c Normal file
Просмотреть файл

@ -0,0 +1,387 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "lock.h"
#include "iot_logging.h"
#include <stdint.h>
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)~(size_t)0)
#endif
typedef struct ALLOCATION_TAG
{
size_t size;
void* ptr;
void* next;
} ALLOCATION;
typedef enum GBALLOC_STATE_TAG
{
GBALLOC_STATE_INIT,
GBALLOC_STATE_NOT_INIT
} GBALLOC_STATE;
static ALLOCATION* head = NULL;
static size_t totalSize = 0;
static size_t maxSize = 0;
static GBALLOC_STATE gballocState = GBALLOC_STATE_NOT_INIT;
static LOCK_HANDLE gballocThreadSafeLock = NULL;
int gballoc_init(void)
{
int result;
if (gballocState != GBALLOC_STATE_NOT_INIT)
{
/* Codes_SRS_GBALLOC_01_025: [Init after Init shall fail and return a non-zero value.] */
result = __LINE__;
}
/* Codes_SRS_GBALLOC_01_026: [gballoc_Init shall create a lock handle that will be used to make the other gballoc APIs thread-safe.] */
else if ((gballocThreadSafeLock = Lock_Init()) == NULL)
{
/* Codes_SRS_GBALLOC_01_027: [If the Lock creation fails, gballoc_init shall return a non-zero value.]*/
result = __LINE__;
}
else
{
gballocState = GBALLOC_STATE_INIT;
/* Codes_ SRS_GBALLOC_01_002: [Upon initialization the total memory used and maximum total memory used tracked by the module shall be set to 0.] */
totalSize = 0;
maxSize = 0;
/* Codes_SRS_GBALLOC_01_024: [gballoc_init shall initialize the gballoc module and return 0 upon success.] */
result = 0;
}
return result;
}
void gballoc_deinit(void)
{
if (gballocState == GBALLOC_STATE_INIT)
{
/* Codes_SRS_GBALLOC_01_028: [gballoc_deinit shall free all resources allocated by gballoc_init.] */
(void)Lock_Deinit(gballocThreadSafeLock);
}
gballocState = GBALLOC_STATE_NOT_INIT;
}
void* gballoc_malloc(size_t size)
{
void* result;
if (gballocState != GBALLOC_STATE_INIT)
{
/* Codes_SRS_GBALLOC_01_039: [If gballoc was not initialized gballoc_malloc shall simply call malloc without any memory tracking being performed.] */
result = malloc(size);
}
/* Codes_SRS_GBALLOC_01_030: [gballoc_malloc shall ensure thread safety by using the lock created by gballoc_Init.] */
else if (LOCK_OK != Lock(gballocThreadSafeLock))
{
/* Codes_SRS_GBALLOC_01_048: [If acquiring the lock fails, gballoc_malloc shall return NULL.] */
LogError("Failed to get the Lock.\r\n");
result = NULL;
}
else
{
ALLOCATION* allocation = (ALLOCATION*)malloc(sizeof(ALLOCATION));
if (allocation == NULL)
{
result = NULL;
}
else
{
/* Codes_SRS_GBALLOC_01_003: [gb_malloc shall call the C99 malloc function and return its result.] */
result = malloc(size);
if (result == NULL)
{
/* Codes_SRS_GBALLOC_01_012: [When the underlying malloc call fails, gballoc_malloc shall return NULL and size should not be counted towards total memory used.] */
free(allocation);
}
else
{
/* Codes_SRS_GBALLOC_01_004: [If the underlying malloc call is successful, gb_malloc shall increment the total memory used with the amount indicated by size.] */
allocation->ptr = result;
allocation->size = size;
allocation->next = head;
head = allocation;
totalSize += size;
/* Codes_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */
if (maxSize < totalSize)
{
maxSize = totalSize;
}
}
}
(void)Unlock(gballocThreadSafeLock);
}
return result;
}
void* gballoc_calloc(size_t nmemb, size_t size)
{
void* result;
if (gballocState != GBALLOC_STATE_INIT)
{
/* Codes_SRS_GBALLOC_01_040: [If gballoc was not initialized gballoc_calloc shall simply call calloc without any memory tracking being performed.] */
result = calloc(nmemb, size);
}
/* Codes_SRS_GBALLOC_01_031: [gballoc_calloc shall ensure thread safety by using the lock created by gballoc_Init] */
else if (LOCK_OK != Lock(gballocThreadSafeLock))
{
/* Codes_SRS_GBALLOC_01_046: [If acquiring the lock fails, gballoc_calloc shall return NULL.] */
LogError("Failed to get the Lock.\r\n");
result = NULL;
}
else
{
ALLOCATION* allocation = (ALLOCATION*)malloc(sizeof(ALLOCATION));
if (allocation == NULL)
{
result = NULL;
}
else
{
/* Codes_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */
result = calloc(nmemb, size);
if (result == NULL)
{
/* Codes_SRS_GBALLOC_01_022: [When the underlying calloc call fails, gballoc_calloc shall return NULL and size should not be counted towards total memory used.] */
free(allocation);
}
else
{
/* Codes_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */
allocation->ptr = result;
allocation->size = nmemb * size;
allocation->next = head;
head = allocation;
totalSize += allocation->size;
/* Codes_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */
if (maxSize < totalSize)
{
maxSize = totalSize;
}
}
}
(void)Unlock(gballocThreadSafeLock);
}
return result;
}
void* gballoc_realloc(void* ptr, size_t size)
{
ALLOCATION* curr;
void* result;
ALLOCATION* allocation = NULL;
if (gballocState != GBALLOC_STATE_INIT)
{
/* Codes_SRS_GBALLOC_01_041: [If gballoc was not initialized gballoc_realloc shall shall simply call realloc without any memory tracking being performed.] */
result = realloc(ptr, size);
}
/* Codes_SRS_GBALLOC_01_032: [gballoc_realloc shall ensure thread safety by using the lock created by gballoc_Init.] */
else if (LOCK_OK != Lock(gballocThreadSafeLock))
{
/* Codes_SRS_GBALLOC_01_047: [If acquiring the lock fails, gballoc_realloc shall return NULL.] */
LogError("Failed to get the Lock.\r\n");
result = NULL;
}
else
{
if (ptr == NULL)
{
/* Codes_SRS_GBALLOC_01_017: [When ptr is NULL, gballoc_realloc shall call the underlying realloc with ptr being NULL and the realloc result shall be tracked by gballoc.] */
allocation = (ALLOCATION*)malloc(sizeof(ALLOCATION));
}
else
{
curr = head;
while (curr != NULL)
{
if (curr->ptr == ptr)
{
allocation = curr;
break;
}
else
{
curr = (ALLOCATION*)curr->next;
}
}
}
if (allocation == NULL)
{
/* Codes_SRS_GBALLOC_01_015: [When allocating memory used for tracking by gballoc_realloc fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */
/* Codes_SRS_GBALLOC_01_016: [When the ptr pointer cannot be found in the pointers tracked by gballoc, gballoc_realloc shall return NULL and the underlying realloc shall not be called.] */
result = NULL;
}
else
{
result = realloc(ptr, size);
if (result == NULL)
{
/* Codes_SRS_GBALLOC_01_014: [When the underlying realloc call fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */
if (ptr == NULL)
{
free(allocation);
}
}
else
{
if (ptr != NULL)
{
/* Codes_SRS_GBALLOC_01_006: [If the underlying realloc call is successful, gballoc_realloc shall look up the size associated with the pointer ptr and decrease the total memory used with that size.] */
allocation->ptr = result;
totalSize -= allocation->size;
allocation->size = size;
}
else
{
/* add block */
allocation->ptr = result;
allocation->size = size;
allocation->next = head;
head = allocation;
}
/* Codes_SRS_GBALLOC_01_007: [If realloc is successful, gballoc_realloc shall also increment the total memory used value tracked by this module.] */
totalSize += size;
/* Codes_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */
if (maxSize < totalSize)
{
maxSize = totalSize;
}
}
}
(void)Unlock(gballocThreadSafeLock);
}
return result;
}
void gballoc_free(void* ptr)
{
ALLOCATION* curr = head;
ALLOCATION* prev = NULL;
if (gballocState != GBALLOC_STATE_INIT)
{
/* Codes_SRS_GBALLOC_01_042: [If gballoc was not initialized gballoc_free shall shall simply call free.] */
free(ptr);
}
/* Codes_SRS_GBALLOC_01_033: [gballoc_free shall ensure thread safety by using the lock created by gballoc_Init.] */
else if (LOCK_OK != Lock(gballocThreadSafeLock))
{
/* Codes_SRS_GBALLOC_01_049: [If acquiring the lock fails, gballoc_free shall do nothing.] */
LogError("Failed to get the Lock.\r\n");
}
else
{
/* Codes_SRS_GBALLOC_01_009: [gballoc_free shall also look up the size associated with the ptr pointer and decrease the total memory used with the associated size amount.] */
while (curr != NULL)
{
if (curr->ptr == ptr)
{
/* Codes_SRS_GBALLOC_01_008: [gballoc_free shall call the C99 free function.] */
free(ptr);
totalSize -= curr->size;
if (prev != NULL)
{
prev->next = curr->next;
}
else
{
head = (ALLOCATION*)curr->next;
}
free(curr);
break;
}
prev = curr;
curr = (ALLOCATION*)curr->next;
}
if ((curr == NULL) && (ptr != NULL))
{
/* Codes_SRS_GBALLOC_01_019: [When the ptr pointer cannot be found in the pointers tracked by gballoc, gballoc_free shall not free any memory.] */
/* could not find the allocation */
LogError("Could not free allocation for address %p (not found)\r\n", ptr);
}
(void)Unlock(gballocThreadSafeLock);
}
}
size_t gballoc_getMaximumMemoryUsed(void)
{
size_t result;
/* Codes_SRS_GBALLOC_01_038: [If gballoc was not initialized gballoc_getMaximumMemoryUsed shall return MAX_INT_SIZE.] */
if (gballocState != GBALLOC_STATE_INIT)
{
LogError("gballoc is not initialized.\r\n");
result = SIZE_MAX;
}
/* Codes_SRS_GBALLOC_01_034: [gballoc_getMaximumMemoryUsed shall ensure thread safety by using the lock created by gballoc_Init.] */
else if (LOCK_OK != Lock(gballocThreadSafeLock))
{
/* Codes_SRS_GBALLOC_01_050: [If the lock cannot be acquired, gballoc_getMaximumMemoryUsed shall return SIZE_MAX.] */
LogError("Failed to get the Lock.\r\n");
result = SIZE_MAX;
}
else
{
/* Codes_SRS_GBALLOC_01_010: [gballoc_getMaximumMemoryUsed shall return the maximum amount of total memory used recorded since the module initialization.] */
result = maxSize;
Unlock(gballocThreadSafeLock);
}
return result;
}
size_t gballoc_getCurrentMemoryUsed(void)
{
size_t result;
/* Codes_SRS_GBALLOC_01_044: [If gballoc was not initialized gballoc_getCurrentMemoryUsed shall return SIZE_MAX.] */
if (gballocState != GBALLOC_STATE_INIT)
{
LogError("gballoc is not initialized.\r\n");
result = SIZE_MAX;
}
/* Codes_SRS_GBALLOC_01_036: [gballoc_getCurrentMemoryUsed shall ensure thread safety by using the lock created by gballoc_Init.]*/
else if (LOCK_OK != Lock(gballocThreadSafeLock))
{
/* Codes_SRS_GBALLOC_01_051: [If the lock cannot be acquired, gballoc_getCurrentMemoryUsed shall return SIZE_MAX.] */
LogError("Failed to get the Lock.\r\n");
result = SIZE_MAX;
}
else
{
/*Codes_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
result = totalSize;
Unlock(gballocThreadSafeLock);
}
return result;
}

239
c/sharedutil/src/hmac.c Normal file
Просмотреть файл

@ -0,0 +1,239 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/**************************** hmac.c ****************************/
/******************** See RFC 4634 for details ******************/
/*
* Description:
* This file implements the HMAC algorithm (Keyed-Hashing for
* Message Authentication, RFC2104), expressed in terms of the
* various SHA algorithms.
*/
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "sha.h"
/*
* hmac
*
* Description:
* This function will compute an HMAC message digest.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* key: [in]
* The secret shared key.
* key_len: [in]
* The length of the secret shared key.
* message_array: [in]
* An array of characters representing the message.
* length: [in]
* The length of the message in message_array
* digest: [out]
* Where the digest is returned.
* NOTE: The length of the digest is determined by
* the value of whichSha.
*
* Returns:
* sha Error Code.
*
*/
int hmac(SHAversion whichSha, const unsigned char *text, int text_len,
const unsigned char *key, int key_len,
uint8_t digest[USHAMaxHashSize])
{
HMACContext ctx;
return hmacReset(&ctx, whichSha, key, key_len) ||
hmacInput(&ctx, text, text_len) ||
hmacResult(&ctx, digest);
}
/*
* hmacReset
*
* Description:
* This function will initialize the hmacContext in preparation
* for computing a new HMAC message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* key: [in]
* The secret shared key.
* key_len: [in]
* The length of the secret shared key.
*
* Returns:
* sha Error Code.
*
*/
int hmacReset(HMACContext *ctx, enum SHAversion whichSha,
const unsigned char *key, int key_len)
{
int i, blocksize, hashsize;
/* inner padding - key XORd with ipad */
unsigned char k_ipad[USHA_Max_Message_Block_Size];
/* temporary buffer when keylen > blocksize */
unsigned char tempkey[USHAMaxHashSize];
if (!ctx) return shaNull;
blocksize = ctx->blockSize = USHABlockSize(whichSha);
hashsize = ctx->hashSize = USHAHashSize(whichSha);
ctx->whichSha = whichSha;
/*
* If key is longer than the hash blocksize,
* reset it to key = HASH(key).
*/
if (key_len > blocksize) {
USHAContext tctx;
int err = USHAReset(&tctx, whichSha) ||
USHAInput(&tctx, key, key_len) ||
USHAResult(&tctx, tempkey);
if (err != shaSuccess) return err;
key = tempkey;
key_len = hashsize;
}
/*
* The HMAC transform looks like:
*
* SHA(K XOR opad, SHA(K XOR ipad, text))
*
* where K is an n byte key.
* ipad is the byte 0x36 repeated blocksize times
* opad is the byte 0x5c repeated blocksize times
* and text is the data being protected.
*/
/* store key into the pads, XOR'd with ipad and opad values */
for (i = 0; i < key_len; i++) {
k_ipad[i] = key[i] ^ 0x36;
ctx->k_opad[i] = key[i] ^ 0x5c;
}
/* remaining pad bytes are '\0' XOR'd with ipad and opad values */
for (; i < blocksize; i++) {
k_ipad[i] = 0x36;
ctx->k_opad[i] = 0x5c;
}
/* perform inner hash */
/* init context for 1st pass */
return USHAReset(&ctx->shaContext, whichSha) ||
/* and start with inner pad */
USHAInput(&ctx->shaContext, k_ipad, blocksize);
}
/*
* hmacInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The HMAC context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/
int hmacInput(HMACContext *ctx, const unsigned char *text,
int text_len)
{
if (!ctx) return shaNull;
/* then text of datagram */
return USHAInput(&ctx->shaContext, text, text_len);
}
/*
* HMACFinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The HMAC context to update
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int hmacFinalBits(HMACContext *ctx,
const uint8_t bits,
unsigned int bitcount)
{
if (!ctx) return shaNull;
/* then final bits of datagram */
return USHAFinalBits(&ctx->shaContext, bits, bitcount);
}
/*
* HMACResult
*
* Description:
* This function will return the N-byte message digest into the
* Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the Nth element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the HMAC hash.
* digest: [out]
* Where the digest is returned.
* NOTE 2: The length of the hash is determined by the value of
* whichSha that was passed to hmacReset().
*
* Returns:
* sha Error Code.
*
*/
int hmacResult(HMACContext *ctx, uint8_t *digest)
{
if (!ctx) return shaNull;
/* finish up 1st pass */
/* (Use digest here as a temporary buffer.) */
return USHAResult(&ctx->shaContext, digest) ||
/* perform outer SHA */
/* init context for 2nd pass */
USHAReset(&ctx->shaContext, ctx->whichSha) ||
/* start with outer pad */
USHAInput(&ctx->shaContext, ctx->k_opad, ctx->blockSize) ||
/* then results of 1st hash */
USHAInput(&ctx->shaContext, digest, ctx->hashSize) ||
/* finish up 2nd pass */
USHAResult(&ctx->shaContext, digest);
}

Просмотреть файл

@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "hmacsha256.h"
#include "hmac.h"
#include "strings.h"
HMACSHA256_RESULT HMACSHA256_ComputeHash(const unsigned char* key, size_t keyLen, const unsigned char* payload, size_t payloadLen, BUFFER_HANDLE hash)
{
HMACSHA256_RESULT result;
if (key == NULL ||
keyLen == 0 ||
payload == NULL ||
payloadLen == 0 ||
hash == NULL)
{
result = HMACSHA256_INVALID_ARG;
}
else
{
if ((BUFFER_enlarge(hash, 32) != 0) ||
(hmac(SHA256, payload, payloadLen, key, keyLen, BUFFER_u_char(hash) ) != 0))
{
result = HMACSHA256_ERROR;
}
else
{
result = HMACSHA256_OK;
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,658 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "httpapiex.h"
#include "iot_logging.h"
#include "strings.h"
#include "crt_abstractions.h"
#include "vector.h"
typedef struct HTTPAPIEX_SAVED_OPTION_TAG
{
const char* optionName;
const void* value;
}HTTPAPIEX_SAVED_OPTION;
typedef struct HTTPAPIEX_HANDLE_DATA_TAG
{
STRING_HANDLE hostName;
int k;
HTTP_HANDLE httpHandle;
VECTOR_HANDLE savedOptions;
}HTTPAPIEX_HANDLE_DATA;
DEFINE_ENUM_STRINGS(HTTPAPIEX_RESULT, HTTPAPIEX_RESULT_VALUES);
#define LOG_HTTAPIEX_ERROR() LogError("error code = %s\r\n", ENUM_TO_STRING(HTTPAPIEX_RESULT, result))
HTTPAPIEX_HANDLE HTTPAPIEX_Create(const char* hostName)
{
HTTPAPIEX_HANDLE result;
/*Codes_SRS_HTTPAPIEX_02_001: [If parameter hostName is NULL then HTTPAPIEX_Create shall return NULL.]*/
if (hostName == NULL)
{
LogError("invalid (NULL) parameter\r\n");
result = NULL;
}
else
{
/*Codes_SRS_HTTPAPIEX_02_005: [If creating the handle fails for any reason, then HTTAPIEX_Create shall return NULL.] */
HTTPAPIEX_HANDLE_DATA* handleData = (HTTPAPIEX_HANDLE_DATA*)malloc(sizeof(HTTPAPIEX_HANDLE_DATA));
if (handleData == NULL)
{
LogError("malloc failed.\r\n");
result = NULL;
}
else
{
/*Codes_SRS_HTTPAPIEX_02_002: [Parameter hostName shall be saved.]*/
handleData->hostName = STRING_construct(hostName);
if (handleData->hostName == NULL)
{
free(handleData);
LogError("unable to STRING_construct\r\n");
result = NULL;
}
else
{
/*Codes_SRS_HTTPAPIEX_02_004: [Otherwise, HTTPAPIEX_Create shall return a HTTAPIEX_HANDLE suitable for further calls to the module.] */
handleData->savedOptions = VECTOR_create(sizeof(HTTPAPIEX_SAVED_OPTION));
if (handleData->savedOptions == NULL)
{
STRING_delete(handleData->hostName);
free(handleData);
result = NULL;
}
else
{
handleData->k = -1;
handleData->httpHandle = NULL;
result = handleData;
}
}
}
}
return result;
}
/*this function builds the default request http headers if none are specified*/
/*returns 0 if no error*/
/*any other code is error*/
static int buildRequestHttpHeadersHandle(HTTPAPIEX_HANDLE_DATA *handleData, BUFFER_HANDLE requestContent, HTTP_HEADERS_HANDLE originalRequestHttpHeadersHandle, bool* isOriginalRequestHttpHeadersHandle, HTTP_HEADERS_HANDLE* toBeUsedRequestHttpHeadersHandle)
{
int result;
if (originalRequestHttpHeadersHandle != NULL)
{
*toBeUsedRequestHttpHeadersHandle = originalRequestHttpHeadersHandle;
*isOriginalRequestHttpHeadersHandle = true;
}
else
{
/*Codes_SRS_HTTPAPIEX_02_009: [If parameter requestHttpHeadersHandle is NULL then HTTPAPIEX_ExecuteRequest shall allocate a temporary internal instance of HTTPHEADERS, shall add to that instance the following headers
Host:{hostname} - as it was indicated by the call to HTTPAPIEX_Create API call
Content-Length:the size of the requestContent parameter, and use this instance to all the subsequent calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.]
*/
*isOriginalRequestHttpHeadersHandle = false;
*toBeUsedRequestHttpHeadersHandle = HTTPHeaders_Alloc();
}
if (*toBeUsedRequestHttpHeadersHandle == NULL)
{
result = __LINE__;
LogError("unable to HTTPHeaders_Alloc\r\n");
}
else
{
char temp[22];
(void)size_tToString(temp, 22, BUFFER_length(requestContent)); /*cannot fail, MAX_uint64 has 19 digits*/
/*Codes_SRS_HTTPAPIEX_02_011: [If parameter requestHttpHeadersHandle is not NULL then HTTPAPIEX_ExecuteRequest shall create or update the following headers of the request:
Host:{hostname}
Content-Length:the size of the requestContent parameter, and shall use the so constructed HTTPHEADERS object to all calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.]
*/
/*Codes_SRS_HTTPAPIEX_02_009: [If parameter requestHttpHeadersHandle is NULL then HTTPAPIEX_ExecuteRequest shall allocate a temporary internal instance of HTTPHEADERS, shall add to that instance the following headers
Host:{hostname} - as it was indicated by the call to HTTPAPIEX_Create API call
Content-Length:the size of the requestContent parameter, and use this instance to all the subsequent calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.]
*/
if (!(
(HTTPHeaders_ReplaceHeaderNameValuePair(*toBeUsedRequestHttpHeadersHandle, "Host", STRING_c_str(handleData->hostName)) == HTTP_HEADERS_OK) &&
(HTTPHeaders_ReplaceHeaderNameValuePair(*toBeUsedRequestHttpHeadersHandle, "Content-Length", temp) == HTTP_HEADERS_OK)
))
{
if (! *isOriginalRequestHttpHeadersHandle)
{
HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle);
}
*toBeUsedRequestHttpHeadersHandle = NULL;
result = __LINE__;
}
else
{
result = 0;
}
}
return result;
}
static int buildResponseHttpHeadersHandle(HTTP_HEADERS_HANDLE originalResponsetHttpHeadersHandle, bool* isOriginalResponseHttpHeadersHandle, HTTP_HEADERS_HANDLE* toBeUsedResponsetHttpHeadersHandle)
{
int result;
if (originalResponsetHttpHeadersHandle == NULL)
{
if ((*toBeUsedResponsetHttpHeadersHandle = HTTPHeaders_Alloc()) == NULL)
{
result = __LINE__;
}
else
{
*isOriginalResponseHttpHeadersHandle = false;
result = 0;
}
}
else
{
*isOriginalResponseHttpHeadersHandle = true;
*toBeUsedResponsetHttpHeadersHandle = originalResponsetHttpHeadersHandle;
result = 0;
}
return result;
}
static int buildBufferIfNotExist(BUFFER_HANDLE originalRequestContent, bool* isOriginalRequestContent, BUFFER_HANDLE* toBeUsedRequestContent)
{
int result;
if (originalRequestContent == NULL)
{
*toBeUsedRequestContent = BUFFER_new();
if (*toBeUsedRequestContent == NULL)
{
result = __LINE__;
}
else
{
*isOriginalRequestContent = false;
result = 0;
}
}
else
{
*isOriginalRequestContent = true;
*toBeUsedRequestContent = originalRequestContent;
result = 0;
}
return result;
}
static unsigned int dummyStatusCode;
static int buildAllRequests(HTTPAPIEX_HANDLE_DATA* handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHttpHeadersHandle, BUFFER_HANDLE responseContent,
const char** toBeUsedRelativePath,
HTTP_HEADERS_HANDLE *toBeUsedRequestHttpHeadersHandle, bool *isOriginalRequestHttpHeadersHandle,
BUFFER_HANDLE *toBeUsedRequestContent, bool *isOriginalRequestContent,
unsigned int** toBeUsedStatusCode,
HTTP_HEADERS_HANDLE *toBeUsedResponseHttpHeadersHandle, bool *isOriginalResponseHttpHeadersHandle,
BUFFER_HANDLE *toBeUsedResponseContent, bool *isOriginalResponseContent)
{
int result;
(void)requestType;
/*Codes_SRS_HTTPAPIEX_02_013: [If requestContent is NULL then HTTPAPIEX_ExecuteRequest shall behave as if a buffer of zero size would have been used, that is, it shall call HTTPAPI_ExecuteRequest with parameter content = NULL and contentLength = 0.]*/
/*Codes_SRS_HTTPAPIEX_02_014: [If requestContent is not NULL then its content and its size shall be used for parameters content and contentLength of HTTPAPI_ExecuteRequest.] */
if (buildBufferIfNotExist(requestContent, isOriginalRequestContent, toBeUsedRequestContent) != 0)
{
result = __LINE__;
LogError("unable to build the request content\r\n");
}
else
{
if (buildRequestHttpHeadersHandle(handle, *toBeUsedRequestContent, requestHttpHeadersHandle, isOriginalRequestHttpHeadersHandle, toBeUsedRequestHttpHeadersHandle) != 0)
{
/*Codes_SRS_HTTPAPIEX_02_010: [If any of the operations in SRS_HTTAPIEX_02_009 fails, then HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_ERROR.] */
result = __LINE__;
if (*isOriginalRequestContent == false)
{
BUFFER_delete(*toBeUsedRequestContent);
}
LogError("unable to build the request http headers handle\r\n");
}
else
{
/*Codes_SRS_HTTPAPIEX_02_008: [If parameter relativePath is NULL then HTTPAPIEX_INVALID_ARG shall not assume a relative path - that is, it will assume an empty path ("").] */
if (relativePath == NULL)
{
*toBeUsedRelativePath = "";
}
else
{
*toBeUsedRelativePath = relativePath;
}
/*Codes_SRS_HTTPAPIEX_02_015: [If statusCode is NULL then HTTPAPIEX_ExecuteRequest shall not write in statusCode the HTTP status code, and it will use a temporary internal int for parameter statusCode to the calls of HTTPAPI_ExecuteRequest.] */
if (statusCode == NULL)
{
/*Codes_SRS_HTTPAPIEX_02_016: [If statusCode is not NULL then If statusCode is NULL then HTTPAPIEX_ExecuteRequest shall use it for parameter statusCode to the calls of HTTPAPI_ExecuteRequest.] */
*toBeUsedStatusCode = &dummyStatusCode;
}
else
{
*toBeUsedStatusCode = statusCode;
}
/*Codes_SRS_HTTPAPIEX_02_017: [If responseHeaders handle is NULL then HTTPAPIEX_ExecuteRequest shall create a temporary internal instance of HTTPHEADERS object and use that for responseHeaders parameter of HTTPAPI_ExecuteRequest call.] */
/*Codes_SRS_HTTPAPIEX_02_019: [If responseHeaders is not NULL, then then HTTPAPIEX_ExecuteRequest shall use that object as parameter responseHeaders of HTTPAPI_ExecuteRequest call.] */
if (buildResponseHttpHeadersHandle(responseHttpHeadersHandle, isOriginalResponseHttpHeadersHandle, toBeUsedResponseHttpHeadersHandle) != 0)
{
/*Codes_SRS_HTTPAPIEX_02_018: [If creating the temporary http headers in SRS_HTTPAPIEX_02_017 fails then HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_ERROR.] */
result = __LINE__;
if (*isOriginalRequestContent == false)
{
BUFFER_delete(*toBeUsedRequestContent);
}
if (*isOriginalRequestHttpHeadersHandle == false)
{
HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle);
}
LogError("unable to build response content\r\n");
}
else
{
/*Codes_SRS_HTTPAPIEX_02_020: [If responseContent is NULL then HTTPAPIEX_ExecuteRequest shall create a temporary internal BUFFER object and use that as parameter responseContent of HTTPAPI_ExecuteRequest call.] */
/*Codes_SRS_HTTPAPIEX_02_022: [If responseContent is not NULL then HTTPAPIEX_ExecuteRequest use that as parameter responseContent of HTTPAPI_ExecuteRequest call.] */
if (buildBufferIfNotExist(responseContent, isOriginalResponseContent, toBeUsedResponseContent) != 0)
{
/*Codes_SRS_HTTPAPIEX_02_021: [If creating the BUFFER_HANDLE in SRS_HTTPAPIEX_02_020 fails, then HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_ERROR.] */
result = __LINE__;
if (*isOriginalRequestContent == false)
{
BUFFER_delete(*toBeUsedRequestContent);
}
if (*isOriginalRequestHttpHeadersHandle == false)
{
HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle);
}
if (*isOriginalResponseHttpHeadersHandle == false)
{
HTTPHeaders_Free(*toBeUsedResponseHttpHeadersHandle);
}
LogError("unable to build response content\r\n");
}
else
{
result = 0;
}
}
}
}
return result;
}
HTTPAPIEX_RESULT HTTPAPIEX_ExecuteRequest(HTTPAPIEX_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode,
HTTP_HEADERS_HANDLE responseHttpHeadersHandle, BUFFER_HANDLE responseContent)
{
HTTPAPIEX_RESULT result;
/*Codes_SRS_HTTPAPIEX_02_006: [If parameter handle is NULL then HTTPAPIEX_ExecuteRequest shall fail and return HTTPAPIEX_INVALID_ARG.]*/
if (handle == NULL)
{
result = HTTPAPIEX_INVALID_ARG;
LOG_HTTAPIEX_ERROR();
}
else
{
/*Codes_SRS_HTTPAPIEX_02_007: [If parameter requestType does not indicate a valid request, HTTPAPIEX_ExecuteRequest shall fail and return HTTPAPIEX_INVALID_ARG.] */
if (requestType >= COUNT_ARG(HTTPAPI_REQUEST_TYPE_VALUES))
{
result = HTTPAPIEX_INVALID_ARG;
LOG_HTTAPIEX_ERROR();
}
else
{
HTTPAPIEX_HANDLE_DATA *handleData = (HTTPAPIEX_HANDLE_DATA *)handle;
/*call to buildAll*/
const char* toBeUsedRelativePath;
HTTP_HEADERS_HANDLE toBeUsedRequestHttpHeadersHandle; bool isOriginalRequestHttpHeadersHandle;
BUFFER_HANDLE toBeUsedRequestContent; bool isOriginalRequestContent;
unsigned int* toBeUsedStatusCode;
HTTP_HEADERS_HANDLE toBeUsedResponseHttpHeadersHandle; bool isOriginalResponseHttpHeadersHandle;
BUFFER_HANDLE toBeUsedResponseContent; bool isOriginalResponseContent;
if (buildAllRequests(handleData, requestType, relativePath, requestHttpHeadersHandle, requestContent, statusCode, responseHttpHeadersHandle, responseContent,
&toBeUsedRelativePath,
&toBeUsedRequestHttpHeadersHandle, &isOriginalRequestHttpHeadersHandle,
&toBeUsedRequestContent, &isOriginalRequestContent,
&toBeUsedStatusCode,
&toBeUsedResponseHttpHeadersHandle, &isOriginalResponseHttpHeadersHandle,
&toBeUsedResponseContent, &isOriginalResponseContent) != 0)
{
result = HTTPAPIEX_ERROR;
LOG_HTTAPIEX_ERROR();
}
else
{
/*Codes_SRS_HTTPAPIEX_02_023: [HTTPAPIEX_ExecuteRequest shall try to execute the HTTP call by ensuring the following API call sequence is respected:]*/
/*Codes_SRS_HTTPAPIEX_02_024: [If any point in the sequence fails, HTTPAPIEX_ExecuteRequest shall attempt to recover by going back to the previous step and retrying that step.]*/
/*Codes_SRS_HTTPAPIEX_02_025: [If the first step fails, then the sequence fails.]*/
/*Codes_SRS_HTTPAPIEX_02_026: [A step shall be retried at most once.]*/
/*Codes_SRS_HTTPAPIEX_02_027: [If a step has been retried then all subsequent steps shall be retried too.]*/
bool st[3] = { false, false, false }; /*the three levels of possible failure in resilient send: HTTAPI_Init, HTTPAPI_CreateConnection, HTTPAPI_ExecuteRequest*/
if (handleData->k == -1)
{
handleData->k = 0;
}
do
{
bool goOn;
if (handleData->k > 2)
{
/* error */
break;
}
if (st[handleData->k] == true) /*already been tried*/
{
goOn = false;
}
else
{
switch (handleData->k)
{
case 0:
{
if (HTTPAPI_Init() != HTTPAPI_OK)
{
goOn = false;
}
else
{
goOn = true;
}
break;
}
case 1:
{
if ((handleData->httpHandle = HTTPAPI_CreateConnection(STRING_c_str(handleData->hostName))) == NULL)
{
goOn = false;
}
else
{
size_t i;
size_t vectorSize = VECTOR_size(handleData->savedOptions);
for (i = 0; i < vectorSize; i++)
{
/*Codes_SRS_HTTPAPIEX_02_035: [HTTPAPIEX_ExecuteRequest shall pass all the saved options (see HTTPAPIEX_SetOption) to the newly create HTTPAPI_HANDLE in step 2 by calling HTTPAPI_SetOption.]*/
/*Codes_SRS_HTTPAPIEX_02_036: [If setting the option fails, then the failure shall be ignored.] */
HTTPAPIEX_SAVED_OPTION* option = VECTOR_element(handleData->savedOptions, i);
if (HTTPAPI_SetOption(handleData->httpHandle, option->optionName, option->value) != HTTPAPI_OK)
{
LogError("HTTPAPI_SetOption failed when called for option %s\r\n", option->optionName);
}
}
goOn = true;
}
break;
}
case 2:
{
if (HTTPAPI_ExecuteRequest(handleData->httpHandle, requestType, toBeUsedRelativePath, toBeUsedRequestHttpHeadersHandle, BUFFER_u_char(toBeUsedRequestContent), BUFFER_length(toBeUsedRequestContent), toBeUsedStatusCode, toBeUsedResponseHttpHeadersHandle, toBeUsedResponseContent) != HTTPAPI_OK)
{
goOn = false;
}
else
{
goOn = true;
}
break;
}
default:
{
/*serious error*/
goOn = false;
break;
}
}
}
if (goOn)
{
if (handleData->k == 2)
{
/*Codes_SRS_HTTPAPIEX_02_028: [HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_OK when a call to HTTPAPI_ExecuteRequest has been completed successfully.]*/
result = HTTPAPIEX_OK;
goto out;
}
else
{
st[handleData->k] = true;
handleData->k++;
st[handleData->k] = false;
}
}
else
{
st[handleData->k] = false;
handleData->k--;
switch (handleData->k)
{
case 0:
{
HTTPAPI_Deinit();
break;
}
case 1:
{
HTTPAPI_CloseConnection(handleData->httpHandle);
handleData->httpHandle = NULL;
break;
}
case 2:
{
break;
}
default:
{
break;
}
}
}
} while (handleData->k >= 0);
/*Codes_SRS_HTTPAPIEX_02_029: [Otherwise, HTTAPIEX_ExecuteRequest shall return HTTPAPIEX_RECOVERYFAILED.] */
result = HTTPAPIEX_RECOVERYFAILED;
LogError("unable to recover sending to a working state\r\n");
out:;
/*in all cases, unbuild the temporaries*/
if (isOriginalRequestContent == false)
{
BUFFER_delete(toBeUsedRequestContent);
}
if (isOriginalRequestHttpHeadersHandle == false)
{
HTTPHeaders_Free(toBeUsedRequestHttpHeadersHandle);
}
if (isOriginalResponseContent == false)
{
BUFFER_delete(toBeUsedResponseContent);
}
if (isOriginalResponseHttpHeadersHandle == false)
{
HTTPHeaders_Free(toBeUsedResponseHttpHeadersHandle);
}
}
}
}
return result;
}
void HTTPAPIEX_Destroy(HTTPAPIEX_HANDLE handle)
{
if (handle != NULL)
{
/*Codes_SRS_HTTPAPIEX_02_042: [HTTPAPIEX_Destroy shall free all the resources used by HTTAPIEX_HANDLE.]*/
size_t i;
size_t vectorSize;
HTTPAPIEX_HANDLE_DATA* handleData = (HTTPAPIEX_HANDLE_DATA*)handle;
if (handleData->k == 2)
{
HTTPAPI_CloseConnection(handleData->httpHandle);
HTTPAPI_Deinit();
}
STRING_delete(handleData->hostName);
vectorSize = VECTOR_size(handleData->savedOptions);
for (i = 0; i < vectorSize; i++)
{
HTTPAPIEX_SAVED_OPTION*savedOption = VECTOR_element(handleData->savedOptions, i);
free((void*)savedOption->optionName);
free((void*)savedOption->value);
}
VECTOR_destroy(handleData->savedOptions);
free(handle);
}
else
{
/*Codes_SRS_HTTPAPIEX_02_043: [If parameter handle is NULL then HTTPAPIEX_Destroy shall take no action.] */
}
}
static bool sameName(const void* element, const void* value)
{
return (strcmp(((HTTPAPIEX_SAVED_OPTION*)element)->optionName, value) == 0) ? true : false;
}
/*return 0 on success, any other value is error*/
/*obs: value is already cloned at the time of calling this function */
static int createOrUpdateOption(HTTPAPIEX_HANDLE_DATA* handleData, const char* optionName, const void* value)
{
/*this function is called after the option value has been saved (cloned)*/
int result;
/*decide bwtween update or create*/
HTTPAPIEX_SAVED_OPTION* whereIsIt = VECTOR_find_if(handleData->savedOptions, sameName, optionName);
if (whereIsIt != NULL)
{
free((void*)(whereIsIt->value));
whereIsIt->value = value;
result = 0;
}
else
{
HTTPAPIEX_SAVED_OPTION newOption;
if (mallocAndStrcpy_s((char**)&(newOption.optionName), optionName) != 0)
{
free((void*)value);
result = __LINE__;
}
else
{
newOption.value = value;
if (VECTOR_push_back(handleData->savedOptions, &newOption, 1) != 0)
{
LogError("unable to VECTOR_push_back\r\n");
free((void*)newOption.optionName);
free((void*)value);
result = __LINE__;
}
else
{
result = 0;
}
}
}
return result;
}
HTTPAPIEX_RESULT HTTPAPIEX_SetOption(HTTPAPIEX_HANDLE handle, const char* optionName, const void* value)
{
HTTPAPIEX_RESULT result;
/*Codes_SRS_HTTPAPIEX_02_032: [If parameter handle is NULL then HTTPAPIEX_SetOption shall return HTTPAPIEX_INVALID_ARG.] */
/*Codes_SRS_HTTPAPIEX_02_033: [If parameter optionName is NULL then HTTPAPIEX_SetOption shall return HTTPAPIEX_INVALID_ARG.] */
/*Codes_SRS_HTTPAPIEX_02_034: [If parameter value is NULL then HTTPAPIEX_SetOption shall return HTTPAPIEX_INVALID_ARG.] */
if (
(handle == NULL) ||
(optionName == NULL) ||
(value == NULL)
)
{
result = HTTPAPIEX_INVALID_ARG;
LOG_HTTAPIEX_ERROR();
}
else
{
const void* savedOption;
HTTPAPI_RESULT saveOptionResult;
/*Codes_SRS_HTTPAPIEX_02_037: [HTTPAPIEX_SetOption shall attempt to save the value of the option by calling HTTPAPI_CloneOption passing optionName and value, irrespective of the existence of a HTTPAPI_HANDLE] */
saveOptionResult = HTTPAPI_CloneOption(optionName, value, &savedOption);
if(saveOptionResult == HTTPAPI_INVALID_ARG)
{
/*Codes_SRS_HTTPAPIEX_02_038: [If HTTPAPI_CloneOption returns HTTPAPI_INVALID_ARG then HTTPAPIEX shall return HTTPAPIEX_INVALID_ARG.] */
result = HTTPAPIEX_INVALID_ARG;
LOG_HTTAPIEX_ERROR();
}
else if (saveOptionResult != HTTPAPI_OK)
{
/*Codes_SRS_HTTPAPIEX_02_040: [For all other return values of HTTPAPI_SetOption, HTTPIAPIEX_SetOption shall return HTTPAPIEX_ERROR.] */
result = HTTPAPIEX_ERROR;
LOG_HTTAPIEX_ERROR();
}
else
{
HTTPAPIEX_HANDLE_DATA* handleData = (HTTPAPIEX_HANDLE_DATA*)handle;
/*Codes_SRS_HTTPAPIEX_02_039: [If HTTPAPI_CloneOption returns HTTPAPI_OK then HTTPAPIEX_SetOption shall create or update the pair optionName/value.]*/
if (createOrUpdateOption(handleData, optionName, savedOption) != 0)
{
/*Codes_SRS_HTTPAPIEX_02_041: [If creating or updating the pair optionName/value fails then shall return HTTPAPIEX_ERROR.] */
result = HTTPAPIEX_ERROR;
LOG_HTTAPIEX_ERROR();
}
else
{
/*Codes_SRS_HTTPAPIEX_02_031: [If HTTPAPI_HANDLE exists then HTTPAPIEX_SetOption shall call HTTPAPI_SetOption passing the same optionName and value and shall return a value conforming to the below table:] */
if (handleData->httpHandle != NULL)
{
HTTPAPI_RESULT HTTPAPI_result = HTTPAPI_SetOption(handleData->httpHandle, optionName, value);
if (HTTPAPI_result == HTTPAPI_OK)
{
result = HTTPAPIEX_OK;
}
else if (HTTPAPI_result == HTTPAPI_INVALID_ARG)
{
result = HTTPAPIEX_INVALID_ARG;
LOG_HTTAPIEX_ERROR();
}
else
{
result = HTTPAPIEX_ERROR;
LOG_HTTAPIEX_ERROR();
}
}
else
{
result = HTTPAPIEX_OK;
}
}
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,142 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <stddef.h>
#include "strings.h"
#include "buffer_.h"
#include "sastoken.h"
#include "httpheaders.h"
#include "httpapiex.h"
#include "httpapiexsas.h"
#include "iot_logging.h"
typedef struct HTTPAPIEX_SAS_STATE_TAG
{
STRING_HANDLE key;
STRING_HANDLE uriResource;
STRING_HANDLE keyName;
}HTTPAPIEX_SAS_STATE;
HTTPAPIEX_SAS_HANDLE HTTPAPIEX_SAS_Create(STRING_HANDLE key, STRING_HANDLE uriResource, STRING_HANDLE keyName)
{
HTTPAPIEX_SAS_HANDLE result = NULL;
if (key == NULL)
{
/*Codes_SRS_HTTPAPIEXSAS_06_001: [If the parameter key is NULL then HTTPAPIEX_SAS_Create shall return NULL.]*/
LogError("No key passed to HTTPAPIEX_SAS_Create.\r\n");
}
else if (uriResource == NULL)
{
/*Codes_SRS_HTTPAPIEXSAS_06_002: [If the parameter uriResource is NULL then HTTPAPIEX_SAS_Create shall return NULL.]*/
LogError("No uri resource passed to HTTPAPIEX_SAS_Create.\r\n");
}
else if (keyName == NULL)
{
/*Codes_SRS_HTTPAPIEXSAS_06_003: [If the parameter keyName is NULL then HTTPAPIEX_SAS_Create shall return NULL.]*/
LogError("No key name passed to HTTPAPIEX_SAS_Create.\r\n");
}
else
{
HTTPAPIEX_SAS_STATE* state = malloc(sizeof(HTTPAPIEX_SAS_STATE));
/*Codes_SRS_HTTPAPIEXSAS_06_004: [If there are any other errors in the instantiation of this handle then HTTPAPIEX_SAS_Create shall return NULL.]*/
if (state != NULL)
{
state->key = NULL;
state->uriResource = NULL;
state->keyName = NULL;
if (((state->key = STRING_clone(key)) == NULL) ||
((state->uriResource = STRING_clone(uriResource)) == NULL) ||
((state->keyName = STRING_clone(keyName)) == NULL))
{
/*Codes_SRS_HTTPAPIEXSAS_06_004: [If there are any other errors in the instantiation of this handle then HTTPAPIEX_SAS_Create shall return NULL.]*/
LogError("Unable to clone the arguments.\r\n");
HTTPAPIEX_SAS_Destroy(state);
}
else
{
result = state;
}
}
}
return result;
}
void HTTPAPIEX_SAS_Destroy(HTTPAPIEX_SAS_HANDLE handle)
{
/*Codes_SRS_HTTPAPIEXSAS_06_005: [If the parameter handle is NULL then HTTAPIEX_SAS_Destroy shall do nothing and return.]*/
if (handle)
{
HTTPAPIEX_SAS_STATE* state = (HTTPAPIEX_SAS_STATE*)handle;
/*Codes_SRS_HTTPAPIEXSAS_06_006: [HTTAPIEX_SAS_Destroy shall deallocate any structures denoted by the parameter handle.]*/
if (state->key)
{
STRING_delete(state->key);
}
if (state->uriResource)
{
STRING_delete(state->uriResource);
}
if (state->keyName)
{
STRING_delete(state->keyName);
}
free(state);
}
}
HTTPAPIEX_RESULT HTTPAPIEX_SAS_ExecuteRequest(HTTPAPIEX_SAS_HANDLE sasHandle, HTTPAPIEX_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode, HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent)
{
/*Codes_SRS_HTTPAPIEXSAS_06_007: [If the parameter sasHandle is NULL then HTTPAPIEX_SAS_ExecuteRequest shall simply invoke HTTPAPIEX_ExecuteRequest with the remaining parameters (following sasHandle) as its arguments and shall return immediately with the result of that call as the result of HTTPAPIEX_SAS_ExecuteRequest.]*/
if (sasHandle != NULL)
{
/*Codes_SRS_HTTPAPIEXSAS_06_008: [if the parameter requestHttpHeadersHandle is NULL then fallthrough.]*/
if (requestHttpHeadersHandle != NULL)
{
/*Codes_SRS_HTTPAPIEXSAS_06_009: [HTTPHeaders_FindHeaderValue shall be invoked with the requestHttpHeadersHandle as its first argument and the string "Authorization" as its second argument.]*/
/*Codes_SRS_HTTPAPIEXSAS_06_010: [If the return result of the invocation of HTTPHeaders_FindHeaderValue is NULL then fallthrough.]*/
if (HTTPHeaders_FindHeaderValue(requestHttpHeadersHandle, "Authorization") != NULL)
{
HTTPAPIEX_SAS_STATE* state = (HTTPAPIEX_SAS_STATE*)sasHandle;
/*Codes_SRS_HTTPAPIEXSAS_06_018: [A value of type time_t that shall be known as currentTime is obtained from calling get_time.]*/
time_t currentTime = get_time(NULL);
/*Codes_SRS_HTTPAPIEXSAS_06_019: [If the value of currentTime is (time_t)-1 is then fallthrough.]*/
if (currentTime == (time_t)-1)
{
LogError("Time does not appear to be working.\r\n");
}
else
{
/*Codes_SRS_HTTPAPIEXSAS_06_011: [SASToken_Create shall be invoked.]*/
/*Codes_SRS_HTTPAPIEXSAS_06_012: [If the return result of SASToken_Create is NULL then fallthrough.]*/
size_t expiry = (size_t)(difftime(currentTime, 0) + 3600);
STRING_HANDLE newSASToken = SASToken_Create(state->key, state->uriResource, state->keyName, expiry);
if (newSASToken != NULL)
{
/*Codes_SRS_HTTPAPIEXSAS_06_013: [HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (newSASToken) as its third argument.]*/
if (HTTPHeaders_ReplaceHeaderNameValuePair(requestHttpHeadersHandle, "Authorization", STRING_c_str(newSASToken)) != HTTP_HEADERS_OK)
{
/*Codes_SRS_HTTPAPIEXSAS_06_014: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/
LogError("Unable to replace the old SAS Token.\r\n");
}
/*Codes_SRS_HTTPAPIEXSAS_06_015: [STRING_delete(newSASToken) will be invoked.]*/
STRING_delete(newSASToken);
}
else
{
LogError("Unable to create a new SAS token.\r\n");
}
}
}
}
}
/*Codes_SRS_HTTPAPIEXSAS_06_016: [HTTPAPIEX_ExecuteRequest with the remaining parameters (following sasHandle) as its arguments will be invoked and the result of that call is the result of HTTPAPIEX_SAS_ExecuteRequest.]*/
return HTTPAPIEX_ExecuteRequest(handle,requestType,relativePath,requestHttpHeadersHandle,requestContent,statusCode,responseHeadersHandle,responseContent);
}

Просмотреть файл

@ -0,0 +1,336 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "map.h"
#include "httpheaders.h"
#include "string.h"
#include "crt_abstractions.h"
#include "iot_logging.h"
static const char COLON_AND_SPACE[] = { ':', ' ', '\0' };
#define COLON_AND_SPACE_LENGTH ((sizeof(COLON_AND_SPACE)/sizeof(COLON_AND_SPACE[0]))-1)
static const char COMMA_AND_SPACE[] = { ',', ' ', '\0' };
#define COMMA_AND_SPACE_LENGTH ((sizeof(COMMA_AND_SPACE)/sizeof(COMMA_AND_SPACE[0]))-1)
DEFINE_ENUM_STRINGS(HTTP_HEADERS_RESULT, HTTP_HEADERS_RESULT_VALUES);
typedef struct HTTP_HEADERS_HANDLE_DATA_TAG
{
MAP_HANDLE headers;
} HTTP_HEADERS_HANDLE_DATA;
HTTP_HEADERS_HANDLE HTTPHeaders_Alloc(void)
{
/*Codes_SRS_HTTP_HEADERS_99_002:[ This API shall produce a HTTP_HANDLE that can later be used in subsequent calls to the module.]*/
HTTP_HEADERS_HANDLE_DATA* result;
result = (HTTP_HEADERS_HANDLE_DATA*)malloc(sizeof(HTTP_HEADERS_HANDLE_DATA));
if (result == NULL)
{
LogError("malloc failed\r\n");
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_004:[ After a successful init, HTTPHeaders_GetHeaderCount shall report 0 existing headers.]*/
result->headers = Map_Create(NULL);
if (result->headers == NULL)
{
LogError("Map_Create failed\r\n");
free(result);
result = NULL;
}
else
{
/*all is fine*/
}
}
/*Codes_SRS_HTTP_HEADERS_99_003:[ The function shall return NULL when the function cannot execute properly]*/
return (HTTP_HEADERS_HANDLE)result;
}
/*Codes_SRS_HTTP_HEADERS_99_005:[ Calling this API shall de-allocate the data structures allocated by previous API calls to the same handle.]*/
void HTTPHeaders_Free(HTTP_HEADERS_HANDLE handle)
{
/*Codes_SRS_HTTP_HEADERS_02_001: [If httpHeadersHandle is NULL then HTTPHeaders_Free shall perform no action.] */
if (handle == NULL)
{
/*do nothing*/
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_005:[ Calling this API shall de-allocate the data structures allocated by previous API calls to the same handle.]*/
HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle;
Map_Destroy(handleData->headers);
free(handleData);
}
}
/*Codes_SRS_HTTP_HEADERS_99_012:[ Calling this API shall record a header from name and value parameters.]*/
static HTTP_HEADERS_RESULT headers_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE handle, const char* name, const char* value, bool replace)
{
HTTP_HEADERS_RESULT result;
/*Codes_SRS_HTTP_HEADERS_99_014:[ The function shall return when the handle is not valid or when name parameter is NULL or when value parameter is NULL.]*/
if (
(handle == NULL) ||
(name == NULL) ||
(value == NULL)
)
{
result = HTTP_HEADERS_INVALID_ARG;
LogError("invalid arg (NULL) , result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_036:[ If name contains the characters outside character codes 33 to 126 then the return value shall be HTTP_HEADERS_INVALID_ARG]*/
/*Codes_SRS_HTTP_HEADERS_99_031:[ If name contains the character ":" then the return value shall be HTTP_HEADERS_INVALID_ARG.]*/
size_t i;
size_t nameLen = strlen(name);
for (i = 0; i < nameLen; i++)
{
if ((name[i] < 33) || (126 < name[i]) || (name[i] == ':'))
{
break;
}
}
if (i < nameLen)
{
result = HTTP_HEADERS_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle;
const char* existingValue = Map_GetValueFromKey(handleData->headers, name);
/*eat up the whitespaces from value, as per RFC 2616, chapter 4.2 "The field value MAY be preceded by any amount of LWS, though a single SP is preferred."*/
/*Codes_SRS_HTTP_HEADERS_02_002: [The LWS from the beginning of the value shall not be stored.] */
while ((value[0] == ' ') || (value[0] == '\t') || (value[0] == '\r') || (value[0] == '\n'))
{
value++;
}
if (!replace && (existingValue != NULL))
{
char* newValue = (char*)malloc(strlen(existingValue) + COMMA_AND_SPACE_LENGTH + strlen(value) + 1);
if (newValue == NULL)
{
/*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/
result = HTTP_HEADERS_ALLOC_FAILED;
LogError("failed to malloc , result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_017:[ If the name already exists in the collection of headers, the function shall concatenate the new value after the existing value, separated by a comma and a space as in: old-value+", "+new-value.]*/
(void)strcpy(newValue, existingValue);
(void)strcat(newValue, COMMA_AND_SPACE);
(void)strcat(newValue, value);
/*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/
if (Map_AddOrUpdate(handleData->headers, name, newValue) != MAP_OK)
{
/*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/
result = HTTP_HEADERS_ERROR;
LogError("failed to Map_AddOrUpdate, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_013:[ The function shall return HTTP_HEADERS_OK when execution is successful.]*/
result = HTTP_HEADERS_OK;
}
free(newValue);
}
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/
if (Map_AddOrUpdate(handleData->headers, name, value) != MAP_OK)
{
/*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/
result = HTTP_HEADERS_ALLOC_FAILED;
LogError("failed to Map_AddOrUpdate, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
result = HTTP_HEADERS_OK;
}
}
}
}
return result;
}
HTTP_HEADERS_RESULT HTTPHeaders_AddHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value)
{
return headers_ReplaceHeaderNameValuePair(httpHeadersHandle, name, value, false);
}
/* Codes_SRS_HTTP_HEADERS_06_001: [This API will perform exactly as HTTPHeaders_AddHeaderNameValuePair except that if the header name already exists the already existing value will be replaced as opposed to concatenated to.] */
HTTP_HEADERS_RESULT HTTPHeaders_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value)
{
return headers_ReplaceHeaderNameValuePair(httpHeadersHandle, name, value, true);
}
const char* HTTPHeaders_FindHeaderValue(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name)
{
const char* result;
/*Codes_SRS_HTTP_HEADERS_99_022:[ The return value shall be NULL if name parameter is NULL or if httpHeadersHandle is NULL]*/
if (
(httpHeadersHandle == NULL) ||
(name == NULL)
)
{
result = NULL;
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_018:[ Calling this API shall retrieve the value for a previously stored name.]*/
/*Codes_SRS_HTTP_HEADERS_99_020:[ The return value shall be different than NULL when the name matches the name of a previously stored name:value pair.] */
/*Codes_SRS_HTTP_HEADERS_99_021:[ In this case the return value shall point to a string that shall strcmp equal to the original stored string.]*/
HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)httpHeadersHandle;
result = Map_GetValueFromKey(handleData->headers, name);
}
return result;
}
HTTP_HEADERS_RESULT HTTPHeaders_GetHeaderCount(HTTP_HEADERS_HANDLE handle, size_t* headerCount)
{
HTTP_HEADERS_RESULT result;
/*Codes_SRS_HTTP_HEADERS_99_024:[ The function shall return HTTP_HEADERS_INVALID_ARG when an invalid handle is passed.]*/
/*Codes_SRS_HTTP_HEADERS_99_025:[ The function shall return HTTP_HEADERS_INVALID_ARG when headersCount is NULL.]*/
if ((handle == NULL) ||
(headerCount == NULL))
{
result = HTTP_HEADERS_INVALID_ARG;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
HTTP_HEADERS_HANDLE_DATA *handleData = (HTTP_HEADERS_HANDLE_DATA *)handle;
const char*const* keys;
const char*const* values;
/*Codes_SRS_HTTP_HEADERS_99_023:[ Calling this API shall provide the number of stored headers.]*/
if (Map_GetInternals(handleData->headers, &keys, &values, headerCount) != MAP_OK)
{
/*Codes_SRS_HTTP_HEADERS_99_037:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs.]*/
result = HTTP_HEADERS_ERROR;
LogError("Map_GetInternals failed, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_026:[ The function shall write in *headersCount the number of currently stored headers and shall return HTTP_HEADERS_OK]*/
result = HTTP_HEADERS_OK;
}
}
return result;
}
/*produces a string in *destination that is equal to name: value*/
HTTP_HEADERS_RESULT HTTPHeaders_GetHeader(HTTP_HEADERS_HANDLE handle, size_t index, char** destination)
{
HTTP_HEADERS_RESULT result = HTTP_HEADERS_OK;
/*Codes_SRS_HTTP_HEADERS_99_028:[ The function shall return NULL if the handle is invalid.]*/
/*Codes_SRS_HTTP_HEADERS_99_032:[ The function shall return HTTP_HEADERS_INVALID_ARG if the destination is NULL]*/
if (
(handle == NULL) ||
(destination == NULL)
)
{
result = HTTP_HEADERS_INVALID_ARG;
LogError("invalid arg (NULL), result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
/*Codes_SRS_HTTP_HEADERS_99_029:[ The function shall return HTTP_HEADERS_INVALID_ARG if index is not valid (for example, out of range) for the currently stored headers.]*/
else
{
HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle;
const char*const* keys;
const char*const* values;
size_t headerCount;
if (Map_GetInternals(handleData->headers, &keys, &values, &headerCount) != MAP_OK)
{
/*Codes_SRS_HTTP_HEADERS_99_034:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs]*/
result = HTTP_HEADERS_ERROR;
LogError("Map_GetInternals failed, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_029:[ The function shall return HTTP_HEADERS_INVALID_ARG if index is not valid (for example, out of range) for the currently stored headers.]*/
if (index >= headerCount)
{
result = HTTP_HEADERS_INVALID_ARG;
LogError("index out of bounds, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
*destination = (char*)malloc(strlen(keys[index]) + COLON_AND_SPACE_LENGTH + strlen(values[index]) + 1);
if (*destination == NULL)
{
/*Codes_SRS_HTTP_HEADERS_99_034:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs]*/
result = HTTP_HEADERS_ERROR;
LogError("unable to malloc, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
}
else
{
/*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/
/*Codes_SRS_HTTP_HEADERS_99_027:[ Calling this API shall produce the string value+": "+pair) for the index header in the *destination parameter.]*/
strcpy(*destination, keys[index]);
strcat(*destination, COLON_AND_SPACE);
strcat(*destination, values[index]);
/*Codes_SRS_HTTP_HEADERS_99_035:[ The function shall return HTTP_HEADERS_OK when the function executed without error.]*/
result = HTTP_HEADERS_OK;
}
}
}
}
return result;
}
HTTP_HEADERS_HANDLE HTTPHeaders_Clone(HTTP_HEADERS_HANDLE handle)
{
HTTP_HEADERS_HANDLE_DATA* result;
/*Codes_SRS_HTTP_HEADERS_02_003: [If handle is NULL then HTTPHeaders_Clone shall return NULL.] */
if (handle == NULL)
{
result = NULL;
}
else
{
/*Codes_SRS_HTTP_HEADERS_02_004: [Otherwise HTTPHeaders_Clone shall clone the content of handle to a new handle.] */
result = malloc(sizeof(HTTP_HEADERS_HANDLE_DATA));
if (result == NULL)
{
/*Codes_SRS_HTTP_HEADERS_02_005: [If cloning fails for any reason, then HTTPHeaders_Clone shall return NULL.] */
}
else
{
HTTP_HEADERS_HANDLE_DATA* handleData = handle;
result->headers = Map_Clone(handleData->headers);
if (result->headers == NULL)
{
/*Codes_SRS_HTTP_HEADERS_02_005: [If cloning fails for any reason, then HTTPHeaders_Clone shall return NULL.] */
free(result);
result = NULL;
}
else
{
/*all is fine*/
}
}
}
return result;
}

161
c/sharedutil/src/io.c Normal file
Просмотреть файл

@ -0,0 +1,161 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include <stddef.h>
#include "io.h"
typedef struct IO_INSTANCE_TAG
{
const IO_INTERFACE_DESCRIPTION* io_interface_description;
IO_HANDLE concrete_io_handle;
} IO_INSTANCE;
IO_HANDLE io_create(const IO_INTERFACE_DESCRIPTION* io_interface_description, const void* io_create_parameters, LOGGER_LOG logger_log)
{
IO_INSTANCE* io_instance;
/* Codes_SRS_IO_01_003: [If the argument io_interface_description is NULL, io_create shall return NULL.] */
if ((io_interface_description == NULL) ||
/* Codes_SRS_IO_01_004: [If any io_interface_description member is NULL, io_create shall return NULL.] */
(io_interface_description->concrete_io_create == NULL) ||
(io_interface_description->concrete_io_destroy == NULL) ||
(io_interface_description->concrete_io_open == NULL) ||
(io_interface_description->concrete_io_close == NULL) ||
(io_interface_description->concrete_io_send == NULL) ||
(io_interface_description->concrete_io_dowork == NULL))
{
io_instance = NULL;
}
else
{
io_instance = (IO_INSTANCE*)malloc(sizeof(IO_INSTANCE));
/* Codes_SRS_IO_01_017: [If allocating the memory needed for the IO interface fails then io_create shall return NULL.] */
if (io_instance != NULL)
{
/* Codes_SRS_IO_01_001: [io_create shall return on success a non-NULL handle to a new IO interface.] */
io_instance->io_interface_description = io_interface_description;
/* Codes_SRS_IO_01_002: [In order to instantiate the concrete IO implementation the function concrete_io_create from the io_interface_description shall be called, passing the io_create_parameters and logger_log arguments.] */
io_instance->concrete_io_handle = io_instance->io_interface_description->concrete_io_create((void*)io_create_parameters, logger_log);
/* Codes_SRS_IO_01_016: [If the underlying concrete_io_create call fails, io_create shall return NULL.] */
if (io_instance->concrete_io_handle == NULL)
{
free(io_instance);
io_instance = NULL;
}
}
}
return (IO_HANDLE)io_instance;
}
void io_destroy(IO_HANDLE io)
{
if (io != NULL)
{
IO_INSTANCE* io_instance = (IO_INSTANCE*)io;
/* Codes_SRS_IO_01_006: [io_destroy shall also call the concrete_io_destroy function that is member of the io_interface_description argument passed to io_create, while passing as argument to concrete_io_destroy the result of the underlying concrete_io_create handle that was called as part of the io_create call.] */
io_instance->io_interface_description->concrete_io_destroy(io_instance->concrete_io_handle);
/* Codes_SRS_IO_01_005: [io_destroy shall free all resources associated with the IO handle.] */
free(io_instance);
}
}
int io_open(IO_HANDLE io, ON_BYTES_RECEIVED on_bytes_received, ON_IO_STATE_CHANGED on_io_state_changed, void* callback_context)
{
int result;
if (io == NULL)
{
/* Codes_SRS_IO_01_021: [If handle is NULL, io_open shall return a non-zero value.] */
result = __LINE__;
}
else
{
IO_INSTANCE* io_instance = (IO_INSTANCE*)io;
/* Codes_SRS_IO_01_019: [io_open shall call the specific concrete_io_open function specified in io_create, passing the receive_callback and receive_callback_context arguments.] */
if (io_instance->io_interface_description->concrete_io_open(io_instance->concrete_io_handle, on_bytes_received, on_io_state_changed, callback_context) != 0)
{
/* Codes_SRS_IO_01_022: [If the underlying concrete_io_open fails, io_open shall return a non-zero value.] */
result = __LINE__;
}
else
{
/* Codes_SRS_IO_01_020: [On success, io_open shall return 0.] */
result = 0;
}
}
return result;
}
int io_close(IO_HANDLE io)
{
int result;
if (io == NULL)
{
/* Codes_SRS_IO_01_025: [If handle is NULL, io_close shall return a non-zero value.] */
result = __LINE__;
}
else
{
IO_INSTANCE* io_instance = (IO_INSTANCE*)io;
/* Codes_SRS_IO_01_023: [io_close shall call the specific concrete_io_close function specified in io_create.] */
if (io_instance->io_interface_description->concrete_io_close(io_instance->concrete_io_handle) != 0)
{
/* Codes_SRS_IO_01_026: [If the underlying concrete_io_close fails, io_close shall return a non-zero value.] */
result = __LINE__;
}
else
{
/* Codes_SRS_IO_01_024: [On success, io_close shall return 0.] */
result = 0;
}
}
return result;
}
int io_send(IO_HANDLE io, const void* buffer, size_t size)
{
int result;
/* Codes_SRS_IO_01_011: [No error check shall be performed on buffer and size.] */
/* Codes_SRS_IO_01_010: [If handle is NULL, io_send shall return a non-zero value.] */
if (io == NULL)
{
result = __LINE__;
}
else
{
IO_INSTANCE* io_instance = (IO_INSTANCE*)io;
/* Codes_SRS_IO_01_008: [io_send shall pass the sequence of bytes pointed to by buffer to the concrete IO implementation specified in io_create, by calling the concrete_io_send function while passing down the buffer and size arguments to it.] */
/* Codes_SRS_IO_01_009: [On success, io_send shall return 0.] */
/* Codes_SRS_IO_01_015: [If the underlying concrete_io_send fails, io_send shall return a non-zero value.] */
result = io_instance->io_interface_description->concrete_io_send(io_instance->concrete_io_handle, buffer, size);
}
return result;
}
void io_dowork(IO_HANDLE io)
{
/* Codes_SRS_IO_01_018: [When the handle argument is NULL, io_dowork shall do nothing.] */
if (io != NULL)
{
IO_INSTANCE* io_instance = (IO_INSTANCE*)io;
/* Codes_SRS_IO_01_012: [io_dowork shall call the concrete IO implementation specified in io_create, by calling the concrete_io_dowork function.] */
io_instance->io_interface_description->concrete_io_dowork(io_instance->concrete_io_handle);
}
}

232
c/sharedutil/src/list.c Normal file
Просмотреть файл

@ -0,0 +1,232 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include <stdbool.h>
#include "list.h"
typedef struct LIST_ITEM_TAG
{
const void* item;
void* next;
} LIST_ITEM;
typedef struct LIST_DATA_TAG
{
LIST_ITEM* head;
} LIST_DATA;
LIST_HANDLE list_create(void)
{
LIST_DATA* result;
/* Codes_SRS_LIST_01_001: [list_create shall create a new list and return a non-NULL handle on success.] */
result = (LIST_DATA*)malloc(sizeof(LIST_DATA));
if (result != NULL)
{
/* Codes_SRS_LIST_01_002: [If any error occurs during the list creation, list_create shall return NULL.] */
result->head = NULL;
}
return result;
}
void list_destroy(LIST_HANDLE handle)
{
/* Codes_SRS_LIST_01_004: [If the handle argument is NULL, no freeing of resources shall occur.] */
if (handle != NULL)
{
/* Codes_SRS_LIST_01_003: [list_destroy shall free all resources associated with the list identified by the handle argument.] */
free(handle);
}
}
int list_add(LIST_HANDLE handle, const void* item)
{
int result;
/* Codes_SRS_LIST_01_006: [If any of the arguments is NULL, list_add shall not add the item to the list and return a non-zero value.] */
if ((handle == NULL) || (item == NULL))
{
result = __LINE__;
}
else
{
LIST_DATA* list = (LIST_DATA*)handle;
LIST_ITEM* new_item = malloc(sizeof(LIST_ITEM));
if (new_item == NULL)
{
/* Codes_SRS_LIST_01_007: [If allocating the new list node fails, list_add shall return a non-zero value.] */
result = __LINE__;
}
else
{
/* Codes_SRS_LIST_01_005: [list_add shall add one item to the tail of the list and on success it shall return 0.] */
new_item->next = NULL;
new_item->item = item;
if (list->head == NULL)
{
list->head = new_item;
}
else
{
LIST_ITEM* current = list->head;
while (current->next != NULL)
{
current = current->next;
}
current->next = new_item;
}
result = 0;
}
}
return result;
}
LIST_ITEM_HANDLE list_get_head_item(LIST_HANDLE handle)
{
LIST_DATA* list = (LIST_DATA*)handle;
LIST_ITEM_HANDLE result;
if (list == NULL)
{
/* Codes_SRS_LIST_01_009: [If the handle argument is NULL, list_get_head_item shall return NULL.] */
result = NULL;
}
else
{
/* Codes_SRS_LIST_01_008: [list_get_head_item shall return the head of the list.] */
/* Codes_SRS_LIST_01_010: [If the list is empty, list_get_head_item_shall_return NULL.] */
result = list->head;
}
return result;
}
LIST_ITEM_HANDLE list_get_next_item(LIST_ITEM_HANDLE item_handle)
{
LIST_ITEM_HANDLE result;
if (item_handle == NULL)
{
/* Codes_SRS_LIST_01_019: [If item_handle is NULL then list_get_next_item shall return NULL.] */
result = NULL;
}
else
{
/* Codes_SRS_LIST_01_018: [list_get_next_item shall return the next item in the list following the item item_handle.] */
result = ((LIST_ITEM*)item_handle)->next;
}
return result;
}
const void* list_item_get_value(LIST_ITEM_HANDLE item_handle)
{
const void* result;
if (item_handle == NULL)
{
/* Codes_SRS_LIST_01_021: [If item_handle is NULL, list_item_get_value shall return NULL.] */
result = NULL;
}
else
{
/* Codes_SRS_LIST_01_020: [list_item_get_value shall return the value associated with the list item identified by the item_handle argument.] */
result = ((LIST_ITEM*)item_handle)->item;
}
return result;
}
LIST_ITEM_HANDLE list_find(LIST_HANDLE handle, LIST_MATCH_FUNCTION match_function, const void* match_context)
{
LIST_ITEM_HANDLE result;
if ((handle == NULL) ||
(match_function == NULL))
{
/* Codes_SRS_LIST_01_012: [If the handle or the match_function argument is NULL, list_find shall return NULL.] */
result = NULL;
}
else
{
LIST_DATA* list = (LIST_DATA*)handle;
LIST_ITEM* current = list->head;
/* Codes_SRS_LIST_01_011: [list_find shall iterate through all items in a list and return the one that satisfies a certain match function.] */
while (current != NULL)
{
/* Codes_SRS_LIST_01_014: [list find shall determine whether an item satisfies the match criteria by invoking the match function for each item in the list until a matching item is found.] */
/* Codes_SRS_LIST_01_013: [The match_function shall get as arguments the list item being attempted to be matched and the match_context as is.] */
if (match_function((LIST_ITEM_HANDLE)current, match_context) == true)
{
/* Codes_SRS_LIST_01_017: [If the match function returns true, list_find shall consider that item as matching.] */
break;
}
/* Codes_SRS_LIST_01_016: [If the match function returns false, list_find shall consider that item as not matching.] */
current = current->next;
}
if (current == NULL)
{
/* Codes_SRS_LIST_01_015: [If the list is empty, list_find shall return NULL.] */
result = NULL;
}
else
{
result = current;
}
}
return result;
}
int list_remove_matching_item(LIST_HANDLE handle, LIST_MATCH_FUNCTION match_function, const void* match_context)
{
int result;
if ((handle == NULL) ||
(match_function == NULL))
{
result = __LINE__;
}
else
{
LIST_DATA* list = (LIST_DATA*)handle;
LIST_ITEM* current = list->head;
LIST_ITEM* previous = NULL;
while (current != NULL)
{
if (match_function((LIST_ITEM_HANDLE)current, match_context) == true)
{
break;
}
current = current->next;
}
if (current == NULL)
{
result = __LINE__;
}
else
{
if (previous == NULL)
{
list->head = previous;
}
else
{
previous->next = current->next;
}
free((void*)current->item);
free(current);
result = 0;
}
}
return result;
}

586
c/sharedutil/src/map.c Normal file
Просмотреть файл

@ -0,0 +1,586 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "map.h"
#include "iot_logging.h"
DEFINE_ENUM_STRINGS(MAP_RESULT, MAP_RESULT_VALUES);
typedef struct MAP_HANDLE_DATA_TAG
{
char** keys;
char** values;
size_t count;
MAP_FILTER_CALLBACK mapFilterCallback;
}MAP_HANDLE_DATA;
#define LOG_MAP_ERROR LogError("result = %s\r\n", ENUM_TO_STRING(MAP_RESULT, result));
MAP_HANDLE Map_Create(MAP_FILTER_CALLBACK mapFilterFunc)
{
/*Codes_SRS_MAP_02_001: [Map_Create shall create a new, empty map.]*/
MAP_HANDLE_DATA* result = (MAP_HANDLE_DATA*)malloc(sizeof(MAP_HANDLE_DATA));
/*Codes_SRS_MAP_02_002: [If during creation there are any error, then Map_Create shall return NULL.]*/
if (result != NULL)
{
/*Codes_SRS_MAP_02_003: [Otherwise, it shall return a non-NULL handle that can be used in subsequent calls.] */
result->keys = NULL;
result->values = NULL;
result->count = 0;
result->mapFilterCallback = mapFilterFunc;
}
return result;
}
void Map_Destroy(MAP_HANDLE handle)
{
/*Codes_SRS_MAP_02_005: [If parameter handle is NULL then Map_Destroy shall take no action.] */
if (handle != NULL)
{
/*Codes_SRS_MAP_02_004: [Map_Destroy shall release all resources associated with the map.] */
MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle;
size_t i;
for (i = 0; i < handleData->count; i++)
{
free(handleData->keys[i]);
free(handleData->values[i]);
}
free(handleData->keys);
free(handleData->values);
free(handleData);
}
}
/*makes a copy of a vector of const char*, having size "size". source cannot be NULL*/
/*returns NULL if it fails*/
static char** Map_CloneVector(const char*const * source, size_t count)
{
char** result;
result = (char**)malloc(count *sizeof(char*));
if (result == NULL)
{
/*do nothing, just return it (NULL)*/
}
else
{
size_t i;
for (i = 0; i < count; i++)
{
if (mallocAndStrcpy_s(result + i, source[i]) != 0)
{
break;
}
}
if (i == count)
{
/*it is all good, proceed to return result*/
}
else
{
size_t j;
for (j = 0; j < i; j++)
{
free(result[j]);
}
free(result);
result = NULL;
}
}
return result;
}
/*Codes_SRS_MAP_02_039: [Map_Clone shall make a copy of the map indicated by parameter handle and return a non-NULL handle to it.]*/
MAP_HANDLE Map_Clone(MAP_HANDLE handle)
{
MAP_HANDLE_DATA* result;
if (handle == NULL)
{
/*Codes_SRS_MAP_02_038: [Map_Clone returns NULL if parameter handle is NULL.]*/
result = NULL;
LogError("invalid arg to Map_Clone (NULL)\r\n");
}
else
{
MAP_HANDLE_DATA * handleData = (MAP_HANDLE_DATA *)handle;
result = (MAP_HANDLE_DATA*)malloc(sizeof(MAP_HANDLE_DATA));
if (result == NULL)
{
/*Codes_SRS_MAP_02_047: [If during cloning, any operation fails, then Map_Clone shall return NULL.] */
/*do nothing, proceed to return it, this is an error case*/
LogError("unable to malloc\r\n");
}
else
{
if (handleData->count == 0)
{
result->count = 0;
result->keys = NULL;
result->values = NULL;
result->mapFilterCallback = NULL;
}
else
{
result->mapFilterCallback = handleData->mapFilterCallback;
result->count = handleData->count;
if( (result->keys = Map_CloneVector((const char* const*)handleData->keys, handleData->count))==NULL)
{
/*Codes_SRS_MAP_02_047: [If during cloning, any operation fails, then Map_Clone shall return NULL.] */
LogError("unable to clone keys\r\n");
free(result);
result = NULL;
}
else if ((result->values = Map_CloneVector((const char* const*)handleData->values, handleData->count)) == NULL)
{
/*Codes_SRS_MAP_02_047: [If during cloning, any operation fails, then Map_Clone shall return NULL.] */
LogError("unable to clone values\r\n");
size_t i;
for (i = 0; i < result->count; i++)
{
free(result->keys[i]);
}
free(result->keys);
free(result);
result = NULL;
}
else
{
/*all fine, return it*/
}
}
}
}
return result;
}
static int Map_IncreaseStorageKeysValues(MAP_HANDLE_DATA* handleData)
{
int result;
char** newKeys = (char**)realloc(handleData->keys, (handleData->count + 1) * sizeof(char*));
if (newKeys == NULL)
{
LogError("realloc error\r\n");
result = __LINE__;
}
else
{
char** newValues;
handleData->keys = newKeys;
handleData->keys[handleData->count] = NULL;
newValues = (char**)realloc(handleData->values, (handleData->count + 1) * sizeof(char*));
if (newValues == NULL)
{
LogError("realloc error\r\n");
if (handleData->count == 0) /*avoiding an implementation defined behavior */
{
free(handleData->keys);
handleData->keys = NULL;
}
else
{
char** undoneKeys = (char**)realloc(handleData->keys, (handleData->count) * sizeof(char*));
if (undoneKeys == NULL)
{
LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size\r\n");
}
else
{
handleData->keys = undoneKeys;
}
}
result = __LINE__;
}
else
{
handleData->values = newValues;
handleData->values[handleData->count] = NULL;
handleData->count++;
result = 0;
}
}
return result;
}
static void Map_DecreaseStorageKeysValues(MAP_HANDLE_DATA* handleData)
{
if (handleData->count == 1)
{
free(handleData->keys);
handleData->keys = NULL;
free(handleData->values);
handleData->values = NULL;
handleData->count = 0;
handleData->mapFilterCallback = NULL;
}
else
{
/*certainly > 1...*/
char** undoneKeys = (char**)realloc(handleData->keys, sizeof(char*)* (handleData->count - 1));
if (undoneKeys == NULL)
{
LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size\r\n");
}
else
{
handleData->keys = undoneKeys;
}
char** undoneValues = (char**)realloc(handleData->values, sizeof(char*)* (handleData->count - 1));
if (undoneValues == NULL)
{
LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size\r\n");
}
else
{
handleData->values = undoneValues;
}
handleData->count--;
}
}
static char** findKey(MAP_HANDLE_DATA* handleData, const char* key)
{
char** result;
if (handleData->keys == NULL)
{
result = NULL;
}
else
{
size_t i;
result = NULL;
for (i = 0; i < handleData->count; i++)
{
if (strcmp(handleData->keys[i], key) == 0)
{
result = handleData->keys + i;
break;
}
}
}
return result;
}
static char** findValue(MAP_HANDLE_DATA* handleData, const char* value)
{
char** result;
if (handleData->values == NULL)
{
result = NULL;
}
else
{
size_t i;
result = NULL;
for (i = 0; i < handleData->count; i++)
{
if (strcmp(handleData->values[i], value) == 0)
{
result = handleData->values + i;
break;
}
}
}
return result;
}
static int insertNewKeyValue(MAP_HANDLE_DATA* handleData, const char* key, const char* value)
{
int result;
if (Map_IncreaseStorageKeysValues(handleData) != 0) /*this increases handleData->count*/
{
result = __LINE__;
}
else
{
if (mallocAndStrcpy_s(&(handleData->keys[handleData->count - 1]), key) != 0)
{
Map_DecreaseStorageKeysValues(handleData);
LogError("unable to mallocAndStrcpy_s\r\n");
result = __LINE__;
}
else
{
if (mallocAndStrcpy_s(&(handleData->values[handleData->count - 1]), value) != 0)
{
free(handleData->keys[handleData->count - 1]);
Map_DecreaseStorageKeysValues(handleData);
LogError("unable to mallocAndStrcpy_s\r\n");
result = __LINE__;
}
else
{
result = 0;
}
}
}
return result;
}
MAP_RESULT Map_Add(MAP_HANDLE handle, const char* key, const char* value)
{
MAP_RESULT result;
/*Codes_SRS_MAP_02_006: [If parameter handle is NULL then Map_Add shall return MAP_INVALID_ARG.] */
/*Codes_SRS_MAP_02_007: [If parameter key is NULL then Map_Add shall return MAP_INVALID_ARG.]*/
/*Codes_SRS_MAP_02_008: [If parameter value is NULL then Map_Add shall return MAP_INVALID_ARG.] */
if (
(handle == NULL) ||
(key == NULL) ||
(value == NULL)
)
{
result = MAP_INVALIDARG;
LOG_MAP_ERROR;
}
else
{
MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle;
/*Codes_SRS_MAP_02_009: [If the key already exists, then Map_Add shall return MAP_KEYEXISTS.] */
if (findKey(handleData, key) != NULL)
{
result = MAP_KEYEXISTS;
}
else
{
/* Codes_SRS_MAP_07_009: [If the mapFilterCallback function is not NULL, then the return value will be check and if it is not zero then Map_Add shall return MAP_FILTER_REJECT.] */
if ( (handleData->mapFilterCallback != NULL) && (handleData->mapFilterCallback(key, value) != 0) )
{
result = MAP_FILTER_REJECT;
}
else
{
/*Codes_SRS_MAP_02_010: [Otherwise, Map_Add shall add the pair <key,value> to the map.] */
if (insertNewKeyValue(handleData, key, value) != 0)
{
/*Codes_SRS_MAP_02_011: [If adding the pair <key,value> fails then Map_Add shall return MAP_ERROR.] */
result = MAP_ERROR;
LOG_MAP_ERROR;
}
else
{
/*Codes_SRS_MAP_02_012: [Otherwise, Map_Add shall return MAP_OK.] */
result = MAP_OK;
}
}
}
}
return result;
}
MAP_RESULT Map_AddOrUpdate(MAP_HANDLE handle, const char* key, const char* value)
{
MAP_RESULT result;
/*Codes_SRS_MAP_02_013: [If parameter handle is NULL then Map_AddOrUpdate shall return MAP_INVALID_ARG.]*/
/*Codes_SRS_MAP_02_014: [If parameter key is NULL then Map_AddOrUpdate shall return MAP_INVALID_ARG.]*/
/*Codes_SRS_MAP_02_015: [If parameter value is NULL then Map_AddOrUpdate shall return MAP_INVALID_ARG.] */
if (
(handle == NULL) ||
(key == NULL) ||
(value == NULL)
)
{
result = MAP_INVALIDARG;
LOG_MAP_ERROR;
}
else
{
MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle;
/* Codes_SRS_MAP_07_008: [If the mapFilterCallback function is not NULL, then the return value will be check and if it is not zero then Map_AddOrUpdate shall return MAP_FILTER_REJECT.] */
if (handleData->mapFilterCallback != NULL && handleData->mapFilterCallback(key, value) != 0)
{
result = MAP_FILTER_REJECT;
}
else
{
char** whereIsIt = findKey(handleData, key);
if (whereIsIt == NULL)
{
/*Codes_SRS_MAP_02_017: [Otherwise, Map_AddOrUpdate shall add the pair <key,value> to the map.]*/
if (insertNewKeyValue(handleData, key, value) != 0)
{
result = MAP_ERROR;
LOG_MAP_ERROR;
}
else
{
result = MAP_OK;
}
}
else
{
/*Codes_SRS_MAP_02_016: [If the key already exists, then Map_AddOrUpdate shall overwrite the value of the existing key with parameter value.]*/
size_t index = whereIsIt - handleData->keys;
size_t valueLength = strlen(value);
/*try to realloc value of this key*/
char* newValue = (char*)realloc(handleData->values[index],valueLength + 1);
if (newValue == NULL)
{
result = MAP_ERROR;
LOG_MAP_ERROR;
}
else
{
memcpy(newValue, value, valueLength + 1);
handleData->values[index] = newValue;
/*Codes_SRS_MAP_02_019: [Otherwise, Map_AddOrUpdate shall return MAP_OK.] */
result = MAP_OK;
}
}
}
}
return result;
}
MAP_RESULT Map_Delete(MAP_HANDLE handle, const char* key)
{
MAP_RESULT result;
/*Codes_SRS_MAP_02_020: [If parameter handle is NULL then Map_Delete shall return MAP_INVALIDARG.]*/
/*Codes_SRS_MAP_02_021: [If parameter key is NULL then Map_Delete shall return MAP_INVALIDARG.]*/
if (
(handle == NULL) ||
(key == NULL)
)
{
result = MAP_INVALIDARG;
LOG_MAP_ERROR;
}
else
{
MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle;
char** whereIsIt = findKey(handleData,key);
if (whereIsIt == NULL)
{
/*Codes_SRS_MAP_02_022: [If key does not exist then Map_Delete shall return MAP_KEYNOTFOUND.]*/
result = MAP_KEYNOTFOUND;
}
else
{
/*Codes_SRS_MAP_02_023: [Otherwise, Map_Delete shall remove the key and its associated value from the map and return MAP_OK.]*/
size_t index = whereIsIt - handleData->keys;
free(handleData->keys[index]);
free(handleData->values[index]);
memmove(handleData->keys + index, handleData->keys + index + 1, (handleData->count - index - 1)*sizeof(char*)); /*if order doesn't matter... then this can be optimized*/
memmove(handleData->values + index, handleData->values + index + 1, (handleData->count - index - 1)*sizeof(char*));
Map_DecreaseStorageKeysValues(handleData);
result = MAP_OK;
}
}
return result;
}
MAP_RESULT Map_ContainsKey(MAP_HANDLE handle, const char* key, bool* keyExists)
{
MAP_RESULT result;
/*Codes_SRS_MAP_02_024: [If parameter handle, key or keyExists are NULL then Map_ContainsKey shall return MAP_INVALIDARG.]*/
if (
(handle ==NULL) ||
(key == NULL) ||
(keyExists == NULL)
)
{
result = MAP_INVALIDARG;
LOG_MAP_ERROR;
}
else
{
MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle;
/*Codes_SRS_MAP_02_025: [Otherwise if a key exists then Map_ContainsKey shall return MAP_OK and shall write in keyExists "true".]*/
/*Codes_SRS_MAP_02_026: [If a key doesn't exist, then Map_ContainsKey shall return MAP_OK and write in keyExists "false".] */
*keyExists = (findKey(handleData, key) != NULL) ? true: false;
result = MAP_OK;
}
return result;
}
MAP_RESULT Map_ContainsValue(MAP_HANDLE handle, const char* value, bool* valueExists)
{
MAP_RESULT result;
/*Codes_SRS_MAP_02_027: [If parameter handle, value or valueExists is NULL then Map_ContainsValue shall return MAP_INVALIDARG.] */
if (
(handle == NULL) ||
(value == NULL) ||
(valueExists == NULL)
)
{
result = MAP_INVALIDARG;
LOG_MAP_ERROR;
}
else
{
MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle;
/*Codes_SRS_MAP_02_028: [Otherwise, if a pair <key, value> has its value equal to the parameter value, the Map_ContainsValue shall return MAP_OK and shall write in valueExists "true".]*/
/*Codes_SRS_MAP_02_029: [Otherwise, if such a <key, value> does not exist, then Map_ContainsValue shall return MAP_OK and shall write in valueExists "false".] */
*valueExists = (findValue(handleData, value) != NULL) ? true : false;
result = MAP_OK;
}
return result;
}
const char* Map_GetValueFromKey(MAP_HANDLE handle, const char* key)
{
const char* result;
/*Codes_SRS_MAP_02_040: [If parameter handle or key is NULL then Map_GetValueFromKey returns NULL.]*/
if (
(handle == NULL) ||
(key == NULL)
)
{
result = NULL;
LogError("invalid parameter to Map_GetValueFromKey\r\n");
}
else
{
MAP_HANDLE_DATA * handleData = (MAP_HANDLE_DATA *)handle;
char** whereIsIt = findKey(handleData, key);
if(whereIsIt == NULL)
{
/*Codes_SRS_MAP_02_041: [If the key is not found, then Map_GetValueFromKey returns NULL.]*/
result = NULL;
}
else
{
/*Codes_SRS_MAP_02_042: [Otherwise, Map_GetValueFromKey returns the key's value.] */
size_t index = whereIsIt - handleData->keys;
result = handleData->values[index];
}
}
return result;
}
MAP_RESULT Map_GetInternals(MAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count)
{
MAP_RESULT result;
/*Codes_SRS_MAP_02_046: [If parameter handle, keys, values or count is NULL then Map_GetInternals shall return MAP_INVALIDARG.] */
if (
(handle == NULL) ||
(keys == NULL) ||
(values == NULL) ||
(count == NULL)
)
{
result = MAP_INVALIDARG;
LOG_MAP_ERROR;
}
else
{
/*Codes_SRS_MAP_02_043: [Map_GetInternals shall produce in *keys an pointer to an array of const char* having all the keys stored so far by the map.]*/
/*Codes_SRS_MAP_02_044: [Map_GetInternals shall produce in *values a pointer to an array of const char* having all the values stored so far by the map.]*/
/*Codes_SRS_MAP_02_045: [ Map_GetInternals shall produce in *count the number of stored keys and values.]*/
MAP_HANDLE_DATA * handleData = (MAP_HANDLE_DATA *)handle;
*keys =(const char* const*)(handleData->keys);
*values = (const char* const*)(handleData->values);
*count = handleData->count;
result = MAP_OK;
}
return result;
}

126
c/sharedutil/src/sastoken.c Normal file
Просмотреть файл

@ -0,0 +1,126 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <time.h>
#include <stddef.h>
#include "crt_abstractions.h"
#include "sastoken.h"
#include "urlencode.h"
#include "hmacsha256.h"
#include "base64.h"
#include "agenttime.h"
#include "strings.h"
#include "buffer_.h"
#include "iot_logging.h"
STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, size_t expiry)
{
STRING_HANDLE result = NULL;
char tokenExpirationTime[32] = { 0 };
/*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/
/*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/
/*Codes_SRS_SASTOKEN_06_007: [If keyName is NULL then SASToken_Create shall return NULL.]*/
if ((key == NULL) ||
(scope == NULL) ||
(keyName == NULL))
{
LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p\r\n", key, scope, keyName);
}
else
{
BUFFER_HANDLE decodedKey;
/*Codes_SRS_SASTOKEN_06_029: [The key parameter is decoded from base64.]*/
if ((decodedKey = Base64_Decoder(STRING_c_str(key))) == NULL)
{
/*Codes_SRS_SASTOKEN_06_030: [If there is an error in the decoding then SASToken_Create shall return NULL.]*/
LogError("Unable to decode the key for generating the SAS.\r\n");
}
else
{
/*Codes_SRS_SASTOKEN_06_026: [If the conversion to string form fails for any reason then SASToken_Create shall return NULL.]*/
if (size_tToString(tokenExpirationTime, sizeof(tokenExpirationTime), expiry) != 0)
{
LogError("For some reason converting seconds to a string failed. No SAS can be generated.\r\n");
}
else
{
STRING_HANDLE toBeHashed = NULL;
BUFFER_HANDLE hash = NULL;
if (((hash = BUFFER_new()) == NULL) ||
((toBeHashed = STRING_new()) == NULL) ||
((result = STRING_new()) == NULL))
{
LogError("Unable to allocate memory to prepare SAS token.\r\n")
}
else
{
/*Codes_SRS_SASTOKEN_06_009: [The scope is the basis for creating a STRING_HANDLE.]*/
/*Codes_SRS_SASTOKEN_06_010: [A "\n" is appended to that string.]*/
/*Codes_SRS_SASTOKEN_06_011: [tokenExpirationTime is appended to that string.]*/
if ((STRING_concat_with_STRING(toBeHashed, scope) != 0) ||
(STRING_concat(toBeHashed, "\n") != 0) ||
(STRING_concat(toBeHashed, tokenExpirationTime) != 0))
{
LogError("Unable to build the input to the HMAC to prepare SAS token.\r\n");
STRING_delete(result);
result = NULL;
}
else
{
STRING_HANDLE base64Signature = NULL;
STRING_HANDLE urlEncodedSignature = NULL;
/*Codes_SRS_SASTOKEN_06_013: [If an error is returned from the HMAC256 function then NULL is returned from SASToken_Create.]*/
/*Codes_SRS_SASTOKEN_06_012: [An HMAC256 hash is calculated using the decodedKey, over toBeHashed.]*/
/*Codes_SRS_SASTOKEN_06_014: [If there are any errors from the following operations then NULL shall be returned.]*/
/*Codes_SRS_SASTOKEN_06_015: [The hash is base 64 encoded.]*/
/*Codes_SRS_SASTOKEN_06_028: [base64Signature shall be url encoded.]*/
/*Codes_SRS_SASTOKEN_06_016: [The string "SharedAccessSignature sr=" is the first part of the result of SASToken_Create.]*/
/*Codes_SRS_SASTOKEN_06_017: [The scope parameter is appended to result.]*/
/*Codes_SRS_SASTOKEN_06_018: [The string "&sig=" is appended to result.]*/
/*Codes_SRS_SASTOKEN_06_019: [The string urlEncodedSignature shall be appended to result.]*/
/*Codes_SRS_SASTOKEN_06_020: [The string "&se=" shall be appended to result.]*/
/*Codes_SRS_SASTOKEN_06_021: [tokenExpirationTime is appended to result.]*/
/*Codes_SRS_SASTOKEN_06_022: [The string "&skn=" is appended to result.]*/
/*Codes_SRS_SASTOKEN_06_023: [The argument keyName is appended to result.]*/
if ((HMACSHA256_ComputeHash(BUFFER_u_char(decodedKey), BUFFER_length(decodedKey), (const unsigned char*)STRING_c_str(toBeHashed), STRING_length(toBeHashed), hash) != HMACSHA256_OK) ||
((base64Signature = Base64_Encode(hash)) == NULL) ||
((urlEncodedSignature = URL_Encode(base64Signature)) == NULL) ||
(STRING_copy(result, "SharedAccessSignature sr=") != 0) ||
(STRING_concat_with_STRING(result, scope) != 0) ||
(STRING_concat(result, "&sig=") != 0) ||
(STRING_concat_with_STRING(result, urlEncodedSignature) != 0) ||
(STRING_concat(result, "&se=") != 0) ||
(STRING_concat(result, tokenExpirationTime) != 0) ||
(STRING_concat(result, "&skn=") != 0) ||
(STRING_concat_with_STRING(result, keyName) != 0))
{
LogError("Unable to build the SAS token.\r\n");
STRING_delete(result);
result = NULL;
}
else
{
/* everything OK */
}
STRING_delete(base64Signature);
STRING_delete(urlEncodedSignature);
}
}
STRING_delete(toBeHashed);
BUFFER_delete(hash);
}
BUFFER_delete(decodedKey);
}
}
return result;
}

441
c/sharedutil/src/sha1.c Normal file
Просмотреть файл

@ -0,0 +1,441 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/**************************** sha1.c ****************************/
/******************** See RFC 4634 for details ******************/
/*
* Description:
* This file implements the Secure Hash Signature Standard
* algorithms as defined in the National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
* published on August 1, 2002, and the FIPS PUB 180-2 Change
* Notice published on February 28, 2004.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-2/fips180-2withchangenotice.pdf
*
* The SHA-1 algorithm produces a 160-bit message digest for a
* given data stream. It should take about 2**n steps to find a
* message with the same digest as a given message and
* 2**(n/2) to find any two messages with the same digest,
* when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code
* uses <stdint.h> (included via "sha.h") to define 32 and 8
* bit unsigned integer types. If your C compiler does not
* support 32 bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. This implementation uses SHA1Input() to hash the bits
* that are a multiple of the size of an 8-bit character, and then
* uses SHA1FinalBits() to hash the final few bits of the input.
*/
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "sha.h"
#include "sha-private.h"
/*
* Define the SHA1 circular left shift macro
*/
#define SHA1_ROTL(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
/*
* add "length" to the length
*/
static uint32_t addTemp;
#define SHA1AddLength(context, length) \
(addTemp = (context)->Length_Low, \
(context)->Corrupted = \
(((context)->Length_Low += (length)) < addTemp) && \
(++(context)->Length_High == 0) ? 1 : 0)
/* Local Function Prototypes */
static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte);
static void SHA1PadMessage(SHA1Context *, uint8_t Pad_Byte);
static void SHA1ProcessMessageBlock(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new SHA1 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Reset(SHA1Context *context)
{
if (!context)
return shaNull;
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
/* Initial Hash Values: FIPS-180-2 section 5.3.1 */
context->Intermediate_Hash[0] = 0x67452301;
context->Intermediate_Hash[1] = 0xEFCDAB89;
context->Intermediate_Hash[2] = 0x98BADCFE;
context->Intermediate_Hash[3] = 0x10325476;
context->Intermediate_Hash[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
return shaSuccess;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/
int SHA1Input(SHA1Context *context,
const uint8_t *message_array, unsigned length)
{
if (!length)
return shaSuccess;
if (!context || !message_array)
return shaNull;
if (context->Computed) {
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
return context->Corrupted;
while (length-- && !context->Corrupted) {
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
if (!SHA1AddLength(context, 8) &&
(context->Message_Block_Index == SHA1_Message_Block_Size))
SHA1ProcessMessageBlock(context);
message_array++;
}
return shaSuccess;
}
/*
* SHA1FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA1FinalBits(SHA1Context *context, const uint8_t message_bits,
unsigned int length)
{
uint8_t masks[8] = {
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
};
uint8_t markbit[8] = {
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
};
if (!length)
return shaSuccess;
if (!context)
return shaNull;
if (context->Computed || (length >= 8) || (length == 0)) {
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
return context->Corrupted;
SHA1AddLength(context, length);
SHA1Finalize(context,
(uint8_t)((message_bits & masks[length]) | markbit[length]));
return shaSuccess;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 19th element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Result(SHA1Context *context,
uint8_t Message_Digest[SHA1HashSize])
{
int i;
if (!context || !Message_Digest)
return shaNull;
if (context->Corrupted)
return context->Corrupted;
if (!context->Computed)
SHA1Finalize(context, 0x80);
for (i = 0; i < SHA1HashSize; ++i)
Message_Digest[i] = (uint8_t)(context->Intermediate_Hash[i >> 2]
>> 8 * (3 - (i & 0x03)));
return shaSuccess;
}
/*
* SHA1Finalize
*
* Description:
* This helper function finishes off the digest calculations.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* Pad_Byte: [in]
* The last byte to add to the digest before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* sha Error Code.
*
*/
static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte)
{
int i;
SHA1PadMessage(context, Pad_Byte);
/* message may be sensitive, clear it out */
for (i = 0; i < SHA1_Message_Block_Size; ++i)
context->Message_Block[i] = 0;
context->Length_Low = 0; /* and clear length */
context->Length_High = 0;
context->Computed = 1;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an
* even 512 bits. The first padding bit must be a '1'. The last
* 64 bits represent the length of the original message. All bits
* in between should be 0. This helper function will pad the
* message according to those rules by filling the Message_Block
* array accordingly. When it returns, it can be assumed that the
* message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
* Pad_Byte: [in]
* The last byte to add to the digest before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index >= (SHA1_Message_Block_Size - 8)) {
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < SHA1_Message_Block_Size)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA1ProcessMessageBlock(context);
}
else
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < (SHA1_Message_Block_Size - 8))
context->Message_Block[context->Message_Block_Index++] = 0;
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
context->Message_Block[59] = (uint8_t)(context->Length_High);
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t)(context->Length_Low);
SHA1ProcessMessageBlock(context);
}
/*
* SHA1ProcessMessageBlock
*
* Description:
* This helper function will process the next 512 bits of the
* message stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the publication.
*/
static void SHA1ProcessMessageBlock(SHA1Context *context)
{
/* Constants defined in FIPS-180-2, section 4.2.1 */
const uint32_t K[4] = {
0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
};
int t; /* Loop counter */
uint32_t temp; /* Temporary word value */
uint32_t W[80]; /* Word sequence */
uint32_t A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = 0; t < 16; t++) {
W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24;
W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
}
for (t = 16; t < 80; t++)
W[t] = SHA1_ROTL(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
for (t = 0; t < 20; t++) {
temp = SHA1_ROTL(5, A) + SHA_Ch(B, C, D) + E + W[t] + K[0];
E = D;
D = C;
C = SHA1_ROTL(30, B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++) {
temp = SHA1_ROTL(5, A) + SHA_Parity(B, C, D) + E + W[t] + K[1];
E = D;
D = C;
C = SHA1_ROTL(30, B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++) {
temp = SHA1_ROTL(5, A) + SHA_Maj(B, C, D) + E + W[t] + K[2];
E = D;
D = C;
C = SHA1_ROTL(30, B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++) {
temp = SHA1_ROTL(5, A) + SHA_Parity(B, C, D) + E + W[t] + K[3];
E = D;
D = C;
C = SHA1_ROTL(30, B);
B = A;
A = temp;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Message_Block_Index = 0;
}

602
c/sharedutil/src/sha224.c Normal file
Просмотреть файл

@ -0,0 +1,602 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/*************************** sha224-256.c ***************************/
/********************* See RFC 4634 for details *********************/
/*
* Description:
* This file implements the Secure Hash Signature Standard
* algorithms as defined in the National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
* published on August 1, 2002, and the FIPS PUB 180-2 Change
* Notice published on February 28, 2004.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-2/fips180-2withchangenotice.pdf
*
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
* message digests for a given data stream. It should take about
* 2**n steps to find a message with the same digest as a given
* message and 2**(n/2) to find any two messages with the same
* digest, when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-224 and SHA-256 are defined in terms of 32-bit "words".
* This code uses <stdint.h> (included via "sha.h") to define 32
* and 8 bit unsigned integer types. If your C compiler does not
* support 32 bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-224 and SHA-256 are designed to work with messages less
* than 2^64 bits long. This implementation uses SHA224/256Input()
* to hash the bits that are a multiple of the size of an 8-bit
* character, and then uses SHA224/256FinalBits() to hash the
* final few bits of the input.
*/
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include "sha.h"
#include "sha-private.h"
/* Define the SHA shift, rotate left and rotate right macro */
#define SHA256_SHR(bits,word) ((word) >> (bits))
#define SHA256_ROTL(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
#define SHA256_ROTR(bits,word) \
(((word) >> (bits)) | ((word) << (32-(bits))))
/* Define the SHA SIGMA and sigma macros */
#define SHA256_SIGMA0(word) \
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
#define SHA256_SIGMA1(word) \
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
#define SHA256_sigma0(word) \
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
#define SHA256_sigma1(word) \
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
/*
* add "length" to the length
*/
static uint32_t addTemp;
#define SHA224_256AddLength(context, length) \
(addTemp = (context)->Length_Low, (context)->Corrupted = \
(((context)->Length_Low += (length)) < addTemp) && \
(++(context)->Length_High == 0) ? 1 : 0)
/* Local Function Prototypes */
static void SHA224_256Finalize(SHA256Context *context,
uint8_t Pad_Byte);
static void SHA224_256PadMessage(SHA256Context *context,
uint8_t Pad_Byte);
static void SHA224_256ProcessMessageBlock(SHA256Context *context);
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
static int SHA224_256ResultN(SHA256Context *context,
uint8_t Message_Digest[], int HashSize);
/* Initial Hash Values: FIPS-180-2 Change Notice 1 */
static uint32_t SHA224_H0[SHA256HashSize / 4] = {
0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
};
/* Initial Hash Values: FIPS-180-2 section 5.3.2 */
static uint32_t SHA256_H0[SHA256HashSize / 4] = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
/*
* SHA224Reset
*
* Description:
* This function will initialize the SHA384Context in preparation
* for computing a new SHA224 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
int SHA224Reset(SHA224Context *context)
{
return SHA224_256Reset(context, SHA224_H0);
}
/*
* SHA224Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/
int SHA224Input(SHA224Context *context, const uint8_t *message_array,
unsigned int length)
{
return SHA256Input(context, message_array, length);
}
/*
* SHA224FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA224FinalBits(SHA224Context *context,
const uint8_t message_bits, unsigned int length)
{
return SHA256FinalBits(context, message_bits, length);
}
/*
* SHA224Result
*
* Description:
* This function will return the 224-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 28th element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
int SHA224Result(SHA224Context *context,
uint8_t Message_Digest[SHA224HashSize])
{
return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
}
/*
* SHA256Reset
*
* Description:
* This function will initialize the SHA256Context in preparation
* for computing a new SHA256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
int SHA256Reset(SHA256Context *context)
{
return SHA224_256Reset(context, SHA256_H0);
}
/*
* SHA256Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*/
int SHA256Input(SHA256Context *context, const uint8_t *message_array,
unsigned int length)
{
if (!length)
return shaSuccess;
if (!context || !message_array)
return shaNull;
if (context->Computed) {
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
return context->Corrupted;
while (length-- && !context->Corrupted) {
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
if (!SHA224_256AddLength(context, 8) &&
(context->Message_Block_Index == SHA256_Message_Block_Size))
SHA224_256ProcessMessageBlock(context);
message_array++;
}
return shaSuccess;
}
/*
* SHA256FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA256FinalBits(SHA256Context *context,
const uint8_t message_bits, unsigned int length)
{
uint8_t masks[8] = {
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
};
uint8_t markbit[8] = {
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
};
if (!length)
return shaSuccess;
if (!context)
return shaNull;
if ((context->Computed) || (length >= 8) || (length == 0)) {
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
return context->Corrupted;
SHA224_256AddLength(context, length);
SHA224_256Finalize(context, (uint8_t)
((message_bits & masks[length]) | markbit[length]));
return shaSuccess;
}
/*
* SHA256Result
*
* Description:
* This function will return the 256-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 32nd element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
int SHA256Result(SHA256Context *context, uint8_t Message_Digest[])
{
return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
}
/*
* SHA224_256Finalize
*
* Description:
* This helper function finishes off the digest calculations.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* Pad_Byte: [in]
* The last byte to add to the digest before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* sha Error Code.
*/
static void SHA224_256Finalize(SHA256Context *context,
uint8_t Pad_Byte)
{
int i;
SHA224_256PadMessage(context, Pad_Byte);
/* message may be sensitive, so clear it out */
for (i = 0; i < SHA256_Message_Block_Size; ++i)
context->Message_Block[i] = 0;
context->Length_Low = 0; /* and clear length */
context->Length_High = 0;
context->Computed = 1;
}
/*
* SHA224_256PadMessage
*
* Description:
* According to the standard, the message must be padded to an
* even 512 bits. The first padding bit must be a '1'. The
* last 64 bits represent the length of the original message.
* All bits in between should be 0. This helper function will pad
* the message according to those rules by filling the
* Message_Block array accordingly. When it returns, it can be
* assumed that the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
* Pad_Byte: [in]
* The last byte to add to the digest before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void SHA224_256PadMessage(SHA256Context *context,
uint8_t Pad_Byte)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index >= (SHA256_Message_Block_Size - 8)) {
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < SHA256_Message_Block_Size)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA224_256ProcessMessageBlock(context);
}
else
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < (SHA256_Message_Block_Size - 8))
context->Message_Block[context->Message_Block_Index++] = 0;
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
context->Message_Block[59] = (uint8_t)(context->Length_High);
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t)(context->Length_Low);
SHA224_256ProcessMessageBlock(context);
}
/*
* SHA224_256ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* context: [in/out]
* The SHA context to update
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the publication.
*/
static void SHA224_256ProcessMessageBlock(SHA256Context *context)
{
/* Constants defined in FIPS-180-2, section 4.2.2 */
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
int t, t4; /* Loop counter */
uint32_t temp1, temp2; /* Temporary word value */
uint32_t W[64]; /* Word sequence */
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = t4 = 0; t < 16; t++, t4 += 4)
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
(((uint32_t)context->Message_Block[t4 + 1]) << 16) |
(((uint32_t)context->Message_Block[t4 + 2]) << 8) |
(((uint32_t)context->Message_Block[t4 + 3]));
for (t = 16; t < 64; t++)
W[t] = SHA256_sigma1(W[t - 2]) + W[t - 7] +
SHA256_sigma0(W[t - 15]) + W[t - 16];
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
F = context->Intermediate_Hash[5];
G = context->Intermediate_Hash[6];
H = context->Intermediate_Hash[7];
for (t = 0; t < 64; t++) {
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E, F, G) + K[t] + W[t];
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A, B, C);
H = G;
G = F;
F = E;
E = D + temp1;
D = C;
C = B;
B = A;
A = temp1 + temp2;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Intermediate_Hash[5] += F;
context->Intermediate_Hash[6] += G;
context->Intermediate_Hash[7] += H;
context->Message_Block_Index = 0;
}
/*
* SHA224_256Reset
*
* Description:
* This helper function will initialize the SHA256Context in
* preparation for computing a new SHA256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* H0
* The initial hash value to use.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
{
if (!context)
return shaNull;
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = H0[0];
context->Intermediate_Hash[1] = H0[1];
context->Intermediate_Hash[2] = H0[2];
context->Intermediate_Hash[3] = H0[3];
context->Intermediate_Hash[4] = H0[4];
context->Intermediate_Hash[5] = H0[5];
context->Intermediate_Hash[6] = H0[6];
context->Intermediate_Hash[7] = H0[7];
context->Computed = 0;
context->Corrupted = 0;
return shaSuccess;
}
/*
* SHA224_256ResultN
*
* Description:
* This helper function will return the 224-bit or 256-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 28th/32nd element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest: [out]
* Where the digest is returned.
* HashSize: [in]
* The size of the hash, either 28 or 32.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256ResultN(SHA256Context *context,
uint8_t Message_Digest[], int HashSize)
{
int i;
if (!context || !Message_Digest)
return shaNull;
if (context->Corrupted)
return context->Corrupted;
if (!context->Computed)
SHA224_256Finalize(context, 0x80);
for (i = 0; i < HashSize; ++i)
Message_Digest[i] = (uint8_t)
(context->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x03)));
return shaSuccess;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,191 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// PUT NO INCLUDES BEFORE HERE
//
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
//
// PUT NO CLIENT LIBRARY INCLUDES BEFORE HERE
//
#include "string_tokenizer.h"
#include "iot_logging.h"
#include "crt_abstractions.h"
#include <stdbool.h>
typedef struct STRING_TOKEN_TAG
{
const char* inputString;
const char* currentPos;
size_t sizeOfinputString;
} STRING_TOKEN;
STRING_TOKENIZER_HANDLE STRING_TOKENIZER_create(STRING_HANDLE handle)
{
STRING_TOKEN *result;
char* inputStringToMalloc;
/* Codes_SRS_STRING_04_001: [STRING_TOKENIZER_create shall return an NULL STRING_TOKENIZER_HANDLE if parameter handle is NULL] */
if (handle == NULL)
{
LogError("Invalid Argument. Handle cannot be NULL.\r\n");
result = NULL;
}
/* Codes_SRS_STRING_04_002: [STRING_TOKENIZER_create shall allocate a new STRING_TOKENIZER_HANDLE having the content of the STRING_HANDLE copied and current position pointing at the beginning of the string] */
else if((result = (STRING_TOKEN *)malloc(sizeof(STRING_TOKEN))) == NULL)
{
LogError("Memory Allocation failed. Cannot allocate STRING_TOKENIZER.\r\n");
}
else if ((mallocAndStrcpy_s(&inputStringToMalloc, STRING_c_str(handle))) != 0)
{
LogError("Memory Allocation Failed. Cannot allocate and copy string Content.\r\n");
free(result);
result = NULL;
}
else
{
result->inputString = inputStringToMalloc;
result->currentPos = result->inputString; //Current Pos will point to the initial position of Token.
result->sizeOfinputString = strlen(result->inputString); //Calculate Size of Current String
}
return (STRING_TOKENIZER_HANDLE)result;
}
int STRING_TOKENIZER_get_next_token(STRING_TOKENIZER_HANDLE tokenizer, STRING_HANDLE output, const char* delimiters)
{
int result;
/* Codes_SRS_STRING_04_004: [STRING_TOKENIZER_get_next_token shall return a nonzero value if any of the 3 parameters is NULL] */
if (tokenizer == NULL || output == NULL || delimiters == NULL)
{
result = __LINE__;
}
else
{
STRING_TOKEN* token = (STRING_TOKEN*)tokenizer;
/* Codes_SRS_STRING_04_011: [Each subsequent call to STRING_TOKENIZER_get_next_token starts searching from the saved position on t and behaves as described above.] */
size_t remainingInputStringSize = token->sizeOfinputString - (token->currentPos - token->inputString);
size_t delimitterSize = strlen(delimiters);
/* First Check if we reached the end of the string*/
/* Codes_SRS_STRING_TOKENIZER_04_014: [STRING_TOKENIZER_get_next_token shall return nonzero value if t contains an empty string.] */
if (remainingInputStringSize == 0)
{
result = __LINE__;
}
else if (delimitterSize == 0)
{
LogError("Empty delimiters parameter.\r\n");
result = __LINE__;
}
else
{
size_t i;
/* Codes_SRS_STRING_04_005: [STRING_TOKENIZER_get_next_token searches the string inside STRING_TOKENIZER_HANDLE for the first character that is NOT contained in the current delimiter] */
for (i = 0; i < remainingInputStringSize; i++)
{
bool foundDelimitter = false;
for (size_t j = 0; j < delimitterSize; j++)
{
if (token->currentPos[i] == delimiters[j])
{
foundDelimitter = true;
break;
}
}
/* Codes_SRS_STRING_04_007: [If such a character is found, STRING_TOKENIZER_get_next_token consider it as the start of a token.] */
if (!foundDelimitter)
{
break;
}
}
/* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */
//At this point update Current Pos to the character of the last token found or end of String.
token->currentPos += i;
//Update the remainingInputStringSize
remainingInputStringSize -= i;
/* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */
if (remainingInputStringSize == 0)
{
result = __LINE__;
}
else
{
bool foundDelimitter = false;
char* endOfTokenPosition=NULL;
size_t amountOfCharactersToCopy;
//At this point the Current Pos is pointing to a character that is point to a nonDelimiter. So, now search for a Delimiter, till the end of the String.
/*Codes_SRS_STRING_04_008: [STRING_TOKENIZER_get_next_token than searches from the start of a token for a character that is contained in the delimiters string.] */
/* Codes_SRS_STRING_04_009: [If no such character is found, STRING_TOKENIZER_get_next_token extends the current token to the end of the string inside t, copies the token to output and returns 0.] */
/* Codes_SRS_STRING_04_010: [If such a character is found, STRING_TOKENIZER_get_next_token consider it the end of the token and copy it's content to output, updates the current position inside t to the next character and returns 0.] */
for (size_t j = 0; j < delimitterSize; j++)
{
if ((endOfTokenPosition = strchr(token->currentPos, delimiters[j])) != NULL)
{
foundDelimitter = true;
break;
}
}
//If token not found, than update the EndOfToken to the end of the inputString;
if (endOfTokenPosition == NULL)
{
amountOfCharactersToCopy = remainingInputStringSize;
}
else
{
amountOfCharactersToCopy = endOfTokenPosition - token->currentPos;
}
//copy here the string to output.
if (STRING_copy_n(output, token->currentPos, amountOfCharactersToCopy) != 0)
{
LogError("Problem copying token to output String.\r\n");
result = __LINE__;
}
else
{
//Update the Current position.
//Check if end of String reached so, currentPos points to the end of String.
if (foundDelimitter)
{
token->currentPos += amountOfCharactersToCopy + 1;
}
else
{
token->currentPos += amountOfCharactersToCopy;
}
result = 0; //Result will be on the output.
}
}
}
}
return result;
}
/* Codes_SRS_STRING_TOKENIZER_04_012: [STRING_TOKENIZER_destroy shall free the memory allocated by the STRING_TOKENIZER_create ] */
void STRING_TOKENIZER_destroy(STRING_TOKENIZER_HANDLE t)
{
/* Codes_SRS_STRING_TOKENIZER_04_013: [When the t argument is NULL, then STRING_TOKENIZER_destroy shall not attempt to free] */
if (t != NULL)
{
STRING_TOKEN* value = (STRING_TOKEN*)t;
free((char*)value->inputString);
value->inputString = NULL;
free(value);
}
}

618
c/sharedutil/src/strings.c Normal file
Просмотреть файл

@ -0,0 +1,618 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// PUT NO INCLUDES BEFORE HERE
//
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <stddef.h>
#include <string.h>
//
// PUT NO CLIENT LIBRARY INCLUDES BEFORE HERE
//
#include "strings.h"
#include "iot_logging.h"
static const char hexToASCII[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
typedef struct STRING_TAG
{
char* s;
}STRING;
/*this function will allocate a new string with just '\0' in it*/
/*return NULL if it fails*/
/* Codes_SRS_STRING_07_001: [STRING_new shall allocate a new STRING_HANDLE pointing to an empty string.] */
STRING_HANDLE STRING_new(void)
{
STRING* result;
if ((result = (STRING*)malloc(sizeof(STRING))) != NULL)
{
if ((result->s = (char*)malloc(1)) != NULL)
{
result->s[0] = '\0';
}
else
{
/* Codes_SRS_STRING_07_002: [STRING_new shall return an NULL STRING_HANDLE on any error that is encountered.] */
free(result);
result = NULL;
}
}
return (STRING_HANDLE)result;
}
/*Codes_SRS_STRING_02_001: [STRING_clone shall produce a new string having the same content as the handle string.*/
STRING_HANDLE STRING_clone(STRING_HANDLE handle)
{
STRING* result;
/*Codes_SRS_STRING_02_002: [If parameter handle is NULL then STRING_clone shall return NULL.]*/
if (handle == NULL)
{
result = NULL;
}
else
{
/*Codes_SRS_STRING_02_003: [If STRING_clone fails for any reason, it shall return NULL.] */
if ((result = (STRING*)malloc(sizeof(STRING))) != NULL)
{
STRING* source = (STRING*)handle;
/*Codes_SRS_STRING_02_003: [If STRING_clone fails for any reason, it shall return NULL.] */
size_t sourceLen = strlen(source->s);
if ((result->s = (char*)malloc(sourceLen + 1)) == NULL)
{
free(result);
result = NULL;
}
else
{
memcpy(result->s, source->s, sourceLen + 1);
}
}
else
{
/*not much to do, result is NULL from malloc*/
}
}
return result;
}
/* Codes_SRS_STRING_07_003: [STRING_construct shall allocate a new string with the value of the specified const char*.] */
STRING_HANDLE STRING_construct(const char* psz)
{
STRING_HANDLE result;
if (psz == NULL)
{
/* Codes_SRS_STRING_07_005: [If the supplied const char* is NULL STRING_construct shall return a NULL value.] */
result = NULL;
}
else
{
STRING* str;
if ((str = (STRING*)malloc(sizeof(STRING))) != NULL)
{
size_t nLen = strlen(psz) + 1;
if ((str->s = (char*)malloc(nLen)) != NULL)
{
memcpy(str->s, psz, nLen);
result = (STRING_HANDLE)str;
}
/* Codes_SRS_STRING_07_032: [STRING_construct encounters any error it shall return a NULL value.] */
else
{
free(str);
result = NULL;
}
}
else
{
/* Codes_SRS_STRING_07_032: [STRING_construct encounters any error it shall return a NULL value.] */
result = NULL;
}
}
return result;
}
/*this function will return a new STRING with the memory for the actual string passed in as a parameter.*/
/*return NULL if it fails.*/
/* The supplied memory must have been allocated with malloc! */
/* Codes_SRS_STRING_07_006: [STRING_new_with_memory shall return a STRING_HANDLE by using the supplied char* memory.] */
STRING_HANDLE STRING_new_with_memory(const char* memory)
{
STRING* result;
if (memory == NULL)
{
/* Codes_SRS_STRING_07_007: [STRING_new_with_memory shall return a NULL STRING_HANDLE if the supplied char* is NULL.] */
result = NULL;
}
else
{
if ((result = (STRING*)malloc(sizeof(STRING))) != NULL)
{
result->s = (char*)memory;
}
}
return (STRING_HANDLE)result;
}
/* Codes_SRS_STRING_07_008: [STRING_new_quoted shall return a valid STRING_HANDLE Copying the supplied const char* value surrounded by quotes.] */
STRING_HANDLE STRING_new_quoted(const char* source)
{
STRING* result;
if (source == NULL)
{
/* Codes_SRS_STRING_07_009: [STRING_new_quoted shall return a NULL STRING_HANDLE if the supplied const char* is NULL.] */
result = NULL;
}
else if ((result = (STRING*)malloc(sizeof(STRING))) != NULL)
{
size_t sourceLength = strlen(source);
if ((result->s = (char*)malloc(sourceLength + 3)) != NULL)
{
result->s[0] = '"';
memcpy(result->s + 1, source, sourceLength);
result->s[sourceLength + 1] = '"';
result->s[sourceLength + 2] = '\0';
}
else
{
/* Codes_SRS_STRING_07_031: [STRING_new_quoted shall return a NULL STRING_HANDLE if any error is encountered.] */
free(result);
result = NULL;
}
}
return (STRING_HANDLE)result;
}
/*this function takes a regular const char* and turns in into "this is a\"JSON\" strings\u0008" (starting and ending quote included)*/
/*the newly created handle needs to be disposed of with STRING_delete*/
/*returns NULL if there are errors*/
STRING_HANDLE STRING_new_JSON(const char* source)
{
STRING* result;
if (source == NULL)
{
/*Codes_SRS_STRING_02_011: [If source is NULL then STRING_new_JSON shall return NULL.] */
result = NULL;
LogError("invalid arg (NULL)\r\n");
}
else
{
size_t i;
size_t nControlCharacters = 0; /*counts how many characters are to be expanded from 1 character to \uxxxx (6 characters)*/
size_t nEscapeCharacters = 0;
size_t vlen = strlen(source);
for (i = 0; i < vlen; i++)
{
/*Codes_SRS_STRING_02_014: [If any character has the value outside [1...127] then STRING_new_JSON shall fail and return NULL.] */
if ((unsigned char)source[i] >= 128) /*this be a UNICODE character begin*/
{
break;
}
else
{
if (source[i] <= 0x1F)
{
nControlCharacters++;
}
else if (
(source[i] == '"') ||
(source[i] == '\\') ||
(source[i] == '/')
)
{
nEscapeCharacters++;
}
}
}
if (i < vlen)
{
result = NULL;
LogError("invalid character in input string\r\n");
}
else
{
if ((result = (STRING*)malloc(sizeof(STRING))) == NULL)
{
/*Codes_SRS_STRING_02_021: [If the complete JSON representation cannot be produced, then STRING_new_JSON shall fail and return NULL.] */
LogError("malloc failure\r\n");
}
else if ((result->s = (char*)malloc(vlen + 5 * nControlCharacters + nEscapeCharacters + 3)) == NULL)
{
/*Codes_SRS_STRING_02_021: [If the complete JSON representation cannot be produced, then STRING_new_JSON shall fail and return NULL.] */
free(result);
result = NULL;
LogError("malloc failed\r\n");
}
else
{
size_t pos = 0;
/*Codes_SRS_STRING_02_012: [The string shall begin with the quote character.] */
result->s[pos++] = '"';
for (i = 0; i < vlen; i++)
{
if (source[i] <= 0x1F)
{
/*Codes_SRS_STRING_02_019: [If the character code is less than 0x20 then it shall be represented as \u00xx, where xx is the hex representation of the character code.]*/
result->s[pos++] = '\\';
result->s[pos++] = 'u';
result->s[pos++] = '0';
result->s[pos++] = '0';
result->s[pos++] = hexToASCII[(source[i] & 0xF0) >> 4]; /*high nibble*/
result->s[pos++] = hexToASCII[source[i] & 0x0F]; /*low nibble*/
}
else if (source[i] == '"')
{
/*Codes_SRS_STRING_02_016: [If the character is " (quote) then it shall be repsented as \".] */
result->s[pos++] = '\\';
result->s[pos++] = '"';
}
else if (source[i] == '\\')
{
/*Codes_SRS_STRING_02_017: [If the character is \ (backslash) then it shall represented as \\.] */
result->s[pos++] = '\\';
result->s[pos++] = '\\';
}
else if (source[i] == '/')
{
/*Codes_SRS_STRING_02_018: [If the character is / (slash) then it shall be represented as \/.] */
result->s[pos++] = '\\';
result->s[pos++] = '/';
}
else
{
/*Codes_SRS_STRING_02_013: [The string shall copy the characters of source "as they are" (until the '\0' character) with the following exceptions:] */
result->s[pos++] = source[i];
}
}
/*Codes_SRS_STRING_02_020: [The string shall end with " (quote).] */
result->s[pos++] = '"';
/*zero terminating it*/
result->s[pos] = '\0';
}
}
}
return result;
}
/*this function will concatenate to the string s1 the string s2, resulting in s1+s2*/
/*returns 0 if success*/
/*any other error code is failure*/
/* Codes_SRS_STRING_07_012: [STRING_concat shall concatenate the given STRING_HANDLE and the const char* value and place the value in the handle.] */
int STRING_concat(STRING_HANDLE handle, const char* s2)
{
int result;
if ((handle == NULL) || (s2 == NULL))
{
/* Codes_SRS_STRING_07_013: [STRING_concat shall return a nonzero number if an error is encountered.] */
result = __LINE__;
}
else
{
STRING* s1 = (STRING*)handle;
size_t s1Length = strlen(s1->s);
size_t s2Length = strlen(s2);
char* temp = (char*)realloc(s1->s, s1Length + s2Length + 1);
if (temp == NULL)
{
/* Codes_SRS_STRING_07_013: [STRING_concat shall return a nonzero number if an error is encountered.] */
result = __LINE__;
}
else
{
s1->s = temp;
memcpy(s1->s + s1Length, s2, s2Length + 1);
result = 0;
}
}
return result;
}
/*this function will concatenate to the string s1 the string s2, resulting in s1+s2*/
/*returns 0 if success*/
/*any other error code is failure*/
/* Codes_SRS_STRING_07_034: [String_Concat_with_STRING shall concatenate a given STRING_HANDLE variable with a source STRING_HANDLE.] */
int STRING_concat_with_STRING(STRING_HANDLE s1, STRING_HANDLE s2)
{
int result;
if ((s1 == NULL) || (s2 == NULL))
{
/* Codes_SRS_STRING_07_035: [String_Concat_with_STRING shall return a nonzero number if an error is encountered.] */
result = __LINE__;
}
else
{
STRING* dest = (STRING*)s1;
STRING* src = (STRING*)s2;
size_t s1Length = strlen(dest->s);
size_t s2Length = strlen(src->s);
char* temp = (char*)realloc(dest->s, s1Length + s2Length + 1);
if (temp == NULL)
{
/* Codes_SRS_STRING_07_035: [String_Concat_with_STRING shall return a nonzero number if an error is encountered.] */
result = __LINE__;
}
else
{
dest->s = temp;
/* Codes_SRS_STRING_07_034: [String_Concat_with_STRING shall concatenate a given STRING_HANDLE variable with a source STRING_HANDLE.] */
memcpy(dest->s + s1Length, src->s, s2Length + 1);
result = 0;
}
}
return result;
}
/*this function will copy the string from s2 to s1*/
/*returns 0 if success*/
/*any other error code is failure*/
/* Codes_SRS_STRING_07_016: [STRING_copy shall copy the const char* into the supplied STRING_HANDLE.] */
int STRING_copy(STRING_HANDLE handle, const char* s2)
{
int result;
if ((handle == NULL) || (s2 == NULL))
{
/* Codes_SRS_STRING_07_017: [STRING_copy shall return a nonzero value if any of the supplied parameters are NULL.] */
result = __LINE__;
}
else
{
STRING* s1 = (STRING*)handle;
/* Codes_SRS_STRING_07_026: [If the underlying char* refered to by s1 handle is equal to char* s2 than STRING_copy shall be a noop and return 0.] */
if (s1->s != s2)
{
size_t s2Length = strlen(s2);
char* temp = (char*)realloc(s1->s, s2Length + 1);
if (temp == NULL)
{
/* Codes_SRS_STRING_07_027: [STRING_copy shall return a nonzero value if any error is encountered.] */
result = __LINE__;
}
else
{
s1->s = temp;
memmove(s1->s, s2, s2Length + 1);
result = 0;
}
}
else
{
/* Codes_SRS_STRING_07_033: [If overlapping pointer address is given to STRING_copy the behavior is undefined.] */
result = 0;
}
}
return result;
}
/*this function will copy n chars from s2 to the string s1, resulting in n chars only from s2 being stored in s1.*/
/*returns 0 if success*/
/*any other error code is failure*/
/* Codes_SRS_STRING_07_018: [STRING_copy_n shall copy the number of characters in const char* or the size_t whichever is lesser.] */
int STRING_copy_n(STRING_HANDLE handle, const char* s2, size_t n)
{
int result;
if ((handle == NULL) || (s2 == NULL))
{
/* Codes_SRS_STRING_07_019: [STRING_copy_n shall return a nonzero value if STRING_HANDLE or const char* is NULL.] */
result = __LINE__;
}
else
{
STRING* s1 = (STRING*)handle;
size_t s2Length = strlen(s2);
char* temp;
if (s2Length > n)
{
s2Length = n;
}
temp = (char*)realloc(s1->s, s2Length + 1);
if (temp == NULL)
{
/* Codes_SRS_STRING_07_028: [STRING_copy_n shall return a nonzero value if any error is encountered.] */
result = __LINE__;
}
else
{
s1->s = temp;
memcpy(s1->s, s2, s2Length);
s1->s[s2Length] = 0;
result = 0;
}
}
return result;
}
/*this function will quote the string passed as argument string =>"string"*/
/*returns 0 if success*/ /*doesn't change the string otherwise*/
/*any other error code is failure*/
/* Codes_SRS_STRING_07_014: [STRING_quote shall "quote" the supplied STRING_HANDLE and return 0 on success.] */
int STRING_quote(STRING_HANDLE handle)
{
int result;
if (handle == NULL)
{
/* Codes_SRS_STRING_07_015: [STRING_quote shall return a nonzero value if any of the supplied parameters are NULL.] */
result = __LINE__;
}
else
{
STRING* s1 = (STRING*)handle;
size_t s1Length = strlen(s1->s);
char* temp = (char*)realloc(s1->s, s1Length + 2 + 1);/*2 because 2 quotes, 1 because '\0'*/
if (temp == NULL)
{
/* Codes_SRS_STRING_07_029: [STRING_quote shall return a nonzero value if any error is encountered.] */
result = __LINE__;
}
else
{
s1->s = temp;
memmove(s1->s + 1, s1->s, s1Length);
s1->s[0] = '"';
s1->s[s1Length + 1] = '"';
s1->s[s1Length + 2] = '\0';
result = 0;
}
}
return result;
}
/*this function will revert a string to an empty state*/
/*Returns 0 if the revert was succesful*/
/* Codes_SRS_STRING_07_022: [STRING_empty shall revert the STRING_HANDLE to an empty state.] */
int STRING_empty(STRING_HANDLE handle)
{
int result;
if (handle == NULL)
{
/* Codes_SRS_STRING_07_023: [STRING_empty shall return a nonzero value if the STRING_HANDLE is NULL.] */
result = __LINE__;
}
else
{
STRING* s1 = (STRING*)handle;
char* temp = (char*)realloc(s1->s, 1);
if (temp == NULL)
{
/* Codes_SRS_STRING_07_030: [STRING_empty shall return a nonzero value if the STRING_HANDLE is NULL.] */
result = __LINE__;
}
else
{
s1->s = temp;
s1->s[0] = '\0';
result = 0;
}
}
return result;
}
/*this function will deallocate a string constructed by str_new*/
/* Codes_SRS_STRING_07_010: [STRING_delete will free the memory allocated by the STRING_HANDLE.] */
void STRING_delete(STRING_HANDLE handle)
{
/* Codes_SRS_STRING_07_011: [STRING_delete will not attempt to free anything with a NULL STRING_HANDLE.] */
if (handle != NULL)
{
STRING* value = (STRING*)handle;
free(value->s);
value->s = NULL;
free(value);
}
}
/* Codes_SRS_STRING_07_020: [STRING_c_str shall return the const char* associated with the given STRING_HANDLE.] */
const char* STRING_c_str(STRING_HANDLE handle)
{
const char* result;
if (handle != NULL)
{
result = ((STRING*)handle)->s;
}
else
{
/* Codes_SRS_STRING_07_021: [STRING_c_str shall return NULL if the STRING_HANDLE is NULL.] */
result = NULL;
}
return result;
}
/* Codes_SRS_STRING_07_024: [STRING_length shall return the length of the underlying char* for the given handle] */
size_t STRING_length(STRING_HANDLE handle)
{
size_t result = 0;
/* Codes_SRS_STRING_07_025: [STRING_length shall return zero if the given handle is NULL.] */
if (handle != NULL)
{
STRING* value = (STRING*)handle;
result = strlen(value->s);
}
return result;
}
/*Codes_SRS_STRING_02_007: [STRING_construct_n shall construct a STRING_HANDLE from first "n" characters of the string pointed to by psz parameter.]*/
STRING_HANDLE STRING_construct_n(const char* psz, size_t n)
{
STRING_HANDLE result;
/*Codes_SRS_STRING_02_008: [If psz is NULL then STRING_construct_n shall return NULL.] */
if (psz == NULL)
{
result = NULL;
LogError("invalid arg (NULL)\r\n");
}
else
{
size_t len = strlen(psz);
/*Codes_SRS_STRING_02_009: [If n is bigger than the size of the string psz, then STRING_construct_n shall return NULL.] */
if (n > len)
{
result = NULL;
LogError("invalig arg (n is bigger than the size of the string)\r\n");
}
else
{
STRING* str;
if ((str = (STRING*)malloc(sizeof(STRING))) != NULL)
{
if ((str->s = (char*)malloc(len + 1)) != NULL)
{
memcpy(str->s, psz, n);
str->s[n] = '\0';
result = (STRING_HANDLE)str;
}
/* Codes_SRS_STRING_02_010: [In all other error cases, STRING_construct_n shall return NULL.] */
else
{
free(str);
result = NULL;
}
}
else
{
/* Codes_SRS_STRING_02_010: [In all other error cases, STRING_construct_n shall return NULL.] */
result = NULL;
}
}
}
return result;
}
/* Codes_SRS_STRING_07_034: [STRING_compare returns an integer greater than, equal to, or less than zero, accordingly as the string pointed to by s1 is greater than, equal to, or less than the string s2.] */
int STRING_compare(STRING_HANDLE s1, STRING_HANDLE s2)
{
int result;
if (s1 == NULL && s2 == NULL)
{
/* Codes_SRS_STRING_07_035: [If h1 and h2 are both NULL then STRING_compare shall return 0.]*/
result = 0;
}
else if (s1 == NULL)
{
/* Codes_SRS_STRING_07_036: [If h1 is NULL and h2 is nonNULL then STRING_compare shall return 1.]*/
result = 1;
}
else if (s2 == NULL)
{
/* Codes_SRS_STRING_07_037: [If h2 is NULL and h1 is nonNULL then STRING_compare shall return -1.] */
result = -1;
}
else
{
/* Codes_SRS_STRING_07_038: [STRING_compare shall compare the char s variable using the strcmp function.] */
STRING* value1 = (STRING*)s1;
STRING* value2 = (STRING*)s2;
result = strcmp(value1->s, value2->s);
}
return result;
}

Просмотреть файл

@ -0,0 +1,356 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// PUT NO INCLUDES BEFORE HERE !!!!
//
#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "gballoc.h"
#include <stddef.h>
#include <string.h>
//
// PUT NO CLIENT LIBRARY INCLUDES BEFORE HERE !!!!
//
#include "urlencode.h"
#include "iot_logging.h"
#include "strings.h"
static const struct {
size_t numberOfChars;
const char* encoding;
} urlEncoding[] = {
{ 1, "\0" },
{ 3, "%01" },
{ 3, "%02" },
{ 3, "%03" },
{ 3, "%04" },
{ 3, "%05" },
{ 3, "%06" },
{ 3, "%07" },
{ 3, "%08" },
{ 3, "%09" },
{ 3, "%0a" },
{ 3, "%0b" },
{ 3, "%0c" },
{ 3, "%0d" },
{ 3, "%0e" },
{ 3, "%0f" },
{ 3, "%10" },
{ 3, "%11" },
{ 3, "%12" },
{ 3, "%13" },
{ 3, "%14" },
{ 3, "%15" },
{ 3, "%16" },
{ 3, "%17" },
{ 3, "%18" },
{ 3, "%19" },
{ 3, "%1a" },
{ 3, "%1b" },
{ 3, "%1c" },
{ 3, "%1d" },
{ 3, "%1e" },
{ 3, "%1f" },
{ 3, "%20" },
{ 1, "!" },
{ 3, "%22" },
{ 3, "%23" },
{ 3, "%24" },
{ 3, "%25" },
{ 3, "%26" },
{ 3, "%27" },
{ 1, "(" },
{ 1, ")" },
{ 1, "*" },
{ 3, "%2b" },
{ 3, "%2c" },
{ 1, "-" },
{ 1, "." },
{ 3, "%2f" },
{ 1, "0" },
{ 1, "1" },
{ 1, "2" },
{ 1, "3" },
{ 1, "4" },
{ 1, "5" },
{ 1, "6" },
{ 1, "7" },
{ 1, "8" },
{ 1, "9" },
{ 3, "%3a" },
{ 3, "%3b" },
{ 3, "%3c" },
{ 3, "%3d" },
{ 3, "%3e" },
{ 3, "%3f" },
{ 3, "%40" },
{ 1, "A" },
{ 1, "B" },
{ 1, "C" },
{ 1, "D" },
{ 1, "E" },
{ 1, "F" },
{ 1, "G" },
{ 1, "H" },
{ 1, "I" },
{ 1, "J" },
{ 1, "K" },
{ 1, "L" },
{ 1, "M" },
{ 1, "N" },
{ 1, "O" },
{ 1, "P" },
{ 1, "Q" },
{ 1, "R" },
{ 1, "S" },
{ 1, "T" },
{ 1, "U" },
{ 1, "V" },
{ 1, "W" },
{ 1, "X" },
{ 1, "Y" },
{ 1, "Z" },
{ 3, "%5b" },
{ 3, "%5c" },
{ 3, "%5d" },
{ 3, "%5e" },
{ 1, "_" },
{ 3, "%60" },
{ 1, "a" },
{ 1, "b" },
{ 1, "c" },
{ 1, "d" },
{ 1, "e" },
{ 1, "f" },
{ 1, "g" },
{ 1, "h" },
{ 1, "i" },
{ 1, "j" },
{ 1, "k" },
{ 1, "l" },
{ 1, "m" },
{ 1, "n" },
{ 1, "o" },
{ 1, "p" },
{ 1, "q" },
{ 1, "r" },
{ 1, "s" },
{ 1, "t" },
{ 1, "u" },
{ 1, "v" },
{ 1, "w" },
{ 1, "x" },
{ 1, "y" },
{ 1, "z" },
{ 3, "%7b" },
{ 3, "%7c" },
{ 3, "%7d" },
{ 3, "%7e" },
{ 3, "%7f" },
{ 6, "%c2%80" },
{ 6, "%c2%81" },
{ 6, "%c2%82" },
{ 6, "%c2%83" },
{ 6, "%c2%84" },
{ 6, "%c2%85" },
{ 6, "%c2%86" },
{ 6, "%c2%87" },
{ 6, "%c2%88" },
{ 6, "%c2%89" },
{ 6, "%c2%8a" },
{ 6, "%c2%8b" },
{ 6, "%c2%8c" },
{ 6, "%c2%8d" },
{ 6, "%c2%8e" },
{ 6, "%c2%8f" },
{ 6, "%c2%90" },
{ 6, "%c2%91" },
{ 6, "%c2%92" },
{ 6, "%c2%93" },
{ 6, "%c2%94" },
{ 6, "%c2%95" },
{ 6, "%c2%96" },
{ 6, "%c2%97" },
{ 6, "%c2%98" },
{ 6, "%c2%99" },
{ 6, "%c2%9a" },
{ 6, "%c2%9b" },
{ 6, "%c2%9c" },
{ 6, "%c2%9d" },
{ 6, "%c2%9e" },
{ 6, "%c2%9f" },
{ 6, "%c2%a0" },
{ 6, "%c2%a1" },
{ 6, "%c2%a2" },
{ 6, "%c2%a3" },
{ 6, "%c2%a4" },
{ 6, "%c2%a5" },
{ 6, "%c2%a6" },
{ 6, "%c2%a7" },
{ 6, "%c2%a8" },
{ 6, "%c2%a9" },
{ 6, "%c2%aa" },
{ 6, "%c2%ab" },
{ 6, "%c2%ac" },
{ 6, "%c2%ad" },
{ 6, "%c2%ae" },
{ 6, "%c2%af" },
{ 6, "%c2%b0" },
{ 6, "%c2%b1" },
{ 6, "%c2%b2" },
{ 6, "%c2%b3" },
{ 6, "%c2%b4" },
{ 6, "%c2%b5" },
{ 6, "%c2%b6" },
{ 6, "%c2%b7" },
{ 6, "%c2%b8" },
{ 6, "%c2%b9" },
{ 6, "%c2%ba" },
{ 6, "%c2%bb" },
{ 6, "%c2%bc" },
{ 6, "%c2%bd" },
{ 6, "%c2%be" },
{ 6, "%c2%bf" },
{ 6, "%c3%80" },
{ 6, "%c3%81" },
{ 6, "%c3%82" },
{ 6, "%c3%83" },
{ 6, "%c3%84" },
{ 6, "%c3%85" },
{ 6, "%c3%86" },
{ 6, "%c3%87" },
{ 6, "%c3%88" },
{ 6, "%c3%89" },
{ 6, "%c3%8a" },
{ 6, "%c3%8b" },
{ 6, "%c3%8c" },
{ 6, "%c3%8d" },
{ 6, "%c3%8e" },
{ 6, "%c3%8f" },
{ 6, "%c3%90" },
{ 6, "%c3%91" },
{ 6, "%c3%92" },
{ 6, "%c3%93" },
{ 6, "%c3%94" },
{ 6, "%c3%95" },
{ 6, "%c3%96" },
{ 6, "%c3%97" },
{ 6, "%c3%98" },
{ 6, "%c3%99" },
{ 6, "%c3%9a" },
{ 6, "%c3%9b" },
{ 6, "%c3%9c" },
{ 6, "%c3%9d" },
{ 6, "%c3%9e" },
{ 6, "%c3%9f" },
{ 6, "%c3%a0" },
{ 6, "%c3%a1" },
{ 6, "%c3%a2" },
{ 6, "%c3%a3" },
{ 6, "%c3%a4" },
{ 6, "%c3%a5" },
{ 6, "%c3%a6" },
{ 6, "%c3%a7" },
{ 6, "%c3%a8" },
{ 6, "%c3%a9" },
{ 6, "%c3%aa" },
{ 6, "%c3%ab" },
{ 6, "%c3%ac" },
{ 6, "%c3%ad" },
{ 6, "%c3%ae" },
{ 6, "%c3%af" },
{ 6, "%c3%b0" },
{ 6, "%c3%b1" },
{ 6, "%c3%b2" },
{ 6, "%c3%b3" },
{ 6, "%c3%b4" },
{ 6, "%c3%b5" },
{ 6, "%c3%b6" },
{ 6, "%c3%b7" },
{ 6, "%c3%b8" },
{ 6, "%c3%b9" },
{ 6, "%c3%ba" },
{ 6, "%c3%bb" },
{ 6, "%c3%bc" },
{ 6, "%c3%bd" },
{ 6, "%c3%be" },
{ 6, "%c3%bf" }
};
STRING_HANDLE URL_EncodeString(const char* textEncode)
{
STRING_HANDLE result;
if (textEncode == NULL)
{
result = NULL;
}
else
{
STRING_HANDLE tempString = STRING_construct(textEncode);
if (tempString == NULL)
{
result = NULL;
}
else
{
result = URL_Encode(tempString);
STRING_delete(tempString);
}
}
return result;
}
STRING_HANDLE URL_Encode(STRING_HANDLE input)
{
STRING_HANDLE result;
if (input == NULL)
{
/*Codes_SRS_URL_ENCODE_06_001: [If input is NULL then URL_Encode will return NULL.]*/
result = NULL;
LogError("URL_Encode:: NULL input\r\n");
}
else
{
size_t lengthOfResult = 0;
char* encodedURL;
const char* currentInput;
unsigned char currentUnsignedChar;
currentInput = STRING_c_str(input);
/*Codes_SRS_URL_ENCODE_06_003: [If input is a zero length string then URL_Encode will return a zero length string.]*/
do
{
currentUnsignedChar = (unsigned char)(*currentInput++);
lengthOfResult += urlEncoding[currentUnsignedChar].numberOfChars;
} while (currentUnsignedChar != 0);
if ((encodedURL = malloc(lengthOfResult)) == NULL)
{
/*Codes_SRS_URL_ENCODE_06_002: [If an error occurs during the encoding of input then URL_Encode will return NULL.]*/
result = NULL;
LogError("URL_Encode:: MALLOC failure on encode.\r\n");
}
else
{
size_t currentEncodePosition = 0;
currentInput = STRING_c_str(input);
do
{
currentUnsignedChar = (unsigned char)(*currentInput++);
if (urlEncoding[currentUnsignedChar].numberOfChars == 1)
{
encodedURL[currentEncodePosition++] = *(urlEncoding[currentUnsignedChar].encoding);
}
else
{
memcpy(encodedURL + currentEncodePosition, urlEncoding[currentUnsignedChar].encoding, urlEncoding[currentUnsignedChar].numberOfChars);
currentEncodePosition += urlEncoding[currentUnsignedChar].numberOfChars;
}
} while (currentUnsignedChar != 0);
result = STRING_new_with_memory(encodedURL);
}
}
return result;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше