全志T113-i平台UB37三模无线模组驱动移植与调试实战
2026/5/22 1:55:07
ngx_http_validate_host 函数 定义在 ./nginx-1.24.0/src/http/ngx_http_request.cstaticngx_int_tngx_http_validate_host(ngx_str_t*host,ngx_pool_t*pool,ngx_uint_talloc){u_char*h,ch;size_ti,dot_pos,host_len;enum{sw_usual=0,sw_literal,sw_rest}state;dot_pos=host->len;host_len=host->len;h=host->data;state=sw_usual;for(i=0;i<host->len;i++){ch=h[i];switch(ch){case'.':if(dot_pos==i-1){returnNGX_DECLINED;}dot_pos=i;break;case':':if(state==sw_usual){host_len=i;state=sw_rest;}break;case'[':if(i==0){state=sw_literal;}break;case']':if(state==sw_literal){host_len=i+1;state=sw_rest;}break;default:if(ngx_path_separator(ch)){returnNGX_DECLINED;}if(ch<=0x20||ch==0x7f){returnNGX_DECLINED;}if(ch>='A'&&ch<='Z'){alloc=1;}break;}}if(dot_pos==host_len-1){host_len--;}if(host_len==0){returnNGX_DECLINED;}if(alloc){host->data=ngx_pnalloc(pool,host_len);if(host->data==NULL){returnNGX_ERROR;}ngx_strlow(host->data,h,host_len);}host->len=host_len;returnNGX_OK;}ngx_http_validate_host 函数 用于验证 HTTP 请求中 `Host` 头的合法性,并对其进行规范化。 它会去除端口部分和末尾多余的点, 拒绝连续点、控制字符、路径分隔符等非法输入; 同时可将主机名转换为小写。 验证通过返回 `NGX_OK`, 非法返回 `NGX_DECLINED`, 内存分配失败返回 `NGX_ERROR`。staticngx_int_tngx_http_validate_host(ngx_str_t*host,ngx_pool_t*pool,ngx_uint_talloc)返回值 NGX_OK —— 验证成功,主机名合法,且规范化操作(如小写转换)顺利完成。 NGX_DECLINED —— 主机名格式非法 NGX_ERROR —— 内部内存分配失败参数1 ngx_str_t *host 既是输入,也是输出 输入: host->data 指向待验证的原始主机名字符串, host->len 为该字符串的字节长度。 输出: 验证成功后,函数会修改此结构体: host->len 被截短为实际的主机名长度(去除端口号、IPv6 地址后的额外字符,以及末尾多余的点)。 若 alloc 条件成立,host->data 会被重新指向内存池中新分配的缓冲区(原数据保持不变), 其中存放已经过小写转换的有效主机名;否则 data 保持原指针不变。参数2 ngx_pool_t *pool 指向 Nginx 内存池的指针 仅在需要分配新内存时使用参数3 ngx_uint_t alloc 用作内存分配与小写转换的控制标志 调用者传入的值: 传入 0 表示“除非绝对必要,否则不分配内存”; 但若主机名中包含大写字母,函数内部会将 alloc 局部值覆盖为 1, 随后仍会分配并转换为小写(因为域名规范要求不区分大小写,小写是标准形式)。 传入 1 表示“无条件分配新缓冲区, 并将主机名转换为小写”(即使原本全是小写也会分配,确保字符串是独立副本且小写)。 函数内部逻辑: 遍历字符时若发现 'A'~'Z',强制 alloc = 1。 函数末尾根据 alloc 是否为 1 决定是否执行 ngx_pnalloc 和 ngx_strlow。 实际效果:调用者可通过传 0 避免不必要的内存拷贝, 但当存在大写字符时,函数会自动分配并转换, 保证最终 host 指向规范化的、全小写的主机名。 若传 1 则总是得到一份新的全小写副本。1 局部变量 2 遍历 3 去除末尾多余的点号 4 检查最终主机名长度 5 内存分配与小写转换 6 更新长度 7 返回成功状态码1 局部变量{u_char*h,ch;size_ti,dot_pos,host_len;enum{sw_usual=0,sw_literal,sw_rest}state;dot_pos=host->len;host_len=host->len;h=host->data;state=sw_usual;h(指向主机名字符串首字符) ch(临时保存当前字符)。 h 用于保留原始数据指针,后续小写转换时需用原数据; ch 在循环中每次取得当前字符,便于判断。声明循环索引 i, 记录最近一个点位置的 dot_pos, 以及实际主机部分长度 host_len。 dot_pos 初始化为 host->len(表示未找到点), host_len 初始为总长度,之后会剔除端口和多余部分。定义状态机变量 state,包含三种状态: sw_usual(0):常规主机名部分。 sw_literal:IPv6 地址括号内。 sw_rest:已遇到端口分隔符 : 或右括号 ],后续字符不计入主机名。 通过状态标识当前解析上下文,用于正确处理 IPv6 地址中的冒号和端口分隔符。初始化 dot_pos 为字符串总长度 避免误判第一个字符就是点的情况 初始化 host_len 为原始字符串长度 之后会根据状态(遇到冒号或右括号)缩短此长度 h 指向主机名字符串的首字符 保存原始数据指针, 即使在后续分配新缓冲区时, 原字符串地址仍被保留用于小写转换 初始状态设为常规主机名解析2 遍历for(i=0;i<host->len;i++){ch=h[i];开始循环,遍历整个输入字符串的每个字符 获取当前字符存入 chswitch(ch){case'.':if(dot_pos==i-1){returnNGX_DECLINED;}dot_pos=i;break;当前字符为点 . 处理 点用于分隔域名标签,需检查连续点 若前一个字符也是点(即 dot_pos 是上一个点的位置,等于 i-1), 则说明出现连续点(如 ..),返回 NGX_DECLINED 拒绝。 域名中不允许连续的点,尽早返回错误 更新 dot_pos 为当前点的位置case':':if(state==sw_usual){host_len=i;state=sw_rest;}break;当前字符为冒号的处理 仅在常规状态下遇到冒号,才认为是端口分隔符 此时将主机名长度截断为当前索引(即冒号前部分), 并进入 sw_rest 状态忽略后续字符。case'[':if(i==0){state=sw_literal;}break;当前字符为左方括号的处理 仅当左括号在字符串起始位置时,才视为 IPv6 地址开始,状态转为 sw_literalcase']':if(state==sw_literal){host_len=i+1;state=sw_rest;}break;如果正处于 IPv6 地址解析状态,右括号表示地址结束 主机名长度更新为 i+1(包含右括号), 状态转为 sw_rest,后续字符忽略 IPv6 地址必须用 [...] 包裹,右括号之后不能再有主机名字符,只能跟端口default:if(ngx_path_separator(ch)){returnNGX_DECLINED;}if(ch<=0x20||ch==0x7f){returnNGX_DECLINED;}if(ch>='A'&&ch<='Z'){alloc=1;}break;}}除 .、:、[、] 外的所有其他字符均在此处理。#1 若字符是路径分隔符(Unix 为 /),直接拒绝。 主机名中不应包含路径分隔符,防止 URL 注入混淆。#2 过滤控制字符和空格: ASCII 码 0x00~0x20(含空格、制表符等)以及 DEL(0x7F)均非法。 这些字符不可打印且可能引发安全问题(如 HTTP 头注入),必须拒绝。#3 若字符为大写字母 A~Z,将局部变量 alloc 置为 1。 域名大小写不敏感,通常规范化要求小写。 检测到大写后强制后续分配内存并转为小写3 去除末尾多余的点号if(dot_pos==host_len-1){host_len--;}如果最后一个有效字符是点(即 dot_pos 等于 host_len - 1), 将 host_len 减一,去除末尾点。 域名末尾允许有一个点(表示绝对域名), 但在 Nginx 内部处理时通常去掉,以利匹配。4 检查最终主机名长度if(host_len==0){returnNGX_DECLINED;}如果去除末尾点后主机名长度为 0(如空字符串或只有一个点),返回无效。 主机名不能为空。5 内存分配与小写转换if(alloc){host->data=ngx_pnalloc(pool,host_len);if(host->data==NULL){returnNGX_ERROR;}ngx_strlow(host->data,h,host_len);}判断是否需要分配新内存并转小写。 alloc 可能由调用者传入 1 要求分配, 也可能因检测到大写字母而被置 1。在内存池中分配 host_len 字节的新缓冲区,并让 host->data 指向它。 若分配失败,返回 NGX_ERROR 告知调用者系统错误。 内存不足无法继续,必须退出。将原始字符串 h 的前 host_len 个字符 转换为小写后 复制到新分配的缓冲区6 更新长度host->len=host_len;更新 host 结构体的长度字段为最终主机名长度。7 返回成功状态码returnNGX_OK;}