The error routines. The 'error' system I've implemented is intended to server 2 purpose, to record the reason why a command failed and to record where in the libraries the failure occurred. It is more or less setup to record a 'trace' of which library components were being traversed when the error occurred. When an error is recorded, it is done so a as single unsigned long which is composed of three parts. The top byte is the 'library' number, the middle 12 bytes is the function code, and the bottom 12 bits is the 'reason' code. Each 'library', or should a say, 'section' of the SSLeay library has a different unique 'library' error number. Each function in the library has a number that is unique for that library. Each 'library' also has a number for each 'error reason' that is only unique for that 'library'. Due to the way these error routines record a 'error trace', there is an array per thread that is used to store the error codes. The various functions in this library are used to access and manipulate this array. void ERR_put_error(int lib, int func,int reason); This routine records an error in library 'lib', function 'func' and reason 'reason'. As errors get 'put' into the buffer, they wrap around and overwrite old errors if too many are written. It is assumed that the last errors are the most important. unsigned long ERR_get_error(void ); This function returns the last error added to the error buffer. In effect it is popping the value off the buffer so repeated calls will continue to return values until there are no more errors to return in which case 0 is returned. unsigned long ERR_peek_error(void ); This function returns the value of the last error added to the error buffer but does not 'pop' it from the buffer. void ERR_clear_error(void ); This function clears the error buffer, discarding all unread errors. While the above described error system obviously produces lots of different error number, a method for 'reporting' these errors in a human readable form is required. To achieve this, each library has the option of 'registering' error strings. typedef struct ERR_string_data_st { unsigned long error; char *string; } ERR_STRING_DATA; The 'ERR_STRING_DATA' contains an error code and the corresponding text string. To add new function error strings for a library, the ERR_STRING_DATA needs to be 'registered' with the library. void ERR_load_strings(unsigned long lib,ERR_STRING_DATA *err); This function 'registers' the array of ERR_STRING_DATA pointed to by 'err' as error text strings for the error library 'lib'. void ERR_free_strings(void); This function free()s all the loaded error strings. char *ERR_error_string(unsigned long error,char *buf); This function returns a text string that is a human readable version of the error represented by 'error'. Buff should be at least 120 bytes long and if it is NULL, the return value is a pointer to a static variable that will contain the error string, otherwise 'buf' is returned. If there is not a text string registered for a particular error, a text string containing the error number is returned instead. void ERR_print_errors(BIO *bp); void ERR_print_errors_fp(FILE *fp); This function is a convenience routine that prints the error string for each error until all errors have been accounted for. char *ERR_lib_error_string(unsigned long e); char *ERR_func_error_string(unsigned long e); char *ERR_reason_error_string(unsigned long e); The above three functions return the 3 different components strings for the error 'e'. ERR_error_string() uses these functions. void ERR_load_ERR_strings(void ); This function 'registers' the error strings for the 'ERR' module. void ERR_load_crypto_strings(void ); This function 'register' the error strings for just about every library in the SSLeay package except for the SSL routines. There is no need to ever register any error text strings and you will probably save in program size. If on the other hand you do 'register' all errors, it is quite easy to determine why a particular routine failed. As a final footnote as to why the error system is designed as it is. 1) I did not want a single 'global' error code. 2) I wanted to know which subroutine a failure occurred in. 3) For Windows NT etc, it should be simple to replace the 'key' routines with code to pass error codes back to the application. 4) I wanted the option of meaningful error text strings. Late breaking news - the changes to support threads. Each 'thread' has an 'ERR_STATE' state associated with it. ERR_STATE *ERR_get_state(void ) will return the 'state' for the calling thread/process. ERR_remove_state(unsigned long pid); will 'free()' this state. If pid == 0 the current 'thread/process' will have it's error state removed. If you do not remove the error state of a thread, this could be considered a form of memory leak, so just after 'reaping' a thread that has died, call ERR_remove_state(pid). Have a read of thread.doc for more details for what is required for multi-threading support. All the other error routines will work correctly when using threads.