2024-08-01 11:05:39 +03:00
|
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
|
|
use tokio_rustls::rustls::{
|
|
|
|
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
|
|
|
|
ClientConfig, SignatureScheme,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> Result<(), tokio_gemini::LibError> {
|
|
|
|
let mut args = std::env::args();
|
|
|
|
let mut insecure = false;
|
|
|
|
let mut url = "gemini://geminiprotocol.net/".to_owned();
|
2024-08-05 15:09:59 +03:00
|
|
|
_ = args.next(); // skip exe path
|
|
|
|
if let Some(arg) = args.next() {
|
2024-08-01 11:05:39 +03:00
|
|
|
if arg == "-k" {
|
|
|
|
insecure = true;
|
2024-08-05 15:09:59 +03:00
|
|
|
if let Some(arg) = args.next() {
|
2024-08-01 11:05:39 +03:00
|
|
|
url = arg;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
url = arg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let client = if insecure {
|
|
|
|
tokio_gemini::Client::from(get_insecure_config())
|
|
|
|
} else {
|
|
|
|
tokio_gemini::Client::default()
|
|
|
|
};
|
|
|
|
let mut resp = client.request(&url).await?;
|
|
|
|
{
|
|
|
|
let status_code = resp.status().status_code();
|
|
|
|
let status_num: u8 = status_code.into();
|
|
|
|
eprintln!("{} {:?}", status_num, status_code);
|
|
|
|
}
|
|
|
|
if resp.status().reply_type() == tokio_gemini::ReplyType::Success {
|
|
|
|
let mime = resp.mime()?;
|
|
|
|
eprintln!("Mime: {}", mime);
|
|
|
|
let mut buf = [0u8, 128];
|
|
|
|
let body = resp.body();
|
|
|
|
if mime.type_() == mime::TEXT {
|
|
|
|
loop {
|
|
|
|
let n = body.read(&mut buf).await?;
|
|
|
|
if n == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
print!("{}", std::str::from_utf8(&buf[..n])?);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
eprintln!("Downloading into content.bin");
|
|
|
|
let mut f = tokio::fs::File::create("content.bin").await?;
|
|
|
|
loop {
|
|
|
|
let n = body.read(&mut buf).await?;
|
|
|
|
if n == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
f.write_all(&buf[..n]).await?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
eprintln!("Message: {}", resp.message());
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_insecure_config() -> ClientConfig {
|
|
|
|
ClientConfig::builder()
|
|
|
|
.dangerous()
|
|
|
|
.with_custom_certificate_verifier(std::sync::Arc::new(NoCertVerification {}))
|
|
|
|
.with_no_client_auth()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct NoCertVerification;
|
|
|
|
|
|
|
|
impl ServerCertVerifier for NoCertVerification {
|
|
|
|
fn verify_server_cert(
|
|
|
|
&self,
|
|
|
|
end_entity: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
|
|
|
|
intermediates: &[tokio_rustls::rustls::pki_types::CertificateDer<'_>],
|
|
|
|
server_name: &tokio_rustls::rustls::pki_types::ServerName<'_>,
|
|
|
|
ocsp_response: &[u8],
|
|
|
|
now: tokio_rustls::rustls::pki_types::UnixTime,
|
|
|
|
) -> Result<ServerCertVerified, tokio_rustls::rustls::Error> {
|
|
|
|
Ok(ServerCertVerified::assertion())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_tls12_signature(
|
|
|
|
&self,
|
|
|
|
message: &[u8],
|
|
|
|
cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
|
|
|
|
dss: &tokio_rustls::rustls::DigitallySignedStruct,
|
|
|
|
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
|
|
|
|
Ok(HandshakeSignatureValid::assertion())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_tls13_signature(
|
|
|
|
&self,
|
|
|
|
message: &[u8],
|
|
|
|
cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
|
|
|
|
dss: &tokio_rustls::rustls::DigitallySignedStruct,
|
|
|
|
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
|
|
|
|
Ok(HandshakeSignatureValid::assertion())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
|
|
|
|
vec![
|
|
|
|
SignatureScheme::ECDSA_NISTP256_SHA256,
|
|
|
|
SignatureScheme::ECDSA_NISTP384_SHA384,
|
|
|
|
SignatureScheme::ECDSA_NISTP521_SHA512,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|