Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:34:54.051Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

proxy.rs

93 lines · 3.5 KB · rust
1//! Proxied connections for the collaboration WebSocket.
2//!
3//! Proxy URLs come from settings or the environment (see
4//! `ProxySettings::proxy_url`); this module dials the proxy, wraps the
5//! connection in TLS when an `https://` proxy asks for it, and tunnels
6//! through it with `proxy_handshake`.
7
8use anyhow::{Context as _, Result};
9use http_client::Url;
10use proxy_handshake::{ProxyScheme, ProxySpec, Target};
11use tokio::net::TcpStream;
12
13pub(crate) trait AsyncReadWrite:
14    tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static
15{
16}
17impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static> AsyncReadWrite
18    for T
19{
20}
21
22/// Whether `NO_PROXY` in the environment excludes `host` from proxying,
23/// matching the exclusions the HTTP client already applies to its own
24/// requests.
25pub(crate) fn excluded_from_proxy(host: &str) -> bool {
26    http_client::read_no_proxy_from_env()
27        .is_some_and(|no_proxy| proxy_handshake::no_proxy_matches(&no_proxy, host))
28}
29
30pub(crate) async fn connect_proxy_stream(
31    proxy: &Url,
32    rpc_host: (&str, u16),
33) -> Result<Box<dyn AsyncReadWrite>> {
34    // If parsing the proxy URL fails, we must avoid falling back to an
35    // insecure connection. Proxies are often used in contexts where security
36    // and privacy are critical, so any fallback could expose users to
37    // significant risks.
38    let spec = ProxySpec::parse(proxy).context("parsing proxy URL")?;
39
40    let target = if spec.remote_dns() {
41        Target::Domain(rpc_host.0.to_string(), rpc_host.1)
42    } else {
43        // SOCKS4 requests carry a raw IPv4 address, so the target must
44        // resolve to one.
45        let requires_ipv4 = matches!(spec.scheme, ProxyScheme::Socks4 { .. });
46        let address = tokio::net::lookup_host(rpc_host)
47            .await
48            .with_context(|| format!("failed to lookup domain {}", rpc_host.0))?
49            .find(|address| !requires_ipv4 || address.is_ipv4())
50            .with_context(|| format!("failed to lookup domain {}", rpc_host.0))?;
51        Target::Address(address)
52    };
53
54    let stream = TcpStream::connect((spec.host.as_str(), spec.port))
55        .await
56        .context("Failed to connect to proxy")?;
57
58    let stream: Box<dyn AsyncReadWrite> = if spec.tls() {
59        Box::new(connect_tls_to_proxy(stream, &spec.host).await?)
60    } else {
61        Box::new(stream)
62    };
63
64    let stream = proxy_handshake::tokio::establish(stream, &spec, &target)
65        .await
66        .context("error connecting through proxy")?;
67    Ok(Box::new(stream))
68}
69
70#[cfg(any(target_os = "windows", target_os = "macos"))]
71async fn connect_tls_to_proxy(
72    stream: TcpStream,
73    proxy_domain: &str,
74) -> Result<tokio_native_tls::TlsStream<TcpStream>> {
75    use tokio_native_tls::{TlsConnector, native_tls};
76
77    let tls_connector = TlsConnector::from(native_tls::TlsConnector::new()?);
78    Ok(tls_connector.connect(proxy_domain, stream).await?)
79}
80
81#[cfg(not(any(target_os = "windows", target_os = "macos")))]
82async fn connect_tls_to_proxy(
83    stream: TcpStream,
84    proxy_domain: &str,
85) -> Result<tokio_rustls::client::TlsStream<TcpStream>> {
86    let proxy_domain = rustls_pki_types::ServerName::try_from(proxy_domain)
87        .context("invalid DNS name for proxy TLS")?
88        .to_owned();
89    let tls_connector =
90        tokio_rustls::TlsConnector::from(std::sync::Arc::new(http_client_tls::tls_config()));
91    Ok(tls_connector.connect(proxy_domain, stream).await?)
92}
93
Served at tenant.openagents/omega Member data and write actions are omitted.