OVMS3/OVMS.V3/components/wolfssl/IDE/INTIME-RTOS/wolfExamples.c

619 lines
19 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <rt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <io.h>
#include "wolfExamples.h"
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
#include <wolfssl/certs_test.h>
#include <wolfcrypt/test/test.h>
#include <wolfcrypt/benchmark/benchmark.h>
/*****************************************************************************
* Globals
****************************************************************************/
RTHANDLE hRootProcess;
DWORD dwKtickInUsecs;
INIT_STRUCT gInit;
static int gServerExit = 0;
static int gServerReady = 0;
static const char menu1[] = "\r\n"
"\tt. WolfCrypt Test\r\n"
"\tb. WolfCrypt Benchmark\r\n"
"\tc. WolfSSL Client Example\r\n"
"\ts. WolfSSL Server Example\r\n"
"\tl. WolfSSL Localhost Client/Server Example\r\n";
/*****************************************************************************
* Configuration
****************************************************************************/
#define TLS_MAXDATASIZE 4096 /* maximum acceptable amount of data */
#define TLS_PORT 11111 /* define default port number */
#define TLS_HOST_LOCAL "127.0.0.1"
#define TLS_HOST_REMOTE "192.168.0.112"
#define SOCK_MAX_PENDING 5
#define THREAD_BASE_PRIO 150
/*****************************************************************************
* TLS Client
****************************************************************************/
int wolfExample_TLSClient(const char* ip, int port)
{
int ret = 0;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL; /* create WOLFSSL object */
int sockFd; /* socket file descriptor */
struct sockaddr_in servAddr; /* struct for server address */
char sendBuff[TLS_MAXDATASIZE], rcvBuff[TLS_MAXDATASIZE];
sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (sockFd < 0) {
printf("Failed to create socket. Error: %d\n", errno);
return errno;
}
memset(&servAddr, 0, sizeof(servAddr)); /* clears memory block for use */
servAddr.sin_family = AF_INET; /* sets addressfamily to internet*/
servAddr.sin_port = htons(port); /* sets port to defined port */
/* looks for the server at the entered address (ip in the command line) */
if (inet_pton(AF_INET, ip, &servAddr.sin_addr) < 1) {
/* checks validity of address */
ret = errno;
printf("Invalid Address. Error: %d\n", ret);
goto exit;
}
if (connect(sockFd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
/* if socket fails to connect to the server*/
ret = errno;
printf("Connect error. Error: %d\n", ret);
goto exit;
}
/* create and initialize WOLFSSL_CTX structure */
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())) == NULL) {
printf("SSL_CTX_new error.\n");
goto exit;
}
/* load CA certificates into wolfSSL_CTX. which will verify the server */
ret = wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_der_2048,
sizeof_ca_cert_der_2048, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
printf("Error %d loading CA cert\n", ret);
goto exit;
}
if ((ssl = wolfSSL_new(ctx)) == NULL) {
printf("wolfSSL_new error.\n");
goto exit;
}
wolfSSL_set_fd(ssl, sockFd);
ret = wolfSSL_connect(ssl);
if (ret == SSL_SUCCESS) {
printf("Message for server:\t");
fgets(sendBuff, TLS_MAXDATASIZE, stdin);
if (wolfSSL_write(ssl, sendBuff, strlen(sendBuff)) != strlen(sendBuff)) {
/* the message is not able to send, or error trying */
ret = wolfSSL_get_error(ssl, 0);
printf("Write error: Error: %d\n", ret);
goto exit;
}
memset(rcvBuff, 0, TLS_MAXDATASIZE);
if (wolfSSL_read(ssl, rcvBuff, TLS_MAXDATASIZE) < 0) {
/* the server failed to send data, or error trying */
ret = wolfSSL_get_error(ssl, 0);
printf("Read error. Error: %d\n", ret);
goto exit;
}
printf("Received: \t%s\n", rcvBuff);
}
exit:
/* frees all data before client termination */
if (sockFd != -1)
close(sockFd);
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
gServerExit = 1;
return ret;
}
/*****************************************************************************
* TLS Server
****************************************************************************/
int wolfExample_TLSServer(int port)
{
int ret = 0;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
int sockFd, clientFd = -1;
struct sockaddr_in serverAddr = {0}, clientAddr = {0};
const char reply[] = "I hear ya fa shizzle!\n";
int addrSize = sizeof(clientAddr);
char buff[256];
sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (sockFd < 0) {
printf("Failed to create socket. Error: %d\n", errno);
return errno;
}
/* create and initialize WOLFSSL_CTX structure */
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())) == NULL) {
fprintf(stderr, "wolfSSL_CTX_new error.\n");
goto exit;
}
/* Load server certificate into WOLFSSL_CTX */
ret = wolfSSL_CTX_use_certificate_buffer(ctx, server_cert_der_2048,
sizeof_server_cert_der_2048, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error %d loading server-cert!\n", ret);
goto exit;
}
/* Load server key into WOLFSSL_CTX */
ret = wolfSSL_CTX_use_PrivateKey_buffer(ctx, server_key_der_2048,
sizeof_server_key_der_2048, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error %d loading server-key!\n", ret);
goto exit;
}
/* Initialize the server address struct to zero */
memset((char *)&serverAddr, 0, sizeof(serverAddr));
/* Fill the server's address family */
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(port);
/* Attach the server socket to our port */
if (bind(sockFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
printf("ERROR: failed to bind\n");
goto exit;
}
printf("Waiting for a connection...\n");
gServerReady = 1;
/* Continuously accept connects while not in an active connection */
while (gServerExit == 0) {
/* listen for a new connection */
ret = listen(sockFd, SOCK_MAX_PENDING);
if (ret == 0) {
/* Wait until a client connects */
clientFd = accept(sockFd, (struct sockaddr*)&clientAddr, &addrSize);
/* If fails to connect, loop back up and wait for a new connection */
if (clientFd == -1) {
printf("failed to accept the connection..\n");
}
/* If it connects, read in and reply to the client */
else {
printf("Client connected successfully\n");
ssl = wolfSSL_new(ctx);
if (ssl == NULL) {
fprintf(stderr, "wolfSSL_new error.\n");
break;
}
/* direct our ssl to our clients connection */
wolfSSL_set_fd(ssl, clientFd);
printf("Using Non-Blocking I/O: %d\n",
wolfSSL_get_using_nonblock(ssl));
for ( ; ; ) {
/* Clear the buffer memory for anything possibly left over */
memset(&buff, 0, sizeof(buff));
/* Read the client data into our buff array */
ret = wolfSSL_read(ssl, buff, sizeof(buff) - 1);
if (ret > 0) {
/* Print any data the client sends to the console */
printf("Client: %s\n", buff);
/* Reply back to the client */
ret = wolfSSL_write(ssl, reply, sizeof(reply) - 1);
if (ret < 0) {
printf("wolfSSL_write error = %d\n",
wolfSSL_get_error(ssl, ret));
gServerExit = 1;
break;
}
}
/* if the client disconnects break the loop */
else {
if (ret < 0)
printf("wolfSSL_read error = %d\n",
wolfSSL_get_error(ssl, ret));
else if (ret == 0)
printf("The client has closed the connection.\n");
gServerExit = 1;
break;
}
}
wolfSSL_free(ssl); /* Free the WOLFSSL object */
ssl = NULL;
}
close(clientFd); /* close the connected socket */
clientFd = -1;
}
} /* while */
exit:
if (clientFd != -1)
close(clientFd);
if (sockFd != -1)
close(sockFd);
wolfSSL_free(ssl); /* Free the WOLFSSL object */
wolfSSL_CTX_free(ctx); /* Free WOLFSSL_CTX */
return ret;
}
/*****************************************************************************
* TLS Local Test
****************************************************************************/
static void wolfSSLLocalServerThread(void* param)
{
int port = (int)((int*)param);
wolfExample_TLSServer(port);
}
int wolfExample_TLSLocal(int port)
{
int ret;
RTHANDLE srvHandle;
/* start server thread */
srvHandle = CreateRtThread(THREAD_BASE_PRIO + 10,
(LPPROC)wolfSSLLocalServerThread, WOLF_EXAMPLES_STACK, (void*)port);
if (srvHandle == BAD_RTHANDLE) {
Fail("Cannot create server thread");
return -1;
}
/* wait for server to be ready */
while (gServerReady != 1) {
RtSleep(0);
}
/* run client */
ret = wolfExample_TLSClient(TLS_HOST_LOCAL, port);
return ret;
}
/*****************************************************************************
* Thread
memset(&args, 0, sizeof(args));
****************************************************************************/
typedef struct func_args {
int argc;
char** argv;
int return_code;
} func_args;
static void wolfExampleThread(void* param)
{
func_args args;
#ifdef DEBUG_WOLFSSL
wolfSSL_Debugging_ON();
#endif
/* initialize wolfSSL */
wolfSSL_Init();
while (1) {
char rc;
gServerExit = 0;
gServerReady = 0;
printf("\r\n\t\t\t\tMENU\r\n");
printf(menu1);
printf("Please select one of the above options: ");
rc = getchar();
switch (rc) {
case 't':
printf("\nCrypt Test\n");
wolfcrypt_test(&args);
printf("Crypt Test: Return code %d\n", args.return_code);
break;
case 'b':
printf("\nBenchmark Test\n");
benchmark_test(&args);
printf("Benchmark Test: Return code %d\n", args.return_code);
break;
case 'c':
wolfExample_TLSClient(TLS_HOST_REMOTE, TLS_PORT);
break;
case 's':
wolfExample_TLSServer(TLS_PORT);
break;
case 'l':
wolfExample_TLSLocal(TLS_PORT);
break;
// All other cases go here
default:
if (rc != '\r' && rc != '\n')
printf("\r\nSelection %c out of range\r\n", rc);
break;
}
}
wolfSSL_Cleanup();
}
/*****************************************************************************
* FUNCTION: Catalog
*
* PARAMETERS: 1. handle of the process whose object directory must be used
* 2. the object whose handle must be cataloged
* 3. the name to be used (upto 14 characters)
*
* RETURNS: TRUE on success
*
* DESCRIPTION: If the given name already exists,
* and the existing name refers to a non-existing object,
* then the existing name is removed before cataloging.
\*****************************************************************************/
BOOLEAN Catalog(
RTHANDLE hProcess,
RTHANDLE hObject,
LPSTR lpszName)
{
RTHANDLE hOld;
if (CatalogRtHandle(hProcess, hObject, lpszName))
return TRUE;
// something wrong: check for the case mentioned above
if (((hOld = LookupRtHandle(hProcess, lpszName, NO_WAIT)) != BAD_RTHANDLE) &&
(GetRtHandleType(hOld) == INVALID_TYPE))
{
// this is the case mentioned above: remove the old entry and try again
if (UncatalogRtHandle(hProcess, lpszName))
return (CatalogRtHandle(hProcess, hObject, lpszName));
}
return FALSE;
}
/*****************************************************************************
* FUNCTION: Cleanup (local function)
*
* DESCRIPTION:
* Tell threads to delete themselves and wait a while;
* if any thread still exists, kill it.
* Remove all other objects as far as they have been created.
\*****************************************************************************/
void Cleanup(void)
{
// indicate that we are cleaning up
gInit.state = CLEANUP_BUSY;
gInit.bShutdown = TRUE;
#ifdef _DEBUG
fprintf(stderr, "wolfExamples started cleaning up\n");
#endif
// remove our name from the root process
if (gInit.bCataloged) {
if (!UncatalogRtHandle(hRootProcess, "wolfExample"))
Fail("Cannot remove my own name");
}
#ifdef _DEBUG
fprintf(stderr, "wolfExamples finished cleaning up\n");
#endif
// lie down
exit(0);
}
/*****************************************************************************
* FUNCTION: Fail
*
* PARAMETERS: same parameters as expected by printf
*
* DESCRIPTION:
* If in debug mode, prints the message, appending a new line and the error number.
* Then the current process is killed graciously:
* If the current thread is the main thread, this is done directly.
* if the current thread is another one, a terminate request is sent and
* the function returns to the calling thread.
\*****************************************************************************/
void Fail(LPSTR lpszMessage, ...)
{
EXCEPTION eh;
RTHANDLE hDelMbx;
DWORD dwTerminate;
#ifdef _DEBUG
va_list ap;
va_start(ap, lpszMessage);
vfprintf(stderr, lpszMessage, ap);
va_end(ap);
fprintf(stderr, "\nError nr=%x %s\n", GetLastRtError(), GetRtErrorText(GetLastRtError()));
#endif
// make sure that exceptions are returned for inline handling
GetRtExceptionHandlerInfo(THREAD_HANDLER, &eh);
eh.ExceptionMode = 0;
SetRtExceptionHandler(&eh);
// if we had not started initializing yet, just get out
if (BEFORE_INIT == gInit.state)
exit(0);
if (gInit.hMain == GetRtThreadHandles(THIS_THREAD))
{
// this is the main thread:
// if we are busy initializing, then do Cleanup
if (INIT_BUSY == gInit.state)
Cleanup(); // does not return
// this is the main thread, but we are not initializing: just return
return;
}
// this is not the main thread:
// ask main thread to do cleanup
// (allow some time to setup the deletion mailbox, ignore errors)
hDelMbx = LookupRtHandle(NULL_RTHANDLE, "R?EXIT_MBOX", 5000);
dwTerminate = TERMINATE;
SendRtData(hDelMbx, &dwTerminate, 4);
}
/*****************************************************************************
*
* FUNCTION: UsecsToKticks
*
* PARAMETERS: 1. number of usecs
*
* RETURNS: number of low level ticks
*
* DESCRIPTION: returns the parameter if it is WAIT_FOREVER
* otherwise rounds up to number of low level ticks
\*****************************************************************************/
DWORD UsecsToKticks(DWORD dwUsecs)
{
if (dwUsecs == WAIT_FOREVER)
return WAIT_FOREVER;
return (dwUsecs + dwKtickInUsecs - 1) / dwKtickInUsecs;
}
/*****************************************************************************
* FUNCTION: main
*
* DESCRIPTION:
* This is the main program module.
* It creates global objects and all threads.
* The main thread then waits for notifications and acts accordingly
\*****************************************************************************/
int main(int argc, char* argv[])
{
SYSINFO sysinfo;
EVENTINFO eiEventInfo;
RTHANDLE taskHandle;
#ifdef _DEBUG
fprintf(stderr, "wolfExamples started\n");
#endif
// obtain handle of root process (cannot fail)
hRootProcess = GetRtThreadHandles(ROOT_PROCESS);
// initialize the structure for cleaning up
memset(&gInit, 0, sizeof(gInit));
gInit.state = BEFORE_INIT;
// get low level tick length in usecs
if (!CopyRtSystemInfo(&sysinfo))
Fail("Cannot copy system info");
dwKtickInUsecs = 10000 / sysinfo.KernelTickRatio;
if (dwKtickInUsecs == 0)
Fail("Invalid low level tick length");
// adjust process max priority (ignore error)
// TODO adjust the 2nd parameter to a value closer to zero if you want to allow more priorities
SetRtProcessMaxPriority(NULL_RTHANDLE, THREAD_BASE_PRIO);
// obtain main thread's handle
gInit.hMain = GetRtThreadHandles(THIS_THREAD);
gInit.state = INIT_BUSY;
// attempt to catalog the thread but ignore error
Catalog(NULL_RTHANDLE, gInit.hMain, "TMain");
// catalog the handle of this process in the root process
if (!Catalog(hRootProcess, GetRtThreadHandles(THIS_PROCESS), "wolfExample")) {
Fail("Cannot catalog process name");
}
gInit.bCataloged = TRUE;
// create thread
taskHandle = CreateRtThread(THREAD_BASE_PRIO + 20,
(LPPROC)wolfExampleThread, WOLF_EXAMPLES_STACK, 0);
if (taskHandle == BAD_RTHANDLE) {
Fail("Cannot create thread");
}
// indicate that initialization has finished
gInit.state = INIT_DONE;
#ifdef _DEBUG
fprintf(stderr, "wolfExamples finished initialization\n");
#endif
// wait for notifications
while (RtNotifyEvent(RT_SYSTEM_NOTIFICATIONS | RT_EXIT_NOTIFICATIONS,
WAIT_FOREVER, &eiEventInfo))
{
switch(eiEventInfo.dwNotifyType)
{
case TERMINATE:
// TODO: this process should terminate
// cleanup the environment
Cleanup(); // does not return
case NT_HOST_UP:
// TODO: react to a Windows host that has come back
break;
case NT_BLUESCREEN:
// TODO: react to a Windows blue screen
break;
case KERNEL_STOPPING:
// TODO: react to the INtime kernel stopping
break;
case NT_HOST_HIBERNATE:
// TODO: react to the Windows host going in hibernation
break;
case NT_HOST_STANDBY:
// TODO: react to the Windows host going in standby mode
break;
case NT_HOST_SHUTDOWN_PENDING:
// TODO: react to a Windows host that is about to shutdown
break;
}
}
Fail("Notify failed");
return 0;
}