如何在不执行DNS查找的情况下创建rustls连接
使用rustls直接通过IP地址发起HTTPS请求
核心实现流程
rustls初始化ClientConnection需要ServerName标识目标服务,但你可以手动指定IP对应的ServerName,同时直接建立TCP连接到该IP,跳过DNS解析步骤。以下是完整的分步实现:
1. 准备rustls客户端配置
首先需要加载系统根证书,用于验证服务器的HTTPS证书,这是安全通信的必要环节:
use rustls::ClientConfig; use rustls::RootCertStore; use rustls_native_certs::load_native_certs; use std::sync::Arc; // 加载系统内置的根证书集合 fn load_root_certs() -> RootCertStore { let mut root_store = RootCertStore::empty(); for cert in load_native_certs().expect("无法加载系统根证书") { root_store.add(&rustls::Certificate(cert.0)).unwrap(); } root_store } // 创建rustls客户端配置实例 let root_store = load_root_certs(); let config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let rc_config = Arc::new(config);
2. 配置目标IP与ServerName
根据服务器的证书要求,有两种ServerName设置方式:
场景1:服务器使用IP绑定的证书
直接将IP地址转换为ServerName:use std::net::IpAddr; use rustls::ServerName; // 替换为你的目标IP地址 let target_ip: IpAddr = "192.0.2.1".parse().unwrap(); let server_name = ServerName::IpAddress(target_ip);场景2:服务器需要SNI域名(多数域名证书场景)
即使连接IP,部分服务器要求SNI字段携带原域名才能返回正确证书,此时ServerName用域名,TCP连接到IP:use rustls::ServerName; // 替换为目标域名 let server_name = "example.com".try_into().unwrap(); // 替换为对应的目标IP let target_ip: IpAddr = "192.0.2.1".parse().unwrap();
3. 建立TCP连接并初始化TLS会话
手动创建TcpStream连接到目标IP的HTTPS端口(默认443),再将其与rustls的ClientConnection绑定:
use std::net::TcpStream; use rustls::Stream; // 建立TCP连接到目标IP的443端口 let tcp_stream = TcpStream::connect((target_ip, 443)).expect("TCP连接失败"); // 初始化rustls客户端TLS连接 let mut client_conn = rustls::ClientConnection::new(rc_config, server_name).expect("TLS连接初始化失败"); // 将TCP流包装为加密的TLS流 let mut tls_stream = Stream::new(&mut client_conn, tcp_stream);
4. 发送HTTP请求并读取响应
通过TLS流发送标准HTTP请求,然后读取服务器响应:
use std::io::{Read, Write}; // 构造GET请求,注意Host字段需匹配服务器要求(场景2需填对应域名) let request = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"; tls_stream.write_all(request).expect("请求发送失败"); tls_stream.flush().expect("流刷新失败"); // 读取并打印服务器响应 let mut response = Vec::new(); tls_stream.read_to_end(&mut response).expect("响应读取失败"); println!("{}", String::from_utf8_lossy(&response));
关键注意事项
- 证书验证匹配:如果服务器用的是IP证书,必须用场景1的
ServerName::IpAddress;如果是域名证书,必须用场景2的SNI域名设置,否则会触发证书验证错误。 - 依赖补充:需要在
Cargo.toml中添加以下依赖:[dependencies] rustls = "0.21" rustls-native-certs = "0.6"
内容的提问来源于stack exchange,提问作者0__
相关产品推荐
相关产品推荐

