如何创建rustls连接而不进行DNS查找

huangapple go评论73阅读模式
英文:

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.

[source]

答案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() -&gt; Arc&lt;rustls::ClientConfig&gt; {
    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 = &quot;example.com&quot;;
    let server_name = host
        .try_into()
        .expect(&quot;Could not parse host into server name&quot;);
    let mut tlsclient = TlsClient::new(sock, server_name, config);
    let httpreq = format!(
        &quot;GET / HTTP/1.0\r\nHost: {}\r\nConnection: \
                           close\r\nAccept-Encoding: identity\r\n\r\n&quot;,
        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(&amp;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&lt;rustls::ClientConfig&gt;,
    ) -&gt; 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(&amp;mut self, ev: &amp;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!(&quot;Connection closed&quot;);
            process::exit(if self.clean_closure { 0 } else { 1 });
        }
    }

    pub fn read_source_to_end(&amp;mut self, rd: &amp;mut dyn io::Read) -&gt; io::Result&lt;usize&gt; {
        let mut buf = Vec::new();
        let len = rd.read_to_end(&amp;mut buf)?;
        self.tls_conn.writer().write_all(&amp;buf).unwrap();
        Ok(len)
    }

    /// We&#39;re ready to do a read.
    pub fn do_read(&amp;mut self) {
        // Read TLS data.  This fails if the underlying TCP connection
        // is broken.
        match self.tls_conn.read_tls(&amp;mut self.socket) {
            Err(error) =&gt; {
                if error.kind() == io::ErrorKind::WouldBlock {
                    return;
                }
                println!(&quot;TLS read error: {:?}&quot;, error);
                self.closing = true;
                return;
            }

            // If we&#39;re ready but there&#39;s no data: EOF.
            Ok(0) =&gt; {
                println!(&quot;EOF&quot;);
                self.closing = true;
                self.clean_closure = true;
                return;
            }

            Ok(_) =&gt; {}
        };

        // 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) =&gt; io_state,
            Err(err) =&gt; {
                println!(&quot;TLS error: {:?}&quot;, 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() &gt; 0 {
            let mut plaintext = Vec::new();
            plaintext.resize(io_state.plaintext_bytes_to_read(), 0u8);
            self.tls_conn.reader().read_exact(&amp;mut plaintext).unwrap();
            io::stdout().write_all(&amp;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(&amp;mut self) {
        self.tls_conn.write_tls(&amp;mut self.socket).unwrap();
    }

    /// Registers self as a &#39;listener&#39; in mio::Registry
    pub fn register(&amp;mut self, registry: &amp;mio::Registry) {
        let interest = self.event_set();
        registry
            .register(&amp;mut self.socket, CLIENT, interest)
            .unwrap();
    }

    /// Reregisters self as a &#39;listener&#39; in mio::Registry.
    pub fn reregister(&amp;mut self, registry: &amp;mio::Registry) {
        let interest = self.event_set();
        registry
            .reregister(&amp;mut self.socket, CLIENT, interest)
            .unwrap();
    }

    /// Use wants_read/wants_write to register for different mio-level
    /// IO readiness events.
    pub fn event_set(&amp;self) -&gt; mio::Interest {
        let rd = self.tls_conn.wants_read();
        let wr = self.tls_conn.wants_write();

        if rd &amp;&amp; wr {
            mio::Interest::READABLE | mio::Interest::WRITABLE
        } else if wr {
            mio::Interest::WRITABLE
        } else {
            mio::Interest::READABLE
        }
    }

    pub fn is_closed(&amp;self) -&gt; bool {
        self.closing
    }
}
impl io::Write for TlsClient {
    fn write(&amp;mut self, bytes: &amp;[u8]) -&gt; io::Result&lt;usize&gt; {
        self.tls_conn.writer().write(bytes)
    }

    fn flush(&amp;mut self) -&gt; io::Result&lt;()&gt; {
        self.tls_conn.writer().flush()
    }
}

impl io::Read for TlsClient {
    fn read(&amp;mut self, bytes: &amp;mut [u8]) -&gt; io::Result&lt;usize&gt; {
        self.tls_conn.reader().read(bytes)
    }
}

huangapple
  • 本文由 发表于 2023年5月28日 19:00:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76351137.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定