Human-Readable Error Messages Internal Error Handling

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