ngx_ssl_error
声明在 src\event\ngx_event_openssl.h
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...);
实现在 src\event\ngx_event_openssl.c
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) { int flags; u_long n; va_list args; u_char *p, *last; u_char errstr[NGX_MAX_CONF_ERRSTR]; const char *data; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); p = ngx_vslprintf(errstr, last - 1, fmt, args); va_end(args); if (ERR_peek_error()) { p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p); for ( ;; ) { n = ERR_peek_error_data(&data, &flags); if (n == 0) { break; } /* ERR_error_string_n() requires at least one byte */ if (p >= last - 1) { goto next; } *p++ = ' '; ERR_error_string_n(n, (char *) p, last - p); while (p < last && *p) { p++; } if (p < last && *data && (flags & ERR_TXT_STRING)) { *p++ = ':'; p = ngx_cpystrn(p, (u_char *) data, last - p); } next: (void) ERR_get_error(); } if (p < last) { *p++ = ')'; } } ngx_log_error(level, log, err, "%*s", p - errstr, errstr); }
函数
ngx_ssl_error
的主要作用是处理 SSL 错误,并将错误信息格式化后记录到日志中函数参数
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
level
: 日志级别,用于指定错误的严重程度(如调试、警告、错误等)。log
: 指向日志对象的指针,用于记录日志。err
: 系统错误码,通常与errno
相关。fmt
和...
: 可变参数,用于格式化自定义的错误信息。
主要变量
int flags; u_long n; va_list args; u_char *p, *last; u_char errstr[NGX_MAX_CONF_ERRSTR]; const char *data; last = errstr + NGX_MAX_CONF_ERRSTR;
errstr
: 一个固定大小的缓冲区,用于存储格式化后的错误信息。last
: 指向errstr
缓冲区末尾的指针,用于防止缓冲区溢出。p
: 当前写入位置的指针,动态更新以追加内容。args
: 用于处理可变参数的va_list
。n
: 存储从 OpenSSL 错误队列中获取的错误编号。data
和flags
: 分别存储与错误相关的附加数据和标志。
在 src/core/ngx_conf_file.h 中 :
#define NGX_MAX_CONF_ERRSTR 1024
NGX_MAX_CONF_ERRSTR
的含义是:在处理配置或错误信息时,允许存储的最大错误字符串长度
NGX_MAX_CONF_ERRSTR
主要用于定义错误信息缓冲区的大小
主要逻辑
(1) 格式化用户提供的错误信息
va_start(args, fmt); p = ngx_vslprintf(errstr, last - 1, fmt, args); va_end(args);
- 使用
ngx_vslprintf
将用户提供的格式化字符串fmt
和可变参数args
写入errstr
缓冲区。p
指针指向当前写入位置,后续操作会在此基础上追加内容。
(2) 检查 OpenSSL 错误队列
if (ERR_peek_error()) { p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
- 调用
ERR_peek_error()
检查 OpenSSL 错误队列中是否有未处理的错误。- 如果存在错误,则在
errstr
中追加(SSL:
,表示接下来的内容与 SSL 错误相关。
(3) 遍历并处理所有 SSL 错误
for (;;) { n = ERR_peek_error_data(&data, &flags); if (n == 0) { break; } /* ERR_error_string_n() requires at least one byte */ if (p >= last - 1) { goto next; } *p++ = ' '; ERR_error_string_n(n, (char *) p, last - p); while (p < last && *p) { p++; } if (p < last && *data && (flags & ERR_TXT_STRING)) { *p++ = ':'; p = ngx_cpystrn(p, (u_char *) data, last - p); } next: (void) ERR_get_error(); }
ERR_peek_error_data
: 获取错误编号n
和附加数据data
,同时检查标志flags
。ERR_error_string_n
: 将错误编号n
转换为可读的错误字符串,并追加到errstr
中。- 附加数据处理 :
- 如果
data
不为空且标志包含ERR_TXT_STRING
,则将data
追加到错误信息中,格式为:data
。- 循环结束条件 :
- 当
ERR_peek_error_data
返回0
时,表示错误队列为空,退出循环。- 每次调用
ERR_get_error()
从队列中移除当前错误。
(4) 结束 SSL 错误信息
if (p < last) { *p++ = ')'; }
- 在所有 SSL 错误信息处理完毕后,追加右括号
)
,表示 SSL 错误部分结束。
(5) 记录日志
ngx_log_error(level, log, err, "%*s", p - errstr, errstr);
- 调用
ngx_log_error
将最终的错误信息记录到日志中。%*s
表示输出一个长度为p - errstr
的字符串,确保不会超出缓冲区范围。
意图
统一错误处理:
- 提供一个通用的函数来处理 SSL 错误,避免重复代码。
- 将用户提供的自定义信息与 OpenSSL 错误信息结合,形成完整的错误描述。
防止缓冲区溢出:
- 使用
last
和p
动态跟踪缓冲区剩余空间,确保不会超出NGX_MAX_CONF_ERRSTR
的限制。增强可读性:
- 将 OpenSSL 错误编号转换为可读的字符串,并附加上下文信息(如附加数据),方便开发者调试。
日志记录:
- 将错误信息记录到日志中,便于后续排查问题。
详解
ERR_peek_error
R_peek_error
是 OpenSSL 提供的一个函数,用于检查当前线程的错误队列中是否存在未处理的错误它的主要作用是:
查看错误队列中的第一个错误编号:
- 如果错误队列中有未处理的错误,
ERR_peek_error
会返回第一个错误的编号(一个非零值)。- 如果错误队列为空,则返回
0
。不移除错误:
- 与
ERR_get_error
不同,ERR_peek_error
只是"查看"错误队列中的第一个错误,而不会将其从队列中移除。在使用
ERR_peek_error
时,需要引入以下 OpenSSL 头文件:#include <openssl/err.h>
<openssl/err.h>
是 OpenSSL 的错误处理模块的头文件,包含了所有与错误处理相关的函数和宏定义
ERR_peek_error_data
ERR_peek_error_data
是 OpenSSL 提供的一个函数,用于从当前线程的错误队列中获取第一个错误编号及其相关的附加数据(如错误描述或上下文信息)。它的主要作用是:
查看错误队列中的第一个错误编号:
- 如果错误队列中有未处理的错误,
ERR_peek_error_data
会返回第一个错误的编号(一个非零值)。- 如果错误队列为空,则返回
0
。获取附加数据:
- 除了错误编号外,
ERR_peek_error_data
还可以返回与错误相关的附加数据(如错误描述字符串)以及数据的标志位(flags)。- 这些附加数据通常是由 OpenSSL 内部生成的,用于提供更多关于错误的上下文信息。
函数原型
unsigned long ERR_peek_error_data(const char **data, int *flags);
参数说明
data
(输出参数):
- 指向一个指针变量,用于存储附加数据的地址。
- 如果没有附加数据,
*data
将被设置为NULL
。
flags
(输出参数):
- 指向一个整型变量,用于存储附加数据的标志位。
- 标志位可能包含以下值(定义在
<openssl/err.h>
中):
ERR_TXT_STRING
: 表示附加数据是一个字符串。ERR_TXT_MALLOCED
: 表示附加数据是动态分配的(需要手动释放)。返回值
- 非零值:表示错误队列中存在错误,返回值为错误编号。
- 0:表示错误队列为空,没有未处理的错误。
使用需要引入 <openssl/err.h>
ERR_error_string_n
ERR_error_string_n
是 OpenSSL 提供的一个函数,用于将错误编号(error code)转换为人类可读的字符串形式。它的主要作用是:
将错误编号转换为描述性字符串:
- 错误编号是由 OpenSSL 内部生成的,通常包含库、函数和具体错误的编码信息。
ERR_error_string_n
将这些编号转换为可读的字符串,方便开发者调试或记录日志。安全性增强:
- 与旧版的
ERR_error_string
不同,ERR_error_string_n
允许指定目标缓冲区的最大长度,从而避免缓冲区溢出问题。
函数原型
void ERR_error_string_n(unsigned long e, char *buf, size_t len);
数说明
e
(输入参数):
- 表示要转换的错误编号(由
ERR_get_error
或ERR_peek_error
返回)。
buf
(输出参数):
- 指向一个字符数组,用于存储转换后的错误字符串。
len
(输入参数):
- 表示目标缓冲区
buf
的最大长度(以字节为单位)。- 确保不会超出缓冲区范围,防止溢出。
返回值
- 无返回值(
void
)。- 转换后的字符串会直接写入
buf
中,并以\0
结尾。需要头文件 <openssl/err.h>
ERR_get_error
是 OpenSSL 提供的一个函数,用于从错误队列中获取当前线程的最新错误代码
函数原型
unsigned long ERR_get_error(void);
返回值
- 返回一个无符号长整型(
unsigned long
),表示当前线程错误队列中的最新错误代码。- 如果错误队列为空,则返回
0
移除错误: 在获取错误代码的同时,会将该错误从错误队列中移除(即"出队"操作)
ngx_log_error