Manipulating Error Queues Internal Error Handling

66 CRYPTO_set_id_callbackNULL; CRYPTO_set_locking_callbackNULL; CRYPTO_set_dynlock_create_callbackNULL; CRYPTO_set_dynlock_lock_callbackNULL; CRYPTO_set_dynlock_destroy_callbackNULL; for i = 0; i CRYPTO_num_locks; i++ MUTEX_CLEANUPmutex_buf[i]; freemutex_buf; mutex_buf = NULL; return 1; }

4.2 Internal Error Handling

OpenSSL has a package, known as the ERR package, devoted to the handling and processing of errors. When an OpenSSL function encounters an error, it creates an error report and logs the information to an error queue. Because the information is logged to a queue, if multiple errors occur, information can be gathered for each of them. It is our responsibility as developers to check the error queue to obtain detailed information when a function returns an error so that we can handle error conditions appropriately. The OpenSSL error handling mechanism is more complex than most other libraries of similar stature, but that also means more information is available to help resolve the error condition. Lets suppose for a moment that OpenSSL didnt log errors onto a queue. Consider, for example, a rather common case in which an application calling into a high-level OpenSSL library function causes OpenSSL to make several successive calls into various lower-level packages that make up OpenSSL. If an error were to occur at a low level, that error would be propagated back up the call stack to the application. The problem is that by the time the application gets the information, its likely to have changed to something less detailed than the initial error as each function in the chain causes a new error to be generated all because of the initial low-level error.

4.2.1 Manipulating Error Queues

When an error occurs in the OpenSSL library, a significant amount of information is logged. Some of the information can be useful in attempting to recover from an error automatically, but much of it is for debugging and reporting the error to a user. The ERR package provides six basic functions that are useful for obtaining information from the error queue. Each function always retrieves the oldest information from the queue so that errors are returned in the order that they were generated. The most basic piece of information that is logged is an error code, which describes the error that occurred. The error code is a 32-bit integer that has meaning only to OpenSSL. That is, OpenSSL defines its own unique error codes for any error condition that it could possibly encounter. It does not rely on error codes defined by any other library, including the standard C runtime. For each of the six basic functions, this error code is the return value from the function. If there is no error in the queue, the return from any of them will be 0, which also tells us that 0 is never a valid error code. This first function retrieves only the error code from the error queue. It also removes that error report from the queue, so the next call will retrieve the next error that occurred or possibly 0 if there are no more errors in the queue: unsigned long ERR_get_errorvoid; The second function also retrieves only the error code from the error queue, but it does not remove the error report from the queue, so the next call will retrieve the same error: 67 unsigned long ERR_peek_errorvoid; The third function builds on the information returned by ERR_get_error and ERR_peek_error . In addition to returning the error code, it also returns the name of the source file and source line number that generated the error. Like ERR_get_error , it also removes the error report from the queue: unsigned long ERR_get_error_lineconst char file, int line; file Receives the name of the source file that generated the error. It is usually supplied to the error handler from the _ _FILE_ _ preprocessor macro. line Receives the source line number that generated the error. It is usually supplied to the error handler from the _ _LINE_ _ preprocessor macro. The fourth function returns the same information as ERR_get_error_line , but like ERR_peek_error , it does not remove the error report from the queue. Its arguments and their meanings are identical to ERR_get_error_line : unsigned long ERR_peek_error_lineconst char file, int line; The fifth function builds on the information returned by ERR_get_error_line and ERR_peek_error_line . In addition to returning the error code, source filename, and line number, it also returns extra data and a set of flags that indicate how that data should be treated. The extra data and flags are supplied when the error is generated. Like ERR_get_error and ERR_get_error_line , this function also removes the error report from the queue: unsigned long ERR_get_error_line_dataconst char file, int line, const char data, int flags; file Receives the name of the source file that generated the error. It is usually supplied to the error handler from the _ _FILE_ _ preprocessor macro. line Receives the source line number that generated the error. It is usually supplied to the error handler from the _ _LINE_ _ preprocessor macro. data Receives a pointer to the extra data that was included with the error report. The pointer that is returned is not a copy, and so it should not be modified or freed. See below. flags Receives a set of flags that define the attributes of the extra data. 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