cocoon/tls.c
xfy911 bc7505dc02 feat(http2): 集成 HTTP/2 到服务器主循环
- server.c: 添加 handle_http2() 函数,在 client_handler 中检测 HTTP/2 连接
- TLS ALPN 协商后自动创建 HTTP/2 会话
- HTTP/2 连接走独立处理路径(handle_http2),支持帧读写
- 连接清理时销毁 HTTP/2 会话
- tls.c: 新增 tls_negotiated_http2(),检查 ALPN 是否协商为 h2
- tls.h: 添加 tls_negotiated_http2() 声明
- 编译通过,39 项集成测试 + 127 单元测试全部通过
2026-06-04 14:43:39 +08:00

340 lines
9.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* tls.c - TLS/SSL 模块实现
*
* 使用 OpenSSL Memory BIO 与 coco 协程 I/O 集成:
* - SSL_read 从 read BIO 消费解密数据
* - SSL_write 向 write BIO 产出加密数据
* - 网络 I/O 由 coco_read/coco_write 或 read/write 完成
*
* @author xfy
*/
#include "tls.h"
#include "log.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "../coco/include/coco.h"
/* 内部TLS 连接条目 */
typedef struct {
SSL *ssl;
BIO *rbio; /* 从 socket 读取的加密数据写入此处 */
BIO *wbio; /* SSL_write 产出的加密数据从此读取 */
} tls_conn_t;
/* fd → TLS 连接映射动态数组O(1) 索引) */
static tls_conn_t **g_map = NULL;
static int g_map_cap = 0;
static SSL_CTX *g_ctx = NULL;
/* 内部O(1) fd 查表 */
static tls_conn_t* tls_lookup(int fd) {
if (fd >= 0 && fd < g_map_cap) return g_map[fd];
return NULL;
}
static void tls_map_set(int fd, tls_conn_t *t) {
if (fd >= g_map_cap) {
int old_cap = g_map_cap;
g_map_cap = fd + 1;
g_map = (tls_conn_t **)realloc(g_map, g_map_cap * sizeof(tls_conn_t *));
for (int i = old_cap; i < g_map_cap; i++) g_map[i] = NULL;
}
g_map[fd] = t;
}
static void tls_map_clear(int fd) {
if (fd >= 0 && fd < g_map_cap) g_map[fd] = NULL;
}
/* 内部:从 socket 读取原始数据(协程感知) */
static ssize_t socket_read(int fd, void *buf, size_t len) {
if (coco_sched_get_current() != NULL) {
return coco_read(fd, buf, len);
}
return read(fd, buf, len);
}
/* 内部:向 socket 写入原始数据 */
static ssize_t socket_write_all(int fd, const void *buf, size_t len) {
if (coco_sched_get_current() != NULL) {
size_t sent = 0;
while (sent < len) {
ssize_t n = coco_write(fd, (const char *)buf + sent, len - sent);
if (n > 0) {
sent += (size_t)n;
} else if (n < 0) {
if (n == -EAGAIN || n == -EINTR || n == COCO_ERROR_WOULD_BLOCK) continue;
return -1;
} else {
return -1;
}
}
return (ssize_t)sent;
} else {
size_t sent = 0;
while (sent < len) {
ssize_t n = write(fd, (const char *)buf + sent, len - sent);
if (n > 0) {
sent += (size_t)n;
} else if (n < 0) {
if (errno == EAGAIN || errno == EINTR) continue;
return -1;
} else {
return -1;
}
}
return (ssize_t)sent;
}
}
/* 内部:将 write BIO 中的加密数据刷到 socket */
static int flush_wbio(int fd, BIO *wbio) {
char buf[16384];
int pending = BIO_read(wbio, buf, sizeof(buf));
while (pending > 0) {
if (socket_write_all(fd, buf, (size_t)pending) < 0) return -1;
pending = BIO_read(wbio, buf, sizeof(buf));
}
return 0;
}
/* ===== 公共 API ===== */
int tls_create_context(const char *cert_path, const char *key_path) {
if (!cert_path || !key_path) return -1;
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
const SSL_METHOD *method = TLS_server_method();
if (!method) return -1;
g_ctx = SSL_CTX_new(method);
if (!g_ctx) return -1;
/* 最低 TLS 1.2 */
SSL_CTX_set_min_proto_version(g_ctx, TLS1_2_VERSION);
/* 加载证书 */
if (SSL_CTX_use_certificate_file(g_ctx, cert_path, SSL_FILETYPE_PEM) <= 0) {
log_error("加载 TLS 证书失败: %s", cert_path);
SSL_CTX_free(g_ctx);
g_ctx = NULL;
return -1;
}
/* 加载私钥 */
if (SSL_CTX_use_PrivateKey_file(g_ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
log_error("加载 TLS 私钥失败: %s", key_path);
SSL_CTX_free(g_ctx);
g_ctx = NULL;
return -1;
}
/* 验证私钥与证书匹配 */
if (!SSL_CTX_check_private_key(g_ctx)) {
log_error("TLS 私钥与证书不匹配");
SSL_CTX_free(g_ctx);
g_ctx = NULL;
return -1;
}
log_info("TLS 上下文已初始化: cert=%s", cert_path);
return 0;
}
void tls_destroy_context(void) {
if (g_ctx) {
SSL_CTX_free(g_ctx);
g_ctx = NULL;
}
if (g_map) {
for (int i = 0; i < g_map_cap; i++) {
if (g_map[i]) {
if (g_map[i]->ssl) SSL_free(g_map[i]->ssl);
if (g_map[i]->rbio) BIO_free(g_map[i]->rbio);
if (g_map[i]->wbio) BIO_free(g_map[i]->wbio);
free(g_map[i]);
}
}
free(g_map);
g_map = NULL;
g_map_cap = 0;
}
}
bool tls_has_context(void) {
return g_ctx != NULL;
}
int tls_accept(int fd) {
if (!g_ctx) return -1;
tls_conn_t *t = (tls_conn_t *)calloc(1, sizeof(tls_conn_t));
if (!t) return -1;
t->ssl = SSL_new(g_ctx);
if (!t->ssl) {
free(t);
return -1;
}
/* 创建 Memory BIO 对 */
t->rbio = BIO_new(BIO_s_mem());
t->wbio = BIO_new(BIO_s_mem());
if (!t->rbio || !t->wbio) {
SSL_free(t->ssl);
free(t);
return -1;
}
BIO_set_mem_eof_return(t->rbio, -1);
BIO_set_mem_eof_return(t->wbio, -1);
SSL_set_bio(t->ssl, t->rbio, t->wbio);
SSL_set_accept_state(t->ssl);
tls_map_set(fd, t);
/* 执行握手循环 */
while (1) {
int ret = SSL_do_handshake(t->ssl);
if (ret == 1) {
log_debug("TLS 握手完成 fd=%d", fd);
return 0; /* 成功 */
}
int err = SSL_get_error(t->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
/* 尝试先刷出 write BIO 中的数据 */
if (flush_wbio(fd, t->wbio) < 0) goto fail;
/* 从 socket 读取加密数据 */
char buf[8192];
ssize_t n = socket_read(fd, buf, sizeof(buf));
if (n > 0) {
BIO_write(t->rbio, buf, (int)n);
} else if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
/* 非阻塞模式下暂时无数据,继续尝试 */
continue;
}
goto fail;
} else {
goto fail;
}
} else if (err == SSL_ERROR_WANT_WRITE) {
if (flush_wbio(fd, t->wbio) < 0) goto fail;
} else {
log_error("TLS 握手失败 fd=%d, err=%d", fd, err);
goto fail;
}
}
fail:
tls_map_clear(fd);
SSL_free(t->ssl);
free(t);
return -1;
}
ssize_t tls_read(int fd, void *buf, size_t len) {
tls_conn_t *t = tls_lookup(fd);
if (!t || !t->ssl) return -1;
while (1) {
int ret = SSL_read(t->ssl, buf, (int)len);
if (ret > 0) return (ssize_t)ret;
int err = SSL_get_error(t->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
/* 从 socket 读取加密数据 */
char tmp[8192];
ssize_t n = socket_read(fd, tmp, sizeof(tmp));
if (n > 0) {
BIO_write(t->rbio, tmp, (int)n);
} else {
return n; /* 0 或 -1 */
}
} else if (err == SSL_ERROR_WANT_WRITE) {
if (flush_wbio(fd, t->wbio) < 0) return -1;
} else if (err == SSL_ERROR_ZERO_RETURN) {
return 0; /* 对端关闭 */
} else {
return -1;
}
}
}
ssize_t tls_write(int fd, const void *buf, size_t len) {
tls_conn_t *t = tls_lookup(fd);
if (!t || !t->ssl) return -1;
size_t total = 0;
while (total < len) {
int ret = SSL_write(t->ssl, (const char *)buf + total, (int)(len - total));
if (ret > 0) {
total += (size_t)ret;
/* 刷出 write BIO */
if (flush_wbio(fd, t->wbio) < 0) return -1;
} else {
int err = SSL_get_error(t->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
char tmp[8192];
ssize_t n = socket_read(fd, tmp, sizeof(tmp));
if (n > 0) {
BIO_write(t->rbio, tmp, (int)n);
} else {
return -1;
}
} else if (err == SSL_ERROR_WANT_WRITE) {
if (flush_wbio(fd, t->wbio) < 0) return -1;
} else {
return -1;
}
}
}
return (ssize_t)total;
}
void tls_close(int fd) {
tls_conn_t *t = tls_lookup(fd);
if (!t) return;
if (t->ssl) {
SSL_shutdown(t->ssl);
/* 刷出最后的 close_notify */
flush_wbio(fd, t->wbio);
SSL_free(t->ssl);
}
/* rbio/wbio 已被 SSL_free 释放SSL_set_bio 转移所有权) */
tls_map_clear(fd);
free(t);
}
bool tls_has_connection(int fd) {
return tls_lookup(fd) != NULL;
}
bool tls_negotiated_http2(int fd) {
tls_conn_t *t = tls_lookup(fd);
if (!t || !t->ssl) return false;
const unsigned char *alpn = NULL;
unsigned int alpn_len = 0;
SSL_get0_alpn_selected(t->ssl, &alpn, &alpn_len);
if (alpn_len == 2 && memcmp(alpn, "h2", 2) == 0) {
return true;
}
return false;
}