#include "app_errdecode.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "app_errno.h"
#include "app_print.h"
#define HEX_PREFIX_LEN 2
#define HEX_BASE 16
#define DEC_BASE 10
#define ERROR_DATABASE_SIZE (sizeof(g_errorDatabase) / sizeof(ErrorEntry))
* Error code database
* Contains common openHiTLS error codes and their descriptions
*/
static const ErrorEntry g_errorDatabase[] = {
{0x00000000, "none", "none", "success", "no error"},
{0x00000001, "system", "generic", "generic error", "system:generic:generic error"},
{0x00000002, "system", "parameter", "invalid parameter", "system:parameter:invalid parameter"},
{0x00000003, "system", "memory", "allocation failed", "system:memory:allocation failed"},
{0x00000004, "system", "buffer", "buffer too small", "system:buffer:buffer too small"},
{0x00000005, "system", "operation", "not supported", "system:operation:not supported"},
{0x0E000065, "SSL", "ssl3_get_record", "wrong version number",
"SSL routines:ssl3_get_record:wrong version number"},
{0x0E000066, "SSL", "ssl_handshake", "protocol version not supported",
"SSL routines:ssl_handshake:protocol version not supported"},
{0x0E000067, "SSL", "ssl3_read_bytes", "certificate verify failed",
"SSL routines:ssl3_read_bytes:certificate verify failed"},
{0x0E000068, "SSL", "ssl_verify_cert", "certificate expired",
"SSL routines:ssl_verify_cert:certificate expired"},
{0x0E000069, "SSL", "ssl_verify_cert", "certificate not yet valid",
"SSL routines:ssl_verify_cert:certificate not yet valid"},
{0x0E00006A, "SSL", "ssl_verify_cert", "certificate chain too long",
"SSL routines:ssl_verify_cert:certificate chain too long"},
{0x0E00006B, "SSL", "ssl_verify_cert", "certificate revoked",
"SSL routines:ssl_verify_cert:certificate revoked"},
{0x0E00006C, "SSL", "ssl_verify_cert", "certificate unknown",
"SSL routines:ssl_verify_cert:certificate unknown"},
{0x0E00006D, "SSL", "ssl_verify_cert", "bad certificate",
"SSL routines:ssl_verify_cert:bad certificate"},
{0x0E00006E, "SSL", "ssl_verify_cert", "unsupported certificate",
"SSL routines:ssl_verify_cert:unsupported certificate"},
{0x1408F10B, "SSL", "ssl3_get_record", "wrong version number",
"SSL routines:ssl3_get_record:wrong version number"},
{0x14094410, "SSL", "ssl3_read_bytes", "sslv3 alert handshake failure",
"SSL routines:ssl3_read_bytes:sslv3 alert handshake failure"},
{0x06000001, "CIPHER", "cipher_init", "initialization failed", "CIPHER routines:cipher_init:initialization failed"},
{0x06000002, "CIPHER", "cipher_update", "update failed", "CIPHER routines:cipher_update:update failed"},
{0x06000003, "CIPHER", "cipher_final", "finalization failed", "CIPHER routines:cipher_final:finalization failed"},
{0x06000004, "CIPHER", "cipher_init", "invalid algorithm", "CIPHER routines:cipher_init:invalid algorithm"},
{0x06000005, "CIPHER", "cipher_init", "invalid key length", "CIPHER routines:cipher_init:invalid key length"},
{0x06000006, "CIPHER", "cipher_init", "invalid IV length", "CIPHER routines:cipher_init:invalid IV length"},
{0x05000001, "MD", "md_init", "initialization failed", "MD routines:md_init:initialization failed"},
{0x05000002, "MD", "md_update", "update failed", "MD routines:md_update:update failed"},
{0x05000003, "MD", "md_final", "finalization failed", "MD routines:md_final:finalization failed"},
{0x05000004, "MD", "md_init", "invalid algorithm", "MD routines:md_init:invalid algorithm"},
{0x04000001, "RAND", "rand_bytes", "generation failed", "RAND routines:rand_bytes:generation failed"},
{0x04000002, "RAND", "rand_seed", "insufficient entropy", "RAND routines:rand_seed:insufficient entropy"},
{0x04000003, "RAND", "rand_seed", "seed failed", "RAND routines:rand_seed:seed failed"},
{0x0D000001, "ASN1", "asn1_encode", "encoding error", "ASN1 routines:asn1_encode:encoding error"},
{0x0D000002, "ASN1", "asn1_decode", "decoding error", "ASN1 routines:asn1_decode:decoding error"},
{0x0D000003, "ASN1", "asn1_parse", "invalid structure", "ASN1 routines:asn1_parse:invalid structure"},
{0x0D000004, "ASN1", "asn1_encode", "buffer overflow", "ASN1 routines:asn1_encode:buffer overflow"},
{0x0B000001, "X509", "x509_parse", "parsing failed",
"X509 routines:x509_parse:parsing failed"},
{0x0B000002, "X509", "x509_verify", "validation failed",
"X509 routines:x509_verify:validation failed"},
{0x0B000003, "X509", "x509_verify_chain", "chain validation failed",
"X509 routines:x509_verify_chain:chain validation failed"},
{0x0B000004, "X509", "x509_verify_sig", "signature verification failed",
"X509 routines:x509_verify_sig:signature verification failed"},
{0x07000001, "PKEY", "pkey_gen", "generation failed", "PKEY routines:pkey_gen:generation failed"},
{0x07000002, "PKEY", "pkey_derive", "derivation failed", "PKEY routines:pkey_derive:derivation failed"},
{0x07000003, "PKEY", "pkey_exchange", "exchange failed", "PKEY routines:pkey_exchange:exchange failed"},
{0x07000004, "PKEY", "pkey_parse", "invalid format", "PKEY routines:pkey_parse:invalid format"},
{0x07000005, "PKEY", "pkey_verify", "verification failed",
"PKEY routines:pkey_verify:verification failed"}
};
static const ErrorEntry *LookupErrorCode(uint64_t code)
{
for (size_t i = 0; i < ERROR_DATABASE_SIZE; i++) {
if (g_errorDatabase[i].code == code) {
return &g_errorDatabase[i];
}
}
return NULL;
}
static ErrorCodeFormat DetectFormat(const char *input)
{
if (input == NULL || *input == '\0') {
return FORMAT_INVALID;
}
if (strncmp(input, "0x", HEX_PREFIX_LEN) == 0 || strncmp(input, "0X", HEX_PREFIX_LEN) == 0) {
return FORMAT_HEX_WITH_PREFIX;
}
int32_t hasHexChar = 0;
for (const char *p = input; *p != '\0'; p++) {
if (!isxdigit((unsigned char)*p)) {
return FORMAT_INVALID;
}
if ((*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')) {
hasHexChar = 1;
}
}
if (hasHexChar) {
return FORMAT_HEX_WITHOUT_PREFIX;
}
return FORMAT_DECIMAL;
}
static int32_t ParseErrorCode(const char *input, int32_t hexMode, uint64_t *code)
{
if (input == NULL || code == NULL) {
return HITLS_APP_INVALID_ARG;
}
char *endptr;
uint64_t result;
const char *parseInput = input;
if (strncmp(input, "0x", HEX_PREFIX_LEN) == 0 || strncmp(input, "0X", HEX_PREFIX_LEN) == 0) {
parseInput = input + HEX_PREFIX_LEN;
result = strtoull(parseInput, &endptr, HEX_BASE);
} else if (hexMode) {
result = strtoull(input, &endptr, HEX_BASE);
} else {
ErrorCodeFormat format = DetectFormat(input);
if (format == FORMAT_HEX_WITHOUT_PREFIX) {
result = strtoull(input, &endptr, HEX_BASE);
} else if (format == FORMAT_DECIMAL) {
result = strtoull(input, &endptr, DEC_BASE);
} else {
return HITLS_APP_INVALID_ARG;
}
}
if (*endptr != '\0') {
return HITLS_APP_INVALID_ARG;
}
if (endptr == parseInput) {
return HITLS_APP_INVALID_ARG;
}
*code = result;
return HITLS_APP_SUCCESS;
}
static const char *GetLibraryName(int32_t libCode)
{
switch (libCode) {
case 0x00: return "none";
case 0x04: return "RAND";
case 0x05: return "MD";
case 0x06: return "CIPHER";
case 0x07: return "PKEY";
case 0x0B: return "X509";
case 0x0D: return "ASN1";
case 0x0E: return "SSL";
case 0x14: return "SSL";
default: return "unknown";
}
}
static const char *GetFunctionName(int32_t funcCode)
{
(void)funcCode;
return "function";
}
static const char *GetReasonString(int32_t reasonCode)
{
(void)reasonCode;
return "reason";
}
static void ExtractErrorFields(uint64_t code, ErrorCodeFields *fields)
{
if (fields == NULL) {
return;
}
fields->fullCode = code;
fields->library = ErrGetLib(code);
fields->function = ErrGetFunc(code);
fields->reason = ErrGetReason(code);
const ErrorEntry *entry = LookupErrorCode(code);
if (entry != NULL) {
fields->libName = entry->library;
fields->funcName = entry->function;
fields->reasonStr = entry->reason;
} else {
fields->libName = GetLibraryName(fields->library);
fields->funcName = GetFunctionName(fields->function);
fields->reasonStr = GetReasonString(fields->reason);
}
}
static void PrintBasicError(uint64_t code, const ErrorEntry *entry)
{
if (entry != NULL) {
AppPrintError("error:%016llX:%s:%s:%s\n", (unsigned long long)code, entry->library, entry->function, entry->reason);
} else {
AppPrintError("error:%016llX:unknown\n", (unsigned long long)code);
}
}
static void PrintVerboseError(const ErrorCodeFields *fields)
{
if (fields == NULL) {
return;
}
AppPrintError("error code: 0x%016llX\n", (unsigned long long)fields->fullCode);
AppPrintError("library : %s (0x%02X)\n", fields->libName, fields->library);
AppPrintError("function : %s (0x%03X)\n", fields->funcName, fields->function);
AppPrintError("reason : %s (0x%03X)\n", fields->reasonStr, fields->reason);
}
static void PrintUsage(const char *programName)
{
AppPrintError("Usage: %s errdecode [options] [error_code ...]\n", programName);
AppPrintError("\n");
AppPrintError("Convert error codes to human-readable strings.\n");
AppPrintError("\n");
AppPrintError("Options:\n");
AppPrintError(" -h, -help Show this help message\n");
AppPrintError(" -v, --verbose Show detailed error code fields\n");
AppPrintError(" --stack Show error stack (if supported)\n");
AppPrintError(" -hex Force hexadecimal parsing\n");
AppPrintError("\n");
AppPrintError("Arguments:\n");
AppPrintError(" error_code Error code in decimal or hexadecimal format\n");
AppPrintError(" Hexadecimal can be with (0x) or without prefix\n");
AppPrintError("\n");
AppPrintError("Examples:\n");
AppPrintError(" %s errdecode 101\n", programName);
AppPrintError(" %s errdecode 0x0E000065\n", programName);
AppPrintError(" %s errdecode -v 0x1408F10B\n", programName);
AppPrintError(" %s errdecode 101 0x0E000065 234567890\n", programName);
AppPrintError(" echo \"0x1408F10B\" | %s errdecode\n", programName);
}
static int32_t ParseCommandLine(int32_t argc, char *argv[], CommandOptions *options)
{
if (options == NULL) {
return HITLS_APP_INVALID_ARG;
}
memset(options, 0, sizeof(CommandOptions));
int32_t argIndex = 1;
while (argIndex < argc && argv[argIndex][0] == '-') {
if (strcmp(argv[argIndex], "-h") == 0 || strcmp(argv[argIndex], "-help") == 0) {
options->helpMode = 1;
return HITLS_APP_SUCCESS;
} else if (strcmp(argv[argIndex], "-v") == 0 || strcmp(argv[argIndex], "--verbose") == 0) {
options->verboseMode = 1;
} else if (strcmp(argv[argIndex], "--stack") == 0) {
options->stackMode = 1;
} else if (strcmp(argv[argIndex], "-hex") == 0) {
options->hexMode = 1;
} else {
AppPrintError("Error: unknown option: %s\n", argv[argIndex]);
return HITLS_APP_INVALID_ARG;
}
argIndex++;
}
if (argIndex < argc) {
options->errorCodes = &argv[argIndex];
options->numCodes = argc - argIndex;
} else {
options->stdinMode = 1;
}
return HITLS_APP_SUCCESS;
}
static int32_t ProcessSingleError(const char *errorCodeStr, const CommandOptions *options)
{
uint64_t code;
int32_t parseResult = ParseErrorCode(errorCodeStr, options->hexMode, &code);
if (parseResult != HITLS_APP_SUCCESS) {
AppPrintError("Error: invalid error code format: %s\n", errorCodeStr);
return HITLS_APP_INVALID_ARG;
}
const ErrorEntry *entry = LookupErrorCode(code);
if (options->verboseMode) {
ErrorCodeFields fields;
ExtractErrorFields(code, &fields);
PrintVerboseError(&fields);
} else {
PrintBasicError(code, entry);
}
return HITLS_APP_SUCCESS;
}
static int32_t ProcessBatchFromArgs(char **codes, int32_t numCodes, const CommandOptions *options)
{
int32_t hasError = 0;
for (int32_t i = 0; i < numCodes; i++) {
int32_t result = ProcessSingleError(codes[i], options);
if (result != HITLS_APP_SUCCESS) {
hasError = 1;
}
}
return hasError ? HITLS_APP_INVALID_ARG : HITLS_APP_SUCCESS;
}
static int32_t ProcessBatchFromStdin(const CommandOptions *options)
{
char line[1024];
int32_t hasError = 0;
while (fgets(line, sizeof(line), stdin) != NULL) {
size_t len = strlen(line);
if (len > 0 && line[len - 1] != '\n' && len == sizeof(line) - 1) {
AppPrintError("Error: input line too long (max %zu characters)\n", sizeof(line) - 1);
int c;
while ((c = getchar()) != '\n' && c != EOF) {
}
hasError = 1;
continue;
}
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
if (line[0] == '\0') {
continue;
}
int32_t result = ProcessSingleError(line, options);
if (result != HITLS_APP_SUCCESS) {
hasError = 1;
}
}
return hasError ? HITLS_APP_INVALID_ARG : HITLS_APP_SUCCESS;
}
static int32_t ProcessErrorStack(const CommandOptions *options)
{
(void)options;
AppPrintError("no errors in queue\n");
return HITLS_APP_SUCCESS;
}
int32_t HITLS_ErrdecodeMain(int32_t argc, char *argv[])
{
CommandOptions options;
int32_t parseResult = ParseCommandLine(argc, argv, &options);
if (parseResult != HITLS_APP_SUCCESS) {
PrintUsage(argv[0]);
return HITLS_APP_INVALID_ARG;
}
if (options.helpMode) {
PrintUsage(argv[0]);
return HITLS_APP_SUCCESS;
}
if (options.stackMode) {
return ProcessErrorStack(&options);
}
if (options.stdinMode) {
return ProcessBatchFromStdin(&options);
} else if (options.numCodes > 0) {
return ProcessBatchFromArgs(options.errorCodes, options.numCodes, &options);
}
PrintUsage(argv[0]);
return HITLS_APP_SUCCESS;
}