Ubuntu 下 nginx-1.24.0 源码分析 -ngx_ssl_error 函数

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 错误队列中获取的错误编号。
  • dataflags: 分别存储与错误相关的附加数据和标志。

在 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 的字符串,确保不会超出缓冲区范围。

意图

  1. 统一错误处理:

    • 提供一个通用的函数来处理 SSL 错误,避免重复代码。
    • 将用户提供的自定义信息与 OpenSSL 错误信息结合,形成完整的错误描述。
  2. 防止缓冲区溢出:

    • 使用 lastp 动态跟踪缓冲区剩余空间,确保不会超出 NGX_MAX_CONF_ERRSTR 的限制。
  3. 增强可读性:

    • 将 OpenSSL 错误编号转换为可读的字符串,并附加上下文信息(如附加数据),方便开发者调试。
  4. 日志记录:

    • 将错误信息记录到日志中,便于后续排查问题。

详解


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_errorERR_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

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_log_error 函数-CSDN博客

相关推荐
鸡鸭扣26 分钟前
Docker:3、在VSCode上安装并运行python程序或JavaScript程序
运维·vscode·python·docker·容器·js
人工干智能4 小时前
科普:“Docker Desktop”和“Docker”以及“WSL”
运维·docker·容器
落笔画忧愁e4 小时前
FastGPT及大模型API(Docker)私有化部署指南
运维·docker·容器
前端郭德纲4 小时前
前端自动化部署的极简方案
运维·前端·自动化
DC_BLOG5 小时前
Linux-GlusterFS进阶配置
linux·运维·服务器
浮华落定7 小时前
Centos开机自启动
linux·运维·centos
End9288 小时前
如何安装虚拟机cenos7系统
大数据·linux·运维
eight *9 小时前
Dockerfile制作镜像示例 X86版本
运维·docker
5:009 小时前
Linux:进程间通信(一.初识进程间通信、匿名管道与命名管道、共享内存)
linux·运维·服务器
揽星逐月酒微醺10 小时前
Nginx web服务器+uWSGI web服务器+Django生产环境部署
服务器·前端·nginx