rustls / tokio-rustls

Async TLS for the Tokio runtime
Apache License 2.0
126 stars 70 forks source link

Add connect_with_stream method #75

Closed radioactiveAHM closed 5 months ago

radioactiveAHM commented 5 months ago

This function is similar to connect_with, but it also provides streams (usually TCP streams) as mutable to the callback function. This feature is essential for scenarios where manual TLS client hello fragmenting is necessary, as demonstrated in the following example.

        let tls_conn = h2tls_connector.connect_with_stream(example_com, tcp, |tls, tcp| {
            // Buffer to store TLS Client Hello
            let mut buff = Vec::with_capacity(1024 * 4);
            let mut cur = std::io::Cursor::new(&mut buff);
            // Write TLS Client Hello to Buffer
            let l = tls.write_tls(&mut cur).unwrap();
            let psize = (l - 5) / 2;
            let xtls = [
                [&[22, 3, 1, 0, buff[5..psize].len() as u8], &buff[5..psize]].concat(),
                [&[22, 3, 1, 0, buff[psize..].len() as u8], &buff[psize..]].concat(),
            ]
            .concat();
            tokio::task::block_in_place(move || {
                tokio::runtime::Handle::current().block_on(async {
                    tcp.write(&xtls).await.unwrap();
                });
            });
        })
        .await;
cpu commented 5 months ago

This feature is essential for scenarios where manual TLS client hello fragmenting is necessary

What kind of scenario necessitates this? Something related to bypassing naive DPI?

radioactiveAHM commented 5 months ago

This feature is essential for scenarios where manual TLS client hello fragmenting is necessary

What kind of scenario necessitates this? Something related to bypassing naive DPI?

I use this method to bypass the Great Firewall (GFW) censorship, which specifically targets the Server Name Indication (SNI). When I fragment the TLS ClientHello packet, the GFW no longer detects my SNI. This approach is my sole solution for evading GFW restrictions, especially since Rustls does not yet support Encrypted Client Hello (ECH) or Encrypted SNI (ESNI).

cpu commented 5 months ago

Thanks for providing more detail.

I can't speak for the other maintainers but I'm a bit skeptical about accepting this change since the use-case seems quite narrow and is unlikely to be long-term stable. The GFW may mishandle fragmented client hello messages today but I expect that will change over time as more handshakes start to naturally fragment (e.g. due to the situation described in tldr.fail).

Rustls does not yet support Encrypted Client Hello (ECH) or Encrypted SNI (ESNI).

Our ECH implementation (https://github.com/rustls/rustls/pull/1718) is quickly approaching merge-ability. IMO that seems like a better route towards accomplishing your overall goal.

radioactiveAHM commented 5 months ago

Thanks for providing more detail.

I can't speak for the other maintainers but I'm a bit skeptical about accepting this change since the use-case seems quite narrow and is unlikely to be long-term stable. The GFW may mishandle fragmented client hello messages today but I expect that will change over time as more handshakes start to naturally fragment (e.g. due to the situation described in tldr.fail).

Rustls does not yet support Encrypted Client Hello (ECH) or Encrypted SNI (ESNI).

Our ECH implementation (https://github.com/rustls/rustls/pull/1718) is quickly approaching merge-ability. IMO that seems like a better route towards accomplishing your overall goal.

You're welcome.

You’re absolutely correct, and there are indeed other use cases. This method serves as a straightforward way to prevent someone, including myself, from encountering issues in the future. Additionally, it proves valuable for early data. At present, my application relies heavily on this approach.

cpu commented 5 months ago

You’re absolutely correct, and there are indeed other use cases.

What sort of other use cases are you thinking of?

Additionally, it proves valuable for early data.

Can you expand on that? I'm not sure I see the connection.

quininer commented 5 months ago

You don't really need it, you can access the internals using .get_mut after .connect_with. even for your case, using block_on is not the right way.