HTTPS and DNS on the PS5: rustls + ring, getrandom shim

- ps5-std-rt: getrandom() over arc4random_buf (FreeBSD 12+ API the
  getrandom crate needs for ring/rustls)
- std-examples: C code in crates builds with prospero-clang/prospero-ar
  (CC_/AR_x86_64-sie-ps5-std); ring had silently used the host gcc
- std-examples/https_probe: DNS via std getaddrinfo, HTTPS GET with
  rustls + ring + webpki-roots, results on port 9959

Tested on CFI-1215A, fw 13.60: DNS resolves; https://example.com 200 and
https://www.rust-lang.org 301 over TLS 1.3 with verified certificates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yadiel Abner Rodriguez Jorge (neokuze)andClaude Opus 5.5 committed 2026-10-16 19:21:18 -04:00
1 parent bdcff9799c
commit e43531da68
7 files changed
+340 -5

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
[package]
name = "https_probe"
version.workspace = true
edition.workspace = true
license.workspace = true
publish = false
[dependencies]
ps5 = { path = "../../crates/ps5", features = ["notify"] }
ps5-std-rt = { path = "../../crates/ps5-std-rt" }
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
webpki-roots = "1"
+96
View File
@@ -0,0 +1,96 @@
//! Diagnostic for DNS and HTTPS: resolves a few names with std
//! (getaddrinfo), then does an HTTPS GET with rustls + ring, verifying the
//! certificate against the Mozilla roots. Results on port 9959
//! (curl http://<ps5-ip>:9959), then exits. Only makes outgoing requests.
use ps5::Toast;
use ps5_std_rt as _;
use std::io::{Read, Write};
use std::net::{TcpStream, ToSocketAddrs};
use std::sync::Arc;
use std::time::{Duration, Instant};
fn toast(msg: &str) {
let _ = Toast::new(msg).sub("https_probe").send();
}
fn main() {
std::panic::set_hook(Box::new(|info| {
let msg = info.payload().downcast_ref::<&str>().map(|s| s.to_string())
.or_else(|| info.payload().downcast_ref::<String>().cloned())
.unwrap_or_default();
let at = info.location().map(|l| format!(" @ {}:{}", l.file(), l.line())).unwrap_or_default();
toast(&format!("panic: {msg}{at}"));
}));
toast("started");
let mut out = Vec::new();
for host in ["example.com:443", "github.com:443", "localhost:80", "192.168.1.34:8080"] {
let t = Instant::now();
out.push(match host.to_socket_addrs() {
Ok(addrs) => {
let v: Vec<String> = addrs.map(|a| a.ip().to_string()).collect();
format!("ok dns {host}: {} ({:?})", v.join(" "), t.elapsed())
}
Err(e) => format!("FAIL dns {host}: {e:?}"),
});
}
for (host, path) in [("example.com", "/"), ("www.rust-lang.org", "/")] {
out.push(match https_get(host, path) {
Ok(v) => format!("ok https {host}{path}: {v}"),
Err(e) => format!("FAIL https {host}{path}: {e}"),
});
}
let fails = out.iter().filter(|l| l.starts_with("FAIL")).count();
serve(&out, fails);
}
fn https_get(host: &str, path: &str) -> Result<String, String> {
let t = Instant::now();
let roots = rustls::RootCertStore { roots: webpki_roots::TLS_SERVER_ROOTS.to_vec() };
let config = rustls::ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
.with_safe_default_protocol_versions()
.map_err(|e| format!("config: {e}"))?
.with_root_certificates(roots)
.with_no_client_auth();
let name = host.to_owned().try_into().map_err(|e| format!("name: {e:?}"))?;
let mut conn = rustls::ClientConnection::new(Arc::new(config), name).map_err(|e| format!("tls: {e}"))?;
let mut sock = TcpStream::connect((host, 443)).map_err(|e| format!("connect: {e}"))?;
sock.set_read_timeout(Some(Duration::from_secs(10))).ok();
let mut tls = rustls::Stream::new(&mut conn, &mut sock);
write!(tls, "GET {path} HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\nUser-Agent: ps5-rust\r\n\r\n")
.map_err(|e| format!("write: {e}"))?;
let mut body = Vec::new();
match tls.read_to_end(&mut body) {
Ok(_) => {}
// Some servers close without close_notify; we already have the data.
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof && !body.is_empty() => {}
Err(e) => return Err(format!("read: {e}")),
}
let status = String::from_utf8_lossy(&body).lines().next().unwrap_or("").to_owned();
let suite = conn.negotiated_cipher_suite().map(|s| format!("{:?}", s.suite())).unwrap_or_default();
let version = conn.protocol_version().map(|v| format!("{v:?}")).unwrap_or_default();
Ok(format!("{status} | {} bytes | {version} {suite} | {:?}", body.len(), t.elapsed()))
}
fn serve(lines: &[String], fails: usize) {
let listener = match std::net::TcpListener::bind("0.0.0.0:9959") {
Ok(l) => l,
Err(err) => return toast(&format!("bind {err}")),
};
toast(&format!("{fails} FAIL - results on port 9959"));
if let Ok((mut s, _)) = listener.accept() {
let mut req = [0u8; 1024];
let _ = s.read(&mut req);
let body = lines.join("\n") + "\n";
let _ = write!(
s,
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
body.len()
);
toast("results sent");
}
}