blob: b4f31ce6d47c48261651205e37656b0a1b869456 [file] [log] [blame]
/**
* @file axCliUtil.c
* @author NXP Semiconductors
* @version 1.0
* @par License
*
* Copyright 2017 NXP
* SPDX-License-Identifier: Apache-2.0
*
* @par Description
* Implementation of command line utility functions
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
// project specific include files
#include "sm_types.h"
// #include "sm_apdu.h"
#include "axCliUtil.h"
#include "tst_sm_util.h"
#define FLOW_VERBOSE_PROBE_A70
#ifdef FLOW_VERBOSE_PROBE_A70
#define FPRINTF(...) printf (__VA_ARGS__)
#else
#define FPRINTF(...)
#endif
// #define DBG_AX_CLI_UTIL
#ifdef DBG_AX_CLI_UTIL
#define DBGPRINTF(...) printf (__VA_ARGS__)
#else
#define DBGPRINTF(...)
#endif
#define AX_CLI_GET_MODE_OPTIONAL 0
#define AX_CLI_GET_MODE_STRICT 1
#define CMD_HANDLER_MAX 20
#define CMD_NAME_MAX 64
typedef int (*a7xCmdHandler_t) (int argc, char ** argv);
typedef struct {
char szName[CMD_NAME_MAX];
a7xCmdHandler_t cmdHandler;
} a7xCmd_t;
int a7xAddCmd(char *szName, a7xCmdHandler_t cmdHandler);
int a7xRemoveCmd(char *szName);
static int nCmd = 0;
static a7xCmd_t a7xCmd[CMD_HANDLER_MAX];
static int axCliGetString_Internal(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent, int mode);
int a7xAddCmd(char *szName, a7xCmdHandler_t cmdHandler)
{
if (nCmd == CMD_HANDLER_MAX)
{
return -1;
}
if (strlen(szName) >= (CMD_NAME_MAX-1) )
{
return -1;
}
strcpy(a7xCmd[nCmd].szName, szName);
a7xCmd[nCmd].cmdHandler = cmdHandler;
nCmd++;
return 0;
}
char *axGetErrorString(int errorCode)
{
switch(errorCode)
{
case AX_CLI_EXEC_OK:
return "exec ok";
case AX_CLI_EXEC_FAILED:
return "exec failed";
case AX_CLI_ERR_IP_ADR_MISSING:
return "err ip adr missing";
case AX_CLI_ERR_CANNOT_CONNECT:
return "err cannot connect";
case AX_CLI_ERR_SELECT_FAILS:
return "err select fails";
case AX_CLI_NOT_IMPLEMENTED:
return "not implemented";
case AX_CLI_CHECK_USAGE:
return "check usage";
case AX_CLI_PEM_CONVERT_FAILED:
return "pem covert failed";
case AX_CLI_FILE_OPEN_FAILED:
return "file open failed";
case AX_CLI_FILE_PEM_READ_FAILED:
return "pemfile read failed";
case AX_CLI_FILE_PEM_WRITE_FAILED:
return "pemfile write failed";
case AX_CLI_BIT_CURVE_ERROR:
return "bit curve error";
case AX_CLI_DYN_ALLOC_ERROR:
return "dyn alloc error";
case AX_CLI_EXEC_HALTED:
return "exec halted";
case AX_CLI_FILE_FORMAT_ERROR:
return "file format error";
case AX_CLI_ARG_RANGE_ERROR:
return "arg range error";
case AX_CLI_ARG_NAME_ERROR:
return "arg name error";
case AX_CLI_ARG_OPTION_ERROR:
return "arg option error";
case AX_CLI_ARG_COUNT_MISTAKE:
return "arg count mistake";
case AX_CLI_ARG_VALUE_ERROR:
return "arg value error";
case AX_CLI_BUFFER_SIZE_ERROR:
return "buffer size error";
case AX_CLI_API_ERROR:
return "api error";
case AX_CLI_WRAP_ERROR:
return "wrap error";
case AX_CLI_NO_OBJECTS:
return "enumeration no object found error";
case AX_CLI_OBJECT_NOT_FOUND:
return "object at index not found error";
case AX_CLI_WRITE_CER_FILE_ERROR:
return "write certificate file error";
case AX_CLI_ERASE_CER_FILE_ERROR:
return "erase certificate file error";
case AX_CLI_UPDATE_CER_FILE_ERROR:
return "update certificate file error";
default:
return "unknown";
}
}
/**
* Extract an integer value from argument array in case of matching option
* \pre argCurrent points to correct position (argument option to be handled)
* \post argCurrent points to next argument option to be handled.
*
* \note szLongOption not yet supported
*
* @param[in] szShortOption Name of option (excluding '-' character)
* @param[in] szLongOption
* @param[in,out] value Integer value to be retrieved (must be a base 10 number)
* @param[in] minVal Minimum value (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value)
* @param[in] maxVal Maximum value (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value)
* @param[in] argc maximum amount of arguments passed as argument
* @param[in] argv argument array
* @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array)
*
* @retval ::AX_CLI_EXEC_OK Upon successful execution
*/
int axCliGetInteger(char *szShortOption, char *szLongOption, int *value, int minVal, int maxVal, int argc, char ** argv, int *argCurrent)
{
long longTmp = 0L;
char szDummy[] = "szDummy";
char *pastConverted = szDummy; // Catch number conversion issues
char szShort[64];
// Do not go beyond the last argument when parsing
if ((*argCurrent + 1) >= argc)
{
return AX_CLI_ARG_COUNT_MISTAKE;
}
// Long option not yet supported
if (strcmp(szLongOption, "") != 0)
{
return AX_CLI_NOT_IMPLEMENTED;
}
szShort[0] = '-';
strcpy(&szShort[1], szShortOption);
if (strcmp(argv[*argCurrent], szShort) == 0) {
longTmp = strtol(argv[*argCurrent+1], &pastConverted, 10);
if (pastConverted == argv[*argCurrent+1]) {
// Conversion failed
printf("%s %s: %s is not an integer\n", szShort, argv[*argCurrent+1], argv[*argCurrent+1]);
return AX_CLI_ARG_VALUE_ERROR;
}
else {
if (longTmp < minVal) { return AX_CLI_ARG_RANGE_ERROR; }
if (longTmp > maxVal) { return AX_CLI_ARG_RANGE_ERROR; }
*value = (int)longTmp;
DBGPRINTF("axCliGetInteger: %s option with %d\n", szShort, *value);
}
}
else {
printf("Argument %s not expected\n", argv[*argCurrent]);
return AX_CLI_ARG_OPTION_ERROR;
}
// Upon success.
(*argCurrent) += 2;
return AX_CLI_EXEC_OK;
}
/**
* Extract a hexadecimal (ASCII) string from argument array in case of matching option,
* store it as a binary array
* \pre argCurrent points to correct position (argument option to be handled)
* \post argCurrent points to next argument option to be handled.
*
* \note szLongOption not yet supported
*
* @param[in] szShortOption Name of option (excluding '-' character)
* @param[in] szLongOption Pointer to the provided hash (or any other bytestring).
* @param[in,out] hex IN: Buffer to contain hex value; OUT: hex value retrieved
* @param[in,out] hexLen IN: Buffersize; OUT: Actual amount of byte retrieved
* @param[in] minLen Minimum length in byte (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value)
* @param[in] maxLen Maximum length in byte (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value)
* @param[in] argc maximum amount of arguments passed as argument
* @param[in] argv argument array
* @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array)
*
* @retval ::AX_CLI_EXEC_OK Upon successful execution
*/
int axCliGetHexString(char *szShortOption, char *szLongOption, U8 *hex, U16 *hexLen, int minLen, int maxLen, int argc, char ** argv, int *argCurrent)
{
char szShort[64];
// Do not go beyond the last argument when parsing
if ((*argCurrent + 1) >= argc)
{
return AX_CLI_ARG_COUNT_MISTAKE;
}
// Long option not yet supported
if (strcmp(szLongOption, "") != 0)
{
return AX_CLI_NOT_IMPLEMENTED;
}
szShort[0] = '-';
strcpy(&szShort[1], szShortOption);
if (strcmp(argv[*argCurrent], szShort) == 0) {
// Check Hex String Argument size. The count of ASCII characters is double the byte count
int nLen = (int)strlen(argv[*argCurrent+1]);
if ( (nLen >= (minLen << 1)) && (nLen <= (maxLen << 1)) && ((nLen % 2) == 0 ) ) {
int nRet = 0;
nRet = axConvertHexString2ByteArray(hex, argv[*argCurrent+1], 0, (int)(nLen >> 1));
*hexLen = (U16)(nLen >> 1);
if (nRet == AX_UTIL_OK) {
#ifdef DBG_AX_CLI_UTIL
axPrintByteArray("hex", hex, *hexLen, AX_COMPACT_32);
#endif
}
else {
printf("axCliGetHexString: Cannot convert %s to HEX.\n", argv[*argCurrent+1]);
return AX_CLI_ARG_VALUE_ERROR;
}
}
else {
printf("axCliGetHexString: argument has wrong length: %s\n\tlen=%d char\'s\n",
argv[*argCurrent+1], (int)nLen);
printf("\tValid range: [%d:%d]\n", minLen << 1, maxLen << 1);
// DBGPRINTF("nLen = %d\n", nLen);
// DBGPRINTF("minLen << 1 = %d\n", );
// DBGPRINTF("maxLen << 1 = %d\n",);
return AX_CLI_ARG_VALUE_ERROR;
}
}
else {
DBGPRINTF("Argument %s not expected\n", argv[*argCurrent]);
return AX_CLI_ARG_OPTION_ERROR;
}
// Upon success.
(*argCurrent) += 2;
return AX_CLI_EXEC_OK;
}
/**
* Extract a string from argument array in case of matching option.
* Don't echo a warning to stdout when the option is missing
* \pre argCurrent points to correct position (argument option to be handled)
* \post argCurrent points to next argument option to be handled.
*
* \note szLongOption not yet supported
*
* @param[in] szShortOption Name of option (excluding '-' character)
* @param[in] szLongOption Pointer to the provided hash (or any other bytestring).
* @param[in,out] szString IN: Buffer to contain string; OUT: string retrieved
* @param[in,out] bufLen IN: Buffersize of szString
* @param[in] argc maximum amount of arguments passed as argument
* @param[in] argv argument array
* @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array)
*
* @retval ::AX_CLI_EXEC_OK Upon successful execution
*/
int axCliGetOptionalString(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent)
{
int mode = AX_CLI_GET_MODE_OPTIONAL;
return axCliGetString_Internal(szShortOption, szLongOption, szString, bufLen, argc, argv, argCurrent, mode);
}
/**
* Extract a string from argument array in case of matching option
* \pre argCurrent points to correct position (argument option to be handled)
* \post argCurrent points to next argument option to be handled.
*
* \note szLongOption not yet supported
*
* @param[in] szShortOption Name of option (excluding '-' character)
* @param[in] szLongOption Pointer to the provided hash (or any other bytestring).
* @param[in,out] szString IN: Buffer to contain string; OUT: string retrieved
* @param[in,out] bufLen IN: Buffersize of szString
* @param[in] argc maximum amount of arguments passed as argument
* @param[in] argv argument array
* @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array)
*
* @retval ::AX_CLI_EXEC_OK Upon successful execution
*/
int axCliGetString(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent)
{
int mode = AX_CLI_GET_MODE_STRICT;
return axCliGetString_Internal(szShortOption, szLongOption, szString, bufLen, argc, argv, argCurrent, mode);
}
static int axCliGetString_Internal(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent, int mode)
{
char szShort[64];
// Do not go beyond the last argument when parsing
if ((*argCurrent + 1) >= argc)
{
return AX_CLI_ARG_COUNT_MISTAKE;
}
// Long option not yet supported
if (strcmp(szLongOption, "") != 0)
{
return AX_CLI_NOT_IMPLEMENTED;
}
szShort[0] = '-';
strcpy(&szShort[1], szShortOption);
if (strcmp(argv[*argCurrent], szShort) == 0) {
// Check Hex String Argument size. The count of ASCII characters is double the byte count
int nLen = (int)strlen(argv[*argCurrent+1]);
if ( nLen < bufLen) {
strcpy(szString, argv[*argCurrent+1]);
}
else {
printf("axCliGetString: illegal argument (too long): %s\n", argv[*argCurrent+1]);
return AX_CLI_ARG_VALUE_ERROR;
}
}
else {
if (mode != AX_CLI_GET_MODE_OPTIONAL) {
printf("Argument %s not expected\n", argv[*argCurrent]);
}
return AX_CLI_ARG_OPTION_ERROR;
}
// Upon success.
(*argCurrent) += 2;
return AX_CLI_EXEC_OK;
}
/**
* Convert a string into an ASCII key (of variable length) and a byte array of
* fixed length. One needs to know the length of the expected byte array up front
* Symbolic Representation of input string
* <key> 00..FF
*
* @param[in,out] key IN: buffer to contain key; OUT: key retrieved
* @param[in] keyBufSize Size of buffer key
* @param[in,out] hex IN: buffer to contain hex array of at least size hexLen; OUT: hex array retrieved
* @param[in] hexLen IN: expected size of hex array
* @param[in] szLine IN: line of text (string) that will be parsed
*/
int axCliGetKeyFixedLenHexValueFromLine(char *key, int keyBufSize, U8 *hex, U16 hexLen, const char *szLine)
{
int nTokens = 0;
char **myargv;
int nRet = AX_CLI_EXEC_FAILED;
int nLen = 0;
if ((nRet = axMakeArgv(szLine, " \r\n", &myargv, &nTokens)) != AX_CLI_EXEC_OK) {
DBGPRINTF("Could not make argument array for %s\n", szLine);
return nRet;
}
if (nTokens == 2) {
if ( (int)strlen(myargv[0]) < keyBufSize) {
strcpy(key, myargv[0]);
if ( (nLen = (int)strlen(myargv[1])) == (hexLen << 1)) {
if ( axConvertHexString2ByteArray(hex, myargv[1], 0, hexLen) == AX_UTIL_OK ) {
nRet = AX_CLI_EXEC_OK;
}
else {
DBGPRINTF("ASCII-Hex-String %s cannot be converted to hex array.\n", myargv[1]);
nRet = AX_CLI_ARG_VALUE_ERROR;
}
}
else {
DBGPRINTF("ASCII-Hex-String %s wrong size.\n", myargv[1]);
nRet = AX_CLI_ARG_VALUE_ERROR;
}
}
}
else {
DBGPRINTF("Cannot handle input line: wrong number of tokens: %d\n", nTokens);
DBGPRINTF("token[0]: %s\n", myargv[0]);
DBGPRINTF("token[1]: %s\n", myargv[1]);
nRet = AX_CLI_ARG_COUNT_MISTAKE;
}
axFreeArgv(myargv);
return nRet;
}
int axMakeArgv(const char *s, const char *delimiters, char ***argvp, int *argc)
{
int i;
int nToken;
const char *sStart;
char *szCopy;
*argc = 0;
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL))
{
return AX_CLI_ARG_VALUE_ERROR;
}
*argvp = NULL;
sStart = s + strspn(s, delimiters);
if ((szCopy = (char *)malloc(strlen(sStart) + 1)) == NULL)
{
return AX_CLI_DYN_ALLOC_ERROR;
}
strcpy(szCopy, sStart);
nToken = 0;
// Count the number of tokens in a first run through
if (strtok(szCopy, delimiters) != NULL)
{
for (nToken = 1; strtok(NULL, delimiters) != NULL; nToken++) ;
}
// Create argument array for ptrs to the tokens
if ((*argvp = (char **)malloc((nToken + 1)*sizeof(char *))) == NULL)
{
free(szCopy);
return AX_CLI_DYN_ALLOC_ERROR;
}
// Insert pointers to tokens into the argument array, they point to segments of the
// allocated string (szCopy)
if (nToken == 0)
{
free(szCopy);
}
else
{
strcpy(szCopy, sStart);
**argvp = strtok(szCopy, delimiters);
for (i = 1; i < nToken; i++)
{
*((*argvp) + i) = strtok(NULL, delimiters);
}
}
// Close pointer array with a final NULL pointer
*((*argvp) + nToken) = NULL;
*argc = nToken;
return AX_CLI_EXEC_OK;
}
void axFreeArgv(char **argv)
{
if (argv == NULL) { return; }
if (*argv != NULL) { free(*argv); }
free(argv);
}