Compare commits

...

3 Commits

Author SHA1 Message Date
xfnw 06dbd0178c add flag to automatically send PINGs on inactivity 2024-04-25 19:58:21 -04:00
xfnw dad739d253 exit instead of returning when shutting down
this avoids pointlessly waiting for uncancelable tasks to finish
2024-04-25 19:58:21 -04:00
xfnw b87588f53d make arguments more clear 2024-04-25 19:58:10 -04:00
2 changed files with 21 additions and 12 deletions

View File

@ -10,7 +10,7 @@ license = "MIT"
[dependencies]
clap = { version = "4.5.2", default-features = false, features = ["derive", "std", "help", "usage"] }
rustls-pemfile = "2.1.1"
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "net", "io-util", "io-std"]}
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "net", "time", "io-util", "io-std"]}
tokio-rustls = { version = "0.25.0", default-features = false, features = ["ring", "tls12"] }
tokio-socks = "0.5.1"

View File

@ -1,8 +1,9 @@
use clap::Parser;
use std::{io::Write, net::SocketAddr, path::PathBuf, sync::Arc};
use std::{io::Write, net::SocketAddr, path::PathBuf, process::exit, sync::Arc, time::Duration};
use tokio::{
io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader},
net::TcpStream,
time::sleep,
};
use tokio_rustls::{
rustls::{self, pki_types},
@ -24,16 +25,20 @@ struct Opt {
insecure: bool,
/// connect with a tls client certificate
#[arg(short = 'c', long)]
#[arg(short, long)]
cert: Option<PathBuf>,
#[arg(long, default_value = "/etc/ssl/cert.pem")]
cafile: PathBuf,
/// connect via socks5 proxy
#[arg(short = 's', long)]
#[arg(short, long, value_name = "ADDRESS")]
socks: Option<SocketAddr>,
/// send pings after inactivity
#[arg(short, long, value_name = "SECS")]
ping: Option<u64>,
#[arg(required = true)]
host: String,
@ -126,14 +131,14 @@ async fn handle_tls<T: io::AsyncReadExt + io::AsyncWriteExt + std::marker::Unpin
} else {
let mut root_cert_store = rustls::RootCertStore::empty();
let mut pem = std::io::BufReader::new(
std::fs::File::open(opt.cafile).expect("cannot open cafile"),
std::fs::File::open(&opt.cafile).expect("cannot open cafile"),
);
for cert in rustls_pemfile::certs(&mut pem) {
root_cert_store.add(cert.unwrap()).unwrap();
}
config.with_root_certificates(root_cert_store)
};
let config = if let Some(cert) = opt.cert {
let config = if let Some(ref cert) = opt.cert {
use rustls_pemfile::Item;
let mut pem = std::io::BufReader::new(
@ -159,20 +164,21 @@ async fn handle_tls<T: io::AsyncReadExt + io::AsyncWriteExt + std::marker::Unpin
config.with_no_client_auth()
};
let connector = TlsConnector::from(Arc::new(config));
let domain = pki_types::ServerName::try_from(opt.host)
let domain = pki_types::ServerName::try_from(opt.host.as_str())
.expect("invalid server name")
.to_owned();
let tlsstream = connector
.connect(domain, stream)
.await
.expect("failed to negotiate tls");
handle_irc(tlsstream).await;
handle_irc(opt, tlsstream).await;
} else {
handle_irc(stream).await;
handle_irc(opt, stream).await;
}
}
async fn handle_irc<T: io::AsyncReadExt + io::AsyncWriteExt>(stream: T) {
async fn handle_irc<T: io::AsyncReadExt + io::AsyncWriteExt>(opt: Opt, stream: T) {
let pingdelay = Duration::from_secs(opt.ping.unwrap_or(0));
let (read, mut write) = io::split(stream);
let mut read = BufReader::new(read);
let mut stdin = BufReader::new(io::stdin());
@ -183,7 +189,7 @@ async fn handle_irc<T: io::AsyncReadExt + io::AsyncWriteExt>(stream: T) {
tokio::select! {
Ok(len) = stdin.read_until(b'\n', &mut stdbuf) => {
if len == 0 {
return;
exit(0);
}
trim_mut(&mut stdbuf);
@ -196,7 +202,7 @@ async fn handle_irc<T: io::AsyncReadExt + io::AsyncWriteExt>(stream: T) {
}
Ok(len) = read.read_until(b'\n', &mut ircbuf) => {
if len == 0 {
return;
exit(0);
}
if let Some(pong) = is_pong(&ircbuf) {
@ -209,6 +215,9 @@ async fn handle_irc<T: io::AsyncReadExt + io::AsyncWriteExt>(stream: T) {
ircbuf.clear();
}
() = sleep(pingdelay), if opt.ping.is_some() => {
write.write_all(b"PING :boop\r\n").await.expect("cannot send");
}
}
}
}