英文:
How to create rustls connection without performing a dns lookup
问题
我已经拥有一个域名的IP地址,但我想执行HTTPS请求。我选择了rustls,然而它的文档只显示了一个示例,该示例(可能)执行DNS查找。我已经知道我希望指向的IP地址。所以我想打开到IP的连接,使用rustls执行必要的步骤,然后发出HTTP请求(GET / HTTP/1.1等)。
从rustls文档中摘录:
let rc_config = Arc::new(config);
let example_com = "example.com".try_into().unwrap();
let mut client = rustls::ClientConnection::new(rc_config, example_com);
我认为我只需在更下面调用接受TcpStream或类似的东西的函数即可。
英文:
I already have the IP for a domain but I want to perform an HTTPS requests. I opted to go with rustls, however its documentation only shows an example which (presumably) performs a DNS lookup. I already know the IP I wish to point to. So I want to open the connection to the IP, do the necessary steps with rustls and then issue the HTTP request (GET / HTTP/1.1, etc.)
exerpt from the rustls docs:
let rc_config = Arc::new(config);
let example_com = "example.com".try_into().unwrap();
let mut client = rustls::ClientConnection::new(rc_config, example_com);
I assume I can just call something further down the line that accepts a TcpStream or something.
答案1
得分: 1
以下是代码的翻译部分:
use std::{
io::Write,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
sync::Arc,
};
use crate::tcp_wrapper::TlsClient;
fn make_config() -> Arc<rustls::ClientConfig> {
let mut root_store = rustls::RootCertStore::empty();
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
}));
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth();
Arc::new(config)
}
fn main() {
let port = 443;
// let addr = lookup_ipv4(args.arg_hostname.as_str(), port);
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(93, 184, 216, 34), 443));
let config = make_config();
let sock = mio::net::TcpStream::connect(addr).unwrap();
let host = "example.com";
let server_name = host
.try_into()
.expect("Could not parse host into server name");
let mut tlsclient = TlsClient::new(sock, server_name, config);
let httpreq = format!(
"GET / HTTP/1.0\r\nHost: {}\r\nConnection: \
close\r\nAccept-Encoding: identity\r\n\r\n",
host
);
tlsclient.write_all(httpreq.as_bytes()).unwrap();
let mut poll = mio::Poll::new().unwrap();
let mut events = mio::Events::with_capacity(32);
tlsclient.register(poll.registry());
loop {
poll.poll(&mut events, None).unwrap();
for ev in events.iter() {
tlsclient.ready(ev);
tlsclient.reregister(poll.registry());
}
}
}
tcp_wrapper.rs
:
use std::process;
use std::sync::Arc;
use mio::net::TcpStream;
use std::io;
use std::io::{Read, Write};
const CLIENT: mio::Token = mio::Token(0);
/// This encapsulates the TCP-level connection, some connection
/// state, and the underlying TLS-level session.
pub struct TlsClient {
socket: TcpStream,
closing: bool,
clean_closure: bool,
tls_conn: rustls::ClientConnection,
}
impl TlsClient {
pub fn new(
sock: TcpStream,
server_name: rustls::ServerName,
cfg: Arc<rustls::ClientConfig>,
) -> Self {
Self {
socket: sock,
closing: false,
clean_closure: false,
tls_conn: rustls::ClientConnection::new(cfg, server_name).unwrap(),
}
}
/// Handles events sent to the TlsClient by mio::Poll
pub fn ready(&mut self, ev: &mio::event::Event) {
assert_eq!(ev.token(), CLIENT);
if ev.is_readable() {
self.do_read();
}
if ev.is_writable() {
self.do_write();
}
if self.is_closed() {
println!("Connection closed");
process::exit(if self.clean_closure { 0 } else { 1 });
}
}
pub fn read_source_to_end(&mut self, rd: &mut dyn io::Read) -> io::Result<usize> {
let mut buf = Vec::new();
let len = rd.read_to_end(&mut buf)?;
self.tls_conn.writer().write_all(&buf).unwrap();
Ok(len)
}
/// We're ready to do a read.
pub fn do_read(&mut self) {
// Read TLS data. This fails if the underlying TCP connection
// is broken.
match self.tls_conn.read_tls(&mut self.socket) {
Err(error) => {
if error.kind() == io::ErrorKind::WouldBlock {
return;
}
println!("TLS read error: {:?}", error);
self.closing = true;
return;
}
// If we're ready but there's no data: EOF.
Ok(0) => {
println!("EOF");
self.closing = true;
self.clean_closure = true;
return;
}
Ok(_) => {}
};
// Reading some TLS data might have yielded new TLS
// messages to process. Errors from this indicate
// TLS protocol problems and are fatal.
let io_state = match self.tls_conn.process_new_packets() {
Ok(io_state) => io_state,
Err(err) => {
println!("TLS error: {:?}", err);
self.closing = true;
return;
}
};
// Having read some TLS data, and processed any new messages,
// we might have new plaintext as a result.
//
// Read it and then write it to stdout.
if io_state.plaintext_bytes_to_read() > 0 {
let mut plaintext = Vec::new();
plaintext.resize(io_state.plaintext_bytes_to_read(), 0u8);
self.tls_conn.reader().read_exact(&mut plaintext).unwrap();
io::stdout().write_all(&plaintext).unwrap();
}
// If we that fails, the peer might have started a clean TLS-level
// session closure.
if io_state.peer_has_closed() {
self.clean_closure = true;
self.closing = true;
}
}
pub fn do_write(&mut self) {
self.tls_conn.write_tls(&mut self.socket).unwrap();
}
/// Registers self as a 'listener' in mio::Registry
pub fn register(&mut self, registry: &mio::Registry) {
let interest = self.event_set();
registry
.register(&mut self.socket, CLIENT, interest)
.unwrap();
}
/// Reregisters self as a 'listener' in mio::Registry.
pub fn reregister(&mut self, registry: &mio::Registry) {
let interest = self.event_set();
registry
.reregister(&mut self.socket, CLIENT, interest)
.unwrap();
}
/// Use wants_read/wants_write to register for different mio-level
/// IO readiness events.
pub fn event_set(&self) -> mio::Interest {
let rd = self.tls_conn.wants_read();
let wr = self.tls_conn.wants_write();
if rd && wr {
mio::Interest::READABLE | mio::Interest::WRITABLE
} else if wr {
mio::Interest::WRITABLE
} else {
mio::Interest::READABLE
}
}
pub fn is_closed(&self) -> bool {
self.closing
}
}
impl io::Write for TlsClient {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.t
<details>
<summary>英文:</summary>
There is an [example][1] of this in the rustls documentation. It is a CLI full CLI program, so here is a condensed version of that example that connects to `example.com` directly.
```rust
use std::{
io::Write,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
sync::Arc,
};
use crate::tcp_wrapper::TlsClient;
fn make_config() -> Arc<rustls::ClientConfig> {
let mut root_store = rustls::RootCertStore::empty();
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
}));
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth();
Arc::new(config)
}
fn main() {
let port = 443;
// let addr = lookup_ipv4(args.arg_hostname.as_str(), port);
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(93, 184, 216, 34), 443));
let config = make_config();
let sock = mio::net::TcpStream::connect(addr).unwrap();
let host = "example.com";
let server_name = host
.try_into()
.expect("Could not parse host into server name");
let mut tlsclient = TlsClient::new(sock, server_name, config);
let httpreq = format!(
"GET / HTTP/1.0\r\nHost: {}\r\nConnection: \
close\r\nAccept-Encoding: identity\r\n\r\n",
host
);
tlsclient.write_all(httpreq.as_bytes()).unwrap();
let mut poll = mio::Poll::new().unwrap();
let mut events = mio::Events::with_capacity(32);
tlsclient.register(poll.registry());
loop {
poll.poll(&mut events, None).unwrap();
for ev in events.iter() {
tlsclient.ready(ev);
tlsclient.reregister(poll.registry());
}
}
}
tcp_wrapper.rs
:
use std::process;
use std::sync::Arc;
use mio::net::TcpStream;
use std::io;
use std::io::{Read, Write};
const CLIENT: mio::Token = mio::Token(0);
/// This encapsulates the TCP-level connection, some connection
/// state, and the underlying TLS-level session.
pub struct TlsClient {
socket: TcpStream,
closing: bool,
clean_closure: bool,
tls_conn: rustls::ClientConnection,
}
impl TlsClient {
pub fn new(
sock: TcpStream,
server_name: rustls::ServerName,
cfg: Arc<rustls::ClientConfig>,
) -> Self {
Self {
socket: sock,
closing: false,
clean_closure: false,
tls_conn: rustls::ClientConnection::new(cfg, server_name).unwrap(),
}
}
/// Handles events sent to the TlsClient by mio::Poll
pub fn ready(&mut self, ev: &mio::event::Event) {
assert_eq!(ev.token(), CLIENT);
if ev.is_readable() {
self.do_read();
}
if ev.is_writable() {
self.do_write();
}
if self.is_closed() {
println!("Connection closed");
process::exit(if self.clean_closure { 0 } else { 1 });
}
}
pub fn read_source_to_end(&mut self, rd: &mut dyn io::Read) -> io::Result<usize> {
let mut buf = Vec::new();
let len = rd.read_to_end(&mut buf)?;
self.tls_conn.writer().write_all(&buf).unwrap();
Ok(len)
}
/// We're ready to do a read.
pub fn do_read(&mut self) {
// Read TLS data. This fails if the underlying TCP connection
// is broken.
match self.tls_conn.read_tls(&mut self.socket) {
Err(error) => {
if error.kind() == io::ErrorKind::WouldBlock {
return;
}
println!("TLS read error: {:?}", error);
self.closing = true;
return;
}
// If we're ready but there's no data: EOF.
Ok(0) => {
println!("EOF");
self.closing = true;
self.clean_closure = true;
return;
}
Ok(_) => {}
};
// Reading some TLS data might have yielded new TLS
// messages to process. Errors from this indicate
// TLS protocol problems and are fatal.
let io_state = match self.tls_conn.process_new_packets() {
Ok(io_state) => io_state,
Err(err) => {
println!("TLS error: {:?}", err);
self.closing = true;
return;
}
};
// Having read some TLS data, and processed any new messages,
// we might have new plaintext as a result.
//
// Read it and then write it to stdout.
if io_state.plaintext_bytes_to_read() > 0 {
let mut plaintext = Vec::new();
plaintext.resize(io_state.plaintext_bytes_to_read(), 0u8);
self.tls_conn.reader().read_exact(&mut plaintext).unwrap();
io::stdout().write_all(&plaintext).unwrap();
}
// If wethat fails, the peer might have started a clean TLS-level
// session closure.
if io_state.peer_has_closed() {
self.clean_closure = true;
self.closing = true;
}
}
pub fn do_write(&mut self) {
self.tls_conn.write_tls(&mut self.socket).unwrap();
}
/// Registers self as a 'listener' in mio::Registry
pub fn register(&mut self, registry: &mio::Registry) {
let interest = self.event_set();
registry
.register(&mut self.socket, CLIENT, interest)
.unwrap();
}
/// Reregisters self as a 'listener' in mio::Registry.
pub fn reregister(&mut self, registry: &mio::Registry) {
let interest = self.event_set();
registry
.reregister(&mut self.socket, CLIENT, interest)
.unwrap();
}
/// Use wants_read/wants_write to register for different mio-level
/// IO readiness events.
pub fn event_set(&self) -> mio::Interest {
let rd = self.tls_conn.wants_read();
let wr = self.tls_conn.wants_write();
if rd && wr {
mio::Interest::READABLE | mio::Interest::WRITABLE
} else if wr {
mio::Interest::WRITABLE
} else {
mio::Interest::READABLE
}
}
pub fn is_closed(&self) -> bool {
self.closing
}
}
impl io::Write for TlsClient {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.tls_conn.writer().write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
self.tls_conn.writer().flush()
}
}
impl io::Read for TlsClient {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
self.tls_conn.reader().read(bytes)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论