| /* |
| * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| #define OSSL_FORCE_ERR_STATE |
| |
| #include <stdio.h> |
| #include "internal/cryptlib.h" |
| #include <openssl/crypto.h> |
| #include <openssl/buffer.h> |
| #include <openssl/err.h> |
| #include "err_local.h" |
| |
| #define ERR_PRINT_BUF_SIZE 4096 |
| void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u), |
| void *u) |
| { |
| CRYPTO_THREAD_ID tid = CRYPTO_THREAD_get_current_id(); |
| unsigned long l; |
| const char *file, *data, *func; |
| int line, flags; |
| |
| while ((l = ERR_get_error_all(&file, &line, &func, &data, &flags)) != 0) { |
| char buf[ERR_PRINT_BUF_SIZE] = ""; |
| char *hex = NULL; |
| int offset; |
| |
| if ((flags & ERR_TXT_STRING) == 0) |
| data = ""; |
| |
| hex = ossl_buf2hexstr_sep((const unsigned char *)&tid, sizeof(tid), '\0'); |
| BIO_snprintf(buf, sizeof(buf), "%s:", hex == NULL ? "<null>" : hex); |
| offset = strlen(buf); |
| ossl_err_string_int(l, func, buf + offset, sizeof(buf) - offset); |
| offset += strlen(buf + offset); |
| BIO_snprintf(buf + offset, sizeof(buf) - offset, ":%s:%d:%s\n", |
| file, line, data); |
| OPENSSL_free(hex); |
| if (cb(buf, strlen(buf), u) <= 0) |
| break; /* abort outputting the error report */ |
| } |
| } |
| |
| /* auxiliary function for incrementally reporting texts via the error queue */ |
| static void put_error(int lib, const char *func, int reason, |
| const char *file, int line) |
| { |
| ERR_new(); |
| ERR_set_debug(file, line, func); |
| ERR_set_error(lib, reason, NULL /* no data here, so fmt is NULL */); |
| } |
| |
| #define TYPICAL_MAX_OUTPUT_BEFORE_DATA 100 |
| #define MAX_DATA_LEN (ERR_PRINT_BUF_SIZE - TYPICAL_MAX_OUTPUT_BEFORE_DATA) |
| void ERR_add_error_txt(const char *separator, const char *txt) |
| { |
| const char *file = NULL; |
| int line; |
| const char *func = NULL; |
| const char *data = NULL; |
| int flags; |
| unsigned long err = ERR_peek_last_error(); |
| |
| if (separator == NULL) |
| separator = ""; |
| if (err == 0) |
| put_error(ERR_LIB_NONE, NULL, 0, "", 0); |
| |
| do { |
| size_t available_len, data_len; |
| const char *curr = txt, *next = txt; |
| const char *leading_separator = separator; |
| int trailing_separator = 0; |
| char *tmp; |
| |
| ERR_peek_last_error_all(&file, &line, &func, &data, &flags); |
| if ((flags & ERR_TXT_STRING) == 0) { |
| data = ""; |
| leading_separator = ""; |
| } |
| data_len = strlen(data); |
| |
| /* workaround for limit of ERR_print_errors_cb() */ |
| if (data_len >= MAX_DATA_LEN |
| || strlen(separator) >= (size_t)(MAX_DATA_LEN - data_len)) |
| available_len = 0; |
| else |
| available_len = MAX_DATA_LEN - data_len - strlen(separator) - 1; |
| /* MAX_DATA_LEN > available_len >= 0 */ |
| |
| if (*separator == '\0') { |
| const size_t len_next = strlen(next); |
| |
| if (len_next <= available_len) { |
| next += len_next; |
| curr = NULL; /* no need to split */ |
| } else { |
| next += available_len; |
| curr = next; /* will split at this point */ |
| } |
| } else { |
| while (*next != '\0' && (size_t)(next - txt) <= available_len) { |
| curr = next; |
| next = strstr(curr, separator); |
| if (next != NULL) { |
| next += strlen(separator); |
| trailing_separator = *next == '\0'; |
| } else { |
| next = curr + strlen(curr); |
| } |
| } |
| if ((size_t)(next - txt) <= available_len) |
| curr = NULL; /* the above loop implies *next == '\0' */ |
| } |
| if (curr != NULL) { |
| /* split error msg at curr since error data would get too long */ |
| if (curr != txt) { |
| tmp = OPENSSL_strndup(txt, curr - txt); |
| if (tmp == NULL) |
| return; |
| ERR_add_error_data(2, separator, tmp); |
| OPENSSL_free(tmp); |
| } |
| put_error(ERR_GET_LIB(err), func, err, file, line); |
| txt = curr; |
| } else { |
| if (trailing_separator) { |
| tmp = OPENSSL_strndup(txt, next - strlen(separator) - txt); |
| if (tmp == NULL) |
| return; |
| /* output txt without the trailing separator */ |
| ERR_add_error_data(2, leading_separator, tmp); |
| OPENSSL_free(tmp); |
| } else { |
| ERR_add_error_data(2, leading_separator, txt); |
| } |
| txt = next; /* finished */ |
| } |
| } while (*txt != '\0'); |
| } |
| |
| void ERR_add_error_mem_bio(const char *separator, BIO *bio) |
| { |
| if (bio != NULL) { |
| char *str; |
| long len = BIO_get_mem_data(bio, &str); |
| |
| if (len > 0) { |
| if (str[len - 1] != '\0') { |
| if (BIO_write(bio, "", 1) <= 0) |
| return; |
| |
| len = BIO_get_mem_data(bio, &str); |
| } |
| if (len > 1) |
| ERR_add_error_txt(separator, str); |
| } |
| } |
| } |
| |
| static int print_bio(const char *str, size_t len, void *bp) |
| { |
| return BIO_write((BIO *)bp, str, len); |
| } |
| |
| void ERR_print_errors(BIO *bp) |
| { |
| ERR_print_errors_cb(print_bio, bp); |
| } |
| |
| #ifndef OPENSSL_NO_STDIO |
| void ERR_print_errors_fp(FILE *fp) |
| { |
| BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE); |
| if (bio == NULL) |
| return; |
| |
| ERR_print_errors_cb(print_bio, bio); |
| BIO_free(bio); |
| } |
| #endif |