《openssl 编程》之错误处理
第十四章???錯誤處理
14.1??概述
?????? 程序設計時,一般通過函數的返回值來判斷是否調用成功。設計良好的函數以及好的錯誤處理能幫助調用者快速找到錯誤原因。錯誤處理應該盡可能多的包含各種信息,包括:
????????? 錯誤碼;
????????? 出錯文件以及行號;
????????? 錯誤原因;
????????? 出錯函數;
????????? 出錯庫;
????????? 出錯模塊與類別信息;
????????? 錯誤堆棧信息等。
并且,出錯信息最好能支持多種輸出??梢允禽敵鲈跇藴瘦敵錾?#xff0c;也可以是文件等形式。
14.2??數據結構
openssl中,通過unsigned long類型來存放錯誤信息。它包含三部分內容:庫代碼、函數代碼以及錯誤原因代碼。其中,庫代碼在crypto/err.h中定義,函數代碼以及錯誤原因代碼由各個功能模塊定義(同類代碼不能與其他的重復,也不能超過一定的大小)。比如err.h中為BIO定義如下庫代碼:
/* library */
#define ERR_LIB_BIO????????? 32
?????? ?????? 而crypto/bio.h中定義了如下函數和錯誤原因代號:
????????????? /* Function codes. */
#define BIO_F_ACPT_STATE?????????????????????????? 100
/* Reason codes. */
#define BIO_R_ACCEPT_ERROR??????????????????????????? 100
錯誤信息通過上述三部分通過計算得到,并且根據此信息能提取各個代碼。計算函數在err.h中定義如下:
#define ERR_PACK(l,f,r)???????????? (((((unsigned long)l)&0xffL)*0x1000000)| \
??????????????????????????? ((((unsigned long)f)&0xfffL)*0x1000)| \
??????????????????????????? ((((unsigned long)r)&0xfffL)))
#define ERR_GET_LIB(l)???????????? (int)((((unsigned long)l)>>24L)&0xffL)
#define ERR_GET_FUNC(l)???????? (int)((((unsigned long)l)>>12L)&0xfffL)
#define ERR_GET_REASON(l)??? (int)((l)&0xfffL)
可以看出,庫的個數不能大于255(0xff),函數個數和錯誤原因不能大于4095(0xfff)。除非計算出來的值與已有的值沒有沖突。
主要數據結構有兩個,定義在crypto/err/err.h中,如下:
1)ERR_STRING_DATA
typedef struct ERR_string_data_st
?????? ?????? {
?????? ?????? unsigned long error;
?????? ?????? const char *string;
?????? ?????? } ERR_STRING_DATA;
?????? 該數據結構的內容由各個功能模塊來設置。其中,error用來存放錯誤信息(由庫代碼、函數代碼以及錯誤原因代碼計算得來),string用來存放文本信息,可以是函數名也可以是錯誤原因。以crypto/bio_err.c為例,它定義了兩個全局表,分別用來存放函數信息和錯誤信息:
?????? ?????? #define ERR_FUNC(func) ERR_PACK(ERR_LIB_BIO,func,0)
#define ERR_REASON(reason) ERR_PACK(ERR_LIB_BIO,0,reason)
static ERR_STRING_DATA BIO_str_functs[]=
{
{ERR_FUNC(BIO_F_ACPT_STATE),? "ACPT_STATE"},
……
?????? ?????? }
static ERR_STRING_DATA BIO_str_reasons[]=
?????? ?????? {
{ERR_REASON(BIO_R_ACCEPT_ERROR)????????? ,"accept error"},
{ERR_REASON(BIO_R_BAD_FOPEN_MODE)??????? ,"bad fopen mode"},
……
}
這兩個表通過ERR_load_BIO_strings函數來添加到錯誤信息哈希表中去。為了便于查找,所有模塊的錯誤信息存放在一個全局哈希表中,在crypto/err.c中實現。
?????? 2)ERR_STATE
typedef struct err_state_st
?????? ?????? {
?????? ?????? unsigned long pid;
?????? ?????? int err_flags[ERR_NUM_ERRORS];
?????? ?????? unsigned long err_buffer[ERR_NUM_ERRORS];
?????? ?????? char *err_data[ERR_NUM_ERRORS];
?????? ?????? int err_data_flags[ERR_NUM_ERRORS];
?????? ?????? const char *err_file[ERR_NUM_ERRORS];
?????? ?????? int err_line[ERR_NUM_ERRORS];
?????? ?????? int top,bottom;
?????? ?????? } ERR_STATE;
該結構用于存放和獲取錯誤信息。由于可能會有多層函數調用(錯誤堆棧),這些信息都是一個數組。每個數組代表了一層函數的錯誤信息。各項意義如下:
?????? ?????? pid:當前線程id。
?????? ?????? err_buffer[i]:第i層錯誤碼,包含庫、函數以及錯誤原因信息。
?????? ?????? err_data[i]:存放第i層操作信息。
err_data_flags[i]:存放err_data[i]相關的標記;比如為ERR_TXT_MALLOCED時,表名err_data[i]中的數據是動態分配內存的,需要釋放;為ERR_TXT_STRING表名err_data[i]中的數據是一個字符串,可以用來打印。
?????? ?????? err_file[i]:第i層錯誤的文件名。
?????? ?????? err_line[i]:第i層錯誤的行號。
top和bottom:用于指明ERR_STATE的使用狀態。top對應與最后一個錯誤(錯誤堆棧的最上層),bottom對應第一個錯誤(錯誤堆棧的最底層)。
?????? ?????? 當用戶需要擴展openssl的模塊時,可以仿照其他已有模塊來實現自己的錯誤處理。
14.3??主要函數
1)??? ERR_add_error_data
在本層錯誤的err_data元素中添加說明信息。該函數一般由各個模塊調用,比如可以用它說明什么操作導致了錯誤。
2)? ERR_clear_error
清除所有的錯誤信息。如果不清楚所有錯誤信息,可能會有其他無關錯誤遺留在ERR_STATE表中。
3)? ERR_error_string/ ERR_error_string_n
根據錯誤碼獲取具體的錯誤信息,包括出錯的庫、出錯的函數以及錯誤原因。
4)?? ERR_free_strings
釋放錯誤信息哈希表;通常在最后調用。
5)?? ERR_func_error_string
根據錯誤號,獲取出錯的函數信息。
6) ERR_get_err_state_table
獲取存放錯誤的哈希表。
7) ERR_get_error
獲取第一個錯誤號。
8)??? ERR_get_error_line
根據錯誤號,獲取錯誤的行號。
9)??? ERR_get_error_line_data
根據錯誤號,獲取出錯信息。
10)? ERR_get_implementation
獲取錯誤處理函數,與哈希表操作相關。
?????? 11)ERR_get_state
????????????? 獲取ERR_STATE表。
?????? 12)ERR_lib_error_string
????????????? 根據錯誤號,獲取是哪個庫出錯。
?????? 13)ERR_load_strings
????????????? 加載錯誤信息,由各個模塊調用。
14)ERR_load_ASN1_strings
ERR_load_BIO_strings
ERR_load_BN_strings
ERR_load_BUF_strings
ERR_load_COMP_strings
ERR_load_CONF_strings
????????????? ERR_load_CRYPTO_strings
ERR_load_crypto_strings
ERR_load_DH_strings
ERR_load_DSA_strings
ERR_load_DSO_strings
ERR_load_EC_strings
ERR_load_ENGINE_strings
ERR_load_ERR_strings
ERR_load_EVP_strings
ERR_load_OBJ_strings
ERR_load_OCSP_strings
ERR_load_PEM_strings
ERR_load_PKCS12_strings
ERR_load_PKCS7_strings
ERR_load_RAND_strings
ERR_load_RSA_strings
ERR_load_UI_strings
ERR_load_X509_strings
ERR_load_X509V3_strings
各個模塊實現的,加載各自錯誤信息。
?????? 15)ERR_peek_error
????????????? 獲取第一個錯誤號。
?????? 16)ERR_peek_error_line
????????????? 獲取第一個錯誤的出錯行。
17)ERR_peek_error_line_data
?????? 獲取第一個錯誤的行數和錯誤信息。
18)ERR_peek_last_error
?????? 獲取最后一個錯誤號。
19)ERR_peek_last_error_line
?????? 獲取最后一個錯誤的行號。
20)ERR_peek_last_error_line_data
?????? 獲取最后一個錯誤的行號和錯誤信息。
21)ERR_print_errors
?????? 將錯誤信息輸出到bio中。
22)ERR_print_errors_cb
?????? 根據用戶設置的回調函數來打印錯誤信息。
23)ERR_print_errors_fp
?????? 將錯誤打印到FILE中。
24)? ERR_put_error
將錯誤信息存放到ERR_STATE 表中top指定的錯誤堆棧(最后的錯誤)。
25)? ERR_reason_error_string
根據錯誤號得到錯誤原因。
26)? ERR_remove_state
刪除線程相關的錯誤信息。
27)? ERR_set_error_data
將錯誤信息存放到ERR_STATE 表中top指定的錯誤堆棧(最后的錯誤)。
28)? ERR_unload_strings
從錯誤哈希表中刪除相關信息。
14.4??編程示例
?????? #include <openssl/err.h>
?????? #include <openssl/bn.h>
?????? int??? mycb(const char *a,size_t b,void *c)
?????? {
????????????? printf("my print : %s\n",a);
????????????? return 0;
?????? }
??????
?????? int??? main()
?????? {
????????????? BIO????????????????????? *berr;
????????????? unsigned long? err;
????????????? const????? char *file,*data,*efunc,*elib,*ereason,*p;
????????????? int???????????????????????? line,flags;
????????????? char?????????????? estring[500];
????????????? FILE???????????????????? *fp;
?????????????
????????????? /*
????????????? ERR_load_crypto_strings();
????????????? */
????????????? ERR_load_BIO_strings();
????????????? ERR_clear_error();
????????????? berr=BIO_new(BIO_s_file());
????????????? BIO_set_fp(berr,stdout,BIO_NOCLOSE);
????????????? BIO_new_file("no.exist","r");
????????????? err=ERR_peek_last_error();
????????????? err=ERR_peek_last_error_line(&file,&line);
????????????? printf("ERR_peek_last_error_line err : %ld,file : %s,line: %d\n",err,file,line);
????????????? err=ERR_peek_last_error_line_data(&file,&line,&data,&flags);
printf("ERR_peek_last_error_line_data err: %ld,file :%s,line :%d,data :%s\n",err,file,line,data);
????????????? err=ERR_peek_error();
????????????? printf("ERR_peek_error err: %ld\n",err);
????????????? err=ERR_peek_error_line(&file,&line);
????????????? printf("ERR_peek_error_line err : %ld,file : %s,line: %d\n",err,file,line);
????????????? err=ERR_peek_error_line_data(&file,&line,&data,&flags);
?????? printf("ERR_peek_error_line_data err : %ld,file :%s,line :%d,data :%s\n",err,file,line,data);
????????????? err = ERR_get_error_line_data(&file,&line,&data,&flags);
?????? printf("ERR_get_error_line_data err : %ld,file :%s,line :%d,data :%s\n",err,file,line,data);
?????? ?????? if(err!=0)
????????????? {
???????????????????? p=ERR_lib_error_string(err);
???????????????????? printf("ERR_lib_error_string : %s\n",p);
????????????? }
????????????? err=ERR_get_error();
????????????? if(err!=0)
????????????? {
???????????????????? printf("ERR_get_error err : %ld\n",err);
???????????????????? efunc=ERR_func_error_string(err);
???????????????????? printf("err func : %s\n",efunc);
???????????????????? elib=ERR_lib_error_string(err);
???????????????????? printf("err lib : %s\n",efunc);
???????????????????? ereason=ERR_reason_error_string(err);
???????????????????? printf("err reason : %s\n",efunc);
???????????????????? efunc=ERR_func_error_string(err);
???????????????????? printf("err func : %s\n",efunc);
???????????????????? elib=ERR_lib_error_string(err);
???????????????????? printf("err lib : %s\n",efunc);
???????????????????? ereason=ERR_reason_error_string(err);
???????????????????? printf("err reason : %s\n",efunc);
???????????????????? ERR_error_string(err,estring);
???????????????????? printf("ERR_error_string : %s\n",estring);
??????
???????????????????? ERR_error_string_n(err,estring,sizeof(estring));
???????????????????? printf("ERR_error_string_n : %s\n",estring);
????????????? }
????????????? err=ERR_get_error_line(&file,&line);
????????????? printf("err file :%s , err line : %d\n",file,line);
????????????? ERR_print_errors(berr);
????????????? BIO_new_file("no.exist2","r");
????????????? fp=fopen("err.log","w");
????????????? ERR_print_errors_fp(fp);
????????????? fclose(fp);
????????????? BIO_new_file("no.exist3","r");
????????????? ERR_print_errors_cb(mycb,NULL);
?????? ERR_put_error(ERR_LIB_BN,BN_F_BNRAND,BN_R_BIGNUM_TOO_LONG,__FILE__,line);
????????????? ERR_print_errors(berr);
????????????? ERR_load_BN_strings();
ERR_put_error(ERR_LIB_BN,BN_F_BNRAND,BN_R_BIGNUM_TOO_LONG,__FILE__,line);
????????????? ERR_print_errors(berr);
ERR_put_error(ERR_LIB_BN,BN_F_BNRAND,BN_R_BIGNUM_TOO_LONG,__FILE__,line);
?????? ?????? ERR_set_error_data("set date test!\n",ERR_TXT_STRING);
????????????? err=ERR_set_mark();
????????????? ERR_print_errors(berr);
????????????? ERR_free_strings();
????????????? BIO_free(berr);
????????????? return 0;
?????? }
總結
以上是生活随笔為你收集整理的《openssl 编程》之错误处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《openssl 编程》之数据压缩
- 下一篇: 《openssl 编程》之 DH