68
The sixth function returns the same information as
ERR_get_error_line_data
, but like
ERR_peek_error
and
ERR_peek_error_line
, it does not remove the error report from the queue. Its arguments and their meanings are identical to
ERR_get_error_line_data
:
unsigned long ERR_peek_error_line_dataconst char file, int line, const char data, int flags;
ERR_get_error_line_data
and
ERR_peek_error_line_data
both retrieve the optional piece of data that can be associated with an error report. This optional piece of data can be
anything, but most frequently, its a string. Stored along with the data is a bit mask of flags that describe the data so that it can be dealt with appropriately by the error handling package. If the
flag
ERR_TXT_MALLOCED
is set, the memory for the data will be freed by a call to OpenSSLs
OPENSSL_free
function. If the flag
ERR_TXT_STRING
is set, the data is safe to be interpreted as a C-style string.
Note that the file and data information that can be obtained from the queue is returned as a pointer to the information on the queue. It is not a copy, so you should not attempt to modify the data. In
the case of the file information, it is usually a constant string from the
_ _FILE_ _
preprocessor macro. For the data information, if you need to store the information for any reason, you should
make a copy and not store the pointer that is returned. When you use the get family of functions to obtain this data, the data remains valid for a short period, but you should be sure to make a copy
before any other error handler function is called if you need to preserve it. Example 4-3
demonstrates how to print out the error information that is in the calling threads error queue.
Example 4-3. Accessing error information on the error queue
void print_errorsvoid {
int flags, line; char data, file;
unsigned long code; code = ERR_get_error_line_datafile, line, data, flags;
while code {
printferror code: lu in s line d.\n, code, file, line; if data flags ERR_TXT_STRING
printferror data: s\n, data; code = ERR_get_error_line_datafile, line, data, flags;
} }
There is one last queue-manipulation function that well discuss here: the function for clearing the error queue. It will delete all errors currently in the queue. In general, there is no need to call this
function unless we are trying to reset the error status for the current thread and dont care about any other errors that are on the queue. There is no way to recover the previous errors once its been
called, so use it judiciously:
void ERR_clear_errorvoid;
4.2.2 Human-Readable Error Messages
In some cases, the most appropriate way to handle an error condition is to display or log the error so that the user of your application can take the necessary steps to resolve the error. To do that, its
best to display a human-readable error message rather than an error code. The error handling
69
package provides standard error messages for its error codes for just this purpose, but before they can be used, they must be loaded.
There are two sets of error messages: one for the errors generated by
libcrypto
, and one for the errors generated by
libssl
. The function
ERR_load_crypto_strings
loads the errors generated by
libcrypto
, and the function
ERR_load_SSL_strings
loads the errors generated by
libssl
. There is an additional function,
SSL_load_error_strings
, which will load both sets of error messages.
Once the error strings are loaded,
ERR_error_string
and
ERR_error_string_n
can be used to translate an error code into an error message that is more meaningful to humans.
Particularly in a multithreaded application,
ERR_error_string
should never be used. It is always best to use
ERR_error_string_n
. Both functions always return a pointer to the start of the buffer into which the translated error message was written.
char ERR_error_stringunsigned long e, char buf;
e The error code that will be translated.
buf The buffer into which the error message is written. The buffer must be at least 256 bytes
in size, or it can be specified as
NULL
, in which case an internal buffer will be used. Use of this buffer is never thread-safe.
char ERR_error_string_nunsigned long e, char buf, size_t len;
e The error code that will be translated.
buf The buffer into which the error message is written. It must never be
NULL
. len
The size of the
buf
argument in bytes. It should include space for the
NULL
terminating character.
The resultant error message is formatted into a colon-separated list of fields. The first field is always the word error, and the second field is always the error code represented in hexadecimal.
The third field is the name of the package that generated the error, such as BIO routines or bignum routines. The fourth field is the name of the function that generated the error, and the
fifth field is the reason why the error was generated. The function name is taken from an internal table that is actually rather small, and may very likely be represented as
funccode
, in which
code
is a number representing the function. To get information about an error,
ERR_get_error_line_data
and
ERR_error_string
should be used. Armed with all of the information from these two functions, we can emit rather detailed error information. The OpenSSL library provides us with two functions that ease this
process for us, however.
ERR_print_errors
will produce an error listing and write it to a BIO.
TE AM
FL Y
Team-Fly
®
70
ERR_print_errors_fp
will produce an error listing and write it to a standard C runtime
FILE
object. The error listings are produced by iterating through each error report in the error queue and removing them as it goes. For each error report,
ERR_get_error_line_data
and
ERR_error_string
are used to obtain the information necessary to produce the listing:
void ERR_print_errorsBIO bp;
bp The BIO that the error listing should be written to.
void ERR_print_errors_fpFILE fp;
fp The
FILE
object that the error listing should be written to.
4.2.3 Threading and Practical Applications