如何使用OpenSSL BIO相关函数实现通过Squid代理的TLS连接?
通过OpenSSL BIO实现Squid代理下的TLS连接
完全可以按照你说的思路实现,这也是HTTP代理中转TLS流量的标准流程——先明文和代理完成CONNECT握手,再在同一个套接字上启用TLS加密。下面是具体的实现步骤和注意事项:
具体实现步骤
1. 创建明文TCP BIO连接代理服务器
不要一开始就用BIO_new_ssl_connect,先创建普通的TCP BIO连接Squid代理:
BIO *tcp_bio = BIO_new_connect("proxy_host:proxy_port"); if (!tcp_bio) { // 错误处理 }
2. 建立与代理的TCP连接
处理非阻塞场景下的连接重试:
int ret; while ((ret = BIO_do_connect(tcp_bio)) <= 0) { if (!BIO_should_retry(tcp_bio)) { // 连接失败,错误处理 break; } // 非阻塞模式下,等待套接字可读/可写后重试 }
3. 发送HTTP CONNECT请求并验证响应
构造CONNECT请求(替换目标服务器地址和授权信息),通过BIO_write发送:
const char *connect_req = "CONNECT target_host:443 HTTP/1.1\r\n" "Host: target_host:443\r\n" "Proxy-Connection: keep-alive\r\n" "User-Agent: Your-Client/1.0\r\n" "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n" "\r\n"; int bytes_written = 0; int req_len = strlen(connect_req); while (bytes_written < req_len) { ret = BIO_write(tcp_bio, connect_req + bytes_written, req_len - bytes_written); if (ret <= 0) { if (!BIO_should_retry(tcp_bio)) { // 发送失败,错误处理 break; } // 非阻塞等待后重试 continue; } bytes_written += ret; }
然后读取代理的响应,必须确认返回HTTP/1.1 200 Connection Established:
char resp_buf[1024]; int bytes_read = 0; while ((ret = BIO_read(tcp_bio, resp_buf + bytes_read, sizeof(resp_buf) - bytes_read - 1)) > 0) { bytes_read += ret; resp_buf[bytes_read] = '\0'; // 检查是否包含完整的响应头(以\r\n\r\n结尾) if (strstr(resp_buf, "\r\n\r\n")) { break; } } // 验证响应状态码 if (!strstr(resp_buf, "HTTP/1.1 200")) { // 代理授权失败,错误处理 }
4. 将TCP BIO与SSL绑定,启用TLS加密
用已有的ssl_ctx创建SSL BIO,并挂载到TCP BIO之上,后续通信自动加密:
BIO *ssl_bio = BIO_new_ssl(ssl_ctx, 1); // 第二个参数1表示自动重试 if (!ssl_bio) { // 错误处理 } // 形成SSL -> TCP的BIO链 ssl_bio = BIO_push(ssl_bio, tcp_bio); // 执行TLS握手 while ((ret = BIO_do_handshake(ssl_bio)) <= 0) { if (!BIO_should_retry(ssl_bio)) { // TLS握手失败,错误处理 break; } // 非阻塞等待后重试 }
关键注意事项
- 非阻塞重试逻辑:所有
BIO_do_connect、BIO_write、BIO_read、BIO_do_handshake操作在非阻塞下可能返回<=0,必须通过BIO_should_retry判断是否重试,同时用select/poll监听套接字状态。 - 授权编码正确性:
Proxy-Authorization: Basic后的字符串是username:password的Base64编码,建议用OpenSSL的EVP_EncodeBlock生成,不要手动硬编码。 - 响应完整性:读取代理响应时要确保拿到完整的HTTP头(直到
\r\n\r\n),避免因部分响应导致后续逻辑出错。 - BIO链管理:后续所有读写操作都要通过最上层的SSL BIO执行,释放资源时要正确清理整个BIO链。
内容的提问来源于stack exchange,提问作者Tommy Lin
相关产品推荐
相关产品推荐

